feat: distinguish SD card error states in GetSdCardFilesAsync#182
Conversation
GetSdCardFilesAsync silently swallowed SCPI errors and returned an empty list, making any failure in the device-side LIST path indistinguishable from "the SD card is empty." A real-world corrupt card on firmware 3.4.4 sent multiple debugging sessions chasing phantom firmware issues before someone thought to swap the card. Add a typed exception hierarchy and classify the response after the existing retry loop so callers can show actionable detail: - SdCardNotPresentException — "No SD Card Detected" - SdCardFilesystemException — "Failed to open directory" with the device's raw error text - SdCardOperationException — base/catch-all for SCPI errors with no more specific marker (RawDeviceResponse + LastScpiError) - SdCardBusyException — defined for completeness (firmware cannot distinguish busy from timeout on the wire) When file lines are present, behavior is unchanged: stray interleaved error lines are still ignored and the list is returned. The empty directory case (no errors, no content) likewise still returns an empty list rather than throwing. Verified on hardware: happy path returns files; pulled card now throws SdCardNotPresentException instead of returning 0 files. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Review Summary by QodoDistinguish SD card error states with typed exceptions
WalkthroughsDescription• Add typed exception hierarchy for SD card operations - SdCardOperationException base class with RawDeviceResponse and LastScpiError - SdCardNotPresentException for missing SD card - SdCardFilesystemException for corrupt/unreadable filesystem - SdCardBusyException for busy device (defined for completeness) • Implement error classification in GetSdCardFilesAsync after retry loop - Specific firmware markers take precedence over generic checks - Empty directories still return empty list (behavior preserved) - File lines present with interleaved errors still return files (behavior preserved) • Update XML documentation for ISdCardOperations and DaqifiStreamingDevice • Expand test coverage with 7 new test cases for error scenarios Diagramflowchart LR
A["GetSdCardFilesAsync"] --> B["Retry loop"]
B --> C["ThrowIfSdCardListError"]
C --> D1["No SD Card Detected"]
C --> D2["Failed to open directory"]
C --> D3["Generic SCPI error"]
C --> D4["Empty directory"]
D1 --> E1["SdCardNotPresentException"]
D2 --> E2["SdCardFilesystemException"]
D3 --> E3["SdCardOperationException"]
D4 --> E4["Return empty list"]
File Changes1. src/Daqifi.Core/Device/SdCard/SdCardOperationException.cs
|
Code Review by Qodo
1.
|
Address two issues raised by Qodo on PR #182: 1. LastScpiError previously could be populated with firmware status text like "Error !! ..." because IsScpiErrorLine matched any "ERROR..." prefix. Tighten to require **ERROR or ERROR: (with the colon) so only true SCPI-formatted lines are surfaced as LastScpiError; non-SCPI text remains in RawDeviceResponse. 2. Consolidate ContainsScpiError to delegate to IsScpiErrorLine so retry trigger and classification share a single predicate. Add IsNonResultLine for permissive content-line detection (catches both SCPI errors and firmware status text), used by hasContentLine and a new defensive fallback for the hypothetical case where the firmware emits status text without an accompanying SCPI error. Two new tests cover the strict LastScpiError behavior and the firmware-text-only fallback path. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
Thanks @qodo-code-review — both findings addressed in d0dda48. Bug #1 — Also added a defensive fallback in Bug #2 — Duplicate predicate: Agreed. New tests cover both behaviors:
Full suite still green: 880 passing on net9.0 + net10.0. |
Summary
Closes #181.
GetSdCardFilesAsyncsilently swallowed SCPI errors and returned an empty list, making real failures (no card, corrupt FS, busy SD subsystem) indistinguishable from "directory is empty." A corrupt SD card on firmware 3.4.4 sent multiple debug sessions chasing phantom firmware bugs before someone thought to swap the card.This adds a typed exception hierarchy and classifies the response after the existing retry loop so callers can show actionable detail.
What changed
Daqifi.Core.Device.SdCard:SdCardOperationException— base; carriesRawDeviceResponseandLastScpiErrorSdCardNotPresentException— "No SD Card Detected"SdCardFilesystemException— "Failed to open directory" with the device's raw error text inDeviceMessageSdCardBusyException— defined for completeness (firmware cannot distinguish busy from timeout on the wire, so this currently surfaces as the base type)DaqifiStreamingDevice.GetSdCardFilesAsyncnow calls a privateThrowIfSdCardListErrorclassifier after the retry loop. Specific firmware markers take precedence over the generic content/error check; an empty directory still returns an empty list.ISdCardOperationsxmldoc updated to document the new exceptions.Behavior preserved
**ERRORlines → returns parsed files (unchanged).finallyblock on the throwing path.Hardware verification
Tested on
/dev/cu.usbmodem2101via the example app:SdCardNotPresentException(previously silently returned 0 files)SD:LISt?response at the firmware level (i.e. firmware mounted it and saw 0 files), so theSdCardFilesystemExceptionand bare-**ERRORpaths are exercised by unit tests against the firmware wire formats from the issue.Test plan
SdCardOperationException🤖 Generated with Claude Code