feat(device): FeatureNotSupportedException + v3.5.0 floor + -113 backstop on GetSdCardStorageAsync - #288
Conversation
…stop on GetSdCardStorageAsync Implements ADR 0001's primary near-term deliverable (closes #254): a DeviceFeature enum, a typed FeatureNotSupportedException, and a MinSupportedFirmware (v3.5.0) floor + live IsFirmwareVersionSupported helper on DaqifiDevice. GetSdCardStorageAsync now parses the SCPI error code and throws FeatureNotSupportedException on -113 "Undefined header" instead of the generic SdCardOperationException, so old-firmware devices get a clear, typed signal rather than a generic error. Verified on hardware (Nyquist1, firmware v3.6.2): connect/stream/ GetSdCardStorageAsync happy paths are unaffected, and IsFirmwareVersionSupported correctly reports true against the real firmware version string. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
PR Summary by QodoAdd firmware feature-gating exception and -113 handling for SD storage query
AI Description
Diagram
High-Level Assessment
Files changed (8)
|
Code Review by Qodo
Context used 1.
|
- TryParseScpiErrorCode now accepts ':', space, or tab as the delimiter after the ERROR/**ERROR token, matching the delimiters ScpiResponseClassifier already treats as valid — a space-delimited "**ERROR -113, ..." line was previously falling through to the generic SdCardOperationException instead of the typed FeatureNotSupportedException. - GetSdCardStorageAsync now passes null (not DeviceType.Unknown) as the exception's Board when the device type hasn't been reported yet, since Board is documented as "if known". - Clarify DeviceFeature.SdStorageQuery's doc comment: the command's actual introduction version (v3.4.6b1) differs from the version the exception reports (MinSupportedFirmware, v3.5.0) — intentional, per ADR 0001's example code, since the v3.5.0 floor is what daqifi-core actually guarantees. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Summary
DeviceFeatureenum (AnalogOutput,SdStorageQuery,CapabilityDocument) and a typedFeatureNotSupportedException(Feature,RequiredVersion,ActualVersion,Board), per ADR 0001's proposed API shape.DaqifiDevice.MinSupportedFirmware(v3.5.0) constant and a live-evaluatedIsFirmwareVersionSupportedproperty (readsMetadata.FirmwareVersionfresh on every access — not cached, per the ADR's "don't cache version-derived flags" rule).GetSdCardStorageAsyncnow parses the numeric SCPI error code out of the existing**ERROR: <code>, "<msg>"line and, on-113("Undefined header"), throwsFeatureNotSupportedExceptioninstead of the genericSdCardOperationException. This is a deliberate behavior change flagged in the ADR: old-firmware failures on this command move from a generic exception to a distinct typed one — callers catchingSdCardOperationExceptionbroadly will need to also catchFeatureNotSupportedException.-113still retries once (like today's-200case) before translating.Implements the primary near-term deliverable from ADR 0001 (
docs/adr/0001-firmware-feature-gating.md, #252). Closes #254.Testing
dotnet build -c Release: 0 warnings, 0 errors (TreatWarningsAsErrors).dotnet test(net9.0 + net10.0): 1340 passed, 0 failed, 2 skipped (real-hardware-only tests).FeatureNotSupportedExceptionconstruction/message,DaqifiDevice.IsFirmwareVersionSupported(unknown/unparseable/below-floor/at-or-above-floor/live-reevaluation), andGetSdCardStorageAsyncthrowingFeatureNotSupportedExceptionon a persistent-113(withFeature/RequiredVersion/ActualVersion/Boardpopulated from device metadata)./dev/cu.usbmodem1101): connect + short stream andGetSdCardStorageAsync()happy paths are unaffected by this change, andIsFirmwareVersionSupportedcorrectly reportstrueagainst the real reported firmware string. The-113path itself couldn't be exercised on this device since its firmware is above the v3.5.0 floor — that wire format was already empirically verified in fix(scpi)!: send SYST:STORage:SD:FILE for SD logging (firmware v3.5.0+) #253.Test plan
dotnet build -c Release— 0 warnings, 0 errorsdotnet test— 1340 passed, 0 failed, 2 skippedFeatureNotSupportedException(no such device on hand for this PR)🤖 Generated with Claude Code