Skip to content

Commit 79f8a97

Browse files
authored
Merge pull request #21 from daqifi/fix/merge-analog-digital-and-sd-channels
fix: merge analog/digital output rows and enable channels for SD logging
2 parents 2aa5738 + 6a94437 commit 79f8a97

2 files changed

Lines changed: 136 additions & 38 deletions

File tree

Daqifi.Core.Cli.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
</ItemGroup>
1313

1414
<ItemGroup Condition="'$(DaqifiCoreProjectPath)' == ''">
15-
<PackageReference Include="Daqifi.Core" Version="0.18.0" />
15+
<PackageReference Include="Daqifi.Core" Version="0.19.4" />
1616
</ItemGroup>
1717

1818
</Project>

Program.cs

Lines changed: 135 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,14 @@ private static async Task<int> RunStreamingSessionAsync(CliOptions options)
180180
}
181181

182182
var messageCount = 0;
183+
184+
// The device sends analog and digital data in separate protobuf
185+
// messages that share the same timestamp. We buffer the pending
186+
// analog message and merge it with the subsequent digital message
187+
// before writing a single combined output row.
188+
DaqifiOutMessage? pendingAnalog = null;
189+
var pendingLock = new object();
190+
183191
device.MessageReceived += (_, eventArgs) =>
184192
{
185193
if (stopCts.IsCancellationRequested)
@@ -192,24 +200,50 @@ private static async Task<int> RunStreamingSessionAsync(CliOptions options)
192200
return;
193201
}
194202

195-
var currentCount = Interlocked.Increment(ref messageCount);
196-
if (options.MessageLimit > 0 && currentCount > options.MessageLimit)
203+
if (options.ShowStatusMessages && ProtobufProtocolHandler.DetectMessageType(message) == ProtobufMessageType.Status)
197204
{
205+
WriteStatusSummary(outputWriter, message);
198206
return;
199207
}
200208

201-
if (IsStreamLikeMessage(message))
209+
if (!IsStreamLikeMessage(message))
202210
{
203-
WriteStreamSample(outputWriter, message, options.OutputFormat);
204-
}
205-
else if (options.ShowStatusMessages && ProtobufProtocolHandler.DetectMessageType(message) == ProtobufMessageType.Status)
206-
{
207-
WriteStatusSummary(outputWriter, message);
211+
return;
208212
}
209213

210-
if (options.MessageLimit > 0 && currentCount >= options.MessageLimit)
214+
lock (pendingLock)
211215
{
212-
stopCts.Cancel();
216+
var hasAnalog = message.AnalogInData.Count > 0 || message.AnalogInDataFloat.Count > 0;
217+
var hasDigital = message.DigitalData.Length > 0;
218+
219+
if (hasAnalog && !hasDigital)
220+
{
221+
// Flush any stale pending message before buffering the new one
222+
if (pendingAnalog != null)
223+
{
224+
WriteMergedSample(outputWriter, pendingAnalog, null, options.OutputFormat, ref messageCount, options.MessageLimit, stopCts);
225+
}
226+
227+
pendingAnalog = message;
228+
return;
229+
}
230+
231+
if (hasDigital && pendingAnalog != null && pendingAnalog.MsgTimeStamp == message.MsgTimeStamp)
232+
{
233+
// Matching pair — merge and write
234+
WriteMergedSample(outputWriter, pendingAnalog, message, options.OutputFormat, ref messageCount, options.MessageLimit, stopCts);
235+
pendingAnalog = null;
236+
return;
237+
}
238+
239+
// Digital-only with no matching analog, or timestamp mismatch
240+
if (pendingAnalog != null)
241+
{
242+
WriteMergedSample(outputWriter, pendingAnalog, null, options.OutputFormat, ref messageCount, options.MessageLimit, stopCts);
243+
pendingAnalog = null;
244+
}
245+
246+
WriteMergedSample(outputWriter, message, null, options.OutputFormat, ref messageCount, options.MessageLimit, stopCts);
213247
}
214248
};
215249

@@ -249,6 +283,16 @@ private static async Task<int> RunStreamingSessionAsync(CliOptions options)
249283
device.Send(ScpiMessageProducer.StopStreaming);
250284
Console.WriteLine("Streaming stopped.");
251285

