LoopKit / Loop

An automated insulin delivery app for iOS, built on LoopKit
https://loopdocs.org
Other
1.51k stars 1.3k forks source link

Total amount of bolus being delivered is incorrect in HUD #2195

Open elnjensen opened 4 months ago

elnjensen commented 4 months ago

Describe the bug In some circumstances, when a bolus is in progress, the total amount to be delivered in the HUD (i.e. the Y in the string "Bolused X of Y U") is incorrect, and may be smaller than the amount already delivered.

This was initially mentioned in a thread in Zulipchat from Sept. 30, 2023, and what seems to be the same issue was recently reported on July 20, 2024 in a thread on Facebook.

Inthe former case, it was determined that the total (Y) amount displayed was actually the amount of the previous bolus that had been delivered.

Attach an Issue Report Issue report and critical event log are copied here from the above-referenced Zulip thread. Efforts are underway to get reports from the new case as well.

Loop-Report-2023-09-30-093206-0700.md Export-20230930T163243Z.zip

To Reproduce Steps to reproduce are currently unknown.

Screenshots

Loop Version

bastiaanv commented 4 months ago

I havent seen this issue with the Dana pumps, so maybe a OmniBLE issue?

The total bolus amount is fetched from the status property of the PumpManager https://github.com/LoopKit/Loop/blob/dev/Loop/View%20Controllers/StatusTableViewController.swift#L720

So whenever the pumpManager calls func pumpManager(_ pumpManager: PumpManager, didUpdate status: PumpManagerStatus, oldStatus: PumpManagerStatus), the PumpManagerStatus.bolusState determines provides the total bolus amount and I believe OmniBLE provides the previous value/ hasn't been able to update the dose of the bolus state, which causes this visual bug.

@itsmojo Just a suggestion as for where to look for :)

rolandet commented 4 months ago

Same issue that I reported back in December 2023 with the Dev branch. Hopefully this new Issue Report will get fixed and not auto-closed.

https://github.com/LoopKit/Loop/issues/2109

elnjensen commented 4 months ago

@rolandet Thanks for connecting this - I did a brief search for existing issues but maybe didn't have the right terms. I see I used the same "X of Y" terminology- maybe I subconsciously remembered your description. 🙂 Good to have more data on this.

itsmojo commented 4 months ago
// 0.20 auto bolus start at 16:30:04 which should finish at 16:30:04 + (4 pulses * 2 sec/pulse) = 16:30:12
COMMAND:  2023-09-30 16:30:03 Message(175c3dea seq:12 [SetInsulinScheduleCommand(nonce:494e532e, bolus(units: 0.2, timeBetweenPulses: 2.0, table: OmniBLEParser.BolusDeliveryTable(entries: [InsulinTableEntry(segments:1, pulses:4, alternateSegmentPulse:false)]))), BolusExtraCommand(units:0.2, timeBetweenPulses:2.0, extendedUnits:0.0, extendedDuration:0.0, acknowledgementBeep:false, completionBeep:false, programReminderInterval:63.0)])
RESPONSE: 2023-09-30 16:30:04 Message(175c3dea seq:13 [StatusResponse(deliveryStatus:Bolusing, progressStatus:Normal, timeActive:34h10m, reservoirLevel:50+, insulinDelivered:59.95, bolusNotDelivered:0.20, lastProgrammingMessageSeqNum:12, alerts:No alerts)])
COMMAND:  2023-09-30 16:30:04 Message(175c3dea seq:14 [GetStatusCommand(normal)])
RESPONSE: 2023-09-30 16:30:04 Message(175c3dea seq:15 [StatusResponse(deliveryStatus:Bolusing, progressStatus:Normal, timeActive:34h10m, reservoirLevel:50+, insulinDelivered:59.95, bolusNotDelivered:0.20, lastProgrammingMessageSeqNum:12, alerts:No alerts)])
COMMAND:  2023-09-30 16:30:05 Message(175c3dea seq:00 [GetStatusCommand(normal)])
RESPONSE: 2023-09-30 16:30:06 Message(175c3dea seq:01 [StatusResponse(deliveryStatus:Bolusing, progressStatus:Normal, timeActive:34h10m, reservoirLevel:50+, insulinDelivered:60.00, bolusNotDelivered:0.15, lastProgrammingMessageSeqNum:12, alerts:No alerts)])

// The 0.20U auto bolus finished at 16:30:12, but there is no status return until 16:30:20.
// But the high level of Loop doesn't check or care and allows the manual 1.8U bolus.

// 1.8U manual bolus started 
// This particular getStatus is PodCommsSessions.bolus() verifying that the unfinalizedBolus has finished
COMMAND:  2023-09-30 16:30:19 Message(175c3dea seq:02 [GetStatusCommand(normal)])
RESPONSE: 2023-09-30 16:30:20 Message(175c3dea seq:03 [StatusResponse(deliveryStatus:Scheduled basal, progressStatus:Normal, timeActive:34h11m, reservoirLevel:50+, insulinDelivered:60.15, bolusNotDelivered:0.00, lastProgrammingMessageSeqNum:12, alerts:No alerts)])

