From 3d522a70576a96eeec2bf9dc2462131c280c297f Mon Sep 17 00:00:00 2001 From: marionbarker Date: Sun, 29 Jun 2025 12:54:02 -0700 Subject: [PATCH] Fixes to handle unacknowledged command with pod faults + Clear unacknowledgedCommand on a fault return from non-status command + Rework to correctly handle previous unacknowledgedCommand on pod fault + Guard against negative bolus if dose cancel is called incorrectly --- OmniKit/OmnipodCommon/UnfinalizedDose.swift | 5 ++++- OmniKit/PumpManager/PodCommsSession.swift | 18 +++++++++++++++--- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/OmniKit/OmnipodCommon/UnfinalizedDose.swift b/OmniKit/OmnipodCommon/UnfinalizedDose.swift index 9e2858b..a4f272e 100644 --- a/OmniKit/OmnipodCommon/UnfinalizedDose.swift +++ b/OmniKit/OmnipodCommon/UnfinalizedDose.swift @@ -155,7 +155,10 @@ public struct UnfinalizedDose: RawRepresentable, Equatable, CustomStringConverti case .bolus: let oldRate = rate if let remaining = remaining { - units = units - remaining + // Guard against negative bolus if incorrectly called a 2nd time or with a bad remaining + if remaining <= units { + units -= remaining + } } else { units = oldRate * newDuration.hours } diff --git a/OmniKit/PumpManager/PodCommsSession.swift b/OmniKit/PumpManager/PodCommsSession.swift index 7fb0df1..097a416 100644 --- a/OmniKit/PumpManager/PodCommsSession.swift +++ b/OmniKit/PumpManager/PodCommsSession.swift @@ -218,11 +218,14 @@ public class PodCommsSession { } else { podState.activeTime = fault.faultEventTimeSinceActivation } - handleCancelDosing(deliveryType: .all, bolusNotDelivered: fault.bolusNotDelivered) let derivedStatusResponse = StatusResponse(detailedStatus: fault) if podState.unacknowledgedCommand != nil { + // Process the pending unacknowledgeCommnd to handle any pending doses matters for an unacknowledged + // command before calling handleCancelDosing() to deal with the final dosing adjustments from pod fault. + // N.B., recoverUnacknowledgedCommand() skips using bolusNotDelivered for a stopProgram with a faulted pod. recoverUnacknowledgedCommand(using: derivedStatusResponse) } + handleCancelDosing(deliveryType: .all, bolusNotDelivered: derivedStatusResponse.bolusNotDelivered) podState.updateFromStatusResponse(derivedStatusResponse, at: currentDate) } log.error("Pod Fault: %@", String(describing: fault)) @@ -307,6 +310,11 @@ public class PodCommsSession { if let fault = response.fault { + if podState.unacknowledgedCommand != nil && blocksToSend[0].blockType != .getStatus { + // Clear the unacknowledgedCommand for this attempted non-getStatus command since + // it was for this send and thus it was actually acknowledged -- with a pod fault. + podState.unacknowledgedCommand = nil + } try throwPodFault(fault: fault) // always throws } @@ -949,7 +957,9 @@ public class PodCommsSession { case .stopProgram(let stopProgram, _, let commandDate, _): if stopProgram.contains(.bolus), let bolus = podState.unfinalizedBolus, !bolus.isFinished(at: commandDate) { - podState.unfinalizedBolus?.cancel(at: commandDate, withRemaining: podStatus.bolusNotDelivered) + // If the pod is faulted, don't use bolusNotDelivered as this will be handled in handlePodFault() + let bolusNotDelivered = podState.isFaulted ? 0 : podStatus.bolusNotDelivered + podState.unfinalizedBolus?.cancel(at: commandDate, withRemaining: bolusNotDelivered) } if stopProgram.contains(.tempBasal), let tempBasal = podState.unfinalizedTempBasal, !tempBasal.isFinished(at: commandDate) { podState.unfinalizedTempBasal?.cancel(at: commandDate) @@ -994,7 +1004,9 @@ public class PodCommsSession { case .stopProgram(let stopProgram, _, let commandDate, _): if stopProgram.contains(.bolus), let bolus = podState.unfinalizedBolus, !bolus.isFinished(at: commandDate) { if !deliveryStatus.bolusing { - podState.unfinalizedBolus?.cancel(at: commandDate, withRemaining: podStatus.bolusNotDelivered) + // If the pod is faulted, don't use bolusNotDelivered as this will be handled in handlePodFault() + let bolusNotDelivered = podState.isFaulted ? 0 : podStatus.bolusNotDelivered + podState.unfinalizedBolus?.cancel(at: commandDate, withRemaining: bolusNotDelivered) podStatusMatched = true } }