286+
// Flush any buffered analog-only message that never got a matching digital
287+
lock (pendingLock)
288+
{
289+
if (pendingAnalog != null)
290+
{
291+
WriteMergedSample(outputWriter, pendingAnalog, null, options.OutputFormat, ref messageCount, options.MessageLimit, stopCts);
292+
pendingAnalog = null;
293+
}
294+
}
295+
252296
if (options.MinSamples > 0 && messageCount < options.MinSamples)
253297
{
254298
Console.Error.WriteLine(
@@ -663,8 +707,39 @@ private static async Task<int> RunSdCardOperationAsync(CliOptions options)
663707
{
664708
streamingDevice.StreamingFrequency = options.SampleRate;
665709

710+
// Enable channels before starting SD card logging. Core's
711+
// StartSdCardLoggingAsync only forwards the channel mask — it does
712+
// not enable channels itself. Without an explicit mask we enable all
713+
// ADC channels (the device reports AnalogInputChannels in its
714+
// capabilities after InitializeAsync) and DIO ports so the log
715+
// file is not empty.
716+
var channelMask = options.ChannelMask;
717+
if (!string.IsNullOrWhiteSpace(channelMask) && !IsValidChannelMask(channelMask))
718+
{
719+
Console.Error.WriteLine($"Invalid channel mask: {channelMask}");
720+
return 1;
721+
}
722+
723+
if (string.IsNullOrWhiteSpace(channelMask))
724+
{
725+
var adcCount = streamingDevice.Metadata.Capabilities.AnalogInputChannels;
726+
if (adcCount > 0)
727+
{
728+
channelMask = new string('1', adcCount);
729+
}
730+
}
731+
732+
if (!string.IsNullOrWhiteSpace(channelMask))
733+
{
734+
streamingDevice.Send(ScpiMessageProducer.EnableAdcChannels(channelMask));
735+
await Task.Delay(100);
736+
}
737+
738+
streamingDevice.Send(ScpiMessageProducer.EnableDioPorts());
739+
await Task.Delay(100);
740+
666741
await streamingDevice.StartSdCardLoggingAsync(
667-
channelMask: options.ChannelMask,
742+
channelMask: channelMask,
668743
format: options.SdLogFormat);
669744
Console.WriteLine("SD card logging started.");
670745

@@ -1071,20 +1146,38 @@ private static bool IsStreamLikeMessage(DaqifiOutMessage message)
10711146
message.DigitalData.Length > 0;
10721147
}
10731148

1074-
private static void WriteStreamSample(TextWriter writer, DaqifiOutMessage message, OutputFormat format)
1149+
private static void WriteMergedSample(
1150+
TextWriter writer,
1151+
DaqifiOutMessage analogMessage,
1152+
DaqifiOutMessage? digitalMessage,
1153+
OutputFormat format,
1154+
ref int messageCount,
1155+
int messageLimit,
1156+
CancellationTokenSource stopCts)
10751157
{
1158+
var currentCount = Interlocked.Increment(ref messageCount);
1159+
if (messageLimit > 0 && currentCount > messageLimit)
1160+
{
1161+
return;
1162+
}
1163+
10761164
switch (format)
10771165
{
10781166
case OutputFormat.Jsonl:
1079-
writer.WriteLine(ToJsonLine(message));
1167+
writer.WriteLine(ToJsonLine(analogMessage, digitalMessage));
10801168
break;
10811169
case OutputFormat.Csv:
1082-
writer.WriteLine(ToCsvLine(message));
1170+
writer.WriteLine(ToCsvLine(analogMessage, digitalMessage));
10831171
break;
10841172
default:
1085-
writer.WriteLine(ToTextLine(message));
1173+
writer.WriteLine(ToTextLine(analogMessage, digitalMessage));
10861174
break;
10871175
}
1176+
1177+
if (messageLimit > 0 && currentCount >= messageLimit)
1178+
{
1179+
stopCts.Cancel();
1180+
}
10881181
}
10891182

10901183
private static void WriteStatusSummary(TextWriter writer, DaqifiOutMessage message)
@@ -1094,19 +1187,19 @@ private static void WriteStatusSummary(TextWriter writer, DaqifiOutMessage messa
10941187
$"fw={message.DeviceFwRev ?? "unknown"} sn={message.DeviceSn}");
10951188
}
10961189

1097-
private static string ToTextLine(DaqifiOutMessage message)
1190+
private static string ToTextLine(DaqifiOutMessage analogMsg, DaqifiOutMessage? digitalMsg)
10981191
{
10991192
var builder = new StringBuilder();
1100-
if (message.MsgTimeStamp != 0)
1193+
if (analogMsg.MsgTimeStamp != 0)
11011194
{
11021195
builder.Append("ts=");
1103-
builder.Append(message.MsgTimeStamp.ToString(CultureInfo.InvariantCulture));
1196+
builder.Append(analogMsg.MsgTimeStamp.ToString(CultureInfo.InvariantCulture));
11041197
builder.Append(' ');
11051198
}
11061199

1107-
var analogValues = message.AnalogInDataFloat.Count > 0
1108-
? message.AnalogInDataFloat.Select(value => value.ToString("F3", CultureInfo.InvariantCulture)).ToList()
1109-
: message.AnalogInData.Select(value => value.ToString(CultureInfo.InvariantCulture)).ToList();
1200+
var analogValues = analogMsg.AnalogInDataFloat.Count > 0
1201+
? analogMsg.AnalogInDataFloat.Select(value => value.ToString("F3", CultureInfo.InvariantCulture)).ToList()
1202+
: analogMsg.AnalogInData.Select(value => value.ToString(CultureInfo.InvariantCulture)).ToList();
11101203

11111204
if (analogValues.Count > 0)
11121205
{
@@ -1119,43 +1212,48 @@ private static string ToTextLine(DaqifiOutMessage message)
11191212
builder.Append(']');
11201213
}
11211214

1122-
if (message.DigitalData.Length > 0)
1215+
// Prefer the paired digital message; fall back to analog message's own digital data
1216+
var digitalSource = digitalMsg ?? analogMsg;
1217+
if (digitalSource.DigitalData.Length > 0)
11231218
{
1124-
var digital = BitConverter.ToString(message.DigitalData.ToByteArray());
1219+
var digital = BitConverter.ToString(digitalSource.DigitalData.ToByteArray());
11251220
builder.Append(" digital=");
11261221
builder.Append(digital);
11271222
}
11281223

11291224
return builder.ToString();
11301225
}
11311226

1132-
private static string ToCsvLine(DaqifiOutMessage message)
1227+
private static string ToCsvLine(DaqifiOutMessage analogMsg, DaqifiOutMessage? digitalMsg)
11331228
{
1134-
var analogValues = message.AnalogInDataFloat.Count > 0
1135-
? message.AnalogInDataFloat.Select(value => value.ToString("F6", CultureInfo.InvariantCulture)).ToList()
1136-
: message.AnalogInData.Select(value => value.ToString(CultureInfo.InvariantCulture)).ToList();
1229+
var analogValues = analogMsg.AnalogInDataFloat.Count > 0
1230+
? analogMsg.AnalogInDataFloat.Select(value => value.ToString("F6", CultureInfo.InvariantCulture)).ToList()
1231+
: analogMsg.AnalogInData.Select(value => value.ToString(CultureInfo.InvariantCulture)).ToList();
11371232

1138-
var timestamp = message.MsgTimeStamp.ToString(CultureInfo.InvariantCulture);
1233+
var timestamp = analogMsg.MsgTimeStamp.ToString(CultureInfo.InvariantCulture);
11391234
var analog = string.Join(",", analogValues);
1140-
var digital = message.DigitalData.Length > 0
1141-
? BitConverter.ToString(message.DigitalData.ToByteArray())
1235+
1236+
var digitalSource = digitalMsg ?? analogMsg;
1237+
var digital = digitalSource.DigitalData.Length > 0
1238+
? BitConverter.ToString(digitalSource.DigitalData.ToByteArray())
11421239
: string.Empty;
11431240

11441241
return $"{timestamp},{analog},{digital}";
11451242
}
11461243

1147-
private static string ToJsonLine(DaqifiOutMessage message)
1244+
private static string ToJsonLine(DaqifiOutMessage analogMsg, DaqifiOutMessage? digitalMsg)
11481245
{
1149-
var analogValues = message.AnalogInDataFloat.Count > 0
1150-
? message.AnalogInDataFloat.Select(value => value.ToString("F6", CultureInfo.InvariantCulture)).ToList()
1151-
: message.AnalogInData.Select(value => value.ToString(CultureInfo.InvariantCulture)).ToList();
1246+
var analogValues = analogMsg.AnalogInDataFloat.Count > 0
1247+
? analogMsg.AnalogInDataFloat.Select(value => value.ToString("F6", CultureInfo.InvariantCulture)).ToList()
1248+
: analogMsg.AnalogInData.Select(value => value.ToString(CultureInfo.InvariantCulture)).ToList();
11521249

1153-
var digitalBytes = message.DigitalData.Length > 0
1154-
? BitConverter.ToString(message.DigitalData.ToByteArray())
1250+
var digitalSource = digitalMsg ?? analogMsg;
1251+
var digitalBytes = digitalSource.DigitalData.Length > 0
1252+
? BitConverter.ToString(digitalSource.DigitalData.ToByteArray())
11551253
: string.Empty;
11561254

11571255
return "{" +
1158-
$"\"ts\":{message.MsgTimeStamp.ToString(CultureInfo.InvariantCulture)}," +
1256+
$"\"ts\":{analogMsg.MsgTimeStamp.ToString(CultureInfo.InvariantCulture)}," +
11591257
$"\"analog\":[{string.Join(",", analogValues)}]," +
11601258
$"\"digital\":\"{digitalBytes}\"" +
11611259
"}";

0 commit comments

Comments
 (0)