From 61e873dbcc821501459c2117ff25ccccef001af1 Mon Sep 17 00:00:00 2001 From: jbr7rr <> Date: Sat, 15 Apr 2023 15:23:26 +0200 Subject: [PATCH] Deactivation, better connection flow --- .../interfaces/pump/defs/PumpType.kt | 1 + .../nightscout/pump/medtrum/MedtrumPlugin.kt | 29 ++- .../nightscout/pump/medtrum/MedtrumPump.kt | 43 +++-- .../pump/medtrum/code/ConnectionState.kt | 7 + .../nightscout/pump/medtrum/code/PatchStep.kt | 8 +- .../medtrum/comm/packets/ActivatePacket.kt | 19 ++ .../medtrum/comm/packets/AuthorizePacket.kt | 10 +- .../comm/packets/NotificationPacket.kt | 20 +- .../comm/packets/SetBolusMotorPacket.kt | 1 + .../medtrum/comm/packets/SynchronizePacket.kt | 2 + .../pump/medtrum/di/MedtrumModule.kt | 10 + .../pump/medtrum/encryption/Crypt.kt | 28 ++- .../pump/medtrum/services/MedtrumService.kt | 71 ++++--- .../pump/medtrum/ui/MedtrumActivity.kt | 22 ++- .../ui/MedtrumDeactivatePatchFragment.kt | 49 +++++ .../medtrum/ui/MedtrumOverviewFragment.kt | 12 +- .../ui/MedtrumStartDeactivationFragment.kt | 49 +++++ .../ui/viewmodel/MedtrumOverviewViewModel.kt | 45 +++-- .../medtrum/ui/viewmodel/MedtrumViewModel.kt | 176 +++++++++++++----- .../fragment_medtrum_deactivate_patch.xml | 66 +++++++ .../fragment_medtrum_start_deactivation.xml | 66 +++++++ pump/medtrum/src/main/res/values/strings.xml | 4 + .../pump/medtrum/MedtrumTestBase.kt | 4 +- .../comm/packets/ActivatePacketTest.kt | 3 +- .../comm/packets/AuthorizePacketTest.kt | 14 +- 25 files changed, 582 insertions(+), 177 deletions(-) create mode 100644 pump/medtrum/src/main/java/info/nightscout/pump/medtrum/code/ConnectionState.kt create mode 100644 pump/medtrum/src/main/java/info/nightscout/pump/medtrum/ui/MedtrumDeactivatePatchFragment.kt create mode 100644 pump/medtrum/src/main/java/info/nightscout/pump/medtrum/ui/MedtrumStartDeactivationFragment.kt create mode 100644 pump/medtrum/src/main/res/layout/fragment_medtrum_deactivate_patch.xml create mode 100644 pump/medtrum/src/main/res/layout/fragment_medtrum_start_deactivation.xml diff --git a/core/interfaces/src/main/java/info/nightscout/interfaces/pump/defs/PumpType.kt b/core/interfaces/src/main/java/info/nightscout/interfaces/pump/defs/PumpType.kt index 4199af95a8..68dbf1277d 100644 --- a/core/interfaces/src/main/java/info/nightscout/interfaces/pump/defs/PumpType.kt +++ b/core/interfaces/src/main/java/info/nightscout/interfaces/pump/defs/PumpType.kt @@ -410,6 +410,7 @@ enum class PumpType { baseBasalSpecialSteps = null, pumpCapability = PumpCapability.MedtrumCapabilities, isPatchPump = true, + maxReservoirReading = 400, source = Source.Medtrum ); diff --git a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/MedtrumPlugin.kt b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/MedtrumPlugin.kt index 66833b1f94..9ab782dd7d 100644 --- a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/MedtrumPlugin.kt +++ b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/MedtrumPlugin.kt @@ -52,8 +52,7 @@ import org.json.JSONObject import javax.inject.Inject import javax.inject.Singleton -@Singleton -class MedtrumPlugin @Inject constructor( +@Singleton class MedtrumPlugin @Inject constructor( injector: HasAndroidInjector, aapsLogger: AAPSLogger, rh: ResourceHelper, @@ -81,8 +80,6 @@ class MedtrumPlugin @Inject constructor( private val disposable = CompositeDisposable() private var medtrumService: MedtrumService? = null - private var mPumpType: PumpType = PumpType.MEDTRUM_NANO - private val mPumpDescription = PumpDescription(mPumpType) override fun onStart() { super.onStart() @@ -120,7 +117,7 @@ class MedtrumPlugin @Inject constructor( } override fun isInitialized(): Boolean { - return medtrumPump.pumpState > MedtrumPumpState.EJECTED + return medtrumPump.pumpState > MedtrumPumpState.EJECTED && medtrumPump.pumpState < MedtrumPumpState.STOPPED } override fun isSuspended(): Boolean { @@ -132,7 +129,8 @@ class MedtrumPlugin @Inject constructor( } override fun isConnected(): Boolean { - return if (!isInitialized()) true else medtrumService?.isConnected ?: true // This is a workaround to prevent AAPS to trigger connects when we are initializing + // This is a workaround to prevent AAPS to trigger connects when we have no patch activated + return if (!medtrumPump.patchActivated) true else medtrumService?.isConnected ?: false } override fun isConnecting(): Boolean = medtrumService?.isConnecting ?: false @@ -142,7 +140,7 @@ class MedtrumPlugin @Inject constructor( } override fun connect(reason: String) { - if (isInitialized()) { + if (medtrumPump.patchActivated) { aapsLogger.debug(LTag.PUMP, "Medtrum connect - reason:$reason") aapsLogger.debug(LTag.PUMP, "Medtrum connect - service::$medtrumService") // aapsLogger.debug(LTag.PUMP, "Medtrum connect - mDeviceSN:$mDeviceSN") @@ -166,6 +164,7 @@ class MedtrumPlugin @Inject constructor( } override fun getPumpStatus(reason: String) { + aapsLogger.debug(LTag.PUMP, "Medtrum getPumpStatus - reason:$reason") if (isInitialized()) { medtrumService?.readPumpStatus() } @@ -202,14 +201,14 @@ class MedtrumPlugin @Inject constructor( } override fun lastDataTime(): Long { - return 0 // TODO + return medtrumPump.lastTimeReceivedFromPump * 1000L } override val baseBasalRate: Double get() = 0.0 // TODO override val reservoirLevel: Double - get() = 0.0 // TODO + get() = medtrumPump.reservoir override val batteryLevel: Int get() = 0 // TODO @@ -228,14 +227,12 @@ class MedtrumPlugin @Inject constructor( override fun setTempBasalPercent(percent: Int, durationInMinutes: Int, profile: Profile, enforceNew: Boolean, tbrType: PumpSync.TemporaryBasalType): PumpEnactResult { aapsLogger.info(LTag.PUMP, "setTempBasalPercent - percent: $percent, durationInMinutes: $durationInMinutes, enforceNew: $enforceNew") - return PumpEnactResult(injector).success(false).enacted(false) - .comment("Medtrum driver does not support percentage temp basals") + return PumpEnactResult(injector).success(false).enacted(false).comment("Medtrum driver does not support percentage temp basals") } override fun setExtendedBolus(insulin: Double, durationInMinutes: Int): PumpEnactResult { aapsLogger.info(LTag.PUMP, "setExtendedBolus - insulin: $insulin, durationInMinutes: $durationInMinutes") - return PumpEnactResult(injector).success(false).enacted(false) - .comment("Medtrum driver does not support extended boluses") + return PumpEnactResult(injector).success(false).enacted(false).comment("Medtrum driver does not support extended boluses") } override fun cancelTempBasal(enforceNew: Boolean): PumpEnactResult { @@ -255,15 +252,15 @@ class MedtrumPlugin @Inject constructor( } override fun model(): PumpType { - return mPumpType + return medtrumPump.pumpType } override fun serialNumber(): String { - return "0" // TODO + return medtrumPump.pumpSN.toString(radix = 16) } override val pumpDescription: PumpDescription - get() = mPumpDescription + get() = PumpDescription(medtrumPump.pumpType) override fun shortStatus(veryShort: Boolean): String { return ""// TODO diff --git a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/MedtrumPump.kt b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/MedtrumPump.kt index 0536be865c..6791190162 100644 --- a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/MedtrumPump.kt +++ b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/MedtrumPump.kt @@ -1,13 +1,9 @@ package info.nightscout.pump.medtrum -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData -import info.nightscout.interfaces.Constants import info.nightscout.interfaces.profile.Instantiator import info.nightscout.interfaces.profile.Profile -import info.nightscout.interfaces.profile.ProfileStore -import info.nightscout.interfaces.pump.PumpSync import info.nightscout.interfaces.pump.defs.PumpType +import info.nightscout.pump.medtrum.code.ConnectionState import info.nightscout.pump.medtrum.comm.enums.AlarmSetting import info.nightscout.pump.medtrum.comm.enums.MedtrumPumpState import info.nightscout.pump.medtrum.extension.toByteArray @@ -15,7 +11,6 @@ import info.nightscout.rx.logging.AAPSLogger import info.nightscout.rx.logging.LTag import info.nightscout.shared.sharedPreferences.SP import info.nightscout.shared.utils.DateUtil -import info.nightscout.shared.utils.T import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import javax.inject.Inject @@ -26,31 +21,44 @@ import kotlin.math.round class MedtrumPump @Inject constructor( private val aapsLogger: AAPSLogger, private val sp: SP, - private val dateUtil: DateUtil, - private val instantiator: Instantiator + private val dateUtil: DateUtil ) { + // Connection state flow + private val _connectionState = MutableStateFlow(ConnectionState.DISCONNECTED) + val connectionStateFlow: StateFlow = _connectionState + var connectionState: ConnectionState + get() = _connectionState.value + set(value) { + _connectionState.value = value + } + // Pump state flow - // TODO We might want to save this in SP, or at least get activated state from SP private val _pumpState = MutableStateFlow(MedtrumPumpState.NONE) val pumpStateFlow: StateFlow = _pumpState - var pumpState: MedtrumPumpState get() = _pumpState.value set(value) { _pumpState.value = value } + var _patchActivated = false + val patchActivated: Boolean + get() = _patchActivated + // Prime progress as state flow private val _primeProgress = MutableStateFlow(0) val primeProgressFlow: StateFlow = _primeProgress - var primeProgress: Int get() = _primeProgress.value set(value) { _primeProgress.value = value } + var pumpSN = 0L + val pumpType: PumpType = PumpType.MEDTRUM_NANO // TODO, type based on pumpSN or pump activation/connection + var patchSessionToken = 0L + // TODO: Save this in SP? This might be a bit tricky as we only know what we have set, not what the pump has set but the pump should not change it, addtionally we should track the active basal profile in pump e.g. Basal patern A, B etc var actualBasalProfile = byteArrayOf(0) var patchId = 0L @@ -86,6 +94,19 @@ class MedtrumPump @Inject constructor( var desiredHourlyMaxInsulin: Int = 40 var desiredDailyMaxInsulin: Int = 180 + fun setPatchActivatedState(activated: Boolean) { + aapsLogger.debug(LTag.PUMP, "setPatchActivatedState: $activated") + _patchActivated = activated + sp.putBoolean(R.string.key_patch_activated, activated) + } + + /** When the activation/deactivation screen, and the connection flow needs to be controlled, + * this can be used to set the ActivatedState without saving to SP, So when app is force closed the state is still maintained */ + fun setPatchActivatedStateTemp(activated: Boolean) { + aapsLogger.debug(LTag.PUMP, "setPatchActivatedStateTemp: $activated") + _patchActivated = activated + } + fun buildMedtrumProfileArray(nsProfile: Profile): ByteArray? { val list = nsProfile.getBasalValues() var basals = byteArrayOf() diff --git a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/code/ConnectionState.kt b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/code/ConnectionState.kt new file mode 100644 index 0000000000..1b64d5f326 --- /dev/null +++ b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/code/ConnectionState.kt @@ -0,0 +1,7 @@ +package info.nightscout.pump.medtrum.code + +enum class ConnectionState { + CONNECTED, + DISCONNECTED, + CONNECTING; +} \ No newline at end of file diff --git a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/code/PatchStep.kt b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/code/PatchStep.kt index 8d615dd713..c3e7bc9ab5 100644 --- a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/code/PatchStep.kt +++ b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/code/PatchStep.kt @@ -1,11 +1,9 @@ package info.nightscout.pump.medtrum.code enum class PatchStep { - SAFE_DEACTIVATION, - MANUALLY_TURNING_OFF_ALARM, - DISCARDED, - DISCARDED_FOR_CHANGE, - DISCARDED_FROM_ALARM, + START_DEACTIVATION, + DEACTIVATE, + DEACTIVATION_COMPLETE, PREPARE_PATCH, PRIME, ATTACH_PATCH, diff --git a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/packets/ActivatePacket.kt b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/packets/ActivatePacket.kt index 4e70df0638..6b5c00d117 100644 --- a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/packets/ActivatePacket.kt +++ b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/packets/ActivatePacket.kt @@ -1,6 +1,8 @@ package info.nightscout.pump.medtrum.comm.packets import dagger.android.HasAndroidInjector +import info.nightscout.interfaces.pump.DetailedBolusInfo +import info.nightscout.interfaces.pump.PumpSync import info.nightscout.pump.medtrum.MedtrumPump import info.nightscout.pump.medtrum.comm.enums.CommandType.ACTIVATE import info.nightscout.pump.medtrum.extension.toByteArray @@ -16,6 +18,7 @@ class ActivatePacket(injector: HasAndroidInjector, private val basalProfile: Byt @Inject lateinit var medtrumPump: MedtrumPump @Inject lateinit var tddCalculator: TddCalculator + @Inject lateinit var pumpSync: PumpSync companion object { @@ -95,6 +98,22 @@ class ActivatePacket(injector: HasAndroidInjector, private val basalProfile: Byt medtrumPump.actualBasalProfile = basalProfile // TODO: Handle history entry medtrumPump.handleBasalStatusUpdate(basalType, basalValue, basalSequence, basalPatchId, basalStartTime, time) + + // Update the pump in the database, technically this is not a new pump only new patch, but still TBR's etc need to be cannceled + pumpSync.connectNewPump() + // Sync canula change + pumpSync.insertTherapyEventIfNewWithTimestamp( + timestamp = System.currentTimeMillis(), + type = DetailedBolusInfo.EventType.CANNULA_CHANGE, + pumpType = medtrumPump.pumpType, + pumpSerial = medtrumPump.pumpSN.toString(radix = 16) + ) + pumpSync.insertTherapyEventIfNewWithTimestamp( + timestamp = System.currentTimeMillis(), + type = DetailedBolusInfo.EventType.INSULIN_CHANGE, + pumpType = medtrumPump.pumpType, + pumpSerial = medtrumPump.pumpSN.toString(radix = 16) + ) } return success diff --git a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/packets/AuthorizePacket.kt b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/packets/AuthorizePacket.kt index f8c6a28729..4cf10a580c 100644 --- a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/packets/AuthorizePacket.kt +++ b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/packets/AuthorizePacket.kt @@ -1,12 +1,16 @@ package info.nightscout.pump.medtrum.comm.packets import dagger.android.HasAndroidInjector +import info.nightscout.pump.medtrum.MedtrumPump import info.nightscout.pump.medtrum.comm.enums.CommandType.AUTH_REQ import info.nightscout.pump.medtrum.encryption.Crypt import info.nightscout.pump.medtrum.extension.toByteArray import info.nightscout.pump.medtrum.extension.toInt +import javax.inject.Inject -class AuthorizePacket(injector: HasAndroidInjector, private val deviceSerial: Long) : MedtrumPacket(injector) { +class AuthorizePacket(injector: HasAndroidInjector) : MedtrumPacket(injector) { + + @Inject lateinit var medtrumPump: MedtrumPump var deviceType: Int = 0 var swVersion: String = "" @@ -30,8 +34,8 @@ class AuthorizePacket(injector: HasAndroidInjector, private val deviceSerial: Lo override fun getRequest(): ByteArray { val role = 2 // Fixed to 2 for pump - val key = Crypt().keyGen(deviceSerial) - return byteArrayOf(opCode) + byteArrayOf(role.toByte()) + 0.toByteArray(4) + key.toByteArray(4) + val key = Crypt().keyGen(medtrumPump.pumpSN) + return byteArrayOf(opCode) + byteArrayOf(role.toByte()) + medtrumPump.patchSessionToken.toByteArray(4) + key.toByteArray(4) } override fun handleResponse(data: ByteArray): Boolean { diff --git a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/packets/NotificationPacket.kt b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/packets/NotificationPacket.kt index 88f9f2bf70..3898488ea4 100644 --- a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/packets/NotificationPacket.kt +++ b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/packets/NotificationPacket.kt @@ -115,15 +115,19 @@ class NotificationPacket(val injector: HasAndroidInjector) { if (fieldMask and MASK_BASAL != 0) { aapsLogger.debug(LTag.PUMPCOMM, "Basal notification received") - var basalType = data.copyOfRange(offset, offset + 1) - var basalSequence = data.copyOfRange(offset + 1, offset + 3) - var basalInterval = data.copyOfRange(offset + 3, offset + 7) - var basalRateAndDelivery = data.copyOfRange(offset + 7, offset + 10).toInt() - var basalRate = basalRateAndDelivery and 0xFFF - var basalDelivery = (basalRateAndDelivery shr 12) - aapsLogger.debug(LTag.PUMPCOMM, "Basal type: $basalType, basal sequence: $basalSequence, basal interval: $basalInterval, basal rate: $basalRate, basal delivery: $basalDelivery") + var basalType = data.copyOfRange(offset, offset + 1).toInt() + var basalSequence = data.copyOfRange(offset + 1, offset + 3).toInt() + var basalPatchId = data.copyOfRange(offset + 3, offset + 5).toInt() + var basalInterval = data.copyOfRange(offset + 5, offset + 9).toInt() + var basalRateAndDelivery = data.copyOfRange(offset + 9, offset + 12).toInt() + var basalRate = (basalRateAndDelivery and 0xFFF) * 0.05 + var basalDelivery = (basalRateAndDelivery shr 12) * 0.05 + aapsLogger.debug( + LTag.PUMPCOMM, + "Basal type: $basalType, basal sequence: $basalSequence, basal patch id: $basalPatchId, basal interval: $basalInterval, basal rate: $basalRate, basal delivery: $basalDelivery" + ) // TODO Sync basal flow - offset += 10 + offset += 12 } if (fieldMask and MASK_SETUP != 0) { diff --git a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/packets/SetBolusMotorPacket.kt b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/packets/SetBolusMotorPacket.kt index 3d3b8919cd..25603ab982 100644 --- a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/packets/SetBolusMotorPacket.kt +++ b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/packets/SetBolusMotorPacket.kt @@ -10,6 +10,7 @@ class SetBolusMotorPacket(injector: HasAndroidInjector) : MedtrumPacket(injector } override fun getRequest(): ByteArray { + // TODO CHECK! Seems to be a feature? to set the bolus to vibrate? TEST! return byteArrayOf(opCode) + 0.toByte() } } diff --git a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/packets/SynchronizePacket.kt b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/packets/SynchronizePacket.kt index 71b022e848..5f649e22cd 100644 --- a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/packets/SynchronizePacket.kt +++ b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/packets/SynchronizePacket.kt @@ -6,6 +6,7 @@ import info.nightscout.pump.medtrum.comm.enums.CommandType.SYNCHRONIZE import info.nightscout.pump.medtrum.comm.enums.MedtrumPumpState import info.nightscout.pump.medtrum.extension.toByteArray import info.nightscout.pump.medtrum.extension.toInt +import info.nightscout.rx.logging.AAPSLogger import info.nightscout.rx.logging.LTag import javax.inject.Inject @@ -37,6 +38,7 @@ class SynchronizePacket(injector: HasAndroidInjector) : MedtrumPacket(injector) var state = MedtrumPumpState.fromByte(data[RESP_STATE_START]) medtrumPump.pumpState = state + aapsLogger.debug(LTag.PUMPCOMM, "SynchronizePacket: state: $state") var fieldMask = data.copyOfRange(RESP_FIELDS_START, RESP_FIELDS_END).toInt() var syncData = data.copyOfRange(RESP_SYNC_DATA_START, data.size) diff --git a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/di/MedtrumModule.kt b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/di/MedtrumModule.kt index 9b5548b746..429cf3f319 100644 --- a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/di/MedtrumModule.kt +++ b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/di/MedtrumModule.kt @@ -9,8 +9,10 @@ import dagger.android.ContributesAndroidInjector import dagger.multibindings.IntoMap import info.nightscout.androidaps.plugins.pump.eopatch.ui.MedtrumActivateFragment import info.nightscout.androidaps.plugins.pump.eopatch.ui.MedtrumAttachPatchFragment +import info.nightscout.androidaps.plugins.pump.eopatch.ui.MedtrumDeactivatePatchFragment import info.nightscout.androidaps.plugins.pump.eopatch.ui.MedtrumPreparePatchFragment import info.nightscout.androidaps.plugins.pump.eopatch.ui.MedtrumPrimeFragment +import info.nightscout.androidaps.plugins.pump.eopatch.ui.MedtrumStartDeactivationFragment import info.nightscout.pump.medtrum.services.MedtrumService import info.nightscout.pump.medtrum.ui.MedtrumActivity import info.nightscout.pump.medtrum.ui.MedtrumOverviewFragment @@ -50,6 +52,14 @@ abstract class MedtrumModule { @ContributesAndroidInjector abstract fun contributesMedtrumOverviewFragment(): MedtrumOverviewFragment + @FragmentScope + @ContributesAndroidInjector + internal abstract fun contributesStartDeactivationFragment(): MedtrumStartDeactivationFragment + + @FragmentScope + @ContributesAndroidInjector + internal abstract fun contributesDeactivatePatchFragment(): MedtrumDeactivatePatchFragment + @FragmentScope @ContributesAndroidInjector internal abstract fun contributesPreparePatchFragment(): MedtrumPreparePatchFragment diff --git a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/encryption/Crypt.kt b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/encryption/Crypt.kt index 4cd3e182bc..ce5126de48 100644 --- a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/encryption/Crypt.kt +++ b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/encryption/Crypt.kt @@ -4,6 +4,7 @@ import info.nightscout.pump.medtrum.extension.toByteArray import info.nightscout.pump.medtrum.extension.toLong class Crypt { + private val RIJNDEAL_S_BOX: IntArray = intArrayOf(99, 124, 119, 123, 242, 107, 111, 197, 48, 1, 103, 43, 254, 215, 171, 118, 202, 130, 201, 125, 250, 89, 71, 240, 173, 212, 162, 175, 156, 164, 114, 192, 183, 253, 147, 38, 54, 63, 247, 204, 52, 165, 229, 241, 113, 216, 49, 21, 4, 199, 35, 195, 24, 150, 5, 154, 7, 18, 128, 226, 235, 39, 178, 117, 9, 131, 44, 26, 27, 110, 90, 160, 82, 59, 214, 179, 41, 227, 47, 132, 83, 209, 0, 237, 32, 252, 177, 91, 106, 203, 190, 57, 74, 76, 88, 207, 208, 239, 170, 251, 67, 77, 51, 133, 69, 249, 2, 127, 80, 60, 159, 168, 81, 163, 64, 143, 146, 157, 56, 245, 188, 182, 218, 33, 16, 255, 243, 210, 205, 12, 19, 236, 95, 151, 68, 23, 196, 167, 126, 61, 100, 93, 25, 115, 96, 129, 79, 220, 34, 42, 144, 136, 70, 238, 184, 20, 222, 94, 11, 219, 224, 50, 58, 10, 73, 6, 36, 92, 194, 211, 172, 98, 145, 149, 228, 121, 231, 200, 55, 109, 141, 213, 78, 169, 108, 86, 244, 234, 101, 122, 174, 8, 186, 120, 37, 46, 28, 166, 180, 198, 232, 221, 116, 31, 75, 189, 139, 138, 112, 62, 181, 102, 72, 3, 246, 14, 97, 53, 87, 185, 134, 193, 29, 158, 225, 248, 152, 17, 105, 217, 142, 148, 155, 30, 135, 233, 206, 85, 40, 223, 140, 161, 137, 13, 191, 230, 66, 104, 65, 153, 45, 15, 176, 84, 187, 22) private val RIJNDEAL_INVERSE_S_BOX: IntArray = intArrayOf(82, 9, 106, 213, 48, 54, 165, 56, 191, 64, 163, 158, 129, 243, 215, 251, 124, 227, 57, 130, 155, 47, 255, 135, 52, 142, 67, 68, 196, 222, 233, 203, 84, 123, 148, 50, 166, 194, 35, 61, 238, 76, 149, 11, 66, 250, 195, 78, 8, 46, 161, 102, 40, 217, 36, 178, 118, 91, 162, 73, 109, 139, 209, 37, 114, 248, 246, 100, 134, 104, 152, 22, 212, 164, 92, 204, 93, 101, 182, 146, 108, 112, 72, 80, 253, 237, 185, 218, 94, 21, 70, 87, 167, 141, 157, 132, 144, 216, 171, 0, 140, 188, 211, 10, 247, 228, 88, 5, 184, 179, 69, 6, 208, 44, 30, 143, 202, 63, 15, 2, 193, 175, 189, 3, 1, 19, 138, 107, 58, 145, 17, 65, 79, 103, 220, 234, 151, 242, 207, 206, 240, 180, 230, 115, 150, 172, 116, 34, 231, 173, 53, 133, 226, 249, 55, 232, 28, 117, 223, 110, 71, 241, 26, 113, 29, 41, 197, 137, 111, 183, 98, 14, 170, 24, 190, 27, 252, 86, 62, 75, 198, 210, 121, 32, 154, 219, 192, 254, 120, 205, 90, 244, 31, 221, 168, 51, 136, 7, 199, 49, 177, 18, 16, 89, 39, 128, 236, 95, 96, 81, 127, 169, 25, 181, 74, 13, 45, 229, 122, 159, 147, 201, 156, 239, 160, 224, 59, 77, 174, 42, 245, 176, 200, 235, 187, 60, 131, 83, 153, 97, 23, 43, 4, 126, 186, 119, 214, 38, 225, 105, 20, 99, 85, 33, 12, 125) @@ -14,16 +15,11 @@ class Crypt { return simpleCrypt(key) } - fun randomGen(input: Long): Long { - val A = 16807 - val Q = 127773 - val R = 2836 - val tmp1 = input / Q - var ret = (input - (tmp1 * Q)) * A - (tmp1 * R) - if (ret < 0) { - ret += 2147483647L - } - return ret + fun generateRandomToken(): Long { + val randomBytes = ByteArray(4) + val random = java.security.SecureRandom() + random.nextBytes(randomBytes) + return randomBytes.toLong() } private fun simpleCrypt(inputData: Long): Long { @@ -42,6 +38,18 @@ class Crypt { return temp xor MED_CIPHER } + private fun randomGen(input: Long): Long { + val A = 16807 + val Q = 127773 + val R = 2836 + val tmp1 = input / Q + var ret = (input - (tmp1 * Q)) * A - (tmp1 * R) + if (ret < 0) { + ret += 2147483647L + } + return ret + } + private fun changeByTable(inputData: Long, tableData: IntArray): Long { val value = inputData.toByteArray(4) val results = ByteArray(4) diff --git a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/services/MedtrumService.kt b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/services/MedtrumService.kt index 68fe0fd3a8..71c566ee38 100644 --- a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/services/MedtrumService.kt +++ b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/services/MedtrumService.kt @@ -18,6 +18,8 @@ import info.nightscout.interfaces.queue.CommandQueue import info.nightscout.interfaces.ui.UiInteraction import info.nightscout.pump.medtrum.MedtrumPlugin import info.nightscout.pump.medtrum.MedtrumPump +import info.nightscout.pump.medtrum.R +import info.nightscout.pump.medtrum.code.ConnectionState import info.nightscout.pump.medtrum.comm.enums.MedtrumPumpState import info.nightscout.pump.medtrum.comm.packets.* import info.nightscout.pump.medtrum.extension.toInt @@ -67,14 +69,13 @@ class MedtrumService : DaggerService(), BLECommCallback { private val disposable = CompositeDisposable() private val mBinder: IBinder = LocalBinder() - private var mDeviceSN: Long = 0 private var currentState: State = IdleState() private var mPacket: MedtrumPacket? = null - var isConnected = false - var isConnecting = false - - // TODO: Stuff like this in a settings class? - private var mLastDeviceTime: Long = 0 + + val isConnected: Boolean + get() = medtrumPump.connectionState == ConnectionState.CONNECTED + val isConnecting: Boolean + get() = medtrumPump.connectionState == ConnectionState.CONNECTING override fun onCreate() { super.onCreate() @@ -88,11 +89,11 @@ class MedtrumService : DaggerService(), BLECommCallback { .observeOn(aapsSchedulers.io) .subscribe({ event -> if (event.isChanged(rh.gs(info.nightscout.pump.medtrum.R.string.key_snInput))) { - pumpSync.connectNewPump() changePump() } }, fabricPrivacy::logException) changePump() + // TODO: We should probably listen to the pump state as well and handle some state changes? Or do we handle that in the packets or medtrumPump? } override fun onDestroy() { @@ -101,12 +102,13 @@ class MedtrumService : DaggerService(), BLECommCallback { } fun connect(from: String): Boolean { - aapsLogger.debug(LTag.PUMP, "connect: called!") + aapsLogger.debug(LTag.PUMP, "connect: called from: $from") if (currentState is IdleState) { - isConnecting = true - isConnected = false - rxBus.send(EventPumpStatusChanged(EventPumpStatusChanged.Status.CONNECTING)) - return bleComm.connect(from, mDeviceSN) + medtrumPump.connectionState = ConnectionState.CONNECTING + if (medtrumPump.patchActivated) { + rxBus.send(EventPumpStatusChanged(EventPumpStatusChanged.Status.CONNECTING)) + } + return bleComm.connect(from, medtrumPump.pumpSN) } else { aapsLogger.error(LTag.PUMPCOMM, "Connect attempt when in non Idle state from: $from") return false @@ -119,12 +121,15 @@ class MedtrumService : DaggerService(), BLECommCallback { } fun startActivate(): Boolean { - // TODO not sure this is the correct way lol, We might need to tell AAPS which profile is active val profile = profileFunction.getProfile()?.let { medtrumPump.buildMedtrumProfileArray(it) } val packet = profile?.let { ActivatePacket(injector, it) } return packet?.let { sendPacketAndGetResponse(it) } == true } + fun deactivatePatch(): Boolean { + return sendPacketAndGetResponse(StopPatchPacket(injector)) + } + fun stopConnecting() { // TODO proper way for this might need send commands etc bleComm.stopConnecting() @@ -136,7 +141,7 @@ class MedtrumService : DaggerService(), BLECommCallback { } fun readPumpStatus() { - // TODO + // TODO read pump history } fun loadEvents(): PumpEnactResult { @@ -150,12 +155,6 @@ class MedtrumService : DaggerService(), BLECommCallback { return result } - fun setUserSettings(): PumpEnactResult { - // TODO need this? Check - val result = PumpEnactResult(injector) - return result - } - fun bolus(insulin: Double, carbs: Int, carbTime: Long, t: EventOverviewBolusProgress.Treatment): Boolean { if (!isConnected) return false // TODO @@ -174,15 +173,15 @@ class MedtrumService : DaggerService(), BLECommCallback { fun changePump() { aapsLogger.debug(LTag.PUMP, "changePump: called!") try { - mDeviceSN = sp.getString(info.nightscout.pump.medtrum.R.string.key_snInput, " ").toLong(radix = 16) + medtrumPump.pumpSN = sp.getString(info.nightscout.pump.medtrum.R.string.key_snInput, " ").toLong(radix = 16) } catch (e: NumberFormatException) { aapsLogger.debug(LTag.PUMP, "changePump: Invalid input!") } - // TODO: What do we do with active patch here? Getting status should be enough? - when (currentState) { - is IdleState -> connect("changePump") - // is ReadyState -> disconnect("changePump") - else -> null // TODO: What to do here? Abort stuff? + medtrumPump.setPatchActivatedState(sp.getBoolean(R.string.key_patch_activated, false)) + medtrumPump.patchSessionToken = sp.getLong(R.string.key_session_token, 0) + if (medtrumPump.patchActivated) { + aapsLogger.debug(LTag.PUMP, "changePump: Patch is already activated, setting as ACTIVE") + medtrumPump.pumpState = MedtrumPumpState.ACTIVE // Set inital status as active will be updated on first connection } } @@ -260,10 +259,10 @@ class MedtrumService : DaggerService(), BLECommCallback { open fun onDisconnected() { aapsLogger.debug(LTag.PUMPCOMM, "onDisconnected") - isConnecting = false - isConnected = false - rxBus.send(EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTED)) - + medtrumPump.connectionState = ConnectionState.DISCONNECTED + if (medtrumPump.patchActivated) { + rxBus.send(EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTED)) + } // TODO: Check flow for this toState(IdleState()) } @@ -277,7 +276,6 @@ class MedtrumService : DaggerService(), BLECommCallback { override fun onEnter() { aapsLogger.debug(LTag.PUMPCOMM, "Medtrum Service reached IdleState") - connect("IdleState onEnter") } override fun onConnected() { @@ -287,7 +285,6 @@ class MedtrumService : DaggerService(), BLECommCallback { override fun onDisconnected() { super.onDisconnected() - connect("IdleState onDisconnected") } } @@ -296,7 +293,7 @@ class MedtrumService : DaggerService(), BLECommCallback { override fun onEnter() { aapsLogger.debug(LTag.PUMPCOMM, "Medtrum Service reached AuthState") - mPacket = AuthorizePacket(injector, mDeviceSN) + mPacket = AuthorizePacket(injector) mPacket?.getRequest()?.let { bleComm.sendMessage(it) } } @@ -427,7 +424,6 @@ class MedtrumService : DaggerService(), BLECommCallback { override fun onIndication(data: ByteArray) { if (mPacket?.handleResponse(data) == true) { // Succes! - // TODO: Handle pump state parameters toState(SubscribeState()) } else if (mPacket?.failed == true) { // Failure @@ -464,9 +460,10 @@ class MedtrumService : DaggerService(), BLECommCallback { override fun onEnter() { aapsLogger.debug(LTag.PUMPCOMM, "Medtrum Service reached ReadyState!") // Now we are fully connected and authenticated and we can start sending commands. Let AAPS know - isConnecting = false - isConnected = true - rxBus.send(EventPumpStatusChanged(EventPumpStatusChanged.Status.CONNECTED)) + medtrumPump.connectionState = ConnectionState.CONNECTED + if (medtrumPump.patchActivated) { + rxBus.send(EventPumpStatusChanged(EventPumpStatusChanged.Status.CONNECTED)) + } } } diff --git a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/ui/MedtrumActivity.kt b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/ui/MedtrumActivity.kt index 1c0edd9f94..77656cefb5 100644 --- a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/ui/MedtrumActivity.kt +++ b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/ui/MedtrumActivity.kt @@ -11,8 +11,10 @@ import androidx.appcompat.app.AlertDialog import androidx.lifecycle.ViewModelProvider import info.nightscout.androidaps.plugins.pump.eopatch.ui.MedtrumActivateFragment import info.nightscout.androidaps.plugins.pump.eopatch.ui.MedtrumAttachPatchFragment +import info.nightscout.androidaps.plugins.pump.eopatch.ui.MedtrumDeactivatePatchFragment import info.nightscout.androidaps.plugins.pump.eopatch.ui.MedtrumPreparePatchFragment import info.nightscout.androidaps.plugins.pump.eopatch.ui.MedtrumPrimeFragment +import info.nightscout.androidaps.plugins.pump.eopatch.ui.MedtrumStartDeactivationFragment import info.nightscout.core.utils.extensions.safeGetSerializableExtra import info.nightscout.pump.medtrum.R import info.nightscout.pump.medtrum.code.PatchStep @@ -42,13 +44,16 @@ class MedtrumActivity : MedtrumBaseActivity() { patchStep.observe(this@MedtrumActivity) { when (it) { - PatchStep.PREPARE_PATCH -> setupViewFragment(MedtrumPreparePatchFragment.newInstance()) - PatchStep.PRIME -> setupViewFragment(MedtrumPrimeFragment.newInstance()) - PatchStep.ATTACH_PATCH -> setupViewFragment(MedtrumAttachPatchFragment.newInstance()) - PatchStep.ACTIVATE -> setupViewFragment(MedtrumActivateFragment.newInstance()) - PatchStep.COMPLETE -> this@MedtrumActivity.finish() // TODO proper finish - PatchStep.CANCEL -> this@MedtrumActivity.finish() - else -> Unit + PatchStep.PREPARE_PATCH -> setupViewFragment(MedtrumPreparePatchFragment.newInstance()) + PatchStep.PRIME -> setupViewFragment(MedtrumPrimeFragment.newInstance()) + PatchStep.ATTACH_PATCH -> setupViewFragment(MedtrumAttachPatchFragment.newInstance()) + PatchStep.ACTIVATE -> setupViewFragment(MedtrumActivateFragment.newInstance()) + PatchStep.COMPLETE -> this@MedtrumActivity.finish() // TODO proper finish + PatchStep.CANCEL -> this@MedtrumActivity.finish() + PatchStep.START_DEACTIVATION -> setupViewFragment(MedtrumStartDeactivationFragment.newInstance()) + PatchStep.DEACTIVATE -> setupViewFragment(MedtrumDeactivatePatchFragment.newInstance()) + PatchStep.DEACTIVATION_COMPLETE -> this@MedtrumActivity.finish() // TODO proper finish + else -> Unit } } } @@ -85,8 +90,7 @@ class MedtrumActivity : MedtrumBaseActivity() { const val EXTRA_START_PATCH_STEP = "EXTRA_START_PATCH_FRAGMENT_UI" const val EXTRA_START_FROM_MENU = "EXTRA_START_FROM_MENU" - @JvmStatic - fun createIntentFromMenu(context: Context, patchStep: PatchStep): Intent { + @JvmStatic fun createIntentFromMenu(context: Context, patchStep: PatchStep): Intent { return Intent(context, MedtrumActivity::class.java).apply { putExtra(EXTRA_START_PATCH_STEP, patchStep) putExtra(EXTRA_START_FROM_MENU, true) diff --git a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/ui/MedtrumDeactivatePatchFragment.kt b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/ui/MedtrumDeactivatePatchFragment.kt new file mode 100644 index 0000000000..0791f8cb79 --- /dev/null +++ b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/ui/MedtrumDeactivatePatchFragment.kt @@ -0,0 +1,49 @@ +package info.nightscout.androidaps.plugins.pump.eopatch.ui + +import android.os.Bundle +import android.view.View +import androidx.lifecycle.ViewModelProvider +import info.nightscout.core.ui.toast.ToastUtils +import info.nightscout.pump.medtrum.R +import info.nightscout.pump.medtrum.code.PatchStep +import info.nightscout.pump.medtrum.databinding.FragmentMedtrumDeactivatePatchBinding +import info.nightscout.pump.medtrum.ui.MedtrumBaseFragment +import info.nightscout.pump.medtrum.ui.viewmodel.MedtrumViewModel +import info.nightscout.rx.logging.AAPSLogger +import info.nightscout.rx.logging.LTag +import javax.inject.Inject + +class MedtrumDeactivatePatchFragment : MedtrumBaseFragment() { + + @Inject lateinit var aapsLogger: AAPSLogger + + companion object { + + fun newInstance(): MedtrumDeactivatePatchFragment = MedtrumDeactivatePatchFragment() + } + + override fun getLayoutId(): Int = R.layout.fragment_medtrum_deactivate_patch + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + aapsLogger.debug(LTag.PUMP, "MedtrumDeactivatePatchFragment onViewCreated") + binding.apply { + viewModel = ViewModelProvider(requireActivity(), viewModelFactory).get(MedtrumViewModel::class.java) + viewModel?.apply { + setupStep.observe(viewLifecycleOwner) { + when (it) { + MedtrumViewModel.SetupStep.STOPPED -> btnPositive.visibility = View.VISIBLE + + MedtrumViewModel.SetupStep.ERROR -> { + ToastUtils.errorToast(requireContext(), "Error deactivate") // TODO: String resource and show error message + moveStep(PatchStep.CANCEL) + } + + else -> Unit // Nothing to do here + } + } + deactivatePatch() + } + } + } +} diff --git a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/ui/MedtrumOverviewFragment.kt b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/ui/MedtrumOverviewFragment.kt index 3ee8141993..66c66249e0 100644 --- a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/ui/MedtrumOverviewFragment.kt +++ b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/ui/MedtrumOverviewFragment.kt @@ -43,13 +43,19 @@ class MedtrumOverviewFragment : MedtrumBaseFragment when (evt.peekContent()) { - EventType.ACTIVATION_CLICKED -> requireContext().apply { + EventType.ACTIVATION_CLICKED -> requireContext().apply { val step = convertToPatchStep(medtrumPump.pumpState) + // TODO is stil needed? if (step != PatchStep.PREPARE_PATCH) { aapsLogger.warn(LTag.PUMP, "MedtrumOverviewFragment: Patch already in activation process, going to $step") } - startActivity(MedtrumActivity.createIntentFromMenu(this, step)) } - else -> Unit + startActivity(MedtrumActivity.createIntentFromMenu(this, step)) + } + + EventType.DEACTIVATION_CLICKED -> requireContext().apply { + startActivity(MedtrumActivity.createIntentFromMenu(this, PatchStep.START_DEACTIVATION)) + } + else -> Unit } } diff --git a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/ui/MedtrumStartDeactivationFragment.kt b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/ui/MedtrumStartDeactivationFragment.kt new file mode 100644 index 0000000000..7e9814c667 --- /dev/null +++ b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/ui/MedtrumStartDeactivationFragment.kt @@ -0,0 +1,49 @@ +package info.nightscout.androidaps.plugins.pump.eopatch.ui + +import android.os.Bundle +import android.view.View +import androidx.lifecycle.ViewModelProvider +import info.nightscout.core.ui.toast.ToastUtils +import info.nightscout.pump.medtrum.R +import info.nightscout.pump.medtrum.code.PatchStep +import info.nightscout.pump.medtrum.databinding.FragmentMedtrumStartDeactivationBinding +import info.nightscout.pump.medtrum.ui.MedtrumBaseFragment +import info.nightscout.pump.medtrum.ui.viewmodel.MedtrumViewModel +import info.nightscout.rx.logging.AAPSLogger +import info.nightscout.rx.logging.LTag +import javax.inject.Inject + +class MedtrumStartDeactivationFragment : MedtrumBaseFragment() { + + @Inject lateinit var aapsLogger: AAPSLogger + + companion object { + + fun newInstance(): MedtrumStartDeactivationFragment = MedtrumStartDeactivationFragment() + } + + override fun getLayoutId(): Int = R.layout.fragment_medtrum_start_deactivation + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + aapsLogger.debug(LTag.PUMP, "MedtrumStartDeactivationFragment onViewCreated") + binding.apply { + viewModel = ViewModelProvider(requireActivity(), viewModelFactory).get(MedtrumViewModel::class.java) + viewModel?.apply { + setupStep.observe(viewLifecycleOwner) { + when (it) { + MedtrumViewModel.SetupStep.READY_DEACTIVATE -> btnPositive.visibility = View.VISIBLE + + MedtrumViewModel.SetupStep.ERROR -> { + ToastUtils.errorToast(requireContext(), "Error deactivate") // TODO: String resource and show error message + moveStep(PatchStep.CANCEL) + } + + else -> Unit // Nothing to do here + } + } + startDeactivation() + } + } + } +} diff --git a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/ui/viewmodel/MedtrumOverviewViewModel.kt b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/ui/viewmodel/MedtrumOverviewViewModel.kt index 2aaddfb663..e1f1c74ecb 100644 --- a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/ui/viewmodel/MedtrumOverviewViewModel.kt +++ b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/ui/viewmodel/MedtrumOverviewViewModel.kt @@ -10,6 +10,7 @@ import info.nightscout.pump.medtrum.ui.event.UIEvent import info.nightscout.pump.medtrum.ui.viewmodel.BaseViewModel import info.nightscout.interfaces.profile.ProfileFunction import info.nightscout.pump.medtrum.MedtrumPump +import info.nightscout.pump.medtrum.code.ConnectionState import info.nightscout.pump.medtrum.comm.enums.MedtrumPumpState import info.nightscout.rx.AapsSchedulers import info.nightscout.rx.bus.RxBus @@ -20,6 +21,7 @@ import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.kotlin.plusAssign import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.cancel import kotlinx.coroutines.launch import javax.inject.Inject @@ -32,7 +34,6 @@ class MedtrumOverviewViewModel @Inject constructor( private val medtrumPump: MedtrumPump ) : BaseViewModel() { - private var disposable: CompositeDisposable = CompositeDisposable() private val scope = CoroutineScope(Dispatchers.Default) private val _eventHandler = SingleLiveEvent>() @@ -48,29 +49,28 @@ class MedtrumOverviewViewModel @Inject constructor( get() = _isPatchActivated init { - // TODO proper connection state from medtrumPump - disposable += rxBus - .toObservable(EventPumpStatusChanged::class.java) - .observeOn(aapsSchedulers.main) - .subscribe({ - _bleStatus.value = when (it.status) { - EventPumpStatusChanged.Status.CONNECTING -> - "{fa-bluetooth-b spin} ${it.secondsElapsed}s" + scope.launch { + medtrumPump.connectionStateFlow.collect { state -> + aapsLogger.debug(LTag.PUMP, "MedtrumViewModel connectionStateFlow: $state") + when (state) { + ConnectionState.CONNECTING -> { + _bleStatus.postValue("{fa-bluetooth-b spin}") + } - EventPumpStatusChanged.Status.CONNECTED -> - "{fa-bluetooth}" + ConnectionState.CONNECTED -> { + _bleStatus.postValue("{fa-bluetooth}") + } - EventPumpStatusChanged.Status.DISCONNECTED -> - "{fa-bluetooth-b}" - - else -> - "" - } - }, fabricPrivacy::logException) + ConnectionState.DISCONNECTED -> { + _bleStatus.postValue("{fa-bluetooth-b}") + } + } + } + } scope.launch { medtrumPump.pumpStateFlow.collect { state -> aapsLogger.debug(LTag.PUMP, "MedtrumViewModel pumpStateFlow: $state") - if (state > MedtrumPumpState.EJECTED) { + if (state > MedtrumPumpState.EJECTED && state < MedtrumPumpState.STOPPED) { _isPatchActivated.postValue(true) } else { _isPatchActivated.postValue(false) @@ -79,6 +79,11 @@ class MedtrumOverviewViewModel @Inject constructor( } } + override fun onCleared() { + super.onCleared() + scope.cancel() + } + fun onClickActivation() { aapsLogger.debug(LTag.PUMP, "Start Patch clicked!") val profile = profileFunction.getProfile() @@ -91,6 +96,6 @@ class MedtrumOverviewViewModel @Inject constructor( fun onClickDeactivation() { aapsLogger.debug(LTag.PUMP, "Stop Patch clicked!") - // TODO + _eventHandler.postValue(UIEvent(EventType.DEACTIVATION_CLICKED)) } } \ No newline at end of file diff --git a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/ui/viewmodel/MedtrumViewModel.kt b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/ui/viewmodel/MedtrumViewModel.kt index 1be28aa66a..2b2650427b 100644 --- a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/ui/viewmodel/MedtrumViewModel.kt +++ b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/ui/viewmodel/MedtrumViewModel.kt @@ -6,10 +6,12 @@ import info.nightscout.core.utils.fabric.FabricPrivacy import info.nightscout.pump.medtrum.MedtrumPlugin import info.nightscout.pump.medtrum.MedtrumPump import info.nightscout.pump.medtrum.R +import info.nightscout.pump.medtrum.code.ConnectionState import info.nightscout.pump.medtrum.services.MedtrumService import info.nightscout.pump.medtrum.code.EventType import info.nightscout.pump.medtrum.code.PatchStep import info.nightscout.pump.medtrum.comm.enums.MedtrumPumpState +import info.nightscout.pump.medtrum.encryption.Crypt import info.nightscout.pump.medtrum.ui.MedtrumBaseNavigator import info.nightscout.pump.medtrum.ui.event.SingleLiveEvent import info.nightscout.pump.medtrum.ui.event.UIEvent @@ -22,6 +24,7 @@ import info.nightscout.rx.logging.LTag import info.nightscout.shared.sharedPreferences.SP import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.cancel import kotlinx.coroutines.launch import javax.inject.Inject @@ -54,37 +57,74 @@ class MedtrumViewModel @Inject constructor( private var mInitPatchStep: PatchStep? = null init { + // TODO destroy scope scope.launch { - medtrumPump.pumpStateFlow.collect { state -> - aapsLogger.debug(LTag.PUMP, "MedtrumViewModel pumpStateFlow: $state") - when (state) { - MedtrumPumpState.NONE, MedtrumPumpState.IDLE -> { - setupStep.postValue(SetupStep.INITIAL) - } + medtrumPump.connectionStateFlow.collect { state -> + aapsLogger.debug(LTag.PUMP, "MedtrumViewModel connectionStateFlow: $state") + if (patchStep.value != null) { + when (state) { + ConnectionState.CONNECTED -> { + if (patchStep.value == PatchStep.START_DEACTIVATION) { + updateSetupStep(SetupStep.READY_DEACTIVATE) + } + } - MedtrumPumpState.FILLED -> { - setupStep.postValue(SetupStep.FILLED) - } + ConnectionState.DISCONNECTED -> { + if (patchStep.value != PatchStep.DEACTIVATION_COMPLETE && patchStep.value != PatchStep.COMPLETE && patchStep.value != PatchStep.CANCEL) { + medtrumService?.connect("Try reconnect from viewModel") + } + } - MedtrumPumpState.PRIMING -> { - // setupStep.postValue(SetupStep.PRIMING) - // TODO: What to do here? start prime counter? - } - - MedtrumPumpState.PRIMED, MedtrumPumpState.EJECTED -> { - setupStep.postValue(SetupStep.PRIMED) - } - - MedtrumPumpState.ACTIVE, MedtrumPumpState.ACTIVE_ALT -> { - setupStep.postValue(SetupStep.ACTIVATED) - } - - else -> { - setupStep.postValue(SetupStep.ERROR) + ConnectionState.CONNECTING -> { + } } } } } + scope.launch { + medtrumPump.pumpStateFlow.collect { state -> + aapsLogger.debug(LTag.PUMP, "MedtrumViewModel pumpStateFlow: $state") + if (patchStep.value != null) { + when (state) { + MedtrumPumpState.NONE, MedtrumPumpState.IDLE -> { + updateSetupStep(SetupStep.INITIAL) + } + + MedtrumPumpState.FILLED -> { + updateSetupStep(SetupStep.FILLED) + } + + MedtrumPumpState.PRIMING -> { + // updateSetupStep(SetupStep.PRIMING) + // TODO: What to do here? start prime counter? + } + + MedtrumPumpState.PRIMED, MedtrumPumpState.EJECTED -> { + updateSetupStep(SetupStep.PRIMED) + } + + MedtrumPumpState.ACTIVE, MedtrumPumpState.ACTIVE_ALT -> { + medtrumPump.setPatchActivatedState(true) + updateSetupStep(SetupStep.ACTIVATED) + } + + MedtrumPumpState.STOPPED -> { + medtrumPump.setPatchActivatedState(false) + updateSetupStep(SetupStep.STOPPED) + } + + else -> { + updateSetupStep(SetupStep.ERROR) + } + } + } + } + } + } + + override fun onCleared() { + super.onCleared() + scope.cancel() } fun moveStep(newPatchStep: PatchStep) { @@ -92,14 +132,27 @@ class MedtrumViewModel @Inject constructor( if (oldPatchStep != newPatchStep) { when (newPatchStep) { - PatchStep.CANCEL -> { - // if (medtrumService?.isConnected == true || medtrumService?.isConnecting == true) medtrumService?.disconnect("Cancel") else { - // } + PatchStep.CANCEL -> { + if (medtrumService?.isConnected == true || medtrumService?.isConnecting == true) medtrumService?.disconnect("Cancel") else { + } + // TODO: For DEACTIVATE STATE we might want to move to force cancel screen + if (oldPatchStep == PatchStep.START_DEACTIVATION || oldPatchStep == PatchStep.DEACTIVATE) { + // Deactivation was canceled + medtrumPump.setPatchActivatedStateTemp(true) + } } - else -> null - }?.let { - // TODO Update lifecycle + PatchStep.COMPLETE -> { + if (medtrumService?.isConnected == true || medtrumService?.isConnecting == true) medtrumService?.disconnect("Complete") else { + } + } + + PatchStep.DEACTIVATION_COMPLETE -> { + if (medtrumService?.isConnected == true || medtrumService?.isConnecting == true) medtrumService?.disconnect("DeactivationComplete") else { + } + } + + else -> {} } } @@ -113,12 +166,20 @@ class MedtrumViewModel @Inject constructor( } fun preparePatch() { - // TODO When we dont need to connect what needs to be done here? + if (medtrumPump.patchActivated == true) { + aapsLogger.warn(LTag.PUMP, "preparePatch: already activated! conflicting state?") + // In this case user could have removed the patch without deactivating it? + medtrumPump.setPatchActivatedState(false) + } + // New session, generate new session token + aapsLogger.info(LTag.PUMP, "preparePatch: new session") + medtrumPump.patchSessionToken = Crypt().generateRandomToken() + sp.putLong(R.string.key_session_token, medtrumPump.patchSessionToken) + // Connect to pump medtrumService?.connect("PreparePatch") } fun startPrime() { - // TODO: Get result from service if (medtrumPump.pumpState == MedtrumPumpState.PRIMING) { aapsLogger.info(LTag.PUMP, "startPrime: already priming!") } else { @@ -126,7 +187,7 @@ class MedtrumViewModel @Inject constructor( aapsLogger.info(LTag.PUMP, "startPrime: success!") } else { aapsLogger.info(LTag.PUMP, "startPrime: failure!") - setupStep.postValue(SetupStep.ERROR) + updateSetupStep(SetupStep.ERROR) } } } @@ -136,20 +197,46 @@ class MedtrumViewModel @Inject constructor( aapsLogger.info(LTag.PUMP, "startActivate: success!") } else { aapsLogger.info(LTag.PUMP, "startActivate: failure!") - setupStep.postValue(SetupStep.ERROR) + updateSetupStep(SetupStep.ERROR) + } + } + + fun startDeactivation() { + // Set active already to false, so UI can control the pump connection instead of AAPS pumpqueue + medtrumPump.setPatchActivatedStateTemp(false) + + // Start connecting if needed + if (medtrumService?.isConnected == true) { + updateSetupStep(SetupStep.READY_DEACTIVATE) + } else if (medtrumService?.isConnecting != true) { + medtrumService?.connect("StartDeactivation") + } + // TODO: Also start timer to check if connection is made, if timed out show force forget patch + } + + fun deactivatePatch() { + if (medtrumService?.deactivatePatch() == true) { + aapsLogger.info(LTag.PUMP, "deactivatePatch: success!") + } else { + aapsLogger.info(LTag.PUMP, "deactivatePatch: failure!") + // Check if this is needed, for now even when it failed, we assume the user will remove the patch and pumpbase + medtrumPump.setPatchActivatedStateTemp(true) // failed to activate, set activate state to true + // TODO: State to force forget the patch or try again + updateSetupStep(SetupStep.ERROR) } } private fun prepareStep(step: PatchStep?): PatchStep { - // TODO Title per screen :) And proper sync with patchstate (step ?: convertToPatchStep(medtrumPump.pumpState)).let { newStep -> when (newStep) { - PatchStep.PREPARE_PATCH -> R.string.step_prepare_patch - PatchStep.PRIME -> R.string.step_prime - PatchStep.ATTACH_PATCH -> R.string.step_attach - PatchStep.ACTIVATE -> R.string.step_activate - PatchStep.COMPLETE -> R.string.step_complete - else -> _title.value + PatchStep.PREPARE_PATCH -> R.string.step_prepare_patch + PatchStep.PRIME -> R.string.step_prime + PatchStep.ATTACH_PATCH -> R.string.step_attach + PatchStep.ACTIVATE -> R.string.step_activate + PatchStep.COMPLETE -> R.string.step_complete + PatchStep.START_DEACTIVATION, PatchStep.DEACTIVATE -> R.string.step_deactivate + PatchStep.DEACTIVATION_COMPLETE -> R.string.step_complete + else -> _title.value }.let { aapsLogger.info(LTag.PUMP, "prepareStep: title before cond: $it") if (_title.value != it) { @@ -164,12 +251,7 @@ class MedtrumViewModel @Inject constructor( } } - enum class SetupStep { - INITIAL, - FILLED, - PRIMED, - ACTIVATED, - ERROR + enum class SetupStep { INITIAL, FILLED, PRIMED, ACTIVATED, ERROR, START_DEACTIVATION, STOPPED, READY_DEACTIVATE } val setupStep = MutableLiveData() diff --git a/pump/medtrum/src/main/res/layout/fragment_medtrum_deactivate_patch.xml b/pump/medtrum/src/main/res/layout/fragment_medtrum_deactivate_patch.xml new file mode 100644 index 0000000000..7499000c0f --- /dev/null +++ b/pump/medtrum/src/main/res/layout/fragment_medtrum_deactivate_patch.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + +