Merge pull request #32 from 0pen-dash/avereha/temp-basals

Avereha/temp basals
This commit is contained in:
Andrei Vereha 2021-06-06 20:54:17 +02:00 committed by GitHub
commit 9036544516
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 350 additions and 229 deletions

View file

@ -17,6 +17,7 @@ import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.event.PodEven
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.BeepType import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.BeepType
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.ResponseType import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.ResponseType
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.state.CommandConfirmed
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.history.DashHistory import info.nightscout.androidaps.plugins.pump.omnipod.dash.history.DashHistory
import info.nightscout.androidaps.plugins.pump.omnipod.dash.history.data.BolusRecord import info.nightscout.androidaps.plugins.pump.omnipod.dash.history.data.BolusRecord
@ -25,9 +26,11 @@ import info.nightscout.androidaps.plugins.pump.omnipod.dash.history.data.TempBas
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.mapProfileToBasalProgram import info.nightscout.androidaps.plugins.pump.omnipod.dash.util.mapProfileToBasalProgram
import info.nightscout.androidaps.queue.commands.CustomCommand import info.nightscout.androidaps.queue.commands.CustomCommand
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.TimeChangeType import info.nightscout.androidaps.utils.TimeChangeType
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.Completable
import io.reactivex.Observable import io.reactivex.Observable
import io.reactivex.Single import io.reactivex.Single
import io.reactivex.rxkotlin.blockingSubscribeBy import io.reactivex.rxkotlin.blockingSubscribeBy
@ -44,6 +47,7 @@ class OmnipodDashPumpPlugin @Inject constructor(
private val sp: SP, private val sp: SP,
private val profileFunction: ProfileFunction, private val profileFunction: ProfileFunction,
private val history: DashHistory, private val history: DashHistory,
private val pumpSync: PumpSync,
injector: HasAndroidInjector, injector: HasAndroidInjector,
aapsLogger: AAPSLogger, aapsLogger: AAPSLogger,
resourceHelper: ResourceHelper, resourceHelper: ResourceHelper,
@ -151,12 +155,12 @@ class OmnipodDashPumpPlugin @Inject constructor(
} }
override fun setNewBasalProfile(profile: Profile): PumpEnactResult { override fun setNewBasalProfile(profile: Profile): PumpEnactResult {
return executeProgrammingCommand( return executeSimpleProgrammingCommand(
history.createRecord( history.createRecord(
commandType = OmnipodCommandType.SET_BASAL_PROFILE commandType = OmnipodCommandType.SET_BASAL_PROFILE
), ),
omnipodManager.setBasalProgram(mapProfileToBasalProgram(profile)) omnipodManager.setBasalProgram(mapProfileToBasalProgram(profile)).ignoreElements()
) ).toPumpEnactResult()
} }
override fun isThisProfileSet(profile: Profile): Boolean = podStateManager.basalProgram?.let { override fun isThisProfileSet(profile: Profile): Boolean = podStateManager.basalProgram?.let {
@ -240,10 +244,10 @@ class OmnipodDashPumpPlugin @Inject constructor(
override fun stopBolusDelivering() { override fun stopBolusDelivering() {
// TODO update Treatments (?) // TODO update Treatments (?)
executeProgrammingCommand( executeSimpleProgrammingCommand(
history.createRecord(OmnipodCommandType.CANCEL_BOLUS), history.createRecord(OmnipodCommandType.CANCEL_BOLUS),
omnipodManager.stopBolus(), omnipodManager.stopBolus().ignoreElements()
) ).toPumpEnactResult()
} }
override fun setTempBasalAbsolute( override fun setTempBasalAbsolute(
@ -253,25 +257,85 @@ class OmnipodDashPumpPlugin @Inject constructor(
enforceNew: Boolean, enforceNew: Boolean,
tbrType: PumpSync.TemporaryBasalType tbrType: PumpSync.TemporaryBasalType
): PumpEnactResult { ): PumpEnactResult {
// TODO update Treatments val tempBasalBeeps = sp.getBoolean(R.string.key_omnipod_common_tbr_beeps_enabled, false)
// TODO check for existing basal
// check existing basal(locally and maybe? get status) return executeSimpleProgrammingCommand(
// if enforceNew -> cancel it() historyEntry = history.createRecord(
// else -> return error that existing basal is running
// set new temp basal
// update treatments
// profit
return executeProgrammingCommand(
history.createRecord(
commandType = OmnipodCommandType.SET_TEMPORARY_BASAL, commandType = OmnipodCommandType.SET_TEMPORARY_BASAL,
tempBasalRecord = TempBasalRecord(duration = durationInMinutes, rate = absoluteRate) tempBasalRecord = TempBasalRecord(duration = durationInMinutes, rate = absoluteRate)
), ),
omnipodManager.setTempBasal( command = omnipodManager.setTempBasal(
absoluteRate, absoluteRate,
durationInMinutes.toShort() durationInMinutes.toShort(),
tempBasalBeeps
)
.filter { podEvent -> podEvent is PodEvent.CommandSent }
.map { pumpSyncTempBasal(it, tbrType) }
.ignoreElements(),
pre = observeNoActiveTempBasal()
).toPumpEnactResult()
}
private fun pumpSyncTempBasal(
podEvent: PodEvent,
tbrType: PumpSync.TemporaryBasalType
): Boolean {
val activeCommand = podStateManager.activeCommand
if (activeCommand == null || podEvent !is PodEvent.CommandSent) {
throw IllegalArgumentException(
"No active command or illegal podEvent: " +
"activeCommand=$activeCommand" +
"podEvent=$podEvent"
)
}
val historyEntry = history.getById(activeCommand.historyId)
val record = historyEntry.record
if (record == null || !(record is TempBasalRecord)) {
throw IllegalArgumentException("Illegal recording in history: $record. Expected a temp basal")
}
val ret = pumpSync.syncTemporaryBasalWithPumpId(
timestamp = historyEntry.createdAt,
rate = record.rate,
duration = T.mins(record.duration.toLong()).msecs(),
isAbsolute = true,
type = tbrType,
pumpId = historyEntry.pumpId(),
pumpType = PumpType.OMNIPOD_DASH,
pumpSerial = serialNumber()
)
aapsLogger.debug(LTag.PUMP, "Pump sync temp basal: $ret")
return ret
}
private fun observeNoActiveTempBasal(): Completable {
return Completable.defer {
val expectedState = pumpSync.expectedPumpState()
if (expectedState.temporaryBasal == null) {
aapsLogger.info(LTag.PUMP, "No temporary basal to cancel")
Completable.complete()
} else {
// enforceNew == true
aapsLogger.info(LTag.PUMP, "Canceling existing temp basal")
executeSimpleProgrammingCommand(
history.createRecord(OmnipodCommandType.CANCEL_TEMPORARY_BASAL),
omnipodManager.stopTempBasal().ignoreElements()
)
}
}
}
private fun observeActiveTempBasal(): Completable {
return Completable.defer {
if (podStateManager.tempBasalActive)
Completable.complete()
else
Completable.error(
java.lang.IllegalStateException(
"There is no active basal to cancel"
) )
) )
} }
}
override fun setTempBasalPercent( override fun setTempBasalPercent(
percent: Int, percent: Int,
@ -292,12 +356,48 @@ class OmnipodDashPumpPlugin @Inject constructor(
} }
override fun cancelTempBasal(enforceNew: Boolean): PumpEnactResult { override fun cancelTempBasal(enforceNew: Boolean): PumpEnactResult {
// TODO update Treatments return executeSimpleProgrammingCommand(
return executeProgrammingCommand( historyEntry = history.createRecord(OmnipodCommandType.CANCEL_TEMPORARY_BASAL),
history.createRecord(OmnipodCommandType.CANCEL_TEMPORARY_BASAL), command = omnipodManager.stopTempBasal().ignoreElements(),
omnipodManager.stopTempBasal() pre = observeActiveTempBasal(),
).toPumpEnactResult()
}
fun Completable.toPumpEnactResult(): PumpEnactResult {
return this.toSingleDefault(PumpEnactResult(injector).success(true).enacted(true))
.onErrorReturnItem(PumpEnactResult(injector).success(false).enacted(false))
.blockingGet()
}
private fun handleCommandConfirmation(confirmation: CommandConfirmed) {
val historyEntry = history.getById(confirmation.historyId)
when (historyEntry.commandType) {
OmnipodCommandType.CANCEL_TEMPORARY_BASAL ->
// We can't invalidate this command,
// and this is why it is pumpSync-ed at this point
if (confirmation.success) {
pumpSync.syncStopTemporaryBasalWithPumpId(
historyEntry.createdAt,
historyEntry.pumpId(),
PumpType.OMNIPOD_DASH,
serialNumber()
) )
} }
OmnipodCommandType.SET_TEMPORARY_BASAL ->
// This treatment was synced before sending the command
if (!confirmation.success) {
// TODO: the ID here is the temp basal id, not the pumpId!!
pumpSync.invalidateTemporaryBasal(historyEntry.pumpId())
}
else ->
aapsLogger.warn(
LTag.PUMP,
"Will not sync confirmed command of type: $historyEntry and " +
"succes: ${confirmation.success}"
)
}
}
override fun cancelExtendedBolus(): PumpEnactResult { override fun cancelExtendedBolus(): PumpEnactResult {
// TODO i18n // TODO i18n
@ -414,26 +514,26 @@ class OmnipodDashPumpPlugin @Inject constructor(
} }
private fun suspendDelivery(): PumpEnactResult { private fun suspendDelivery(): PumpEnactResult {
return executeProgrammingCommand( return executeSimpleProgrammingCommand(
history.createRecord(OmnipodCommandType.RESUME_DELIVERY), history.createRecord(OmnipodCommandType.RESUME_DELIVERY),
omnipodManager.suspendDelivery() omnipodManager.suspendDelivery().ignoreElements()
) ).toPumpEnactResult()
} }
private fun resumeDelivery(): PumpEnactResult { private fun resumeDelivery(): PumpEnactResult {
return profileFunction.getProfile()?.let { return profileFunction.getProfile()?.let {
executeProgrammingCommand( executeSimpleProgrammingCommand(
history.createRecord(OmnipodCommandType.RESUME_DELIVERY), history.createRecord(OmnipodCommandType.RESUME_DELIVERY),
omnipodManager.setBasalProgram(mapProfileToBasalProgram(it)) omnipodManager.setBasalProgram(mapProfileToBasalProgram(it)).ignoreElements()
) ).toPumpEnactResult()
} ?: PumpEnactResult(injector).success(false).enacted(false).comment("No profile active") // TODO i18n } ?: PumpEnactResult(injector).success(false).enacted(false).comment("No profile active") // TODO i18n
} }
private fun deactivatePod(): PumpEnactResult { private fun deactivatePod(): PumpEnactResult {
return executeProgrammingCommand( return executeSimpleProgrammingCommand(
history.createRecord(OmnipodCommandType.DEACTIVATE_POD), history.createRecord(OmnipodCommandType.DEACTIVATE_POD),
omnipodManager.deactivatePod() omnipodManager.deactivatePod().ignoreElements()
) ).toPumpEnactResult()
} }
private fun handleTimeChange(): PumpEnactResult { private fun handleTimeChange(): PumpEnactResult {
@ -447,10 +547,10 @@ class OmnipodDashPumpPlugin @Inject constructor(
} }
private fun playTestBeep(): PumpEnactResult { private fun playTestBeep(): PumpEnactResult {
return executeProgrammingCommand( return executeSimpleProgrammingCommand(
history.createRecord(OmnipodCommandType.PLAY_TEST_BEEP), history.createRecord(OmnipodCommandType.PLAY_TEST_BEEP),
omnipodManager.playBeep(BeepType.LONG_SINGLE_BEEP) omnipodManager.playBeep(BeepType.LONG_SINGLE_BEEP).ignoreElements()
) ).toPumpEnactResult()
} }
override fun timezoneOrDSTChanged(timeChangeType: TimeChangeType) { override fun timezoneOrDSTChanged(timeChangeType: TimeChangeType) {
@ -474,50 +574,27 @@ class OmnipodDashPumpPlugin @Inject constructor(
commandQueue.customCommand(CommandHandleTimeChange(false), null) commandQueue.customCommand(CommandHandleTimeChange(false), null)
} }
private fun observeAddNewActiveCommandToHistory(observeCreateHistoryEntry: Single<String>): Observable<PodEvent> { private fun executeSimpleProgrammingCommand(
return observeCreateHistoryEntry.flatMapObservable { historyEntry: Single<String>,
podStateManager.createActiveCommand(it).toObservable<PodEvent>() command: Completable,
} pre: Completable = Completable.complete(),
} ): Completable {
return Completable.concat(
private fun executeProgrammingCommand(
observeCreateHistoryEntry: Single<String>,
command: Observable<PodEvent>
): PumpEnactResult {
return Single.create<PumpEnactResult> { source ->
Observable.concat(
listOf( listOf(
podStateManager.observeNoActiveCommand(), pre,
observeAddNewActiveCommandToHistory(observeCreateHistoryEntry), podStateManager.observeNoActiveCommand().ignoreElements(),
command, historyEntry
history.updateFromState(podStateManager).toObservable(), .flatMap { podStateManager.createActiveCommand(it) }
podStateManager.updateActiveCommand().toObservable(), .ignoreElement(),
command.doOnError {
podStateManager.activeCommand?.sendError = it
aapsLogger.error(LTag.PUMP, "Error executing command", it)
}.onErrorComplete(),
history.updateFromState(podStateManager),
podStateManager.updateActiveCommand()
.map { handleCommandConfirmation(it) }
.ignoreElement()
) )
).subscribeBy(
onNext = { podEvent ->
aapsLogger.debug(
LTag.PUMP,
"Received PodEvent: $podEvent"
) )
},
onError = { throwable ->
aapsLogger.error(LTag.PUMP, "Error executing command", throwable)
// Here we assume that onError will be called only BEFORE we manage to send a command
// If it gets called later, we will have the command as "not sent" in history and will not try to
// get it's final status, even if it was send
podStateManager.maybeMarkActiveCommandFailed()
source.onSuccess(
PumpEnactResult(injector).success(false).enacted(false).comment(throwable.toString())
)
},
onComplete = {
aapsLogger.debug("Command completed")
source.onSuccess(
PumpEnactResult(injector).success(true).enacted(true)
)
}
)
}.blockingGet()
} }
} }

View file

@ -24,7 +24,7 @@ interface OmnipodDashManager {
fun setTime(): Observable<PodEvent> fun setTime(): Observable<PodEvent>
fun setTempBasal(rate: Double, durationInMinutes: Short): Observable<PodEvent> fun setTempBasal(rate: Double, durationInMinutes: Short, tempBasalBeeps: Boolean): Observable<PodEvent>
fun stopTempBasal(): Observable<PodEvent> fun stopTempBasal(): Observable<PodEvent>

View file

@ -460,14 +460,14 @@ class OmnipodDashManagerImpl @Inject constructor(
return Observable.empty() return Observable.empty()
} }
private fun observeSendProgramTempBasalCommand(rate: Double, durationInMinutes: Short): Observable<PodEvent> { private fun observeSendProgramTempBasalCommand(rate: Double, durationInMinutes: Short, tempBasalBeeps: Boolean): Observable<PodEvent> {
return Observable.defer { return Observable.defer {
// TODO cancel current temp basal (if active)
bleManager.sendCommand( bleManager.sendCommand(
ProgramTempBasalCommand.Builder() ProgramTempBasalCommand.Builder()
.setSequenceNumber(podStateManager.messageSequenceNumber) .setSequenceNumber(podStateManager.messageSequenceNumber)
.setUniqueId(podStateManager.uniqueId!!.toInt()) .setUniqueId(podStateManager.uniqueId!!.toInt())
.setNonce(NONCE) .setNonce(NONCE)
.setProgramReminder(ProgramReminder(tempBasalBeeps, tempBasalBeeps, 0))
.setRateInUnitsPerHour(rate) .setRateInUnitsPerHour(rate)
.setDurationInMinutes(durationInMinutes) .setDurationInMinutes(durationInMinutes)
.build(), .build(),
@ -476,11 +476,11 @@ class OmnipodDashManagerImpl @Inject constructor(
} }
} }
override fun setTempBasal(rate: Double, durationInMinutes: Short): Observable<PodEvent> { override fun setTempBasal(rate: Double, durationInMinutes: Short, tempBasalBeeps: Boolean): Observable<PodEvent> {
return Observable.concat( return Observable.concat(
observePodRunning, observePodRunning,
observeConnectToPod, observeConnectToPod,
observeSendProgramTempBasalCommand(rate, durationInMinutes) observeSendProgramTempBasalCommand(rate, durationInMinutes, tempBasalBeeps)
) )
// TODO these would be common for any observable returned in a public function in this class // TODO these would be common for any observable returned in a public function in this class
.doOnNext(PodEventInterceptor()) .doOnNext(PodEventInterceptor())
@ -700,7 +700,7 @@ class OmnipodDashManagerImpl @Inject constructor(
inner class ErrorInterceptor : Consumer<Throwable> { inner class ErrorInterceptor : Consumer<Throwable> {
override fun accept(throwable: Throwable) { override fun accept(throwable: Throwable) {
logger.debug(LTag.PUMP, "Intercepted error in OmnipodDashManagerImpl: ${throwable.javaClass.simpleName}") logger.debug(LTag.PUMP, "Intercepted error in OmnipodDashManagerImpl: $throwable")
} }
} }

View file

@ -1,6 +1,6 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.status.ConnectionStatus import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.session.ConnectionState
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.event.PodEvent import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.event.PodEvent
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.Command import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.Command
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.Response import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.Response
@ -11,7 +11,7 @@ interface OmnipodDashBleManager {
fun sendCommand(cmd: Command, responseType: KClass<out Response>): Observable<PodEvent> fun sendCommand(cmd: Command, responseType: KClass<out Response>): Observable<PodEvent>
fun getStatus(): ConnectionStatus fun getStatus(): ConnectionState
fun connect(): Observable<PodEvent> fun connect(): Observable<PodEvent>

View file

@ -11,7 +11,6 @@ import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptio
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.pair.LTKExchanger import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.pair.LTKExchanger
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.scan.PodScanner import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.scan.PodScanner
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.session.* import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.session.*
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.status.ConnectionStatus
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.event.PodEvent import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.event.PodEvent
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.Command import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.Command
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.Response import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.Response
@ -34,7 +33,6 @@ class OmnipodDashBleManagerImpl @Inject constructor(
context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
private val bluetoothAdapter: BluetoothAdapter = bluetoothManager.adapter private val bluetoothAdapter: BluetoothAdapter = bluetoothManager.adapter
private var connection: Connection? = null private var connection: Connection? = null
private var status: ConnectionStatus = ConnectionStatus.IDLE
private val ids = Ids(podState) private val ids = Ids(podState)
override fun sendCommand(cmd: Command, responseType: KClass<out Response>): Observable<PodEvent> = override fun sendCommand(cmd: Command, responseType: KClass<out Response>): Observable<PodEvent> =
@ -85,13 +83,9 @@ class OmnipodDashBleManagerImpl @Inject constructor(
?: throw NotConnectedException("Missing session") ?: throw NotConnectedException("Missing session")
} }
override fun getStatus(): ConnectionStatus { override fun getStatus(): ConnectionState {
// TODO is this used? return connection?.let { getStatus() }
var s: ConnectionStatus ?: NotConnected
synchronized(status) {
s = status
}
return s
} }
override fun connect(): Observable<PodEvent> = Observable.create { emitter -> override fun connect(): Observable<PodEvent> = Observable.create { emitter ->
@ -106,9 +100,10 @@ class OmnipodDashBleManagerImpl @Inject constructor(
?: throw FailedToConnectException("Missing bluetoothAddress, activate the pod first") ?: throw FailedToConnectException("Missing bluetoothAddress, activate the pod first")
val podDevice = bluetoothAdapter.getRemoteDevice(podAddress) val podDevice = bluetoothAdapter.getRemoteDevice(podAddress)
val conn = connection val conn = connection
?: Connection(podDevice, aapsLogger, context) ?: Connection(podDevice, aapsLogger, context, podState)
connection = conn connection = conn
if (conn.connectionState() is Connected) { if (conn.connectionState() is Connected) {
podState.lastConnection = System.currentTimeMillis()
if (conn.session == null) { if (conn.session == null) {
emitter.onNext(PodEvent.EstablishingSession) emitter.onNext(PodEvent.EstablishingSession)
establishSession(1.toByte()) establishSession(1.toByte())
@ -121,7 +116,7 @@ class OmnipodDashBleManagerImpl @Inject constructor(
} }
conn.connect() conn.connect()
emitter.onNext(PodEvent.BluetoothConnected(podAddress)) emitter.onNext(PodEvent.BluetoothConnected(podAddress))
podState.lastConnection = System.currentTimeMillis()
emitter.onNext(PodEvent.EstablishingSession) emitter.onNext(PodEvent.EstablishingSession)
establishSession(1.toByte()) establishSession(1.toByte())
emitter.onNext(PodEvent.Connected) emitter.onNext(PodEvent.Connected)
@ -190,7 +185,7 @@ class OmnipodDashBleManagerImpl @Inject constructor(
emitter.onNext(PodEvent.BluetoothConnecting) emitter.onNext(PodEvent.BluetoothConnecting)
val podDevice = bluetoothAdapter.getRemoteDevice(podAddress) val podDevice = bluetoothAdapter.getRemoteDevice(podAddress)
val conn = Connection(podDevice, aapsLogger, context) val conn = Connection(podDevice, aapsLogger, context, podState)
connection = conn connection = conn
emitter.onNext(PodEvent.BluetoothConnected(podAddress)) emitter.onNext(PodEvent.BluetoothConnected(podAddress))

View file

@ -1,5 +0,0 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.io
enum class IOState {
IDLE, WRITING, READING
}

View file

@ -20,6 +20,7 @@ import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.io.CmdBl
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.io.DataBleIO import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.io.DataBleIO
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.io.IncomingPackets import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.io.IncomingPackets
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message.MessageIO import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message.MessageIO
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.state.OmnipodDashPodStateManager
sealed class ConnectionState sealed class ConnectionState
@ -29,7 +30,8 @@ object NotConnected : ConnectionState()
class Connection( class Connection(
private val podDevice: BluetoothDevice, private val podDevice: BluetoothDevice,
private val aapsLogger: AAPSLogger, private val aapsLogger: AAPSLogger,
context: Context context: Context,
private val podState: OmnipodDashPodStateManager
) : DisconnectHandler { ) : DisconnectHandler {
private val incomingPackets = IncomingPackets() private val incomingPackets = IncomingPackets()
@ -50,13 +52,15 @@ class Connection(
aapsLogger.debug(LTag.PUMPBTCOMM, "Connecting to ${podDevice.address}") aapsLogger.debug(LTag.PUMPBTCOMM, "Connecting to ${podDevice.address}")
val autoConnect = false val autoConnect = false
podState.bluetoothConnectionState = OmnipodDashPodStateManager.BluetoothConnectionState.CONNECTING
gattConnection = podDevice.connectGatt(context, autoConnect, bleCommCallbacks, BluetoothDevice.TRANSPORT_LE) gattConnection = podDevice.connectGatt(context, autoConnect, bleCommCallbacks, BluetoothDevice.TRANSPORT_LE)
// OnDisconnect can be called after this point!!! // OnDisconnect can be called after this point!!!
val state = waitForConnection() val state = waitForConnection()
if (state !is Connected) { if (state !is Connected) {
podState.bluetoothConnectionState = OmnipodDashPodStateManager.BluetoothConnectionState.DISCONNECTED
throw FailedToConnectException(podDevice.address) throw FailedToConnectException(podDevice.address)
} }
podState.bluetoothConnectionState = OmnipodDashPodStateManager.BluetoothConnectionState.CONNECTED
val discoverer = ServiceDiscoverer(aapsLogger, gattConnection, bleCommCallbacks) val discoverer = ServiceDiscoverer(aapsLogger, gattConnection, bleCommCallbacks)
val discoveredCharacteristics = discoverer.discoverServices() val discoveredCharacteristics = discoverer.discoverServices()
cmdBleIO = CmdBleIO( cmdBleIO = CmdBleIO(
@ -90,13 +94,17 @@ class Connection(
disconnect() disconnect()
} }
aapsLogger.debug("Connecting") aapsLogger.debug("Connecting")
podState.bluetoothConnectionState = OmnipodDashPodStateManager.BluetoothConnectionState.CONNECTING
if (!gattConnection.connect()) { if (!gattConnection.connect()) {
throw FailedToConnectException("connect() returned false") throw FailedToConnectException("connect() returned false")
} }
if (waitForConnection() is NotConnected) { if (waitForConnection() !is Connected) {
podState.bluetoothConnectionState = OmnipodDashPodStateManager.BluetoothConnectionState.DISCONNECTED
throw FailedToConnectException(podDevice.address) throw FailedToConnectException(podDevice.address)
} }
podState.bluetoothConnectionState = OmnipodDashPodStateManager.BluetoothConnectionState.CONNECTED
val discoverer = ServiceDiscoverer(aapsLogger, gattConnection, bleCommCallbacks) val discoverer = ServiceDiscoverer(aapsLogger, gattConnection, bleCommCallbacks)
val discovered = discoverer.discoverServices() val discovered = discoverer.discoverServices()
@ -110,6 +118,8 @@ class Connection(
fun disconnect() { fun disconnect() {
aapsLogger.debug(LTag.PUMPBTCOMM, "Disconnecting") aapsLogger.debug(LTag.PUMPBTCOMM, "Disconnecting")
podState.bluetoothConnectionState = OmnipodDashPodStateManager.BluetoothConnectionState.DISCONNECTED
gattConnection.disconnect() gattConnection.disconnect()
bleCommCallbacks.resetConnection() bleCommCallbacks.resetConnection()
session = null session = null

View file

@ -1,10 +0,0 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.status
enum class ConnectionStatus {
IDLE,
BUSY,
CONNECTING,
ESTABLISHING_SESSION,
PAIRING,
RUNNING_COMMAND;
}

View file

@ -65,6 +65,4 @@ sealed class PodEvent {
return "ResponseReceived(command=$command, response=$response)" return "ResponseReceived(command=$command, response=$response)"
} }
} }
data class CommandConfirmed(val historyId: String, val success: Boolean) : PodEvent()
} }

View file

@ -0,0 +1,3 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.state
class CommandConfirmed(val historyId: String, val success: Boolean)

View file

@ -8,9 +8,9 @@ import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.DefaultStatusResponse import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.DefaultStatusResponse
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.SetUniqueIdResponse import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.SetUniqueIdResponse
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.VersionResponse import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.VersionResponse
import io.reactivex.Completable
import io.reactivex.Maybe import io.reactivex.Maybe
import io.reactivex.Observable import io.reactivex.Observable
import io.reactivex.Single
import java.io.Serializable import java.io.Serializable
import java.util.* import java.util.*
@ -22,6 +22,7 @@ interface OmnipodDashPodStateManager {
val isSuspended: Boolean val isSuspended: Boolean
val isPodRunning: Boolean val isPodRunning: Boolean
var lastConnection: Long var lastConnection: Long
var bluetoothConnectionState: BluetoothConnectionState
val lastUpdatedSystem: Long // System.currentTimeMillis() val lastUpdatedSystem: Long // System.currentTimeMillis()
val lastStatusResponseReceived: Long val lastStatusResponseReceived: Long
@ -66,17 +67,21 @@ interface OmnipodDashPodStateManager {
fun updateFromPairing(uniqueId: Id, pairResult: PairResult) fun updateFromPairing(uniqueId: Id, pairResult: PairResult)
fun reset() fun reset()
fun createActiveCommand(historyId: String): Completable fun createActiveCommand(historyId: String): Single<ActiveCommand>
fun updateActiveCommand(): Maybe<PodEvent> fun updateActiveCommand(): Maybe<CommandConfirmed>
fun observeNoActiveCommand(): Observable<PodEvent> fun observeNoActiveCommand(): Observable<PodEvent>
fun maybeMarkActiveCommandFailed()
data class ActiveCommand( data class ActiveCommand(
val sequence: Short, val sequence: Short,
val createdRealtime: Long, val createdRealtime: Long,
var sentRealtime: Long = 0, var sentRealtime: Long = 0,
val historyId: String val historyId: String,
var sendError: Throwable?,
) )
// TODO: set created to "now" on boot // TODO: set created to "now" on boot
data class TempBasal(val startTime: Long, val rate: Double, val durationInMinutes: Short) : Serializable data class TempBasal(val startTime: Long, val rate: Double, val durationInMinutes: Short) : Serializable
enum class BluetoothConnectionState {
CONNECTING, CONNECTED, DISCONNECTED
}
} }

View file

@ -17,9 +17,9 @@ import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.SetUniqueIdResponse import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.SetUniqueIdResponse
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.VersionResponse import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.VersionResponse
import info.nightscout.androidaps.utils.sharedPreferences.SP import info.nightscout.androidaps.utils.sharedPreferences.SP
import io.reactivex.Completable
import io.reactivex.Maybe import io.reactivex.Maybe
import io.reactivex.Observable import io.reactivex.Observable
import io.reactivex.Single
import java.io.Serializable import java.io.Serializable
import java.util.* import java.util.*
import javax.inject.Inject import javax.inject.Inject
@ -148,7 +148,7 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
get() = podState.tempBasal get() = podState.tempBasal
override val tempBasalActive: Boolean override val tempBasalActive: Boolean
get() = tempBasal != null && tempBasal!!.startTime + tempBasal!!.durationInMinutes * 60 * 1000 > System.currentTimeMillis() get() = podState.deliveryStatus in arrayOf(DeliveryStatus.TEMP_BASAL_ACTIVE, DeliveryStatus.BOLUS_AND_TEMP_BASAL_ACTIVE)
override var basalProgram: BasalProgram? override var basalProgram: BasalProgram?
get() = podState.basalProgram get() = podState.basalProgram
@ -160,6 +160,14 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
override val lastStatusResponseReceived: Long override val lastStatusResponseReceived: Long
get() = podState.lastStatusResponseReceived get() = podState.lastStatusResponseReceived
override var bluetoothConnectionState: OmnipodDashPodStateManager.BluetoothConnectionState
get() = podState.bluetoothConnectionState
set(bluetoothConnectionState) {
podState.bluetoothConnectionState = bluetoothConnectionState
rxBus.send(EventOmnipodDashPumpValuesChanged())
// do not store
}
override fun increaseMessageSequenceNumber() { override fun increaseMessageSequenceNumber() {
podState.messageSequenceNumber = ((podState.messageSequenceNumber.toInt() + 1) and 0x0f).toShort() podState.messageSequenceNumber = ((podState.messageSequenceNumber.toInt() + 1) and 0x0f).toShort()
store() store()
@ -183,14 +191,17 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
get() = podState.activeCommand get() = podState.activeCommand
@Synchronized @Synchronized
override fun createActiveCommand(historyId: String) = Completable.create { source -> override fun createActiveCommand(historyId: String): Single<OmnipodDashPodStateManager.ActiveCommand> {
return Single.create { source ->
if (activeCommand == null) { if (activeCommand == null) {
podState.activeCommand = OmnipodDashPodStateManager.ActiveCommand( val command = OmnipodDashPodStateManager.ActiveCommand(
podState.messageSequenceNumber, podState.messageSequenceNumber,
createdRealtime = SystemClock.elapsedRealtime(), createdRealtime = SystemClock.elapsedRealtime(),
historyId = historyId historyId = historyId,
sendError = null,
) )
source.onComplete() podState.activeCommand = command
source.onSuccess(command)
} else { } else {
source.onError( source.onError(
java.lang.IllegalStateException( java.lang.IllegalStateException(
@ -200,6 +211,7 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
) )
} }
} }
}
@Synchronized @Synchronized
override fun observeNoActiveCommand(): Observable<PodEvent> { override fun observeNoActiveCommand(): Observable<PodEvent> {
@ -218,35 +230,35 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
} }
@Synchronized @Synchronized
override fun maybeMarkActiveCommandFailed() { override fun updateActiveCommand() = Maybe.create<CommandConfirmed> { source ->
podState.activeCommand?.run {
if (sentRealtime < createdRealtime) {
// command was not sent
podState.activeCommand = null
}
}
}
@Synchronized
override fun updateActiveCommand() = Maybe.create<PodEvent> { source ->
podState.activeCommand?.run { podState.activeCommand?.run {
logger.debug( logger.debug(
"Trying to confirm active command with parameters: $activeCommand " + "Trying to confirm active command with parameters: $activeCommand " +
"lastResponse=$lastStatusResponseReceived " + "lastResponse=$lastStatusResponseReceived " +
"$sequenceNumberOfLastProgrammingCommand $historyId" "$sequenceNumberOfLastProgrammingCommand $historyId"
) )
if (createdRealtime >= lastStatusResponseReceived)
if (sentRealtime < createdRealtime) { // command was not sent, clear it up
podState.activeCommand = null
source.onError(
this.sendError
?: java.lang.IllegalStateException(
"Could not send command and sendError is " +
"missing"
)
)
} else if (createdRealtime >= lastStatusResponseReceived)
// we did not receive a valid response yet // we did not receive a valid response yet
source.onComplete() source.onComplete()
else { else {
podState.activeCommand = null podState.activeCommand = null
if (sequenceNumberOfLastProgrammingCommand == sequence) if (sequenceNumberOfLastProgrammingCommand == sequence)
source.onSuccess(PodEvent.CommandConfirmed(historyId, true)) source.onSuccess(CommandConfirmed(historyId, true))
else else
source.onSuccess(PodEvent.CommandConfirmed(historyId, false)) source.onSuccess(CommandConfirmed(historyId, false))
} }
} } ?: source.onComplete()
?: source.onComplete() // no active programming command // no active programming command
} }
override fun increaseEapAkaSequenceNumber(): ByteArray { override fun increaseEapAkaSequenceNumber(): ByteArray {
@ -377,7 +389,8 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
var lastConnection: Long = 0 var lastConnection: Long = 0
var lastUpdatedSystem: Long = 0 var lastUpdatedSystem: Long = 0
var lastStatusResponseReceived: Long = 0 var lastStatusResponseReceived: Long = 0
var bluetoothConnectionState: OmnipodDashPodStateManager.BluetoothConnectionState =
OmnipodDashPodStateManager.BluetoothConnectionState.DISCONNECTED
var messageSequenceNumber: Short = 0 var messageSequenceNumber: Short = 0
var sequenceNumberOfLastProgrammingCommand: Short? = null var sequenceNumberOfLastProgrammingCommand: Short? = null
var activationTime: Long? = null var activationTime: Long? = null

View file

@ -35,6 +35,14 @@ class DashHistory @Inject constructor(
currentTimeMillis() currentTimeMillis()
) )
fun getById(id: String): HistoryRecord {
val entry = dao.byIdBlocking(id)
if (entry == null) {
throw java.lang.IllegalArgumentException("history entry [$id] not found")
}
return historyMapper.entityToDomain(entry)
}
@Suppress("ReturnCount") @Suppress("ReturnCount")
fun createRecord( fun createRecord(
commandType: OmnipodCommandType, commandType: OmnipodCommandType,
@ -77,7 +85,6 @@ class DashHistory @Inject constructor(
fun updateFromState(podState: OmnipodDashPodStateManager) = Completable.defer { fun updateFromState(podState: OmnipodDashPodStateManager) = Completable.defer {
podState.activeCommand?.run { podState.activeCommand?.run {
when { when {
createdRealtime <= podState.lastStatusResponseReceived && createdRealtime <= podState.lastStatusResponseReceived &&
sequence == podState.sequenceNumberOfLastProgrammingCommand -> sequence == podState.sequenceNumberOfLastProgrammingCommand ->
dao.setInitialResult(historyId, InitialResult.SENT) dao.setInitialResult(historyId, InitialResult.SENT)

View file

@ -1,6 +1,8 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.history.data package info.nightscout.androidaps.plugins.pump.omnipod.dash.history.data
import com.github.guepardoapps.kulid.ULID
import info.nightscout.androidaps.plugins.pump.omnipod.common.definition.OmnipodCommandType import info.nightscout.androidaps.plugins.pump.omnipod.common.definition.OmnipodCommandType
import java.nio.ByteBuffer
data class HistoryRecord( data class HistoryRecord(
val id: String, // ULID val id: String, // ULID
@ -11,4 +13,9 @@ data class HistoryRecord(
val record: Record?, val record: Record?,
val resolvedResult: ResolvedResult?, val resolvedResult: ResolvedResult?,
val resolvedAt: Long? val resolvedAt: Long?
) ) {
fun pumpId(): Long {
val entropy = ULID.getEntropy(id)
return ByteBuffer.wrap(entropy).getLong()
}
}

View file

@ -14,7 +14,7 @@ enum class BolusType {
companion object { companion object {
fun fromBolusInfoBolusType(type: DetailedBolusInfo.BolusType): BolusType { fun fromBolusInfoBolusType(type: DetailedBolusInfo.BolusType): BolusType {
return when (type) { return when (type) {
DetailedBolusInfo.BolusType.SMB -> SMB; DetailedBolusInfo.BolusType.SMB -> SMB
else -> DEFAULT else -> DEFAULT
} }
} }

View file

@ -22,6 +22,9 @@ abstract class HistoryRecordDao {
@Query("SELECT * from historyrecords WHERE createdAt <= :since") @Query("SELECT * from historyrecords WHERE createdAt <= :since")
abstract fun allSince(since: Long): Single<List<HistoryRecordEntity>> abstract fun allSince(since: Long): Single<List<HistoryRecordEntity>>
@Query("SELECT * FROM historyrecords WHERE id = :id LIMIT 1")
abstract fun byIdBlocking(id: String): HistoryRecordEntity?
@Insert(onConflict = OnConflictStrategy.REPLACE) @Insert(onConflict = OnConflictStrategy.REPLACE)
abstract fun saveBlocking(historyRecordEntity: HistoryRecordEntity) abstract fun saveBlocking(historyRecordEntity: HistoryRecordEntity)

View file

@ -13,6 +13,7 @@ import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.activities.ErrorHelperActivity import info.nightscout.androidaps.activities.ErrorHelperActivity
import info.nightscout.androidaps.events.EventPreferenceChange import info.nightscout.androidaps.events.EventPreferenceChange
import info.nightscout.androidaps.interfaces.CommandQueueProvider import info.nightscout.androidaps.interfaces.CommandQueueProvider
import info.nightscout.androidaps.interfaces.PumpSync
import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification
import info.nightscout.androidaps.plugins.general.overview.notifications.Notification import info.nightscout.androidaps.plugins.general.overview.notifications.Notification
@ -58,6 +59,7 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
@Inject lateinit var sp: SP @Inject lateinit var sp: SP
@Inject lateinit var dateUtil: DateUtil @Inject lateinit var dateUtil: DateUtil
@Inject lateinit var aapsSchedulers: AapsSchedulers @Inject lateinit var aapsSchedulers: AapsSchedulers
@Inject lateinit var pumpSync: PumpSync
companion object { companion object {
@ -211,11 +213,26 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
private fun updateUi() { private fun updateUi() {
// TODO update bluetooth status // TODO update bluetooth status
updateBluetoothStatus()
updateOmnipodStatus() updateOmnipodStatus()
updatePodActionButtons() updatePodActionButtons()
updateQueueStatus() updateQueueStatus()
} }
private fun updateBluetoothStatus() {
bluetoothStatusBinding.omnipodDashBluetoothAddress.text = podStateManager.bluetoothAddress
?: PLACEHOLDER
bluetoothStatusBinding.omnipodDashBluetoothStatus.text =
when (podStateManager.bluetoothConnectionState) {
OmnipodDashPodStateManager.BluetoothConnectionState.CONNECTED ->
"{fa-bluetooth}"
OmnipodDashPodStateManager.BluetoothConnectionState.DISCONNECTED ->
"{fa-bluetooth-b}"
OmnipodDashPodStateManager.BluetoothConnectionState.CONNECTING ->
"{fa-bluetooth-b spin}"
}
}
private fun updateOmnipodStatus() { private fun updateOmnipodStatus() {
updateLastConnection() updateLastConnection()
updateLastBolus() updateLastBolus()
@ -246,10 +263,9 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
podStateManager.firmwareVersion.toString(), podStateManager.firmwareVersion.toString(),
podStateManager.bluetoothVersion.toString() podStateManager.bluetoothVersion.toString()
) )
podInfoBinding.timeOnPod.text = podStateManager.minutesSinceActivation.toString() + " minutes"
// TODO // TODO
/* /*
podInfoBinding.timeOnPod.text = readableZonedTime(podStateManager.time)
podInfoBinding.timeOnPod.setTextColor(if (podStateManager.timeDeviatesMoreThan(OmnipodConstants.TIME_DEVIATION_THRESHOLD)) { podInfoBinding.timeOnPod.setTextColor(if (podStateManager.timeDeviatesMoreThan(OmnipodConstants.TIME_DEVIATION_THRESHOLD)) {
Color.RED Color.RED
} else { } else {
@ -373,7 +389,9 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
if (podStateManager.isSuspended) { if (podStateManager.isSuspended) {
resourceHelper.gs(R.string.omnipod_common_pod_status_suspended) resourceHelper.gs(R.string.omnipod_common_pod_status_suspended)
} else { } else {
resourceHelper.gs(R.string.omnipod_common_pod_status_running) resourceHelper.gs(R.string.omnipod_common_pod_status_running) +
podStateManager.deliveryStatus?.let { " " + podStateManager.deliveryStatus.toString() }
// TODO Display deliveryStatus in a nice way
} }
// TODO // TODO
/* /*
@ -421,10 +439,11 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
} }
private fun updateTempBasal() { private fun updateTempBasal() {
if (podStateManager.isActivationCompleted && podStateManager.tempBasalActive) { val tempBasal = podStateManager.tempBasal
val startTime = podStateManager.tempBasal!!.startTime if (podStateManager.isActivationCompleted && podStateManager.tempBasalActive && tempBasal != null) {
val rate = podStateManager.tempBasal!!.rate val startTime = tempBasal.startTime
val duration = podStateManager.tempBasal!!.durationInMinutes val rate = tempBasal.rate
val duration = tempBasal.durationInMinutes
val minutesRunning = 0 // TODO val minutesRunning = 0 // TODO
@ -560,12 +579,8 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
} }
*/ */
private fun readableDuration(time: Long): String { private fun readableDuration(dateTime: Long): String {
// TODO val duration = Duration(dateTime, System.currentTimeMillis())
return "TODO"
/*
val duration = Duration(dateTime, DateTime.now())
val hours = duration.standardHours.toInt() val hours = duration.standardHours.toInt()
val minutes = duration.standardMinutes.toInt() val minutes = duration.standardMinutes.toInt()
val seconds = duration.standardSeconds.toInt() val seconds = duration.standardSeconds.toInt()
@ -585,8 +600,10 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
seconds < 24 * 60 * 60 -> { // < 1 day seconds < 24 * 60 * 60 -> { // < 1 day
val minutesLeft = minutes % 60 val minutesLeft = minutes % 60
if (minutesLeft > 0) if (minutesLeft > 0)
return resourceHelper.gs(R.string.omnipod_common_time_ago, return resourceHelper.gs(
resourceHelper.gs(R.string.omnipod_common_composite_time, resourceHelper.gq(R.plurals.omnipod_common_hours, hours, hours), resourceHelper.gq(R.plurals.omnipod_common_minutes, minutesLeft, minutesLeft))) R.string.omnipod_common_time_ago,
resourceHelper.gs(R.string.omnipod_common_composite_time, resourceHelper.gq(R.plurals.omnipod_common_hours, hours, hours), resourceHelper.gq(R.plurals.omnipod_common_minutes, minutesLeft, minutesLeft))
)
return resourceHelper.gs(R.string.omnipod_common_time_ago, resourceHelper.gq(R.plurals.omnipod_common_hours, hours, hours)) return resourceHelper.gs(R.string.omnipod_common_time_ago, resourceHelper.gq(R.plurals.omnipod_common_hours, hours, hours))
} }
@ -594,12 +611,13 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
val days = hours / 24 val days = hours / 24
val hoursLeft = hours % 24 val hoursLeft = hours % 24
if (hoursLeft > 0) if (hoursLeft > 0)
return resourceHelper.gs(R.string.omnipod_common_time_ago, return resourceHelper.gs(
resourceHelper.gs(R.string.omnipod_common_composite_time, resourceHelper.gq(R.plurals.omnipod_common_days, days, days), resourceHelper.gq(R.plurals.omnipod_common_hours, hoursLeft, hoursLeft))) R.string.omnipod_common_time_ago,
resourceHelper.gs(R.string.omnipod_common_composite_time, resourceHelper.gq(R.plurals.omnipod_common_days, days, days), resourceHelper.gq(R.plurals.omnipod_common_hours, hoursLeft, hoursLeft))
)
return resourceHelper.gs(R.string.omnipod_common_time_ago, resourceHelper.gq(R.plurals.omnipod_common_days, days, days)) return resourceHelper.gs(R.string.omnipod_common_time_ago, resourceHelper.gq(R.plurals.omnipod_common_days, days, days))
} }
} }
*/
} }
private fun isQueueEmpty(): Boolean { private fun isQueueEmpty(): Boolean {

View file

@ -1,44 +1,6 @@
<merge xmlns:android="http://schemas.android.com/apk/res/android" <merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1.5"
android:gravity="end"
android:paddingStart="5dp"
android:paddingEnd="5dp"
android:text="@string/omnipod_dash_overview_bluetooth_address"
android:textSize="14sp" />
<TextView
android:layout_width="5dp"
android:layout_height="wrap_content"
android:layout_weight="0"
android:gravity="center_horizontal"
android:paddingStart="2dp"
android:paddingEnd="2dp"
android:text=":"
android:textSize="14sp"
tools:ignore="HardcodedText" />
<TextView
android:id="@+id/omnipod_dash_bluetooth_status"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="start"
android:paddingStart="5dp"
android:paddingEnd="5dp"
android:textSize="14sp" />
</LinearLayout>
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -65,7 +27,8 @@
android:textSize="14sp" android:textSize="14sp"
tools:ignore="HardcodedText" /> tools:ignore="HardcodedText" />
<com.joanzapata.iconify.widget.IconTextView
<TextView
android:id="@+id/omnipod_dash_bluetooth_address" android:id="@+id/omnipod_dash_bluetooth_address"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -73,6 +36,43 @@
android:gravity="start" android:gravity="start"
android:paddingStart="5dp" android:paddingStart="5dp"
android:paddingEnd="5dp" android:paddingEnd="5dp"
android:textSize="14sp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1.5"
android:gravity="end"
android:paddingStart="5dp"
android:paddingEnd="5dp"
android:text="@string/omnipod_dash_overview_bluetooth_address"
android:textSize="14sp" />
<TextView
android:layout_width="5dp"
android:layout_height="wrap_content"
android:layout_weight="0"
android:gravity="center_horizontal"
android:paddingStart="2dp"
android:paddingEnd="2dp"
android:text=":"
android:textSize="14sp"
tools:ignore="HardcodedText" />
<com.joanzapata.iconify.widget.IconTextView
android:id="@+id/omnipod_dash_bluetooth_status"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="start"
android:paddingStart="5dp"
android:paddingEnd="5dp"
android:text="{fa-bluetooth-b} " android:text="{fa-bluetooth-b} "
android:textSize="14sp" android:textSize="14sp"
tools:ignore="HardcodedText" /> tools:ignore="HardcodedText" />

View file

@ -1,8 +1,8 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.endecrypt package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.endecrypt
import info.nightscout.androidaps.extensions.toHex
import info.nightscout.androidaps.logging.AAPSLoggerTest import info.nightscout.androidaps.logging.AAPSLoggerTest
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message.MessagePacket import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message.MessagePacket
import info.nightscout.androidaps.extensions.toHex
import org.junit.Assert import org.junit.Assert
import org.junit.Test import org.junit.Test
import org.spongycastle.util.encoders.Hex import org.spongycastle.util.encoders.Hex

View file

@ -1,9 +1,9 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message
import com.google.crypto.tink.subtle.Hex import com.google.crypto.tink.subtle.Hex
import info.nightscout.androidaps.extensions.toHex
import info.nightscout.androidaps.logging.AAPSLoggerTest import info.nightscout.androidaps.logging.AAPSLoggerTest
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.extensions.toHex
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Test import org.junit.Test

View file

@ -1,8 +1,8 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message
import com.google.crypto.tink.subtle.Hex import com.google.crypto.tink.subtle.Hex
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.packet.PayloadJoiner
import info.nightscout.androidaps.extensions.toHex import info.nightscout.androidaps.extensions.toHex
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.packet.PayloadJoiner
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Test import org.junit.Test

View file

@ -1,8 +1,8 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message
import info.nightscout.androidaps.extensions.toHex
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.packet.PayloadJoiner import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.packet.PayloadJoiner
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.packet.PayloadSplitter import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.packet.PayloadSplitter
import info.nightscout.androidaps.extensions.toHex
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Test import org.junit.Test
import java.util.* import java.util.*

View file

@ -1,8 +1,8 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message
import com.google.crypto.tink.subtle.Hex import com.google.crypto.tink.subtle.Hex
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.packet.PayloadSplitter
import info.nightscout.androidaps.extensions.toHex import info.nightscout.androidaps.extensions.toHex
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.packet.PayloadSplitter
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue import org.junit.Assert.assertTrue
import org.junit.Test import org.junit.Test

View file

@ -1,9 +1,9 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.pair package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.pair
import info.nightscout.androidaps.extensions.toHex
import info.nightscout.androidaps.logging.AAPSLoggerTest import info.nightscout.androidaps.logging.AAPSLoggerTest
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.util.RandomByteGenerator import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.util.RandomByteGenerator
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.util.X25519KeyGenerator import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.util.X25519KeyGenerator
import info.nightscout.androidaps.extensions.toHex
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Test import org.junit.Test
import org.mockito.ArgumentMatchers.anyInt import org.mockito.ArgumentMatchers.anyInt

View file

@ -1,7 +1,7 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.session package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.session
import info.nightscout.androidaps.logging.AAPSLoggerTest
import info.nightscout.androidaps.extensions.toHex import info.nightscout.androidaps.extensions.toHex
import info.nightscout.androidaps.logging.AAPSLoggerTest
import org.junit.Assert import org.junit.Assert
import org.junit.Test import org.junit.Test
import org.spongycastle.util.encoders.Hex import org.spongycastle.util.encoders.Hex

View file

@ -1,7 +1,7 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.session package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.session
import info.nightscout.androidaps.logging.AAPSLoggerTest
import info.nightscout.androidaps.extensions.toHex import info.nightscout.androidaps.extensions.toHex
import info.nightscout.androidaps.logging.AAPSLoggerTest
import org.junit.Assert import org.junit.Assert
import org.junit.Test import org.junit.Test
import org.spongycastle.util.encoders.Hex import org.spongycastle.util.encoders.Hex