Repository navigation
fix(channels): a device that reports channels already enabled leaves Start Logging permanently disabled #165
Description
Parity gap #1 from the 2026-09-01 audit of upstream b469ca1b..36995fed. Full ranked list: #141.
What a user can do in the WPF app that they cannot do here
In daqifi-desktop, connecting a device leaves every channel tile inactive, so the user picks channels and presses Start Logging.
Here, on a device whose firmware reports a non-empty enabled-channel mask, every analog tile renders active the moment the first status message lands, "Select All" does nothing, no channel is ever subscribed, and Start Logging stays disabled with no way for the user to enable it. The app cannot log data at all until the device is put back into a state where it reports nothing enabled.
Upstream hit exactly this (their issue #811) and fixed it in d801c348.
Why it happens here
Four things line up, all confirmed by hand:
| file | what it does | |
|---|---|---|
| 1 | Daqifi.Avalonia/Daqifi.Desktop/Device/AbstractStreamingDevice.cs (Connect, ~:412) |
calls OnCoreDeviceInitialized() and returns — no adoption step |
| 2 | Daqifi.Avalonia/Daqifi.Desktop/Channel/AnalogChannel.cs:86 |
IsActive => _coreChannel.IsEnabled — reads straight through to Core |
| 3 | Daqifi.Avalonia/Daqifi.Desktop/ViewModels/ChannelsPaneViewModel.cs (SelectAll) |
tiles.Where(t => !t.IsActive) — skips tiles already reporting active |
| 4 | Daqifi.Avalonia/Daqifi.Desktop/ViewModels/DaqifiViewModel.cs:1801 |
CanToggleLogging = ActiveChannels.Count > 0, fed from LoggingManager.SubscribedChannels |
Core 1.7.0 resyncs analog IsEnabled from analog_in_port_enabled on every status message, so (2) is live, not a one-shot.
This is worse here than it ever was upstream: upstream added the guard for Core 1.4.0, and this app pins 1.7.0 without it.
Upstream's fix
daqifi-desktop/Daqifi.Desktop/Device/AbstractStreamingDevice.cs:
AdoptDeviceChannelSet(coreDevice)— called at:382, immediately afterInitializeAsync(); defined:485. IfcoreDevice.Channels.Count(c => c.IsEnabled) > 0, it callscoreDevice.DisableAllChannels()and logs how many it cleared.IsConnectionFatal(coreDevice, ex)—:528, used as an exception filter so a device merely refusing the command is survivable (the user can still pick by hand) while a dropped transport propagates andConnect()correctly returns false rather than handingConnectionManagera device that is already gone.
Read both in full before porting — the exception-filter shape is load-bearing, not stylistic.
Trigger, honestly stated
Not reproduced on the bench. The DAQiFi Nq1 at /dev/cu.usbmodem1101 on firmware 3.7.2 reported enabledAnalogChannels: [] immediately after connect — before and after enabling three channels and reconnecting. So on that board today the mask does not survive a reconnect and the failure does not appear. The missing guard is confirmed by reading the code; the device state that triggers it is one this board did not present. Whoever picks this up should find a device or firmware that does persist the mask, or reproduce it by injection.
Acceptance criterion
Connecting a device whose Core channel set reports one or more channels already enabled leaves the app's active set empty: every tile inactive, SubscribedChannels empty, Select All enables all of them, and Start Logging becomes available once at least one is selected. A device that refuses DisableAllChannels logs a warning and still connects; a device whose transport dropped during the attempt fails the connection as it does today.
Platform matrix
Desktop, Android, iOS — all three. Nothing here is platform-conditional; it is on the shared connect path.
Notes for whoever takes it
AnalogChannel.ReplaceCoreChannel(:174) has awasEnabledsave/restore that looks like it preserves the app's enable intent and does not — it reads back whatever Core just resynced from the device mask. Do not read it and conclude this is already handled.- Upstream dropped
ReplaceCoreChannelentirely in05f0b8e1; that is a separate, lower-priority cleanup (see Upstream sync digest — daqifi-desktop -> port #141).
Activity
tylerkron commentedon Sep 1, 2026
Correction: the trigger DOES reproduce on the bench Nq1. This issue's "Trigger, honestly stated" section says it did not; that was a false negative, and PR #174 has the A/B.
The audit's probe read the enabled mask through a different path and saw
enabledAnalogChannels: []after a reconnect. Driving the app's own connect path (SerialStreamingDevice.Connect()) against the same board at/dev/cu.usbmodem1101(fw 3.7.2), under the board lock, the mask does come back:AdoptDeviceChannelSetin the DLL): enable AI0–AI2, disconnect, reconnect →After Connect(): 32 channels, 3 active [AI0, AI1, AI2], with nothing subscribed toLoggingManager. That is exactly the state in which Select All is a no-op and Start Logging never becomes available.After Connect(): 32 channels, 0 active [], and the app log carriesCleared 3 channel(s) the device reported as already enabled …. Confirmed twice, ~4 minutes apart.The reason a single probe was not enough: on this board the mask is not reliably empty after a reconnect — it is empty on some reconnects and carries the previous session's set on others. So the audit's observation was real, just not general. Worth remembering for the next "could not reproduce" on this hardware.
Everything else in the issue — the four lining-up causes, the upstream fix, the acceptance criterion — held up exactly as written.