refactor(device): extract the session snapshot/restore decision into a collaborator (part of #344) - #441
Conversation
…a collaborator (part of #344) Moves the #379 reconnect snapshot out of DaqifiStreamingDevice into Device/Internal/StreamingSessionSnapshot: capturing a session and working out what restoring it implies are pure operations over a channel list, and they were sitting in the middle of the device class as a private nested type plus two long methods. Same decision/effect split #439 used for SessionCommandInterpreter. The device keeps the effects and their ordering — clearing IsStreaming, disabling everything first, sending the enable mask, restarting the stream — because those are the only parts it actually owns. No interface change, no constructor wiring, no new field. The enabled set is now private to the snapshot instead of an exposed HashSet, so identity matching can't be bypassed by a caller. DaqifiStreamingDevice.cs: 1196 -> 1148 lines. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
/agentic_review |
PR Summary by QodoRefactor device reconnect snapshot/restore into StreamingSessionSnapshot collaborator
AI Description
Diagram
High-Level Assessment
Files changed (3)
|
Code Review by Qodo🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0)
Great, no issues found!Qodo reviewed your code and found no material issues that require reviewTo customize comments, go to the Qodo configuration screen, or learn more in the docs. |
|
Ready for review: Qodo re-review at |
Part of #344 — not merging, this is for review.
Problem
DaqifiStreamingDevicestill carried the whole #379 reconnect-snapshot mechanism inline: a private nestedStreamingSessionSnapshotclass,CaptureSessionSnapshot, and aRestoreSessionSnapshotAsyncthat mixed deciding what to restore with applying it. That's ~120 lines of the god-class doing work that is really just set arithmetic over a channel list.Fix
New
Device/Internal/StreamingSessionSnapshot(with aSessionRestorePlanresult), following the exact decision/effect split #439 established forSessionCommandInterpreter:IsStreaming, the two cancellation checks,DisableAllChannels()before re-applying,EnableChannels(...), andStartStreaming().Deliberately host-free — no
IDeviceOperationHostmember added, no new field, no constructor wiring. That keeps the public surface and the internal seam untouched, and it keeps this slice's hunks disjoint from the still-open #440.One small tightening that came with the move: the enabled set is now private to the snapshot (
EnabledChannelCountis all that's exposed) instead of a publicly-readableHashSet, so identity matching can't be bypassed.DaqifiStreamingDevice.cs: 1196 → 1148 lines.Evidence this changed nothing
DeviceReconnectTestsis left completely untouched on purpose — all 44 of its tests are the "the extraction changed nothing" evidence, and editing them would weaken that.Tests: +13 new
StreamingSessionSnapshotTests. FULL suite green on net9 + net10 — 2667 passed / 2 skipped each (+23Daqifi.Mcp.Testson net9; Mcp is net9-only), 0 warnings.Bench (real Nq1, fw 3.7.2, USB, non-destructive — 12/12 checks passed). A physical unplug isn't available unattended, so instead of skipping hardware validation the harness drove the protected capture/restore members directly on a live device and tore the session down the way re-initialization does:
Send(mask3@ 200 Hz) — 395 samples each on Analog0/Analog1 over 2.5 s.CaptureSessionSnapshot(), thenStopStreaming()+DisableAllChannels(): 0 channels enabled, 0 samples arriving.RestoreSessionSnapshotAsync(ResumeStreaming: true)→ returnedtrue,IsStreamingback totrue, exactlyAnalog0,Analog1re-enabled, and 395 samples each flowing again with nothing on any other channel. That last part is the bit a mock can't show: the plan handed back the device's live channel objects, so the enable mask actually reached the firmware.RestoreSessionSnapshotAsync(ResumeStreaming: false)→ returnedfalse, channels restored,IsStreamingstayedfalse, and 0 samples — the stream really was not restarted.(158 Hz effective vs 200 Hz requested is the known fw 3.7.2 clock mismatch, not a regression.)
closes nothing on its own — part of #344.