Skip to content

Commit 7f1f52c

Browse files
tylerkronclaude
andauthored
fix: fix USB streaming — AnalogInDataFloat support and USB stream interface init (#149)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 46826d9 commit 7f1f52c

4 files changed

Lines changed: 93 additions & 8 deletions

File tree

src/Daqifi.Core.Tests/Device/Protocol/ProtobufProtocolHandlerTests.cs

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -122,20 +122,55 @@ public async void HandleAsync_WithNonProtobufMessage_DoesNotCallHandlers()
122122
Assert.False(streamHandlerCalled);
123123
}
124124

125+
[Fact]
126+
public async Task HandleAsync_WithFloatStreamMessage_CallsStreamHandler()
127+
{
128+
// Arrange - USB firmware sends pre-scaled float values (AnalogInDataFloat)
129+
var streamHandlerCalled = false;
130+
DaqifiOutMessage receivedMessage = null;
131+
132+
var handler = new ProtobufProtocolHandler(
133+
streamMessageHandler: msg =>
134+
{
135+
streamHandlerCalled = true;
136+
receivedMessage = msg;
137+
});
138+
139+
var streamMessage = new DaqifiOutMessage
140+
{
141+
MsgTimeStamp = 99999
142+
};
143+
streamMessage.AnalogInDataFloat.Add(1.234f);
144+
streamMessage.AnalogInDataFloat.Add(2.345f);
145+
146+
var inboundMessage = new GenericInboundMessage<object>(streamMessage);
147+
148+
// Act
149+
await handler.HandleAsync(inboundMessage);
150+
151+
// Assert
152+
Assert.True(streamHandlerCalled, "Stream handler should be called for AnalogInDataFloat messages");
153+
Assert.NotNull(receivedMessage);
154+
Assert.Equal(99999u, receivedMessage.MsgTimeStamp);
155+
Assert.Equal(2, receivedMessage.AnalogInDataFloat.Count);
156+
}
157+
125158
[Theory]
126-
[InlineData(8u, 0u, 0u, 0u, 0, 0, ProtobufMessageType.Status)]
127-
[InlineData(0u, 16u, 0u, 0u, 0, 0, ProtobufMessageType.Status)]
128-
[InlineData(0u, 0u, 2u, 0u, 0, 0, ProtobufMessageType.Status)]
129-
[InlineData(0u, 0u, 0u, 12345u, 1, 0, ProtobufMessageType.Stream)]
130-
[InlineData(0u, 0u, 0u, 12345u, 0, 1, ProtobufMessageType.Stream)]
131-
[InlineData(0u, 0u, 0u, 0u, 0, 0, ProtobufMessageType.Unknown)]
159+
[InlineData(8u, 0u, 0u, 0u, 0, 0, false, ProtobufMessageType.Status)]
160+
[InlineData(0u, 16u, 0u, 0u, 0, 0, false, ProtobufMessageType.Status)]
161+
[InlineData(0u, 0u, 2u, 0u, 0, 0, false, ProtobufMessageType.Status)]
162+
[InlineData(0u, 0u, 0u, 12345u, 1, 0, false, ProtobufMessageType.Stream)] // int data
163+
[InlineData(0u, 0u, 0u, 12345u, 0, 1, false, ProtobufMessageType.Stream)] // digital data
164+
[InlineData(0u, 0u, 0u, 12345u, 0, 0, true, ProtobufMessageType.Stream)] // float data (USB firmware)
165+
[InlineData(0u, 0u, 0u, 0u, 0, 0, false, ProtobufMessageType.Unknown)]
132166
public void DetectMessageType_ReturnsCorrectType(
133167
uint analogInPortNum,
134168
uint digitalPortNum,
135169
uint analogOutPortNum,
136170
uint msgTimeStamp,
137171
int analogDataCount,
138172
int digitalDataLength,
173+
bool hasFloatData,
139174
ProtobufMessageType expectedType)
140175
{
141176
// Arrange
@@ -157,6 +192,11 @@ public void DetectMessageType_ReturnsCorrectType(
157192
message.DigitalData = Google.Protobuf.ByteString.CopyFrom(new byte[digitalDataLength]);
158193
}
159194

195+
if (hasFloatData)
196+
{
197+
message.AnalogInDataFloat.Add(1.5f);
198+
}
199+
160200
// Act
161201
var result = ProtobufProtocolHandler.DetectMessageType(message);
162202

src/Daqifi.Core/Communication/Consumers/StreamMessageConsumer.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ public bool StopSafely(int timeoutMs = 1000)
101101
_isRunning = false;
102102
var stopped = _consumerThread?.Join(timeoutMs) ?? true;
103103
_consumerThread = null;
104+
_messageBuffer.Clear();
104105

105106
return stopped;
106107
}

src/Daqifi.Core/Device/DaqifiStreamingDevice.cs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using Daqifi.Core.Communication;
12
using Daqifi.Core.Communication.Messages;
23
using Daqifi.Core.Communication.Producers;
34
using Daqifi.Core.Communication.Transport;
@@ -101,6 +102,45 @@ public DaqifiStreamingDevice(string name, IStreamTransport transport) : base(nam
101102
StreamingFrequency = 100;
102103
}
103104

105+
/// <summary>
106+
/// Initializes the device with the standard SCPI sequence and, for USB/serial connections,
107+
/// additionally sets the streaming interface to USB so data is routed to the serial consumer
108+
/// rather than to a previously-configured WiFi destination.
109+
/// </summary>
110+
/// <remarks>
111+
/// The DAQiFi firmware persists the last configured stream interface across sessions.
112+
/// If the device was previously set to stream to WiFi (<c>SYSTem:STReam:INTerface 1</c>),
113+
/// it will continue sending data over WiFi even when connected via USB — causing the serial
114+
/// consumer to receive nothing. Sending <c>SYSTem:STReam:INTerface 0</c> during USB
115+
/// initialization ensures data flows to the serial port.
116+
/// </remarks>
117+
/// <returns>A task representing the asynchronous initialization operation.</returns>
118+
public override async Task InitializeAsync()
119+
{
120+
// Capture pre-init state so we can skip the USB step on repeated calls
121+
// (base.InitializeAsync() guards with _isInitialized and returns early on repeats).
122+
var needsUsbInit = IsUsbConnection && State != DeviceState.Ready;
123+
124+
await base.InitializeAsync();
125+
126+
if (needsUsbInit)
127+
{
128+
// Direct streaming to the USB interface. Uses ExecuteTextCommandAsync so the
129+
// command is sent in text mode (protobuf consumer temporarily stopped) and any
130+
// SCPI error response is captured rather than garbling the protobuf stream.
131+
var lines = await ExecuteTextCommandAsync(
132+
() => Send(ScpiMessageProducer.SetStreamInterface(StreamInterface.Usb)),
133+
responseTimeoutMs: 500,
134+
cancellationToken: CancellationToken.None);
135+
136+
if (ContainsScpiError(lines))
137+
{
138+
throw new InvalidOperationException(
139+
"Device returned a SCPI error while setting stream interface to USB.");
140+
}
141+
}
142+
}
143+
104144
/// <summary>
105145
/// Starts streaming data from the device at the configured frequency.
106146
/// </summary>

src/Daqifi.Core/Device/Protocol/ProtobufProtocolHandler.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,8 +135,12 @@ private static bool IsStatusMessage(DaqifiOutMessage message)
135135
/// <returns><c>true</c> if the message contains streaming data; otherwise, <c>false</c>.</returns>
136136
private static bool IsStreamMessage(DaqifiOutMessage message)
137137
{
138-
// Stream messages contain timestamp and data
138+
// Stream messages contain timestamp and data.
139+
// USB firmware sends AnalogInDataFloat (pre-scaled floats) while WiFi firmware
140+
// may send AnalogInData (raw integer ADC counts). Both must be detected.
139141
return message.MsgTimeStamp != 0 &&
140-
(message.AnalogInData.Count > 0 || message.DigitalData.Length > 0);
142+
(message.AnalogInData.Count > 0 ||
143+
message.AnalogInDataFloat.Count > 0 ||
144+
message.DigitalData.Length > 0);
141145
}
142146
}

0 commit comments

Comments
 (0)