Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unacknowledged command problems and recovery weaknesses #2275

Open
itsmojo opened this issue Dec 27, 2024 · 0 comments
Open

Unacknowledged command problems and recovery weaknesses #2275

itsmojo opened this issue Dec 27, 2024 · 0 comments

Comments

@itsmojo
Copy link
Contributor

itsmojo commented Dec 27, 2024

Describe the bug
By inspection of the current Loop code, there are some situations that appear to have some unrecoverable problems if an comms error resulting in a unacknowledgedCommand occurs at the wrong place. Many of these are because PodCommsSessions functions like prime(), programInitialBasalSchedule(), insertCannula() & checkInsertionCompleted all use send([GetStatusCommand()]) & instead of calling getStatus() which is the only place that handles cases where there is a pending unacknowledged command to be handled. prime() & insertCannula() use a custom bolus command and thus don't use bolus() (which can set podState.unacknowledgedCommand), but programInitialBasalSchedule() uses setBasalSchedule() which can set podState.unacknowledgedCommand and will also fail if podState.unacknowledgedCommand is non-nil upon entry.

deactivatePod() checks for podState.unacknowledgedCommand != nil after calling send([deactivatePod]), but this is too late as the deactivate pod command will change the pod's last command seq # variable that is needed to correct resolve any unacknowledged commands from an earlier insulin related command (normally from the cancelDelivery() done if the pod is not already suspended). Instead Loop should try to resolve any pending unacknowledged command immediately before doing the pod deactivation before the needed state is destroyed.

Additionally the unacknowledged command handling in configureAlerts() and acknowledgeAlerts() in Loop dev will only throw unacknowledgedCommandPending when called with a pending unacknowledged command when pod setup has completed to avoid getting in a situation where the pod setup cannot complete due of potential repeated unrecoverable unacknowledgedCommandPending errors. Instead a better way to go would be to have these routines attempt to do immediate unacknowledged command recovery if there is a pending unacknowledged command independent of whether the pod setup has completed.

