fix(mcp): re-validate the live sample rate when a channel-configure call shrinks the cap - #472
Conversation
…all shrinks the cap set_sample_rate's device-cap guard was set-time only: configure_analog_channels and configure_digital_channels refreshed CapabilityStreaming.CurrentMaximumRateHz after every channel change, but never re-checked the rate already running against the new cap. Widening the channel set could leave StreamingFrequency above the device's cap for that set — a value the guard would reject outright if re-requested — while ConfigureResult echoed it back as if nothing were wrong. The firmware's response to an over-cap rate is silent: it refuses with "Data out of range" and streams zero samples, no exception, no ErrorOccurred. - Extract the cap arithmetic (device cap, hardware ceiling, optional --max-sample-rate-hz clamp; live-rate-vs-cap enforcement) into a pure, directly-testable SampleRateCapCalculator. - ConfigureAnalogChannelsAsync/ConfigureDigitalChannelsAsync now re-validate the live rate against the refreshed cap after every channel change, lowering it when it no longer fits and reporting the adjustment via the new SampleRateAdjustedFromHz field on ConfigureResult/ConfigureDigitalResult. A cap of 0 (nothing enabled) leaves the rate alone rather than driving it to 0. - set_sample_rate's 0-cap rejection now says to enable a channel first, instead of reading like the requested rate was too high. - StartLoggingAsync re-checks the live rate against the cap as a use-time backstop, since that is the point an out-of-range rate actually reaches the firmware. - New SampleRateCapCalculatorTests cover the bench-measured 7746 Hz -> 3518 Hz reorder trap from the issue, the 0-cap non-flooring behavior, and the server-option/device-cap interaction. Fixes #447
PR Summary by QodoRevalidate live sample rate after channel configuration lowers device cap
AI Description
Diagram
High-Level Assessment
Files changed (5)
|
Code Review by Qodo
1.
|
|
/agentic_review |
|
Code review by qodo was updated up to the latest commit 9b5a0c8 |
…ive cap Qodo review on #472: CapabilityDocumentParser.ReadInt accepts any int32, so a malformed capability document with a negative current_max_rate_hz produced a negative effective cap. That was handled inconsistently: SetSampleRateAsync misreported it as 'no channels enabled' (cap <= 0), and StartLoggingAsync's over-cap backstop was disabled outright, since that check only fires when cap > 0 - silently defeating the exact safety net this PR added. Treat a negative currentMaxRateHz the same as null (not reported) and fall back to the hardware ceiling, consistent with how the parser's other fields already treat out-of-contract values as absent rather than a signal.
|
/agentic_review |
|
Code review by qodo was updated up to the latest commit e8cc987 |
Summary
set_sample_rate's device-cap guard (CapabilityStreaming.CurrentMaximumRateHz, refreshed after every channel-configuration call) only validates the rate at the moment it is set.configure_analog_channels/configure_digital_channelsalready refresh the cap, but never re-check the rate that is already live against it — so widening the channel set can leaveStreamingFrequencyabove the new, lower cap for that channel set.Fixes #447.
Bench evidence from the issue (not re-run here — no device on hand)
After that third call:
ConfigureResultechoed back{"enabledAnalogChannels":[0..15], "sampleRateHz": 7746}— a rate the guard itself would reject if re-requested (set_sample_rate(7746)→"exceeds the maximum 3518 Hz"). The firmware's response to an over-cap rate is silent:-222,"Data out of range"and zero samples, no exception, noErrorOccurred.Fix
Following the issue's suggested approach:
SampleRateCapCalculator(ComputeCapHz/EnforceCap), used by bothSetSampleRateAsyncand the two configure calls.ConfigureAnalogChannelsAsync/ConfigureDigitalChannelsAsyncnow re-validate the live rate against the refreshed cap after every channel change. When it no longer fits, it's lowered to the cap and the adjustment is reported via a newSampleRateAdjustedFromHzfield onConfigureResult/ConfigureDigitalResult(nullwhen no adjustment was needed) — so the agent is told, not silently overridden.0(nothing enabled) leaves the rate alone rather than driving it to0, per the issue's guidance.set_sample_ratewith a0cap now says to enable a channel first, instead of the generic "exceeds the maximum 0 Hz" message that reads as "you asked for too much."StartLoggingAsyncre-checks the live rate against the cap as a use-time backstop — the point an out-of-range rate would actually reach the firmware — throwing instead of letting a logging session come back with silently zero samples.configure_analog_channels/configure_digital_channelsnow mention the auto-adjustment andsampleRateAdjustedFromHz.Testing
No device on hand for this pass, so I leaned on making the fix's logic directly unit-testable rather than requiring a fake connected-device harness (that infrastructure is #465's job):
SampleRateCapCalculatorTests(new, 11 cases) — reproduces the exact 7746 Hz → 3518 Hz reorder trap from the issue, the 0-cap non-flooring behavior, hardware-max flooring/bounding, and the--max-sample-rate-hzinteraction.dotnet test Daqifi.Core.sln— full suite green: 2853 Core tests (2 skipped real-hardware, unchanged) + 34 Mcp tests (23 existing + 11 new).dotnet build Daqifi.Core.sln— 0 warnings, 0 errors.🤖 Generated with Claude Code