diff --git a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/PluginsModule.kt b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/PluginsModule.kt index cd8e179160..f861de7b07 100644 --- a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/PluginsModule.kt +++ b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/PluginsModule.kt @@ -42,6 +42,7 @@ import info.nightscout.androidaps.plugins.pump.combo.ComboPlugin import info.nightscout.androidaps.plugins.pump.insight.LocalInsightPlugin import info.nightscout.androidaps.plugins.pump.mdi.MDIPlugin import info.nightscout.androidaps.plugins.pump.medtronic.MedtronicPumpPlugin +import info.nightscout.androidaps.plugins.pump.omnipod.dash.OmnipodDashPumpPlugin import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin import info.nightscout.androidaps.plugins.sensitivity.SensitivityAAPSPlugin import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref1Plugin @@ -154,11 +155,11 @@ abstract class PluginsModule { @IntKey(150) abstract fun bindMedtronicPumpPlugin(plugin: MedtronicPumpPlugin): PluginBase - // @Binds - // @PumpDriver - // @IntoMap - // @IntKey(155) - // abstract fun bindOmnipodPumpPlugin(plugin: OmnipodErosPumpPlugin): PluginBase + @Binds + @PumpDriver + @IntoMap + @IntKey(156) + abstract fun bindOmnipodDashPumpPlugin(plugin: OmnipodDashPumpPlugin): PluginBase @Binds @PumpDriver @@ -370,4 +371,4 @@ abstract class PluginsModule { @Qualifier annotation class APS -} \ No newline at end of file +} diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/OmnipodDashPumpPlugin.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/OmnipodDashPumpPlugin.kt index 4f49c6f4f1..31070b8773 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/OmnipodDashPumpPlugin.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/OmnipodDashPumpPlugin.kt @@ -3,6 +3,7 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash import android.content.Context import android.os.Handler import android.os.Looper +import android.text.format.DateFormat import dagger.android.HasAndroidInjector import info.nightscout.androidaps.activities.ErrorHelperActivity.Companion.runAlarm import info.nightscout.androidaps.data.DetailedBolusInfo @@ -11,6 +12,9 @@ import info.nightscout.androidaps.events.EventPreferenceChange import info.nightscout.androidaps.events.EventProfileSwitchChanged import info.nightscout.androidaps.events.EventRefreshOverview import info.nightscout.androidaps.events.EventTempBasalChange +import info.nightscout.androidaps.extensions.convertedToAbsolute +import info.nightscout.androidaps.extensions.plannedRemainingMinutes +import info.nightscout.androidaps.extensions.toStringFull import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag @@ -36,9 +40,12 @@ import info.nightscout.androidaps.plugins.pump.omnipod.dash.history.data.BolusRe import info.nightscout.androidaps.plugins.pump.omnipod.dash.history.data.BolusType import info.nightscout.androidaps.plugins.pump.omnipod.dash.history.data.TempBasalRecord import info.nightscout.androidaps.plugins.pump.omnipod.dash.ui.OmnipodDashOverviewFragment +import info.nightscout.androidaps.plugins.pump.omnipod.dash.util.Constants import info.nightscout.androidaps.plugins.pump.omnipod.dash.util.mapProfileToBasalProgram import info.nightscout.androidaps.queue.commands.Command import info.nightscout.androidaps.queue.commands.CustomCommand +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.DecimalFormatter.to2Decimal import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.TimeChangeType @@ -49,8 +56,8 @@ import io.reactivex.Completable import io.reactivex.Single import io.reactivex.disposables.CompositeDisposable import io.reactivex.rxkotlin.plusAssign -import io.reactivex.rxkotlin.subscribeBy import org.json.JSONObject +import java.lang.Exception import java.time.Duration import java.time.ZonedDateTime import java.util.* @@ -73,6 +80,7 @@ class OmnipodDashPumpPlugin @Inject constructor( private val context: Context, private val aapsSchedulers: AapsSchedulers, private val fabricPrivacy: FabricPrivacy, + private val dateUtil: DateUtil, injector: HasAndroidInjector, aapsLogger: AAPSLogger, @@ -84,7 +92,7 @@ class OmnipodDashPumpPlugin @Inject constructor( private val handler: Handler = Handler(Looper.getMainLooper()) private lateinit var statusChecker: Runnable - var nextPodWarningCheck: Long = 0 + private var nextPodWarningCheck: Long = 0 @Volatile var stopConnecting: CountDownLatch? = null private var disposables: CompositeDisposable = CompositeDisposable() @@ -127,8 +135,6 @@ class OmnipodDashPumpPlugin @Inject constructor( val tbr = expectedState.temporaryBasal if (tbr == null || tbr.rate != 0.0) { aapsLogger.info(LTag.PUMP, "createFakeTBRWhenNoActivePod") - // calling connectNewPump() here because pumpSerial could have changed(from 4241 to "n/a") - pumpSync.connectNewPump() pumpSync.syncTemporaryBasalWithPumpId( timestamp = System.currentTimeMillis(), rate = 0.0, @@ -137,7 +143,9 @@ class OmnipodDashPumpPlugin @Inject constructor( type = PumpSync.TemporaryBasalType.PUMP_SUSPEND, pumpId = Random.Default.nextLong(), // we don't use this, just make sure it's unique pumpType = PumpType.OMNIPOD_DASH, - pumpSerial = serialNumber() + pumpSerial = Constants.PUMP_SERIAL_FOR_FAKE_TBR // switching the serialNumber here would need a + // call to connectNewPump. If we do that, then we will have a TBR started by the "n/a" pump and + // cancelled by "4241". This did not work ok. ) } } @@ -510,7 +518,7 @@ class OmnipodDashPumpPlugin @Inject constructor( // Omnipod only reports reservoir level when there's < 1023 pulses left return podStateManager.pulsesRemaining?.let { - it * 0.05 + it * PodConstants.POD_PULSE_BOLUS_UNITS } ?: 75.0 } @@ -574,7 +582,7 @@ class OmnipodDashPumpPlugin @Inject constructor( ).filter { podEvent -> podEvent.isCommandSent() } .map { pumpSyncBolusStart(requestedBolusAmount, detailedBolusInfo.bolusType) } .ignoreElements(), - post = waitForBolusDeliveryToComplete(BOLUS_RETRIES, requestedBolusAmount, detailedBolusInfo.bolusType) + post = waitForBolusDeliveryToComplete(requestedBolusAmount, detailedBolusInfo.bolusType) .map { deliveredBolusAmount = it aapsLogger.info(LTag.PUMP, "deliverTreatment: deliveredBolusAmount=$deliveredBolusAmount") @@ -638,14 +646,13 @@ class OmnipodDashPumpPlugin @Inject constructor( } private fun waitForBolusDeliveryToComplete( - maxTries: Int, requestedBolusAmount: Double, bolusType: DetailedBolusInfo.BolusType ): Single = Single.defer { if (bolusCanceled && podStateManager.activeCommand != null) { var errorGettingStatus: Throwable? = null - for (tries in 1..maxTries) { + for (tries in 1..BOLUS_RETRIES) { errorGettingStatus = getPodStatus().blockingGet() if (errorGettingStatus != null) { aapsLogger.debug(LTag.PUMP, "waitForBolusDeliveryToComplete errorGettingStatus=$errorGettingStatus") @@ -676,7 +683,7 @@ class OmnipodDashPumpPlugin @Inject constructor( ) } - for (tryNumber in 1..maxTries) { + for (tryNumber in 1..BOLUS_RETRIES) { updateBolusProgressDialog("Checking delivery status", 100) val cmd = if (bolusCanceled) @@ -724,13 +731,15 @@ class OmnipodDashPumpPlugin @Inject constructor( } private fun estimateBolusDeliverySeconds(requestedBolusAmount: Double): Long { - return ceil(requestedBolusAmount / 0.05).toLong() * 2 + 3 + return ceil(requestedBolusAmount / PodConstants.POD_PULSE_BOLUS_UNITS).toLong() * 2 + 3 } private fun pumpSyncBolusStart( requestedBolusAmount: Double, bolusType: DetailedBolusInfo.BolusType ): Boolean { + require(requestedBolusAmount > 0) { "requestedBolusAmount has to be positive" } + val activeCommand = podStateManager.activeCommand if (activeCommand == null) { throw IllegalArgumentException( @@ -776,7 +785,7 @@ class OmnipodDashPumpPlugin @Inject constructor( ) val ret = executeProgrammingCommand( - pre = observeNoActiveTempBasal(true), + pre = observeNoActiveTempBasal(), historyEntry = history.createRecord( commandType = OmnipodCommandType.SET_TEMPORARY_BASAL, tempBasalRecord = TempBasalRecord(duration = durationInMinutes, rate = absoluteRate) @@ -846,38 +855,28 @@ class OmnipodDashPumpPlugin @Inject constructor( return ret } - private fun observeNoActiveTempBasal(enforceNew: Boolean): Completable { + private fun observeNoActiveTempBasal(): Completable { return Completable.defer { - when { - podStateManager.deliveryStatus !in - arrayOf(DeliveryStatus.TEMP_BASAL_ACTIVE, DeliveryStatus.BOLUS_AND_TEMP_BASAL_ACTIVE) -> { - // TODO: what happens if we try to cancel nonexistent temp basal? - aapsLogger.info(LTag.PUMP, "No temporary basal to cancel") - Completable.complete() - } - - !enforceNew -> - Completable.error( - IllegalStateException( - "Temporary basal already active and enforceNew is not set." - ) + if (podStateManager.deliveryStatus !in + arrayOf(DeliveryStatus.TEMP_BASAL_ACTIVE, DeliveryStatus.BOLUS_AND_TEMP_BASAL_ACTIVE) + ) { + // TODO: what happens if we try to cancel nonexistent temp basal? + aapsLogger.info(LTag.PUMP, "No temporary basal to cancel") + Completable.complete() + } else { + // enforceNew == true + aapsLogger.info(LTag.PUMP, "Canceling existing temp basal") + executeProgrammingCommand( + historyEntry = history.createRecord(OmnipodCommandType.CANCEL_TEMPORARY_BASAL), + command = omnipodManager.stopTempBasal(hasTempBasalBeepEnabled()).ignoreElements() + ).doOnComplete { + notifyOnUnconfirmed( + Notification.OMNIPOD_TBR_ALERTS, + "Cancelling temp basal might have failed." + + "If a temp basal was previously running, it might have been cancelled." + + "Please manually refresh the Pod status from the Omnipod tab.", // TODO: i8n + R.raw.boluserror, ) - - else -> { - // enforceNew == true - aapsLogger.info(LTag.PUMP, "Canceling existing temp basal") - executeProgrammingCommand( - historyEntry = history.createRecord(OmnipodCommandType.CANCEL_TEMPORARY_BASAL), - command = omnipodManager.stopTempBasal(hasTempBasalBeepEnabled()).ignoreElements() - ).doOnComplete { - notifyOnUnconfirmed( - Notification.OMNIPOD_TBR_ALERTS, - "Cancelling temp basal might have failed." + - "If a temp basal was previously running, it might have been cancelled." + - "Please manually refresh the Pod status from the Omnipod tab.", // TODO: i8n - R.raw.boluserror, - ) - } } } } @@ -952,8 +951,53 @@ class OmnipodDashPumpPlugin @Inject constructor( } override fun getJSONStatus(profile: Profile, profileName: String, version: String): JSONObject { - // TODO - return JSONObject() + val now = System.currentTimeMillis() + if (podStateManager.lastUpdatedSystem + 60 * 60 * 1000L < now) { + return JSONObject() + } + val pumpJson = JSONObject() + val status = JSONObject() + val extended = JSONObject() + try { + val podStatus = when { + podStateManager.isPodRunning && podStateManager.isSuspended -> + "suspended" + podStateManager.isPodRunning -> + "normal" + else -> + "no active Pod" + } + status.put("status", podStatus) + status.put("timestamp", dateUtil.toISOString(podStateManager.lastUpdatedSystem)) + + extended.put("Version", version) + try { + extended.put("ActiveProfile", profileName) + } catch (ignored: Exception) { + } + val tb = pumpSync.expectedPumpState().temporaryBasal + tb?.run { + extended.put("TempBasalAbsoluteRate", this.convertedToAbsolute(now, profile)) + extended.put("TempBasalStart", dateUtil.dateAndTimeString(this.timestamp)) + extended.put("TempBasalRemaining", this.plannedRemainingMinutes) + } + podStateManager.lastBolus?.run { + extended.put("LastBolus", dateUtil.dateAndTimeString(this.startTime)) + extended.put("LastBolusAmount", this.deliveredUnits() ?: this.requestedUnits) + } + extended.put("BaseBasalRate", baseBasalRate) + + pumpJson.put("status", status) + pumpJson.put("extended", extended) + if (podStateManager.pulsesRemaining == null) { + pumpJson.put("reservoir_display_override", "50+") + } + pumpJson.put("reservoir", reservoirLevel.toInt()) + pumpJson.put("clock", dateUtil.toISOString(now)) + } catch (e: Exception) { + aapsLogger.error(LTag.PUMP, "Unhandled exception: $e") + } + return pumpJson } override val pumpDescription: PumpDescription = Companion.pumpDescription @@ -972,8 +1016,33 @@ class OmnipodDashPumpPlugin @Inject constructor( } override fun shortStatus(veryShort: Boolean): String { - // TODO - return "TODO" + if (!podStateManager.isActivationCompleted) { + return resourceHelper.gs(R.string.omnipod_common_short_status_no_active_pod) + } + var ret = "" + if (podStateManager.lastUpdatedSystem != 0L) { + val agoMsec: Long = System.currentTimeMillis() - podStateManager.lastUpdatedSystem + val agoMin = (agoMsec / 60.0 / 1000.0).toInt() + ret += resourceHelper.gs(R.string.omnipod_common_short_status_last_connection, agoMin) + "\n" + } + podStateManager.lastBolus?.run { + ret += resourceHelper.gs( + R.string.omnipod_common_short_status_last_bolus, to2Decimal(this.deliveredUnits() ?: this.requestedUnits), + DateFormat.format("HH:mm", Date(this.startTime)) + ) + "\n" + } + val (temporaryBasal, extendedBolus, _, profile) = pumpSync.expectedPumpState() + temporaryBasal?.run { + ret += resourceHelper.gs( + R.string.omnipod_common_short_status_temp_basal, + this.toStringFull(dateUtil) + ) + "\n" + } + ret += resourceHelper.gs( + R.string.omnipod_common_short_status_reservoir, + podStateManager.pulsesRemaining?.let { reservoirLevel.toString() } ?: "50+" + ) + return ret.trim() } override val isFakingTempsByExtendedBoluses: Boolean @@ -1346,7 +1415,7 @@ class OmnipodDashPumpPlugin @Inject constructor( pumpId = historyEntry.pumpId(), pumpType = PumpType.OMNIPOD_DASH, pumpSerial = serialNumber(), - type = null // TODO: set the correct bolus type here!!! + type = null ) } rxBus.send(EventDismissNotification(Notification.OMNIPOD_UNCERTAIN_SMB)) @@ -1356,6 +1425,10 @@ class OmnipodDashPumpPlugin @Inject constructor( if (confirmation.success) { podStateManager.lastBolus?.run { val deliveredUnits = markComplete() + if (deliveredUnits < 0) { + aapsLogger.error(LTag.PUMP, "Negative delivered units!!! $deliveredUnits") + return + } val bolusHistoryEntry = history.getById(historyId) val sync = pumpSync.syncBolusWithPumpId( timestamp = bolusHistoryEntry.createdAt, diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/OmnipodDashManagerImpl.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/OmnipodDashManagerImpl.kt index d64ea2ef42..923109c5c1 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/OmnipodDashManagerImpl.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/OmnipodDashManagerImpl.kt @@ -9,6 +9,9 @@ import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.* import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.GetVersionCommand.Companion.DEFAULT_UNIQUE_ID import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.* import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.PodConstants.Companion.MAX_POD_LIFETIME +import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.PodConstants.Companion.POD_EXPIRATION_ALERT_HOURS +import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.PodConstants.Companion.POD_EXPIRATION_ALERT_HOURS_DURATION +import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.PodConstants.Companion.POD_EXPIRATION_IMMINENT_ALERT_HOURS import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.* import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.state.OmnipodDashPodStateManager import info.nightscout.androidaps.utils.rx.AapsSchedulers @@ -278,7 +281,7 @@ class OmnipodDashManagerImpl @Inject constructor( .setUniqueId(podStateManager.uniqueId!!.toInt()) .setSequenceNumber(podStateManager.messageSequenceNumber) .setNonce(NONCE) - .setNumberOfUnits(podStateManager.firstPrimeBolusVolume!! * 0.05) + .setNumberOfUnits(podStateManager.firstPrimeBolusVolume!! * PodConstants.POD_PULSE_BOLUS_UNITS) .setDelayBetweenPulsesInEighthSeconds(podStateManager.primePulseRate!!.toByte()) .setProgramReminder(ProgramReminder(atStart = false, atEnd = false, atInterval = 0)) .build(), @@ -380,7 +383,7 @@ class OmnipodDashManagerImpl @Inject constructor( ) observables.add( observeSendProgramBolusCommand( - podStateManager.secondPrimeBolusVolume!! * 0.05, + podStateManager.secondPrimeBolusVolume!! * PodConstants.POD_PULSE_BOLUS_UNITS, podStateManager.primePulseRate!!.toByte(), confirmationBeeps = false, completionBeeps = false @@ -394,10 +397,10 @@ class OmnipodDashManagerImpl @Inject constructor( AlertConfiguration( AlertType.EXPIRATION, enabled = true, - durationInMinutes = TimeUnit.HOURS.toMinutes(7).toShort(), + durationInMinutes = TimeUnit.HOURS.toMinutes(POD_EXPIRATION_ALERT_HOURS_DURATION).toShort(), autoOff = false, AlertTrigger.TimerTrigger( - TimeUnit.HOURS.toMinutes(72).toShort() + TimeUnit.HOURS.toMinutes(POD_EXPIRATION_ALERT_HOURS).toShort() ), // FIXME use activation time BeepType.FOUR_TIMES_BIP_BEEP, BeepRepetitionType.XXX3 @@ -408,7 +411,7 @@ class OmnipodDashManagerImpl @Inject constructor( durationInMinutes = 0, autoOff = false, AlertTrigger.TimerTrigger( - TimeUnit.HOURS.toMinutes(79).toShort() + TimeUnit.HOURS.toMinutes(POD_EXPIRATION_IMMINENT_ALERT_HOURS).toShort() ), // FIXME use activation time BeepType.FOUR_TIMES_BIP_BEEP, BeepRepetitionType.XXX4 diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/message/IncorrectPacketException.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/message/IncorrectPacketException.kt index 049e8fa5d2..a02b9afa8a 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/message/IncorrectPacketException.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/message/IncorrectPacketException.kt @@ -4,5 +4,5 @@ import info.nightscout.androidaps.extensions.toHex class IncorrectPacketException( val payload: ByteArray, - val expectedIndex: Byte? = null + private val expectedIndex: Byte? = null ) : Exception("Invalid payload: ${payload.toHex()}. Expected index: $expectedIndex") diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/message/MessageIO.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/message/MessageIO.kt index 6e750069bc..5b8d614dde 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/message/MessageIO.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/message/MessageIO.kt @@ -29,7 +29,7 @@ class MessageIO( private val dataBleIO: DataBleIO, ) { - val receivedOutOfOrder = LinkedHashMap() + private val receivedOutOfOrder = LinkedHashMap() var maxMessageReadTries = 3 var messageReadTries = 0 diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/message/MessagePacket.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/message/MessagePacket.kt index 5506703311..cb6bef176f 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/message/MessagePacket.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/message/MessagePacket.kt @@ -2,6 +2,7 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.Id import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.CouldNotParseMessageException +import info.nightscout.androidaps.plugins.pump.omnipod.dash.util.Flag import java.nio.ByteBuffer /*** @@ -80,13 +81,13 @@ data class MessagePacket( if (payload.copyOfRange(0, 2).decodeToString() != MAGIC_PATTERN) { throw CouldNotParseMessageException(payload) } - val f1 = Flag(payload[2].toInt()) + val f1 = Flag(payload[2].toInt() and 0xff) val sas = f1.get(3) != 0 val tfs = f1.get(4) != 0 val version = ((f1.get(0) shl 2) or (f1.get(1) shl 1) or (f1.get(2) shl 0)).toShort() val eqos = (f1.get(7) or (f1.get(6) shl 1) or (f1.get(5) shl 2)).toShort() - val f2 = Flag(payload[3].toInt()) + val f2 = Flag(payload[3].toInt() and 0xff) val ack = f2.get(0) != 0 val priority = f2.get(1) != 0 val lastMessage = f2.get(2) != 0 @@ -125,24 +126,6 @@ data class MessagePacket( } } -private class Flag(var value: Int = 0) { - - fun set(idx: Byte, set: Boolean) { - val mask = 1 shl (7 - idx) - if (!set) - return - value = value or mask - } - - fun get(idx: Byte): Int { - val mask = 1 shl (7 - idx) - if (value and mask == 0) { - return 0 - } - return 1 - } -} - internal fun Byte.toUnsignedInt() = this.toInt() and 0xff private fun ByteArray.assertSizeAtLeast(size: Int) { diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/definition/PodConstants.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/definition/PodConstants.kt index 915c6b7bde..4a2124aace 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/definition/PodConstants.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/definition/PodConstants.kt @@ -4,6 +4,19 @@ import java.time.Duration class PodConstants { companion object { - val MAX_POD_LIFETIME = Duration.ofHours(80) + val MAX_POD_LIFETIME: Duration = Duration.ofHours(80) + + // Expiration alert time in minutes since activation and duration in minutes + const val POD_EXPIRATION_ALERT_HOURS = 72L + const val POD_EXPIRATION_ALERT_HOURS_DURATION = 7L + + // Expiration eminent alert time in minutes since activation + const val POD_EXPIRATION_IMMINENT_ALERT_HOURS = 79L + + // Bolus & Priming units + const val POD_PULSE_BOLUS_UNITS = 0.05 + + // Reservoir units alert threshold + const val DEFAULT_MAX_RESERVOIR_ALERT_THRESHOLD: Short = 20 } } diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/response/DefaultStatusResponse.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/response/DefaultStatusResponse.kt index 584349de78..15ec19e534 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/response/DefaultStatusResponse.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/response/DefaultStatusResponse.kt @@ -5,6 +5,7 @@ import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definitio import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.PodStatus import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.util.AlertUtil import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.util.byValue +import java.nio.ByteBuffer import java.util.* import kotlin.experimental.and @@ -13,18 +14,21 @@ class DefaultStatusResponse( ) : ResponseBase(ResponseType.DEFAULT_STATUS_RESPONSE, encoded) { val messageType: Byte = encoded[0] - val deliveryStatus: DeliveryStatus = byValue((encoded[1].toInt() shr 4 and 0x0f).toByte(), DeliveryStatus.UNKNOWN) - val podStatus: PodStatus = byValue((encoded[1] and 0x0f), PodStatus.UNKNOWN) - val totalPulsesDelivered: Short = - (encoded[2] and 0x0f shl 9 or (encoded[3].toInt() and 0xff shl 1) or (encoded[4].toInt() and 0xff ushr 7)).toShort() - val sequenceNumberOfLastProgrammingCommand: Short = (encoded[4] ushr 3 and 0x0f).toShort() - val bolusPulsesRemaining: Short = ((encoded[4] and 0x07 shl 10 or (encoded[5].toInt() and 0xff) and 2047).toShort()) + private var first4bytes = ByteBuffer.wrap(byteArrayOf(encoded[2], encoded[3], encoded[4], encoded[5])).int + private var last4bytes = ByteBuffer.wrap(byteArrayOf(encoded[6], encoded[7], encoded[8], encoded[9])).int + + val podStatus: PodStatus = byValue((encoded[1] and 0x0f), PodStatus.UNKNOWN) + val deliveryStatus: DeliveryStatus = byValue(((encoded[1].toInt() and 0xff) shr 4 and 0x0f).toByte(), DeliveryStatus.UNKNOWN) + + val totalPulsesDelivered: Short = (first4bytes ushr 11 ushr 4 and 0x1FFF).toShort() + val sequenceNumberOfLastProgrammingCommand: Short = (first4bytes ushr 11 and 0X0F).toShort() + val bolusPulsesRemaining: Short = (first4bytes and 0X7FF).toShort() + val activeAlerts: EnumSet = - AlertUtil.decodeAlertSet((encoded[6].toInt() and 0xff shl 1 or (encoded[7] ushr 7)).toByte()) - val minutesSinceActivation: Short = - (encoded[7] and 0x7f shl 6 or (encoded[8].toInt() and 0xff ushr 2 and 0x3f)).toShort() - val reservoirPulsesRemaining: Short = (encoded[8] shl 8 or encoded[9].toInt() and 0x3ff).toShort() + AlertUtil.decodeAlertSet((last4bytes ushr 10 ushr 13 and 0xFF).toByte()) + val minutesSinceActivation: Short = ((last4bytes ushr 10 and 0x1FFF)).toShort() + val reservoirPulsesRemaining: Short = (last4bytes and 0X3FF).toShort() override fun toString(): String { return "DefaultStatusResponse(" + diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/state/OmnipodDashPodStateManagerImpl.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/state/OmnipodDashPodStateManagerImpl.kt index 0195dbeb67..bc02649cb0 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/state/OmnipodDashPodStateManagerImpl.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/state/OmnipodDashPodStateManagerImpl.kt @@ -311,7 +311,7 @@ class OmnipodDashPodStateManagerImpl @Inject constructor( private fun updateLastBolusFromResponse(bolusPulsesRemaining: Short) { podState.lastBolus?.run { - val remainingUnits = bolusPulsesRemaining.toDouble() * 0.05 + val remainingUnits = bolusPulsesRemaining.toDouble() * PodConstants.POD_PULSE_BOLUS_UNITS this.bolusUnitsRemaining = remainingUnits if (remainingUnits == 0.0) { this.deliveryComplete = true @@ -627,11 +627,13 @@ class OmnipodDashPodStateManagerImpl @Inject constructor( } override fun connectionSuccessRatio(): Float { - val attempts = connectionAttempts - if (attempts == 0) { + if (connectionAttempts == 0) { return 0.0F + } else if (connectionAttempts <= successfulConnections) { + // Prevent bogus quality > 1 during initialisation + return 1.0F } - return successfulConnections.toFloat() / attempts.toFloat() + return successfulConnections.toFloat() / connectionAttempts.toFloat() } override fun reset() { diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/ui/OmnipodDashOverviewFragment.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/ui/OmnipodDashOverviewFragment.kt index d0267f0589..00a9cc6968 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/ui/OmnipodDashOverviewFragment.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/ui/OmnipodDashOverviewFragment.kt @@ -32,6 +32,7 @@ import info.nightscout.androidaps.plugins.pump.omnipod.dash.databinding.OmnipodD import info.nightscout.androidaps.plugins.pump.omnipod.dash.databinding.OmnipodDashOverviewBluetoothStatusBinding import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.ActivationProgress import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.AlertType +import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.PodConstants import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.state.OmnipodDashPodStateManager import info.nightscout.androidaps.queue.Callback import info.nightscout.androidaps.queue.events.EventQueueChanged @@ -68,7 +69,6 @@ class OmnipodDashOverviewFragment : DaggerFragment() { @Inject lateinit var buildHelper: BuildHelper companion object { - private const val REFRESH_INTERVAL_MILLIS = 15 * 1000L // 15 seconds private const val PLACEHOLDER = "-" private const val MAX_TIME_DEVIATION_MINUTES = 10L @@ -372,7 +372,7 @@ class OmnipodDashOverviewFragment : DaggerFragment() { if (podStateManager.isActivationCompleted && podStateManager.pulsesDelivered != null) { resourceHelper.gs( R.string.omnipod_common_overview_total_delivered_value, - podStateManager.pulsesDelivered!! * 0.05 + (podStateManager.pulsesDelivered!! * PodConstants.POD_PULSE_BOLUS_UNITS) ) } else { PLACEHOLDER @@ -387,11 +387,11 @@ class OmnipodDashOverviewFragment : DaggerFragment() { // TODO // val lowReservoirThreshold = (omnipodAlertUtil.lowReservoirAlertUnits // ?: OmnipodConstants.DEFAULT_MAX_RESERVOIR_ALERT_THRESHOLD).toDouble() - val lowReservoirThreshold: Short = 20 + val lowReservoirThreshold: Short = PodConstants.DEFAULT_MAX_RESERVOIR_ALERT_THRESHOLD podInfoBinding.reservoir.text = resourceHelper.gs( R.string.omnipod_common_overview_reservoir_value, - (podStateManager.pulsesRemaining!! * 0.05) + (podStateManager.pulsesRemaining!! * PodConstants.POD_PULSE_BOLUS_UNITS) ) podInfoBinding.reservoir.setTextColor( if (podStateManager.pulsesRemaining!! < lowReservoirThreshold) { diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/ui/wizard/activation/viewmodel/action/DashInitializePodViewModel.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/ui/wizard/activation/viewmodel/action/DashInitializePodViewModel.kt index 78f8c48d8d..715387abeb 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/ui/wizard/activation/viewmodel/action/DashInitializePodViewModel.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/ui/wizard/activation/viewmodel/action/DashInitializePodViewModel.kt @@ -14,8 +14,6 @@ import info.nightscout.androidaps.plugins.pump.omnipod.dash.util.I8n import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.sharedPreferences.SP import io.reactivex.Single -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.rxkotlin.addTo import io.reactivex.rxkotlin.plusAssign import io.reactivex.rxkotlin.subscribeBy import javax.inject.Inject diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/ui/wizard/activation/viewmodel/action/DashInsertCannulaViewModel.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/ui/wizard/activation/viewmodel/action/DashInsertCannulaViewModel.kt index a716d2b56b..29a574549a 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/ui/wizard/activation/viewmodel/action/DashInsertCannulaViewModel.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/ui/wizard/activation/viewmodel/action/DashInsertCannulaViewModel.kt @@ -16,13 +16,12 @@ import info.nightscout.androidaps.plugins.pump.omnipod.common.ui.wizard.activati import info.nightscout.androidaps.plugins.pump.omnipod.dash.R import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.OmnipodDashManager import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.state.OmnipodDashPodStateManager +import info.nightscout.androidaps.plugins.pump.omnipod.dash.util.Constants import info.nightscout.androidaps.plugins.pump.omnipod.dash.util.I8n import info.nightscout.androidaps.plugins.pump.omnipod.dash.util.mapProfileToBasalProgram import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.sharedPreferences.SP import io.reactivex.Single -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.rxkotlin.addTo import io.reactivex.rxkotlin.plusAssign import io.reactivex.rxkotlin.subscribeBy import javax.inject.Inject @@ -67,43 +66,48 @@ class DashInsertCannulaViewModel @Inject constructor( super.disposable += omnipodManager.activatePodPart2(basalProgram, expirationHoursBeforeShutdown) .subscribeBy( - onNext = { podEvent -> - logger.debug( - LTag.PUMP, - "Received PodEvent in Pod activation part 2: $podEvent" - ) - }, - onError = { throwable -> - logger.error(LTag.PUMP, "Error in Pod activation part 2", throwable) - source.onSuccess(PumpEnactResult(injector).success(false).comment(I8n.textFromException(throwable, resourceHelper))) - }, - onComplete = { - logger.debug("Pod activation part 2 completed") - podStateManager.basalProgram = basalProgram - pumpSync.connectNewPump() - pumpSync.insertTherapyEventIfNewWithTimestamp( - timestamp = System.currentTimeMillis(), - type = DetailedBolusInfo.EventType.CANNULA_CHANGE, - pumpType = PumpType.OMNIPOD_DASH, - pumpSerial = podStateManager.uniqueId?.toString() ?: "n/a" - ) - pumpSync.insertTherapyEventIfNewWithTimestamp( - timestamp = System.currentTimeMillis(), - type = DetailedBolusInfo.EventType.INSULIN_CHANGE, - pumpType = PumpType.OMNIPOD_DASH, - pumpSerial = podStateManager.uniqueId?.toString() ?: "n/a" - ) - pumpSync.syncStopTemporaryBasalWithPumpId( - timestamp = System.currentTimeMillis(), - endPumpId = System.currentTimeMillis(), - pumpType = PumpType.OMNIPOD_DASH, - pumpSerial = podStateManager.uniqueId?.toString() ?: "n/a" - ) - podStateManager.updateExpirationAlertSettings(expirationReminderEnabled, expirationHours) - rxBus.send(EventDismissNotification(Notification.OMNIPOD_POD_NOT_ATTACHED)) - source.onSuccess(PumpEnactResult(injector).success(true)) - } - ) + onNext = { podEvent -> + logger.debug( + LTag.PUMP, + "Received PodEvent in Pod activation part 2: $podEvent" + ) + }, + onError = { throwable -> + logger.error(LTag.PUMP, "Error in Pod activation part 2", throwable) + source.onSuccess(PumpEnactResult(injector).success(false).comment(I8n.textFromException(throwable, resourceHelper))) + }, + onComplete = { + logger.debug("Pod activation part 2 completed") + podStateManager.basalProgram = basalProgram + + pumpSync.syncStopTemporaryBasalWithPumpId( + timestamp = System.currentTimeMillis(), + endPumpId = System.currentTimeMillis(), + pumpType = PumpType.OMNIPOD_DASH, + pumpSerial = Constants.PUMP_SERIAL_FOR_FAKE_TBR // cancel the fake TBR with the same pump + // serial that it was created with + ) + + pumpSync.connectNewPump() + + pumpSync.insertTherapyEventIfNewWithTimestamp( + timestamp = System.currentTimeMillis(), + type = DetailedBolusInfo.EventType.CANNULA_CHANGE, + pumpType = PumpType.OMNIPOD_DASH, + pumpSerial = podStateManager.uniqueId?.toString() ?: "n/a" + ) + pumpSync.insertTherapyEventIfNewWithTimestamp( + timestamp = System.currentTimeMillis(), + type = DetailedBolusInfo.EventType.INSULIN_CHANGE, + pumpType = PumpType.OMNIPOD_DASH, + pumpSerial = podStateManager.uniqueId?.toString() ?: "n/a" + ) + + podStateManager.updateExpirationAlertSettings(expirationReminderEnabled, expirationHours) + rxBus.send(EventDismissNotification(Notification.OMNIPOD_POD_NOT_ATTACHED)) + source.onSuccess(PumpEnactResult(injector).success(true)) + } + ) } } diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/util/Constants.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/util/Constants.kt new file mode 100644 index 0000000000..18d640012c --- /dev/null +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/util/Constants.kt @@ -0,0 +1,7 @@ +package info.nightscout.androidaps.plugins.pump.omnipod.dash.util + +class Constants { + companion object { + const val PUMP_SERIAL_FOR_FAKE_TBR = "4241" + } +} diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/util/Flag.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/util/Flag.kt new file mode 100644 index 0000000000..618815cbd1 --- /dev/null +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/util/Flag.kt @@ -0,0 +1,19 @@ +package info.nightscout.androidaps.plugins.pump.omnipod.dash.util + +class Flag(var value: Int = 0) { + + fun set(idx: Byte, set: Boolean) { + val mask = 1 shl (7 - idx) + if (!set) + return + value = value or mask + } + + fun get(idx: Byte): Int { + val mask = 1 shl (7 - idx) + if (value and mask == 0) { + return 0 + } + return 1 + } +} diff --git a/omnipod-dash/src/test/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/response/DefaultStatusResponseTest.kt b/omnipod-dash/src/test/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/response/DefaultStatusResponseTest.kt index bcce591db4..d2a14907e9 100644 --- a/omnipod-dash/src/test/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/response/DefaultStatusResponseTest.kt +++ b/omnipod-dash/src/test/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/response/DefaultStatusResponseTest.kt @@ -1,5 +1,6 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response +import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.AlertType import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.DeliveryStatus import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.PodStatus import org.apache.commons.codec.DecoderException @@ -27,7 +28,7 @@ class DefaultStatusResponseTest { } /** - * response (hex) 08202EAA0C0A1D1905281000004387D3039A + * response (hex) 1D1905281000004387D3039A Status response: 29 Pod status: RUNNING_BELOW_MIN_VOLUME Basal active: true @@ -66,7 +67,7 @@ class DefaultStatusResponseTest { } /** - * response (hex) 08202EAA080A1D180519C00E0039A7FF8085 + * response (hex) 1D180519C00E0039A7FF8085 Status response: 29 Pod status: RUNNING_ABOVE_MIN_VOLUME Basal active: true @@ -103,4 +104,116 @@ class DefaultStatusResponseTest { Assert.assertEquals(1023.toShort(), response.reservoirPulsesRemaining) Assert.assertEquals(2611.toShort(), response.totalPulsesDelivered) } + + /** response (hex) 1D990714201F0042ED8801DE + Status response: 29 + Pod status: RUNNING_BELOW_MIN_VOLUME + Basal active: true + Temp Basal active: false + Immediate bolus active: false + Extended bolus active: true + Bolus pulses remaining: 31 + sequence number of last programing command: 4 + Total full pulses delivered: 3624 + Full reservoir pulses remaining: 392 + Time since activation: 4283 + */ + @Test @Throws(DecoderException::class) fun testValidResponseReservoirPulsesRemaining() { + val encoded = Hex.decodeHex("1D990714201F0042ED8801DE") + val response = DefaultStatusResponse(encoded) + Assert.assertArrayEquals(encoded, response.encoded) + Assert.assertNotSame(encoded, response.encoded) + Assert.assertEquals(ResponseType.DEFAULT_STATUS_RESPONSE, response.responseType) + Assert.assertEquals(ResponseType.DEFAULT_STATUS_RESPONSE.value, response.messageType) + Assert.assertEquals(DeliveryStatus.UNKNOWN, response.deliveryStatus) // Extended bolus active + Assert.assertEquals(PodStatus.RUNNING_BELOW_MIN_VOLUME, response.podStatus) + Assert.assertEquals(4.toShort(), response.sequenceNumberOfLastProgrammingCommand) + Assert.assertEquals(31.toShort(), response.bolusPulsesRemaining) + Assert.assertEquals(0, response.activeAlerts.size) + Assert.assertEquals(4283.toShort(), response.minutesSinceActivation) + Assert.assertEquals(392.toShort(), response.reservoirPulsesRemaining) + Assert.assertEquals(3624.toShort(), response.totalPulsesDelivered) + } + + /** response (hex) 1d68002601f400002bff0368 + Status response: 29 + Pod status: RUNNING_BELOW_MIN_VOLUME + Basal active: true + Temp Basal active: false + Immediate bolus active: false + Extended bolus active: true + Bolus pulses remaining: 31 + sequence number of last programing command: 4 + Total full pulses delivered: 3624 + Full reservoir pulses remaining: 392 + Time since activation: 4283 + */ + @Test @Throws(DecoderException::class) fun testValidResponseBolusPulsesRemaining3() { + val encoded = Hex.decodeHex("1d68002601f400002bff0368") + val response = DefaultStatusResponse(encoded) + Assert.assertArrayEquals(encoded, response.encoded) + Assert.assertNotSame(encoded, response.encoded) + Assert.assertEquals(500.toShort(), response.bolusPulsesRemaining) + Assert.assertEquals(0, response.activeAlerts.size) + } + /** response (hex) 1d28002e91e400002fff8256 + Status response: 29 + Pod status: RUNNING_BELOW_MIN_VOLUME + Basal active: true + Temp Basal active: false + Immediate bolus active: false + Extended bolus active: true + Bolus pulses remaining: 31 + sequence number of last programing command: 4 + Total full pulses delivered: 3624 + Full reservoir pulses remaining: 392 + Time since activation: 4283 + */ + @Test @Throws(DecoderException::class) fun testValidResponseBolusPulsesRemaining4() { + val encoded = Hex.decodeHex("1d28002e91e400002fff8256") + val response = DefaultStatusResponse(encoded) + Assert.assertArrayEquals(encoded, response.encoded) + Assert.assertNotSame(encoded, response.encoded) + Assert.assertEquals(484.toShort(), response.bolusPulsesRemaining) + Assert.assertEquals(0, response.activeAlerts.size) + } + + /* + 1D980559C820404393FF83AA + Pod status: RUNNING_ABOVE_MIN_VOLUME + Basal active: true + Temp Basal active: false + Immediate bolus active: false + Extended bolus active: true + Bolus pulses remaining: 32 + sequence number of last programing command: 9 + Total full pulses delivered: 2739 + Full reservoir pulses remaining: 1023 + Time since activation: 4324 + Alert 1 is InActive + Alert 2 is InActive + Alert 3 is InActive + Alert 4 is InActive + Alert 5 is InActive + Alert 6 is InActive + Alert 7 is Active + Occlusion alert active false + */ + @Test @Throws(DecoderException::class) fun testValidResponseActiveAlert1() { + val encoded = Hex.decodeHex("1D980559C820404393FF83AA") + val response = DefaultStatusResponse(encoded) + Assert.assertArrayEquals(encoded, response.encoded) + Assert.assertNotSame(encoded, response.encoded) + Assert.assertEquals(ResponseType.DEFAULT_STATUS_RESPONSE, response.responseType) + Assert.assertEquals(ResponseType.DEFAULT_STATUS_RESPONSE.value, response.messageType) + Assert.assertEquals(DeliveryStatus.UNKNOWN, response.deliveryStatus) + Assert.assertEquals(PodStatus.RUNNING_ABOVE_MIN_VOLUME, response.podStatus) + Assert.assertEquals(9.toShort(), response.sequenceNumberOfLastProgrammingCommand) + Assert.assertEquals(32.toShort(), response.bolusPulsesRemaining) + Assert.assertEquals(1, response.activeAlerts.size) + Assert.assertEquals(4324.toShort(), response.minutesSinceActivation) + Assert.assertEquals(1023.toShort(), response.reservoirPulsesRemaining) + Assert.assertEquals(2739.toShort(), response.totalPulsesDelivered) + Assert.assertEquals(true, response.activeAlerts.contains(AlertType.EXPIRATION)) + } }