And in the same vane, all other PodCommsSession functions that currently need to fail with a unacknowledgedCommandPending error when called with a pending unacknowledged command (because executing the pod command would change the pod's last programming command seq # variable needed to be able to correctly recover) should be updated to instead attempt to automatically do unacknowledged command recovery immediately and only if that still fails, return an unacknowledgedCommandPending error. This would reduce the likelihood of the user ever having to see an "Unacknowledged command pending" error for Loop to be able to correctly recover from certain uncertain unacknowledged command comms situations.

Attach an Issue Report
Here's an example section from parsing a recent report using OmniBLEParser with comments. In this case, a user got an "unacknowledged command is pending" error attempting to Disable "Silence Pod" after having the iPhone closed and left laying in one place while the user moved about between multiple rooms and an unacknowledged command situation was created by a failed auto bolus.

COMMAND:  2024-12-18 01:43:51 Message(1765099d seq:13 [CancelDeliveryCommand(nonce:494e532e, deliveryType:TempBasal, beepType:noBeepCancel)])
RESPONSE: 2024-12-18 01:43:58 Message(1765099d seq:14 [StatusResponse(deliveryStatus:Scheduled basal, progressStatus:Normal, timeActive:1h13m, reservoirLevel:50+, insulinDelivered:2.90, bolusNotDelivered:0.00, lastProgrammingMessageSeqNum:13, alerts:No alerts)])

// set 0.05 U/hr temp basal using seq:15
COMMAND:  2024-12-18 01:44:00 Message(1765099d seq:15 [SetInsulinScheduleCommand(nonce:494e532e, tempBasal(secondsRemaining: 1800, firstSegmentPulses: 0, table: BasalDeliveryTable([InsulinTableEntry(segments:1, pulses:0, alternateSegmentPulse:false)]))), TempBasalExtraCommand(completionBeep:false, programReminderInterval:0s, remainingPulses:0.5, delayUntilFirstPulse:1h, rateEntries:[RateEntry(rate:0.05, duration:30m)])])
RESPONSE: 2024-12-18 01:44:07 Message(1765099d seq:00 [StatusResponse(deliveryStatus:Temp basal running, progressStatus:Normal, timeActive:1h13m, reservoirLevel:50+, insulinDelivered:2.90, bolusNotDelivered:0.00, lastProgrammingMessageSeqNum:15, alerts:No alerts)])

// try to do a 0.3U autobolus, but fails on send with incorrectResponse error (N.B. command wasn't received)
COMMAND:  2024-12-18 01:44:10 Message(1765099d seq:01 [SetInsulinScheduleCommand(nonce:494e532e, bolus(units: 0.3, timeBetweenPulses: 2.0, table: OmniBLEParser.BolusDeliveryTable(entries: [InsulinTableEntry(segments:1, pulses:6, alternateSegmentPulse:false)]))), BolusExtraCommand(units:0.3, timeBetweenPulses:2.0, extendedUnits:0.0, extendedDuration:0.0, acknowledgementBeep:true, completionBeep:false, programReminderInterval:63.0)])
          2024-12-18 01:44:15 Unacknowledged message sending command seq:1, error = incorrectResponse

// Perhaps some place calling getPodStatus() such as ensureCurrentPumpData().
// Note that this noSeqStatus pod request has no response, so podState.unacknowledgedCommand
// is still set for the unacknowledged bolus command, seq:01
COMMAND:  2024-12-18 01:44:15 Message(1765099d seq:01 [GetStatusCommand(noSeqStatus)])

          2024-12-18 01:44:21 Pod disconnected
          2024-12-18 01:44:23 Pod connected
// Probably this error is from probably reading the response from the seq:01 [GetStatusCommand(noSeqStatus)]
// ten seconds ago -- but had a pod disconnect & reconnect in the meantime
          2024-12-18 01:44:25 Unacknowledged message reading response for sent command seq:1, error = incorrectResponse

// > > > Between 01:44:15 and 01:44:28, a "Silence Pod" was attempted that failed with "Unacknowledged command pending" << <
// This shows that in this sequence, this would have messed up the pod state to do the checking for unacknowledged bolus
// with the Silence Pod command without the current code in Loop dev that prevent this from happening
// (at least if pod setup has completed). But it would have been been nicer if Loop would have attempted
// to resolve the pending unacknowledged command automatically and only throw an "Unacknowledged
// command pending" error the pod comms and pending unacknowledged command could not be still be resolved.

          2024-12-18 01:44:25 Pod disconnected
          2024-12-18 01:44:26 Pod connected
// This session.getStatus(noSeqGetStatus: true) is probably from the Post-connect status
// This one finally succeeds and so should with lastProgrammingMessageSeqNum:15 indicates that the bolus command
// was never received and so recoverUnacknowledgedCommand() should set podState.unacknowledgedCommand = nil
// so that the next attempt to set (un)silence the pod would succeed.
COMMAND:  2024-12-18 01:44:28 Message(1765099d seq:01 [GetStatusCommand(noSeqStatus)])
RESPONSE: 2024-12-18 01:44:28 Message(1765099d seq:02 [StatusResponse(deliveryStatus:Temp basal running, progressStatus:Normal, timeActive:1h13m, reservoirLevel:50+, insulinDelivered:2.90, bolusNotDelivered:0.00, lastProgrammingMessageSeqNum:15, alerts:No alerts)])

// Successful Silence Pod command sequence on a user retry
COMMAND:  2024-12-18 01:44:36 Message(1765099d seq:03 [AcknowledgeAlertCommand(blockType:acknowledgeAlert, length:5, alerts:slot0AutoOff, slot1NotUsed, slot2ShutdownImminent, slot3ExpirationReminder, slot4LowReservoir, slot5SuspendedReminder, slot6SuspendTimeExpired, slot7Expired), ConfigureAlertsCommand(nonce:494e532e, configurations:[
AlertConfiguration(slot:slot2ShutdownImminent, trigger:triggerTime=77h47m, beepRepeat:every15Minutes, silent:true),
AlertConfiguration(slot:slot3ExpirationReminder, trigger:triggerTime=69h47m, beepRepeat:every1MinuteFor3MinutesAndRepeatEvery15Minutes, silent:true),
AlertConfiguration(slot:slot4LowReservoir, trigger:10U, beepRepeat:every1MinuteFor3MinutesAndRepeatEvery60Minutes, silent:true),
AlertConfiguration(slot:slot7Expired, duration:7h, trigger:triggerTime=70h47m, beepRepeat:every60Minutes, silent:true)]), BeepConfigCommand(beepType:noBeepNonCancel, basalIntervalBeep:0.0, tempBasalCompletionBeep:false, tempBasalIntervalBeep:0.0, bolusCompletionBeep:false, bolusIntervalBeep:0.0)])
RESPONSE: 2024-12-18 01:44:37 Message(1765099d seq:04 [StatusResponse(deliveryStatus:Temp basal running, progressStatus:Normal, timeActive:1h13m, reservoirLevel:50+, insulinDelivered:2.90, bolusNotDelivered:0.00, lastProgrammingMessageSeqNum:3, alerts:No alerts)])

To Reproduce
Very difficult to reproduce without artificially introducing fake unacknowledged command errors.

Expected behavior
I'd like to pod setup and deactivation to always work correctly without any problems even if unacknowledged command situations occur at the wrong places.

Additionally, I'd like not have to see "Unacknowledged command pending" errors if the pod has a pending unacknowledged command error when the pod is currently in range and the app could have been able to attempt an automatic recovery on its own instead of failing to protect the unacknowledged command recover code.

Screenshots
NA

Phone
NA

Loop Version
All versions current Loop 3.x versions (3.0 to 3.4.4).

CGM
NA

Pump
Omnipod Eros or DASH

Additional context
Proposed PR's which correct all the noted weaknesses for both OmniBLE and OmniKit.

https://github.com/LoopKit/OmniBLE/pull/139/files
https://github.com/LoopKit/OmniKit/pull/47/files

Use new tryToResolvePendingCommand() func that try to use getStatus() to resolve a
pending unacknowledged command in most PodCommSession functions instead of just
failing if called with a pending unacknowledged command. Reworked logic to prevent
possible incorrect unacknowledged command handling during pod setup & deactivation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant