Skip to content

Commit

Permalink
Merge pull request #676 from itsmojo/loop-release/v2.2.5
Browse files Browse the repository at this point in the history
Previous PR's with various Omnipod fixes and improvements
  • Loading branch information
ps2 authored Aug 12, 2021
2 parents 1d0ca30 + ade80cb commit 9af6e2d
Show file tree
Hide file tree
Showing 21 changed files with 855 additions and 522 deletions.
67 changes: 50 additions & 17 deletions OmniKit/MessageTransport/MessageBlocks/DetailedStatus.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public struct DetailedStatus : PodInfo, Equatable {
public let podProgressStatus: PodProgressStatus
public let deliveryStatus: DeliveryStatus
public let bolusNotDelivered: Double
public let podMessageCounter: UInt8
public let lastProgrammingMessageSeqNum: UInt8 // updated by pod for 03, 08, $11, $19, $1A, $1C, $1E & $1F command messages
public let totalInsulinDelivered: Double
public let faultEventCode: FaultEventCode
public let faultEventTimeSinceActivation: TimeInterval?
Expand All @@ -28,10 +28,10 @@ public struct DetailedStatus : PodInfo, Equatable {
public let unacknowledgedAlerts: AlertSet
public let faultAccessingTables: Bool
public let errorEventInfo: ErrorEventInfo?
public let receiverLowGain: Int8
public let radioRSSI: Int8
public let receiverLowGain: UInt8
public let radioRSSI: UInt8
public let previousPodProgressStatus: PodProgressStatus?
public let unknownValue: Data
// YYYY is uninitialized data for Eros
public let data: Data

public init(encodedData: Data) throws {
Expand All @@ -48,9 +48,9 @@ public struct DetailedStatus : PodInfo, Equatable {

self.bolusNotDelivered = Pod.pulseSize * Double((Int(encodedData[3] & 0x3) << 8) | Int(encodedData[4]))

self.podMessageCounter = encodedData[5]
self.lastProgrammingMessageSeqNum = encodedData[5]

self.totalInsulinDelivered = Pod.pulseSize * Double((Int(encodedData[6]) << 8) | Int(encodedData[7]))
self.totalInsulinDelivered = Pod.pulseSize * Double(encodedData[6...7].toBigEndian(UInt16.self))

self.faultEventCode = FaultEventCode(rawValue: encodedData[8])

Expand All @@ -73,31 +73,65 @@ public struct DetailedStatus : PodInfo, Equatable {

self.unacknowledgedAlerts = AlertSet(rawValue: encodedData[15])

self.faultAccessingTables = encodedData[16] == 2
self.faultAccessingTables = (encodedData[16] & 2) != 0

if encodedData[17] == 0x00 {
self.errorEventInfo = nil // this byte is not valid (no fault has occurred)
} else {
self.errorEventInfo = ErrorEventInfo(rawValue: encodedData[17])
}

self.receiverLowGain = Int8(encodedData[18] >> 6)
self.radioRSSI = Int8(encodedData[18] & 0x3F)
self.receiverLowGain = UInt8(encodedData[18] >> 6)
self.radioRSSI = UInt8(encodedData[18] & 0x3F)

if encodedData[19] == 0xFF {
self.previousPodProgressStatus = nil // this byte is not valid (no fault has occurred)
} else {
self.previousPodProgressStatus = PodProgressStatus(rawValue: encodedData[19] & 0xF)!
}

self.unknownValue = encodedData[20...21]

self.data = Data(encodedData)
}

public var isFaulted: Bool {
return faultEventCode.faultType != .noFaults || podProgressStatus == .activationTimeExceeded
}

// Returns an appropropriate PDM style Ref string for the Detailed Status.
// For most types, Ref: TT-VVVHH-IIIRR-FFF computed as {19|17}-{VV}{SSSS/60}-{NNNN/20}{RRRR/20}-PP
public var pdmRef: String? {
let TT, VVV, HH, III, RR, FFF: UInt8
let refStr = LocalizedString("Ref", comment: "PDM style 'Ref' string")

switch faultEventCode.faultType {
case .noFaults, .reservoirEmpty, .exceededMaximumPodLife80Hrs:
return nil // no PDM Ref # generated for these cases
case .insulinDeliveryCommandError:
// This fault is treated as a PDM fault which uses an alternate Ref format
return String(format: "%@:\u{00a0}11-144-0018-00049", refStr) // all fixed values for this fault
case .occluded:
// Ref: 17-000HH-IIIRR-000
TT = 17 // Occlusion detected Ref type
VVV = 0 // no VVV value for an occlusion fault
FFF = 0 // no FFF value for an occlusion fault
default:
// Ref: 19-VVVHH-IIIRR-FFF
TT = 19 // pod fault Ref type
VVV = data[17] // use the raw VV byte value
FFF = faultEventCode.rawValue
}

HH = UInt8(timeActive.hours)
III = UInt8(totalInsulinDelivered)

if let reservoirLevel = self.reservoirLevel {
RR = UInt8(reservoirLevel)
} else {
RR = 51 // value used for 50+ U
}

return String(format: "%@:\u{00a0}%02d-%03d%02d-%03d%02d-%03d", refStr, TT, VVV, HH, III, RR, FFF)
}
}

extension DetailedStatus: CustomDebugStringConvertible {
Expand All @@ -109,7 +143,7 @@ extension DetailedStatus: CustomDebugStringConvertible {
"* podProgressStatus: \(podProgressStatus)",
"* deliveryStatus: \(deliveryStatus.description)",
"* bolusNotDelivered: \(bolusNotDelivered.twoDecimals) U",
"* podMessageCounter: \(podMessageCounter)",
"* lastProgrammingMessageSeqNum: \(lastProgrammingMessageSeqNum)",
"* totalInsulinDelivered: \(totalInsulinDelivered.twoDecimals) U",
"* faultEventCode: \(faultEventCode.description)",
"* faultEventTimeSinceActivation: \(faultEventTimeSinceActivation?.stringValue ?? "none")",
Expand All @@ -121,7 +155,6 @@ extension DetailedStatus: CustomDebugStringConvertible {
"* receiverLowGain: \(receiverLowGain)",
"* radioRSSI: \(radioRSSI)",
"* previousPodProgressStatus: \(previousPodProgressStatus?.description ?? "NA")",
"* unknownValue: 0x\(unknownValue.hexadecimalString)",
"",
].joined(separator: "\n")
}
Expand Down Expand Up @@ -169,14 +202,14 @@ extension Double {

// Type for the ErrorEventInfo VV byte if valid
// a: insulin state table corruption found during error logging
// bb: internal 2-bit variable set and manipulated in main loop routines
// bb: internal 2-bit occlusion type
// c: immediate bolus in progress during error
// dddd: Pod Progress at time of first logged fault event
//
public struct ErrorEventInfo: CustomStringConvertible, Equatable {
let rawValue: UInt8
let insulinStateTableCorruption: Bool // 'a' bit
let internalVariable: Int // 'bb' 2-bit internal variable
let occlusionType: Int // 'bb' 2-bit occlusion type
let immediateBolusInProgress: Bool // 'c' bit
let podProgressStatus: PodProgressStatus // 'dddd' bits

Expand All @@ -189,7 +222,7 @@ public struct ErrorEventInfo: CustomStringConvertible, Equatable {
return [
"rawValue: 0x\(hexString)",
"insulinStateTableCorruption: \(insulinStateTableCorruption)",
"internalVariable: \(internalVariable)",
"occlusionType: \(occlusionType)",
"immediateBolusInProgress: \(immediateBolusInProgress)",
"podProgressStatus: \(podProgressStatus)",
].joined(separator: ", ")
Expand All @@ -198,7 +231,7 @@ public struct ErrorEventInfo: CustomStringConvertible, Equatable {
init(rawValue: UInt8) {
self.rawValue = rawValue
self.insulinStateTableCorruption = (rawValue & 0x80) != 0
self.internalVariable = Int((rawValue & 0x60) >> 5)
self.occlusionType = Int((rawValue & 0x60) >> 5)
self.immediateBolusInProgress = (rawValue & 0x10) != 0
self.podProgressStatus = PodProgressStatus(rawValue: rawValue & 0xF)!
}
Expand Down
6 changes: 3 additions & 3 deletions OmniKit/MessageTransport/MessageBlocks/StatusResponse.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public struct StatusResponse : MessageBlock {
public let reservoirLevel: Double?
public let insulin: Double
public let bolusNotDelivered: Double
public let podMessageCounter: UInt8
public let lastProgrammingMessageSeqNum: UInt8 // updated by pod for 03, 08, $11, $19, $1A, $1C, $1E & $1F command messages
public let alerts: AlertSet


Expand Down Expand Up @@ -48,7 +48,7 @@ public struct StatusResponse : MessageBlock {
let lowInsulinBits = Int(encodedData[4] >> 7)
self.insulin = Double(highInsulinBits | midInsulinBits | lowInsulinBits) / Pod.pulsesPerUnit

self.podMessageCounter = (encodedData[4] >> 3) & 0xf
self.lastProgrammingMessageSeqNum = (encodedData[4] >> 3) & 0xf

self.bolusNotDelivered = Double((Int(encodedData[4] & 0x3) << 8) | Int(encodedData[5])) / Pod.pulsesPerUnit

Expand All @@ -65,7 +65,7 @@ public struct StatusResponse : MessageBlock {

extension StatusResponse: CustomDebugStringConvertible {
public var debugDescription: String {
return "StatusResponse(deliveryStatus:\(deliveryStatus), progressStatus:\(podProgressStatus), timeActive:\(timeActive.stringValue), reservoirLevel:\(String(describing: reservoirLevel)), delivered:\(insulin), bolusNotDelivered:\(bolusNotDelivered), seq:\(podMessageCounter), alerts:\(alerts))"
return "StatusResponse(deliveryStatus:\(deliveryStatus), progressStatus:\(podProgressStatus), timeActive:\(timeActive.stringValue), reservoirLevel:\(String(describing: reservoirLevel)), delivered:\(insulin), bolusNotDelivered:\(bolusNotDelivered), lastProgrammingMessageSeqNum:\(lastProgrammingMessageSeqNum), alerts:\(alerts))"
}
}

97 changes: 68 additions & 29 deletions OmniKit/MessageTransport/MessageBlocks/VersionResponse.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,24 @@ public struct VersionResponse : MessageBlock {

public let pmVersion: FirmwareVersion
public let piVersion: FirmwareVersion
public let podProgressStatus: PodProgressStatus
public let lot: UInt32
public let tid: UInt32
public let gain: UInt8? // Only in the shorter assignAddress response
public let rssi: UInt8? // Only in the shorter assignAddress response
public let address: UInt32

public let productID: UInt8 // always 2 (for PM = PI = 2.7.0), 2nd gen Omnipod?
public let podProgressStatus: PodProgressStatus

// These values only included in the shorter 0x15 VersionResponse for the AssignAddress command.
public let gain: UInt8? // 2-bit value, max gain is at 0, min gain is at 2
public let rssi: UInt8? // 6-bit value, max rssi seen 61

// These values only included in the longer 0x1B VersionResponse for the SetupPod command.
public let pulseSize: Double? // VVVV / 100,000, must be 0x1388 / 100,000 = 0.05U
public let secondsPerBolusPulse: Double? // BR / 8, nominally 0x10 / 8 = 2 seconds per pulse
public let secondsPerPrimePulse: Double? // PR / 8, nominally 0x08 / 8 = 1 seconds per priming pulse
public let primeUnits: Double? // PP * pulseSize, nominally 0x34 * 0.05U = 2.6U
public let cannulaInsertionUnits: Double? // CP * pulseSize, nominally 0x0A * 0.05U = 0.5U
public let serviceDuration: TimeInterval? // PL hours, nominally 0x50 = 80 hours

public let data: Data

public init(encodedData: Data) throws {
Expand All @@ -48,58 +59,86 @@ public struct VersionResponse : MessageBlock {

switch responseLength {
case assignAddressVersionLength:
// This is the shorter 0x15 response to the 07 AssignAddress command
// 01 15 020700 020700 02 02 0000a377 0003ab37 9f 1f00ee87
// This is the shorter 0x15 response for the 07 AssignAddress command.
// 0 1 2 5 8 9 10 14 18 19
// 01 LL MXMYMZ IXIYIZ 02 0J LLLLLLLL TTTTTTTT GS IIIIIIII
// 01 LL MXMYMZ IXIYIZ ID 0J LLLLLLLL TTTTTTTT GS IIIIIIII
// 01 15 020700 020700 02 02 0000a377 0003ab37 9f 1f00ee87
//
// LL = 0x15 (assignAddressVersionLength)
// PM = MX.MY.MZ
// PI = IX.IY.IZ
// 0J = Pod progress state (typically 02, could be 01)
// PM MX.MY.MZ = 02.07.02 (for PM 2.7.0)
// PI IX.IY.IZ = 02.07.02 (for PI 2.7.0)
// ID = Product ID (always 02 for PM = PI = 2.7.0)
// 0J = Pod progress state (typically 02, but could be 01, for this particular response)
// LLLLLLLL = Lot
// TTTTTTTT = Tid
// GS = ggssssss (Gain/RSSI)
// IIIIIIII = address
// IIIIIIII = connection ID address

pmVersion = FirmwareVersion(encodedData: encodedData.subdata(in: 2..<5))
piVersion = FirmwareVersion(encodedData: encodedData.subdata(in: 5..<8))
if let podProgress = PodProgressStatus(rawValue: encodedData[9]) {
self.podProgressStatus = podProgress
} else {
productID = encodedData[8]
guard let progressStatus = PodProgressStatus(rawValue: encodedData[9]) else {
throw MessageBlockError.parseError
}
podProgressStatus = progressStatus
lot = encodedData[10...].toBigEndian(UInt32.self)
tid = encodedData[14...].toBigEndian(UInt32.self)
gain = (encodedData[18] & 0xc0) >> 6
rssi = encodedData[18] & 0x3f
address = encodedData[19...].toBigEndian(UInt32.self)

// These values only included in the longer 0x1B VersionResponse for the 03 SetupPod command.
pulseSize = nil
secondsPerBolusPulse = nil
secondsPerPrimePulse = nil
primeUnits = nil
cannulaInsertionUnits = nil
serviceDuration = nil

case setupPodVersionLength:
// This is the longer 0x1B response to the 03 SetupPod command
// 01 1b 13881008340a50 020700 020700 02 03 0000a62b 00044794 1f00ee87
// 0 1 2 9 12 16 17 21 25
// 01 LL 13881008340A50 MXMYMZ IXIYIZ 02 0J LLLLLLLL TTTTTTTT IIIIIIII
// LL = 0x1B (setupPodVersionMessageLength)
// PM = MX.MY.MZ
// PI = IX.IY.IZ
// 0J = Pod progress state (should always be 03)
// This is the longer 0x1B response for the 03 SetupPod command.
// 0 1 2 4 5 6 7 8 9 12 15 16 17 21 25
// 01 LL VVVV BR PR PP CP PL MXMYMZ IXIYIZ ID 0J LLLLLLLL TTTTTTTT IIIIIIII
// 01 1b 1388 10 08 34 0a 50 020700 020700 02 03 0000a62b 00044794 1f00ee87
//
// LL = 0x1b (setupPodVersionMessageLength)
// VVVV = 0x1388, pulse Volume in micro-units of U100 insulin per tenth of pulse (5000/100000 = 0.05U per pulse)
// BR = 0x10, Basic pulse Rate in # of eighth secs per pulse (16/8 = 2 seconds per pulse)
// PR = 0x08, Prime pulse Rate in # of eighth secs per pulse for priming boluses (8/8 = 1 second per priming pulse)
// PP = 0x34 = 52, # of Prime Pulses (52 pulses x 0.05U/pulse = 2.6U)
// CP = 0x0A = 10, # of Cannula insertion Pulses (10 pulses x 0.05U/pulse = 0.5U)
// PL = 0x50 = 80, # of hours maximum Pod Life
// PM = MX.MY.MZ = 02.07.02 (for PM 2.7.0)
// PI = IX.IY.IZ = 02.07.02 (for PI 2.7.0)
// ID = Product ID (always 02 for PM = PI = 2.7.0)
// 0J = Pod progress state (should be 03 for this particular response)
// LLLLLLLL = Lot
// TTTTTTTT = Tid
// IIIIIIII = address
// IIIIIIII = connection ID address

pmVersion = FirmwareVersion(encodedData: encodedData.subdata(in: 9..<12))
piVersion = FirmwareVersion(encodedData: encodedData.subdata(in: 12..<15))
if let podProgress = PodProgressStatus(rawValue: encodedData[16]) {
self.podProgressStatus = podProgress
} else {
productID = encodedData[15]
guard let progressStatus = PodProgressStatus(rawValue: encodedData[16]) else {
throw MessageBlockError.parseError
}
podProgressStatus = progressStatus
lot = encodedData[17...].toBigEndian(UInt32.self)
tid = encodedData[21...].toBigEndian(UInt32.self)
gain = nil // No GS byte in the longer SetupPod response
rssi = nil // No GS byte in the longer SetupPod response
address = encodedData[25...].toBigEndian(UInt32.self)

// These values should be verified elsewhere and appropriately handled.
pulseSize = Double(encodedData[2...].toBigEndian(UInt16.self)) / 100000
secondsPerBolusPulse = Double(encodedData[4]) / 8
secondsPerPrimePulse = Double(encodedData[5]) / 8
primeUnits = Double(encodedData[6]) * Pod.pulseSize
cannulaInsertionUnits = Double(encodedData[7]) * Pod.pulseSize
serviceDuration = TimeInterval.hours(Double(encodedData[8]))

// These values only included in the shorter 0x15 VersionResponse for the AssignAddress command.
gain = nil
rssi = nil

default:
throw MessageBlockError.parseError
}
Expand All @@ -116,7 +155,7 @@ public struct VersionResponse : MessageBlock {

extension VersionResponse: CustomDebugStringConvertible {
public var debugDescription: String {
return "VersionResponse(lot:\(lot), tid:\(tid), gain:\(gain?.description ?? "NA"), rssi:\(rssi?.description ?? "NA") address:\(Data(bigEndian: address).hexadecimalString), podProgressStatus:\(podProgressStatus), pmVersion:\(pmVersion), piVersion:\(piVersion))"
return "VersionResponse(lot:\(lot), tid:\(tid), address:\(Data(bigEndian: address).hexadecimalString), pmVersion:\(pmVersion), piVersion:\(piVersion), productID:\(productID), podProgressStatus:\(podProgressStatus), gain:\(gain?.description ?? "NA"), rssi:\(rssi?.description ?? "NA"), pulseSize:\(pulseSize?.description ?? "NA"), secondsPerBolusPulse:\(secondsPerBolusPulse?.description ?? "NA"), secondsPerPrimePulse:\(secondsPerPrimePulse?.description ?? "NA"), primeUnits:\(primeUnits?.description ?? "NA"), cannulaInsertionUnits:\(cannulaInsertionUnits?.description ?? "NA"), serviceDuration:\(serviceDuration?.description ?? "NA"), )"
}
}

Loading

0 comments on commit 9af6e2d

Please sign in to comment.