Deactivation, better connection flow

This commit is contained in:
jbr7rr 2023-04-15 15:23:26 +02:00
parent fff833dae3
commit 61e873dbcc
25 changed files with 582 additions and 177 deletions

View file

@ -410,6 +410,7 @@ enum class PumpType {
baseBasalSpecialSteps = null,
pumpCapability = PumpCapability.MedtrumCapabilities,
isPatchPump = true,
maxReservoirReading = 400,
source = Source.Medtrum
);

View file

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

View file

@ -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> = _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<MedtrumPumpState> = _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<Int> = _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()

View file

@ -0,0 +1,7 @@
package info.nightscout.pump.medtrum.code
enum class ConnectionState {
CONNECTED,
DISCONNECTED,
CONNECTING;
}

View file

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

View file

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

View file

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

View file

@ -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) {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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<ActivityMedtrumBinding>() {
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<ActivityMedtrumBinding>() {
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)

View file

@ -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<FragmentMedtrumDeactivatePatchBinding>() {
@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()
}
}
}
}

View file

@ -43,13 +43,19 @@ class MedtrumOverviewFragment : MedtrumBaseFragment<FragmentMedtrumOverviewBindi
viewmodel?.apply {
eventHandler.observe(viewLifecycleOwner) { evt ->
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
}
}

View file

@ -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<FragmentMedtrumStartDeactivationBinding>() {
@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()
}
}
}
}

View file

@ -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<MedtrumBaseNavigator>() {
private var disposable: CompositeDisposable = CompositeDisposable()
private val scope = CoroutineScope(Dispatchers.Default)
private val _eventHandler = SingleLiveEvent<UIEvent<EventType>>()
@ -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))
}
}

View file

@ -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<SetupStep>()

View file

@ -0,0 +1,66 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<import type="info.nightscout.pump.medtrum.code.PatchStep" />
<variable
name="viewModel"
type="info.nightscout.pump.medtrum.ui.viewmodel.MedtrumViewModel" />
</data>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="30dp"
android:layout_marginEnd="30dp"
android:fillViewport="true"
android:scrollbars="none">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:context=".ui.MedtrumActivity">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/layout_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
<Button
android:id="@+id/btn_negative"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:contentDescription="@string/cancel"
android:text="@string/cancel"
app:layout_constraintEnd_toStartOf="@id/btn_positive"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:onSafeClick="@{() -> viewModel.moveStep(PatchStep.CANCEL)}" />
<Button
android:id="@+id/btn_positive"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
android:contentDescription="@string/string_start_complete"
android:text="@string/string_start_complete"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/btn_negative"
app:layout_constraintTop_toTopOf="parent"
app:onSafeClick="@{() -> viewModel.moveStep(PatchStep.DEACTIVATION_COMPLETE)}"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
</layout>

View file

@ -0,0 +1,66 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<import type="info.nightscout.pump.medtrum.code.PatchStep" />
<variable
name="viewModel"
type="info.nightscout.pump.medtrum.ui.viewmodel.MedtrumViewModel" />
</data>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="30dp"
android:layout_marginEnd="30dp"
android:fillViewport="true"
android:scrollbars="none">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:context=".ui.MedtrumActivity">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/layout_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
<Button
android:id="@+id/btn_negative"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:contentDescription="@string/cancel"
android:text="@string/cancel"
app:layout_constraintEnd_toStartOf="@id/btn_positive"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:onSafeClick="@{() -> viewModel.moveStep(PatchStep.CANCEL)}" />
<Button
android:id="@+id/btn_positive"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
android:contentDescription="@string/string_start_deactivate"
android:text="@string/string_start_deactivate"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/btn_negative"
app:layout_constraintTop_toTopOf="parent"
app:onSafeClick="@{() -> viewModel.moveStep(PatchStep.DEACTIVATE)}"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
</layout>

View file

@ -3,6 +3,8 @@
<!-- MedtrumPump -->
<string name="key_snInput" translatable="false">snInput</string>
<string name="key_medtrumpump_settings" translatable="false">medtrumpump_settings</string>
<string name="key_patch_activated" translatable="false">medtrum_patch_activated</string>
<string name="key_session_token" translatable="false">medtrum_session_token</string>
<string name="medtrum">Medtrum</string>
<string name="medtrum_pump_shortname">MEDTRUM</string>
@ -20,12 +22,14 @@
<string name="string_start_prime">Start priming</string>
<string name="string_start_activate">Start activation</string>
<string name="string_start_complete">Complete</string>
<string name="string_start_deactivate">Deactivate patch</string>
<string name ="step_prepare_patch">Prepare patch</string>
<string name ="step_prime">Priming</string>
<string name ="step_attach">Attach patch</string>
<string name ="step_activate">Activation</string>
<string name ="step_complete">Complete</string>
<string name ="step_deactivate">Deactivation</string>
<!-- settings-->
<string name="snInput_title">SN</string>

View file

@ -3,6 +3,7 @@ package info.nightscout.pump.medtrum
import info.nightscout.androidaps.TestBaseWithProfile
import info.nightscout.shared.sharedPreferences.SP
import info.nightscout.interfaces.profile.Instantiator
import info.nightscout.interfaces.pump.PumpSync
import info.nightscout.interfaces.stats.TddCalculator
import org.junit.jupiter.api.BeforeEach
import org.mockito.Mock
@ -12,11 +13,12 @@ open class MedtrumTestBase: TestBaseWithProfile() {
@Mock lateinit var sp: SP
@Mock lateinit var instantiator: Instantiator
@Mock lateinit var tddCalculator: TddCalculator
@Mock lateinit var pumpSync: PumpSync
lateinit var medtrumPump: MedtrumPump
@BeforeEach
fun setup() {
medtrumPump = MedtrumPump(aapsLogger, sp, dateUtil, instantiator)
medtrumPump = MedtrumPump(aapsLogger, sp, dateUtil)
}
}

View file

@ -17,6 +17,7 @@ class ActivatePacketTest : MedtrumTestBase() {
it.aapsLogger = aapsLogger
it.medtrumPump = medtrumPump
it.tddCalculator = tddCalculator
it.pumpSync = pumpSync
}
}
}
@ -35,7 +36,7 @@ class ActivatePacketTest : MedtrumTestBase() {
val result = packet.getRequest()
// Expected values
val expectedByteArray = byteArrayOf(18, 0, 0, 1, 6, 0, 0, 0, 32, 3, 16, 14, 0, 0, 1, 3, 16, 14, 0, 0, 1, 2, 12, 12, 12)
val expectedByteArray = byteArrayOf(18, 0, 12, 1, 6, 0, 0, 30, 32, 3, 16, 14, 0, 0, 1, 3, 16, 14, 0, 0, 1, 2, 12, 12, 12)
assertEquals(expectedByteArray.contentToString(), result.contentToString())
}

View file

@ -13,8 +13,9 @@ class AuthorizePacketTest : MedtrumTestBase() {
private val packetInjector = HasAndroidInjector {
AndroidInjector {
if (it is MedtrumPacket) {
if (it is AuthorizePacket) {
it.aapsLogger = aapsLogger
it.medtrumPump = medtrumPump
}
}
}
@ -22,16 +23,17 @@ class AuthorizePacketTest : MedtrumTestBase() {
@Test fun getRequestGivenPacketAndSNWhenCalledThenReturnAuthorizePacket() {
// Inputs
val opCode = 5
val sn = 2859923929
medtrumPump.pumpSN = 2859923929
medtrumPump.patchSessionToken = 667
// Call
val packet = AuthorizePacket(packetInjector, sn)
val packet = AuthorizePacket(packetInjector)
val result = packet.getRequest()
// Expected values
val key = 3364239851
val type = 2
val expectedByteArray = byteArrayOf(opCode.toByte()) + type.toByte() + 0.toByteArray(4) + key.toByteArray(4)
val expectedByteArray = byteArrayOf(opCode.toByte()) + type.toByte() + medtrumPump.patchSessionToken.toByteArray(4) + key.toByteArray(4)
assertEquals(10, result.size)
assertEquals(expectedByteArray.contentToString(), result.contentToString())
}
@ -47,7 +49,7 @@ class AuthorizePacketTest : MedtrumTestBase() {
// Call
val response = byteArrayOf(0) + opCode.toByte() + 0.toByteArray(2) + responseCode.toByteArray(2) + 0.toByte() + deviceType.toByte() + swVerX.toByte() + swVerY.toByte() + swVerZ.toByte()
val packet = AuthorizePacket(packetInjector, 0)
val packet = AuthorizePacket(packetInjector)
val result = packet.handleResponse(response)
// Expected values
@ -65,7 +67,7 @@ class AuthorizePacketTest : MedtrumTestBase() {
// Call
val response = byteArrayOf(0) + opCode.toByte() + 0.toByteArray(2) + responseCode.toByteArray(2)
val packet = AuthorizePacket(packetInjector, 0)
val packet = AuthorizePacket(packetInjector)
packet.opCode = opCode.toByte()
val result = packet.handleResponse(response)