This commit is contained in:
Milos Kozak 2021-10-10 23:28:14 +02:00
commit 036b64d018
15 changed files with 362 additions and 142 deletions

View file

@ -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.insight.LocalInsightPlugin
import info.nightscout.androidaps.plugins.pump.mdi.MDIPlugin import info.nightscout.androidaps.plugins.pump.mdi.MDIPlugin
import info.nightscout.androidaps.plugins.pump.medtronic.MedtronicPumpPlugin 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.pump.virtual.VirtualPumpPlugin
import info.nightscout.androidaps.plugins.sensitivity.SensitivityAAPSPlugin import info.nightscout.androidaps.plugins.sensitivity.SensitivityAAPSPlugin
import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref1Plugin import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref1Plugin
@ -154,11 +155,11 @@ abstract class PluginsModule {
@IntKey(150) @IntKey(150)
abstract fun bindMedtronicPumpPlugin(plugin: MedtronicPumpPlugin): PluginBase abstract fun bindMedtronicPumpPlugin(plugin: MedtronicPumpPlugin): PluginBase
// @Binds @Binds
// @PumpDriver @PumpDriver
// @IntoMap @IntoMap
// @IntKey(155) @IntKey(156)
// abstract fun bindOmnipodPumpPlugin(plugin: OmnipodErosPumpPlugin): PluginBase abstract fun bindOmnipodDashPumpPlugin(plugin: OmnipodDashPumpPlugin): PluginBase
@Binds @Binds
@PumpDriver @PumpDriver

View file

@ -3,6 +3,7 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash
import android.content.Context import android.content.Context
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import android.text.format.DateFormat
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.activities.ErrorHelperActivity.Companion.runAlarm import info.nightscout.androidaps.activities.ErrorHelperActivity.Companion.runAlarm
import info.nightscout.androidaps.data.DetailedBolusInfo 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.EventProfileSwitchChanged
import info.nightscout.androidaps.events.EventRefreshOverview import info.nightscout.androidaps.events.EventRefreshOverview
import info.nightscout.androidaps.events.EventTempBasalChange 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.interfaces.*
import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag 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.BolusType
import info.nightscout.androidaps.plugins.pump.omnipod.dash.history.data.TempBasalRecord 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.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.plugins.pump.omnipod.dash.util.mapProfileToBasalProgram
import info.nightscout.androidaps.queue.commands.Command import info.nightscout.androidaps.queue.commands.Command
import info.nightscout.androidaps.queue.commands.CustomCommand 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.FabricPrivacy
import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.TimeChangeType import info.nightscout.androidaps.utils.TimeChangeType
@ -49,8 +56,8 @@ import io.reactivex.Completable
import io.reactivex.Single import io.reactivex.Single
import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign import io.reactivex.rxkotlin.plusAssign
import io.reactivex.rxkotlin.subscribeBy
import org.json.JSONObject import org.json.JSONObject
import java.lang.Exception
import java.time.Duration import java.time.Duration
import java.time.ZonedDateTime import java.time.ZonedDateTime
import java.util.* import java.util.*
@ -73,6 +80,7 @@ class OmnipodDashPumpPlugin @Inject constructor(
private val context: Context, private val context: Context,
private val aapsSchedulers: AapsSchedulers, private val aapsSchedulers: AapsSchedulers,
private val fabricPrivacy: FabricPrivacy, private val fabricPrivacy: FabricPrivacy,
private val dateUtil: DateUtil,
injector: HasAndroidInjector, injector: HasAndroidInjector,
aapsLogger: AAPSLogger, aapsLogger: AAPSLogger,
@ -84,7 +92,7 @@ class OmnipodDashPumpPlugin @Inject constructor(
private val handler: Handler = Handler(Looper.getMainLooper()) private val handler: Handler = Handler(Looper.getMainLooper())
private lateinit var statusChecker: Runnable private lateinit var statusChecker: Runnable
var nextPodWarningCheck: Long = 0 private var nextPodWarningCheck: Long = 0
@Volatile var stopConnecting: CountDownLatch? = null @Volatile var stopConnecting: CountDownLatch? = null
private var disposables: CompositeDisposable = CompositeDisposable() private var disposables: CompositeDisposable = CompositeDisposable()
@ -127,8 +135,6 @@ class OmnipodDashPumpPlugin @Inject constructor(
val tbr = expectedState.temporaryBasal val tbr = expectedState.temporaryBasal
if (tbr == null || tbr.rate != 0.0) { if (tbr == null || tbr.rate != 0.0) {
aapsLogger.info(LTag.PUMP, "createFakeTBRWhenNoActivePod") aapsLogger.info(LTag.PUMP, "createFakeTBRWhenNoActivePod")
// calling connectNewPump() here because pumpSerial could have changed(from 4241 to "n/a")
pumpSync.connectNewPump()
pumpSync.syncTemporaryBasalWithPumpId( pumpSync.syncTemporaryBasalWithPumpId(
timestamp = System.currentTimeMillis(), timestamp = System.currentTimeMillis(),
rate = 0.0, rate = 0.0,
@ -137,7 +143,9 @@ class OmnipodDashPumpPlugin @Inject constructor(
type = PumpSync.TemporaryBasalType.PUMP_SUSPEND, type = PumpSync.TemporaryBasalType.PUMP_SUSPEND,
pumpId = Random.Default.nextLong(), // we don't use this, just make sure it's unique pumpId = Random.Default.nextLong(), // we don't use this, just make sure it's unique
pumpType = PumpType.OMNIPOD_DASH, 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 // Omnipod only reports reservoir level when there's < 1023 pulses left
return podStateManager.pulsesRemaining?.let { return podStateManager.pulsesRemaining?.let {
it * 0.05 it * PodConstants.POD_PULSE_BOLUS_UNITS
} ?: 75.0 } ?: 75.0
} }
@ -574,7 +582,7 @@ class OmnipodDashPumpPlugin @Inject constructor(
).filter { podEvent -> podEvent.isCommandSent() } ).filter { podEvent -> podEvent.isCommandSent() }
.map { pumpSyncBolusStart(requestedBolusAmount, detailedBolusInfo.bolusType) } .map { pumpSyncBolusStart(requestedBolusAmount, detailedBolusInfo.bolusType) }
.ignoreElements(), .ignoreElements(),
post = waitForBolusDeliveryToComplete(BOLUS_RETRIES, requestedBolusAmount, detailedBolusInfo.bolusType) post = waitForBolusDeliveryToComplete(requestedBolusAmount, detailedBolusInfo.bolusType)
.map { .map {
deliveredBolusAmount = it deliveredBolusAmount = it
aapsLogger.info(LTag.PUMP, "deliverTreatment: deliveredBolusAmount=$deliveredBolusAmount") aapsLogger.info(LTag.PUMP, "deliverTreatment: deliveredBolusAmount=$deliveredBolusAmount")
@ -638,14 +646,13 @@ class OmnipodDashPumpPlugin @Inject constructor(
} }
private fun waitForBolusDeliveryToComplete( private fun waitForBolusDeliveryToComplete(
maxTries: Int,
requestedBolusAmount: Double, requestedBolusAmount: Double,
bolusType: DetailedBolusInfo.BolusType bolusType: DetailedBolusInfo.BolusType
): Single<Double> = Single.defer { ): Single<Double> = Single.defer {
if (bolusCanceled && podStateManager.activeCommand != null) { if (bolusCanceled && podStateManager.activeCommand != null) {
var errorGettingStatus: Throwable? = null var errorGettingStatus: Throwable? = null
for (tries in 1..maxTries) { for (tries in 1..BOLUS_RETRIES) {
errorGettingStatus = getPodStatus().blockingGet() errorGettingStatus = getPodStatus().blockingGet()
if (errorGettingStatus != null) { if (errorGettingStatus != null) {
aapsLogger.debug(LTag.PUMP, "waitForBolusDeliveryToComplete errorGettingStatus=$errorGettingStatus") 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) updateBolusProgressDialog("Checking delivery status", 100)
val cmd = if (bolusCanceled) val cmd = if (bolusCanceled)
@ -724,13 +731,15 @@ class OmnipodDashPumpPlugin @Inject constructor(
} }
private fun estimateBolusDeliverySeconds(requestedBolusAmount: Double): Long { 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( private fun pumpSyncBolusStart(
requestedBolusAmount: Double, requestedBolusAmount: Double,
bolusType: DetailedBolusInfo.BolusType bolusType: DetailedBolusInfo.BolusType
): Boolean { ): Boolean {
require(requestedBolusAmount > 0) { "requestedBolusAmount has to be positive" }
val activeCommand = podStateManager.activeCommand val activeCommand = podStateManager.activeCommand
if (activeCommand == null) { if (activeCommand == null) {
throw IllegalArgumentException( throw IllegalArgumentException(
@ -776,7 +785,7 @@ class OmnipodDashPumpPlugin @Inject constructor(
) )
val ret = executeProgrammingCommand( val ret = executeProgrammingCommand(
pre = observeNoActiveTempBasal(true), pre = observeNoActiveTempBasal(),
historyEntry = history.createRecord( historyEntry = history.createRecord(
commandType = OmnipodCommandType.SET_TEMPORARY_BASAL, commandType = OmnipodCommandType.SET_TEMPORARY_BASAL,
tempBasalRecord = TempBasalRecord(duration = durationInMinutes, rate = absoluteRate) tempBasalRecord = TempBasalRecord(duration = durationInMinutes, rate = absoluteRate)
@ -846,38 +855,28 @@ class OmnipodDashPumpPlugin @Inject constructor(
return ret return ret
} }
private fun observeNoActiveTempBasal(enforceNew: Boolean): Completable { private fun observeNoActiveTempBasal(): Completable {
return Completable.defer { return Completable.defer {
when { if (podStateManager.deliveryStatus !in
podStateManager.deliveryStatus !in arrayOf(DeliveryStatus.TEMP_BASAL_ACTIVE, DeliveryStatus.BOLUS_AND_TEMP_BASAL_ACTIVE)
arrayOf(DeliveryStatus.TEMP_BASAL_ACTIVE, DeliveryStatus.BOLUS_AND_TEMP_BASAL_ACTIVE) -> { ) {
// TODO: what happens if we try to cancel nonexistent temp basal? // TODO: what happens if we try to cancel nonexistent temp basal?
aapsLogger.info(LTag.PUMP, "No temporary basal to cancel") aapsLogger.info(LTag.PUMP, "No temporary basal to cancel")
Completable.complete() Completable.complete()
} } else {
// enforceNew == true
!enforceNew -> aapsLogger.info(LTag.PUMP, "Canceling existing temp basal")
Completable.error( executeProgrammingCommand(
IllegalStateException( historyEntry = history.createRecord(OmnipodCommandType.CANCEL_TEMPORARY_BASAL),
"Temporary basal already active and enforceNew is not set." 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 { override fun getJSONStatus(profile: Profile, profileName: String, version: String): JSONObject {
// TODO val now = System.currentTimeMillis()
return JSONObject() 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 override val pumpDescription: PumpDescription = Companion.pumpDescription
@ -972,8 +1016,33 @@ class OmnipodDashPumpPlugin @Inject constructor(
} }
override fun shortStatus(veryShort: Boolean): String { override fun shortStatus(veryShort: Boolean): String {
// TODO if (!podStateManager.isActivationCompleted) {
return "TODO" 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 override val isFakingTempsByExtendedBoluses: Boolean
@ -1346,7 +1415,7 @@ class OmnipodDashPumpPlugin @Inject constructor(
pumpId = historyEntry.pumpId(), pumpId = historyEntry.pumpId(),
pumpType = PumpType.OMNIPOD_DASH, pumpType = PumpType.OMNIPOD_DASH,
pumpSerial = serialNumber(), pumpSerial = serialNumber(),
type = null // TODO: set the correct bolus type here!!! type = null
) )
} }
rxBus.send(EventDismissNotification(Notification.OMNIPOD_UNCERTAIN_SMB)) rxBus.send(EventDismissNotification(Notification.OMNIPOD_UNCERTAIN_SMB))
@ -1356,6 +1425,10 @@ class OmnipodDashPumpPlugin @Inject constructor(
if (confirmation.success) { if (confirmation.success) {
podStateManager.lastBolus?.run { podStateManager.lastBolus?.run {
val deliveredUnits = markComplete() val deliveredUnits = markComplete()
if (deliveredUnits < 0) {
aapsLogger.error(LTag.PUMP, "Negative delivered units!!! $deliveredUnits")
return
}
val bolusHistoryEntry = history.getById(historyId) val bolusHistoryEntry = history.getById(historyId)
val sync = pumpSync.syncBolusWithPumpId( val sync = pumpSync.syncBolusWithPumpId(
timestamp = bolusHistoryEntry.createdAt, timestamp = bolusHistoryEntry.createdAt,

View file

@ -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.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.*
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.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.response.*
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.state.OmnipodDashPodStateManager import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.state.OmnipodDashPodStateManager
import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.utils.rx.AapsSchedulers
@ -278,7 +281,7 @@ class OmnipodDashManagerImpl @Inject constructor(
.setUniqueId(podStateManager.uniqueId!!.toInt()) .setUniqueId(podStateManager.uniqueId!!.toInt())
.setSequenceNumber(podStateManager.messageSequenceNumber) .setSequenceNumber(podStateManager.messageSequenceNumber)
.setNonce(NONCE) .setNonce(NONCE)
.setNumberOfUnits(podStateManager.firstPrimeBolusVolume!! * 0.05) .setNumberOfUnits(podStateManager.firstPrimeBolusVolume!! * PodConstants.POD_PULSE_BOLUS_UNITS)
.setDelayBetweenPulsesInEighthSeconds(podStateManager.primePulseRate!!.toByte()) .setDelayBetweenPulsesInEighthSeconds(podStateManager.primePulseRate!!.toByte())
.setProgramReminder(ProgramReminder(atStart = false, atEnd = false, atInterval = 0)) .setProgramReminder(ProgramReminder(atStart = false, atEnd = false, atInterval = 0))
.build(), .build(),
@ -380,7 +383,7 @@ class OmnipodDashManagerImpl @Inject constructor(
) )
observables.add( observables.add(
observeSendProgramBolusCommand( observeSendProgramBolusCommand(
podStateManager.secondPrimeBolusVolume!! * 0.05, podStateManager.secondPrimeBolusVolume!! * PodConstants.POD_PULSE_BOLUS_UNITS,
podStateManager.primePulseRate!!.toByte(), podStateManager.primePulseRate!!.toByte(),
confirmationBeeps = false, confirmationBeeps = false,
completionBeeps = false completionBeeps = false
@ -394,10 +397,10 @@ class OmnipodDashManagerImpl @Inject constructor(
AlertConfiguration( AlertConfiguration(
AlertType.EXPIRATION, AlertType.EXPIRATION,
enabled = true, enabled = true,
durationInMinutes = TimeUnit.HOURS.toMinutes(7).toShort(), durationInMinutes = TimeUnit.HOURS.toMinutes(POD_EXPIRATION_ALERT_HOURS_DURATION).toShort(),
autoOff = false, autoOff = false,
AlertTrigger.TimerTrigger( AlertTrigger.TimerTrigger(
TimeUnit.HOURS.toMinutes(72).toShort() TimeUnit.HOURS.toMinutes(POD_EXPIRATION_ALERT_HOURS).toShort()
), // FIXME use activation time ), // FIXME use activation time
BeepType.FOUR_TIMES_BIP_BEEP, BeepType.FOUR_TIMES_BIP_BEEP,
BeepRepetitionType.XXX3 BeepRepetitionType.XXX3
@ -408,7 +411,7 @@ class OmnipodDashManagerImpl @Inject constructor(
durationInMinutes = 0, durationInMinutes = 0,
autoOff = false, autoOff = false,
AlertTrigger.TimerTrigger( AlertTrigger.TimerTrigger(
TimeUnit.HOURS.toMinutes(79).toShort() TimeUnit.HOURS.toMinutes(POD_EXPIRATION_IMMINENT_ALERT_HOURS).toShort()
), // FIXME use activation time ), // FIXME use activation time
BeepType.FOUR_TIMES_BIP_BEEP, BeepType.FOUR_TIMES_BIP_BEEP,
BeepRepetitionType.XXX4 BeepRepetitionType.XXX4

View file

@ -4,5 +4,5 @@ import info.nightscout.androidaps.extensions.toHex
class IncorrectPacketException( class IncorrectPacketException(
val payload: ByteArray, val payload: ByteArray,
val expectedIndex: Byte? = null private val expectedIndex: Byte? = null
) : Exception("Invalid payload: ${payload.toHex()}. Expected index: $expectedIndex") ) : Exception("Invalid payload: ${payload.toHex()}. Expected index: $expectedIndex")

View file

@ -29,7 +29,7 @@ class MessageIO(
private val dataBleIO: DataBleIO, private val dataBleIO: DataBleIO,
) { ) {
val receivedOutOfOrder = LinkedHashMap<Byte, ByteArray>() private val receivedOutOfOrder = LinkedHashMap<Byte, ByteArray>()
var maxMessageReadTries = 3 var maxMessageReadTries = 3
var messageReadTries = 0 var messageReadTries = 0

View file

@ -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.Id
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.CouldNotParseMessageException 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 import java.nio.ByteBuffer
/*** /***
@ -80,13 +81,13 @@ data class MessagePacket(
if (payload.copyOfRange(0, 2).decodeToString() != MAGIC_PATTERN) { if (payload.copyOfRange(0, 2).decodeToString() != MAGIC_PATTERN) {
throw CouldNotParseMessageException(payload) throw CouldNotParseMessageException(payload)
} }
val f1 = Flag(payload[2].toInt()) val f1 = Flag(payload[2].toInt() and 0xff)
val sas = f1.get(3) != 0 val sas = f1.get(3) != 0
val tfs = f1.get(4) != 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 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 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 ack = f2.get(0) != 0
val priority = f2.get(1) != 0 val priority = f2.get(1) != 0
val lastMessage = f2.get(2) != 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 internal fun Byte.toUnsignedInt() = this.toInt() and 0xff
private fun ByteArray.assertSizeAtLeast(size: Int) { private fun ByteArray.assertSizeAtLeast(size: Int) {

View file

@ -4,6 +4,19 @@ import java.time.Duration
class PodConstants { class PodConstants {
companion object { 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
} }
} }

View file

@ -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.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.AlertUtil
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.util.byValue import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.util.byValue
import java.nio.ByteBuffer
import java.util.* import java.util.*
import kotlin.experimental.and import kotlin.experimental.and
@ -13,18 +14,21 @@ class DefaultStatusResponse(
) : ResponseBase(ResponseType.DEFAULT_STATUS_RESPONSE, encoded) { ) : ResponseBase(ResponseType.DEFAULT_STATUS_RESPONSE, encoded) {
val messageType: Byte = encoded[0] 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() private var first4bytes = ByteBuffer.wrap(byteArrayOf(encoded[2], encoded[3], encoded[4], encoded[5])).int
val bolusPulsesRemaining: Short = ((encoded[4] and 0x07 shl 10 or (encoded[5].toInt() and 0xff) and 2047).toShort()) 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<AlertType> = val activeAlerts: EnumSet<AlertType> =
AlertUtil.decodeAlertSet((encoded[6].toInt() and 0xff shl 1 or (encoded[7] ushr 7)).toByte()) AlertUtil.decodeAlertSet((last4bytes ushr 10 ushr 13 and 0xFF).toByte())
val minutesSinceActivation: Short = val minutesSinceActivation: Short = ((last4bytes ushr 10 and 0x1FFF)).toShort()
(encoded[7] and 0x7f shl 6 or (encoded[8].toInt() and 0xff ushr 2 and 0x3f)).toShort() val reservoirPulsesRemaining: Short = (last4bytes and 0X3FF).toShort()
val reservoirPulsesRemaining: Short = (encoded[8] shl 8 or encoded[9].toInt() and 0x3ff).toShort()
override fun toString(): String { override fun toString(): String {
return "DefaultStatusResponse(" + return "DefaultStatusResponse(" +

View file

@ -311,7 +311,7 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
private fun updateLastBolusFromResponse(bolusPulsesRemaining: Short) { private fun updateLastBolusFromResponse(bolusPulsesRemaining: Short) {
podState.lastBolus?.run { podState.lastBolus?.run {
val remainingUnits = bolusPulsesRemaining.toDouble() * 0.05 val remainingUnits = bolusPulsesRemaining.toDouble() * PodConstants.POD_PULSE_BOLUS_UNITS
this.bolusUnitsRemaining = remainingUnits this.bolusUnitsRemaining = remainingUnits
if (remainingUnits == 0.0) { if (remainingUnits == 0.0) {
this.deliveryComplete = true this.deliveryComplete = true
@ -627,11 +627,13 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
} }
override fun connectionSuccessRatio(): Float { override fun connectionSuccessRatio(): Float {
val attempts = connectionAttempts if (connectionAttempts == 0) {
if (attempts == 0) {
return 0.0F 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() { override fun reset() {

View file

@ -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.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.ActivationProgress
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.AlertType 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.plugins.pump.omnipod.dash.driver.pod.state.OmnipodDashPodStateManager
import info.nightscout.androidaps.queue.Callback import info.nightscout.androidaps.queue.Callback
import info.nightscout.androidaps.queue.events.EventQueueChanged import info.nightscout.androidaps.queue.events.EventQueueChanged
@ -68,7 +69,6 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
@Inject lateinit var buildHelper: BuildHelper @Inject lateinit var buildHelper: BuildHelper
companion object { companion object {
private const val REFRESH_INTERVAL_MILLIS = 15 * 1000L // 15 seconds private const val REFRESH_INTERVAL_MILLIS = 15 * 1000L // 15 seconds
private const val PLACEHOLDER = "-" private const val PLACEHOLDER = "-"
private const val MAX_TIME_DEVIATION_MINUTES = 10L private const val MAX_TIME_DEVIATION_MINUTES = 10L
@ -372,7 +372,7 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
if (podStateManager.isActivationCompleted && podStateManager.pulsesDelivered != null) { if (podStateManager.isActivationCompleted && podStateManager.pulsesDelivered != null) {
resourceHelper.gs( resourceHelper.gs(
R.string.omnipod_common_overview_total_delivered_value, R.string.omnipod_common_overview_total_delivered_value,
podStateManager.pulsesDelivered!! * 0.05 (podStateManager.pulsesDelivered!! * PodConstants.POD_PULSE_BOLUS_UNITS)
) )
} else { } else {
PLACEHOLDER PLACEHOLDER
@ -387,11 +387,11 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
// TODO // TODO
// val lowReservoirThreshold = (omnipodAlertUtil.lowReservoirAlertUnits // val lowReservoirThreshold = (omnipodAlertUtil.lowReservoirAlertUnits
// ?: OmnipodConstants.DEFAULT_MAX_RESERVOIR_ALERT_THRESHOLD).toDouble() // ?: 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( podInfoBinding.reservoir.text = resourceHelper.gs(
R.string.omnipod_common_overview_reservoir_value, R.string.omnipod_common_overview_reservoir_value,
(podStateManager.pulsesRemaining!! * 0.05) (podStateManager.pulsesRemaining!! * PodConstants.POD_PULSE_BOLUS_UNITS)
) )
podInfoBinding.reservoir.setTextColor( podInfoBinding.reservoir.setTextColor(
if (podStateManager.pulsesRemaining!! < lowReservoirThreshold) { if (podStateManager.pulsesRemaining!! < lowReservoirThreshold) {

View file

@ -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.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP import info.nightscout.androidaps.utils.sharedPreferences.SP
import io.reactivex.Single import io.reactivex.Single
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.addTo
import io.reactivex.rxkotlin.plusAssign import io.reactivex.rxkotlin.plusAssign
import io.reactivex.rxkotlin.subscribeBy import io.reactivex.rxkotlin.subscribeBy
import javax.inject.Inject import javax.inject.Inject

View file

@ -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.R
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.OmnipodDashManager 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.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.I8n
import info.nightscout.androidaps.plugins.pump.omnipod.dash.util.mapProfileToBasalProgram import info.nightscout.androidaps.plugins.pump.omnipod.dash.util.mapProfileToBasalProgram
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP import info.nightscout.androidaps.utils.sharedPreferences.SP
import io.reactivex.Single import io.reactivex.Single
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.addTo
import io.reactivex.rxkotlin.plusAssign import io.reactivex.rxkotlin.plusAssign
import io.reactivex.rxkotlin.subscribeBy import io.reactivex.rxkotlin.subscribeBy
import javax.inject.Inject import javax.inject.Inject
@ -67,43 +66,48 @@ class DashInsertCannulaViewModel @Inject constructor(
super.disposable += omnipodManager.activatePodPart2(basalProgram, expirationHoursBeforeShutdown) super.disposable += omnipodManager.activatePodPart2(basalProgram, expirationHoursBeforeShutdown)
.subscribeBy( .subscribeBy(
onNext = { podEvent -> onNext = { podEvent ->
logger.debug( logger.debug(
LTag.PUMP, LTag.PUMP,
"Received PodEvent in Pod activation part 2: $podEvent" "Received PodEvent in Pod activation part 2: $podEvent"
) )
}, },
onError = { throwable -> onError = { throwable ->
logger.error(LTag.PUMP, "Error in Pod activation part 2", throwable) logger.error(LTag.PUMP, "Error in Pod activation part 2", throwable)
source.onSuccess(PumpEnactResult(injector).success(false).comment(I8n.textFromException(throwable, resourceHelper))) source.onSuccess(PumpEnactResult(injector).success(false).comment(I8n.textFromException(throwable, resourceHelper)))
}, },
onComplete = { onComplete = {
logger.debug("Pod activation part 2 completed") logger.debug("Pod activation part 2 completed")
podStateManager.basalProgram = basalProgram podStateManager.basalProgram = basalProgram
pumpSync.connectNewPump()
pumpSync.insertTherapyEventIfNewWithTimestamp( pumpSync.syncStopTemporaryBasalWithPumpId(
timestamp = System.currentTimeMillis(), timestamp = System.currentTimeMillis(),
type = DetailedBolusInfo.EventType.CANNULA_CHANGE, endPumpId = System.currentTimeMillis(),
pumpType = PumpType.OMNIPOD_DASH, pumpType = PumpType.OMNIPOD_DASH,
pumpSerial = podStateManager.uniqueId?.toString() ?: "n/a" pumpSerial = Constants.PUMP_SERIAL_FOR_FAKE_TBR // cancel the fake TBR with the same pump
) // serial that it was created with
pumpSync.insertTherapyEventIfNewWithTimestamp( )
timestamp = System.currentTimeMillis(),
type = DetailedBolusInfo.EventType.INSULIN_CHANGE, pumpSync.connectNewPump()
pumpType = PumpType.OMNIPOD_DASH,
pumpSerial = podStateManager.uniqueId?.toString() ?: "n/a" pumpSync.insertTherapyEventIfNewWithTimestamp(
) timestamp = System.currentTimeMillis(),
pumpSync.syncStopTemporaryBasalWithPumpId( type = DetailedBolusInfo.EventType.CANNULA_CHANGE,
timestamp = System.currentTimeMillis(), pumpType = PumpType.OMNIPOD_DASH,
endPumpId = System.currentTimeMillis(), pumpSerial = podStateManager.uniqueId?.toString() ?: "n/a"
pumpType = PumpType.OMNIPOD_DASH, )
pumpSerial = podStateManager.uniqueId?.toString() ?: "n/a" pumpSync.insertTherapyEventIfNewWithTimestamp(
) timestamp = System.currentTimeMillis(),
podStateManager.updateExpirationAlertSettings(expirationReminderEnabled, expirationHours) type = DetailedBolusInfo.EventType.INSULIN_CHANGE,
rxBus.send(EventDismissNotification(Notification.OMNIPOD_POD_NOT_ATTACHED)) pumpType = PumpType.OMNIPOD_DASH,
source.onSuccess(PumpEnactResult(injector).success(true)) pumpSerial = podStateManager.uniqueId?.toString() ?: "n/a"
} )
)
podStateManager.updateExpirationAlertSettings(expirationReminderEnabled, expirationHours)
rxBus.send(EventDismissNotification(Notification.OMNIPOD_POD_NOT_ATTACHED))
source.onSuccess(PumpEnactResult(injector).success(true))
}
)
} }
} }

View file

@ -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"
}
}

View file

@ -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
}
}

View file

@ -1,5 +1,6 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response 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.DeliveryStatus
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.PodStatus import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.PodStatus
import org.apache.commons.codec.DecoderException import org.apache.commons.codec.DecoderException
@ -27,7 +28,7 @@ class DefaultStatusResponseTest {
} }
/** /**
* response (hex) 08202EAA0C0A1D1905281000004387D3039A * response (hex) 1D1905281000004387D3039A
Status response: 29 Status response: 29
Pod status: RUNNING_BELOW_MIN_VOLUME Pod status: RUNNING_BELOW_MIN_VOLUME
Basal active: true Basal active: true
@ -66,7 +67,7 @@ class DefaultStatusResponseTest {
} }
/** /**
* response (hex) 08202EAA080A1D180519C00E0039A7FF8085 * response (hex) 1D180519C00E0039A7FF8085
Status response: 29 Status response: 29
Pod status: RUNNING_ABOVE_MIN_VOLUME Pod status: RUNNING_ABOVE_MIN_VOLUME
Basal active: true Basal active: true
@ -103,4 +104,116 @@ class DefaultStatusResponseTest {
Assert.assertEquals(1023.toShort(), response.reservoirPulsesRemaining) Assert.assertEquals(1023.toShort(), response.reservoirPulsesRemaining)
Assert.assertEquals(2611.toShort(), response.totalPulsesDelivered) 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))
}
} }