// 1.8U manual bolus command sent at 16:30:20 that will end in 36 pulses * 2 sec/pulse from now at 16:31:32
COMMAND:  2023-09-30 16:30:20 Message(175c3dea seq:04 [SetInsulinScheduleCommand(nonce:494e532e, bolus(units: 1.8, timeBetweenPulses: 2.0, table: OmniBLEParser.BolusDeliveryTable(entries: [InsulinTableEntry(segments:1, pulses:36, alternateSegmentPulse:false)]))), BolusExtraCommand(units:1.8, timeBetweenPulses:2.0, extendedUnits:0.0, extendedDuration:0.0, acknowledgementBeep:false, completionBeep:false, programReminderInterval:0.0)])
RESPONSE: 2023-09-30 16:30:20 Message(175c3dea seq:05 [StatusResponse(deliveryStatus:Bolusing, progressStatus:Normal, timeActive:34h11m, reservoirLevel:50+, insulinDelivered:60.15, bolusNotDelivered:1.80, lastProgrammingMessageSeqNum:4, alerts:No alerts)])
// Don't know why this get status was done here, but with only 1 sec elapsed the full 1.80U bolus is still remaining
COMMAND:  2023-09-30 16:30:21 Message(175c3dea seq:06 [GetStatusCommand(normal)])
RESPONSE: 2023-09-30 16:30:21 Message(175c3dea seq:07 [StatusResponse(deliveryStatus:Bolusing, progressStatus:Normal, timeActive:34h11m, reservoirLevel:50+, insulinDelivered:60.15, bolusNotDelivered:1.80, lastProgrammingMessageSeqNum:4, alerts:No alerts)])

// "Bolused 0.65U of 0.20U" message would occurred at 13 pulses (26 seconds) past the start of the 1.2U bolus at 16:30:04 which is at 16:30:30 which is during the 1.8U manual bolus starting at 16:30:20 and ending at 16:31:32. This matches the screenshot with the phone time showing a time of 09:30.

// This cancel bolus command stopped the in-progress 1.8U manual bolus with 0.25U (5 pulses or 10 secs) remaining
COMMAND:  2023-09-30 16:31:24 Message(175c3dea seq:08 [CancelDeliveryCommand(nonce:494e532e, deliveryType:4, beepType:noBeepCancel)])
RESPONSE: 2023-09-30 16:31:25 Message(175c3dea seq:09 [StatusResponse(deliveryStatus:Scheduled basal, progressStatus:Normal, timeActive:34h12m, reservoirLevel:50+, insulinDelivered:61.75, bolusNotDelivered:0.25, lastProgrammingMessageSeqNum:8, alerts:No alerts)])

In this Loop Report, a 0.20U automatic bolus was started at 09:30:04 (local time) and then a 1.8U manual bolus was started at 09:30:20. The 0.20U bolus ended at 09:30:12, but there were no status returns between 09:30:12 and when the 1.8U bolus was started. N.B., there was a getStatus at 09:30:19 with a response at 09:30:20 that was done in PodCommSesssion.bolus() to verify that the previous bolus had indeed finished. With the OmniXXX bolusState() not including the !bolus.isFinished() test starting in April 2022 in Loop 3.0, the bolusState value contained in the OmniXXXPumpManager.status var would have never indicated that the original bolus had completed. This particular problem has been fixed in OmniBLE PR #125 and OmniKit PR #36 which was too recent and was unfortunately not included in Loop 3.4.1.

The included screenshot shows "Bolused 0.65 of 0.20 U". This would have had to occur at 09:30:30 based on the time since the 0.20U bolus started which is partially into the 2nd 1.8U manual bolus. I have noticed that Loop is happy to start a bolus while one is still in progress as reported by OmniXXXPumpManager.status.bolusState and then relies on OmniXXXPumpManager to detect if a bolus isn't currently active. This can result in odd bolus progress displays. For example, an attempted 2nd bolus with one currently active will display "Starting Bolus" and put up a new bolus progress circle overwriting the in-progress one and then after it fails (from the result of its getStatus showing the previous bolus is still in-progress), the bolus progress loop will generally revert back to the original bolus display. It's not hard to imagine that if the getStatus done in the 2nd bolus shows that 1st bolus has finished (as in this example) and the 2nd bolus is allowed to proceed, that the bolus progress display could end up getting in an additional confused state continuing by using the original bolus dose for its display, but calculating an in-progress value greater than 1.0 based on the elapsed time from the 1st bolus and than a higher than should be possible bolus amount shown because of the 2nd bolus is active.

I believe that this problem should be reevaluated after applying OmniBLE PR #125 and OmniKit PR #36 to see how much of a difference it makes. Getting OmniXXXPumpManager.status.bolusState to function similarly to the Medtronic and Loop 2.x PumpManagers might be sufficient to prevent this situation from occurring. If it still can occur, it might requiring fixing Loop to never issue a bolus if the bolusState indicates that there is an active bolus (which would be a good thing to do for other reasons anyways). The APSManager uses the OmniXXXPumpManager.status.bolusState to try to verify that the pump is not currently bolusing before starting a bolus which avoids situations like this, but this caused the APSManager code to have to add explicit getStatus calls to get a reasonable OmniXXXPumpManager.status.bolusState values until OmniBLE PR #125 and OmniKit PR #36 fixed the bad bolusState return problems.

DavidRYL commented 4 months ago

My only comment on this is that I've tried to reproduce this multiple times giving a manual bolus while an auto bolus is in progress and it won't allow. Maybe it had to be a specific second that auto bolus was actually done but it's not showing on the UI that it is done.

marionbarker commented 4 months ago

@DavidRYL This will be hard to reproduce on a real phone.

You have to catch the timing such that Loop knows the autobolus completed (based on time) and then you have to send the manual bolus command before the pump manager state updates. (My terminology might not be exact - this is the gist of it).

The changes Joe (itsmojo) mentioned are not in Loop right now.

I can do some testing with a simulator to try to reproduce the error and then, if I figure out a procedure to make it happen reliably, do the test again with the code updates to determine if that is sufficient to avoid this in the future - or if there is some other change required.

Thanks so much for reporting this.

In the meantime, if you see a mismatch between active bolus and what the display says should be delivered, you can cancel the bolus. The act of canceling should clear up the display issue. Loop will know how much was actually delivered and the next recommendation will be less by that amount - possibly adjusted by a new CGM reading.