Patch activation flow
This commit is contained in:
parent
af6445a3dc
commit
fff833dae3
26 changed files with 720 additions and 116 deletions
|
@ -8,6 +8,7 @@ import android.os.IBinder
|
|||
import dagger.android.HasAndroidInjector
|
||||
import info.nightscout.core.ui.toast.ToastUtils
|
||||
import info.nightscout.core.utils.fabric.FabricPrivacy
|
||||
import info.nightscout.interfaces.notifications.Notification
|
||||
import info.nightscout.interfaces.plugin.PluginDescription
|
||||
import info.nightscout.interfaces.plugin.PluginType
|
||||
import info.nightscout.interfaces.profile.Profile
|
||||
|
@ -33,6 +34,7 @@ import info.nightscout.rx.AapsSchedulers
|
|||
import info.nightscout.rx.bus.RxBus
|
||||
import info.nightscout.rx.events.EventAppExit
|
||||
import info.nightscout.rx.events.EventAppInitialized
|
||||
import info.nightscout.rx.events.EventDismissNotification
|
||||
import info.nightscout.rx.events.EventOverviewBolusProgress
|
||||
import info.nightscout.rx.events.EventPreferenceChange
|
||||
import info.nightscout.rx.logging.AAPSLogger
|
||||
|
@ -170,11 +172,33 @@ class MedtrumPlugin @Inject constructor(
|
|||
}
|
||||
|
||||
override fun setNewBasalProfile(profile: Profile): PumpEnactResult {
|
||||
return PumpEnactResult(injector).success(true).enacted(true) // TODO
|
||||
// New profile will be set when patch is activated
|
||||
if (!isInitialized()) return PumpEnactResult(injector).success(true).enacted(true)
|
||||
|
||||
return if (medtrumService?.updateBasalsInPump(profile) == true) {
|
||||
rxBus.send(EventDismissNotification(Notification.FAILED_UPDATE_PROFILE))
|
||||
uiInteraction.addNotificationValidFor(Notification.PROFILE_SET_OK, rh.gs(info.nightscout.core.ui.R.string.profile_set_ok), Notification.INFO, 60)
|
||||
PumpEnactResult(injector).success(true).enacted(true)
|
||||
} else {
|
||||
uiInteraction.addNotification(Notification.FAILED_UPDATE_PROFILE, rh.gs(info.nightscout.core.ui.R.string.failed_update_basal_profile), Notification.URGENT)
|
||||
PumpEnactResult(injector)
|
||||
}
|
||||
}
|
||||
|
||||
override fun isThisProfileSet(profile: Profile): Boolean {
|
||||
return false // TODO
|
||||
if (!isInitialized()) return true
|
||||
var result = false
|
||||
val profileBytes = medtrumPump.buildMedtrumProfileArray(profile)
|
||||
if (profileBytes?.size == medtrumPump.actualBasalProfile.size) {
|
||||
result = true
|
||||
for (i in profileBytes.indices) {
|
||||
if (profileBytes[i] != medtrumPump.actualBasalProfile[i]) {
|
||||
result = false
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
override fun lastDataTime(): Long {
|
||||
|
@ -204,12 +228,14 @@ 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) // TODO
|
||||
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) // TODO
|
||||
return PumpEnactResult(injector).success(false).enacted(false)
|
||||
.comment("Medtrum driver does not support extended boluses")
|
||||
}
|
||||
|
||||
override fun cancelTempBasal(enforceNew: Boolean): PumpEnactResult {
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
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
|
||||
|
@ -14,6 +16,8 @@ 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
|
||||
import javax.inject.Singleton
|
||||
import kotlin.math.round
|
||||
|
@ -26,20 +30,29 @@ class MedtrumPump @Inject constructor(
|
|||
private val instantiator: Instantiator
|
||||
) {
|
||||
|
||||
enum class PatchActivationState(val state: Int) {
|
||||
NONE(0),
|
||||
IDLE(1),
|
||||
ACTIVATING(2),
|
||||
ACTIVATED(3),
|
||||
DEACTIVATING(4),
|
||||
DEACTIVATED(5),
|
||||
ERROR(6)
|
||||
// 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
|
||||
}
|
||||
|
||||
// Pump state and parameters
|
||||
var pumpState = MedtrumPumpState.NONE // TODO save in SP
|
||||
var patchActivationState = PatchActivationState.NONE // TODO save in SP
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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
|
||||
var lastKnownSequenceNumber = 0
|
||||
var lastTimeReceivedFromPump = 0L // Time in seconds!
|
||||
|
@ -48,7 +61,6 @@ class MedtrumPump @Inject constructor(
|
|||
var patchAge = 0L // Time in seconds!
|
||||
|
||||
var reservoir = 0.0
|
||||
var primeProgress = 0
|
||||
|
||||
var batteryVoltage_A = 0.0
|
||||
var batteryVoltage_B = 0.0
|
||||
|
@ -67,7 +79,6 @@ class MedtrumPump @Inject constructor(
|
|||
var lastStopSequence = 0
|
||||
var lastStopPatchId = 0
|
||||
|
||||
|
||||
// TODO set these setting on init
|
||||
// User settings (desired values, to be set on pump)
|
||||
var desiredPatchExpiration = false
|
||||
|
@ -75,7 +86,6 @@ class MedtrumPump @Inject constructor(
|
|||
var desiredHourlyMaxInsulin: Int = 40
|
||||
var desiredDailyMaxInsulin: Int = 180
|
||||
|
||||
|
||||
fun buildMedtrumProfileArray(nsProfile: Profile): ByteArray? {
|
||||
val list = nsProfile.getBasalValues()
|
||||
var basals = byteArrayOf()
|
||||
|
@ -87,7 +97,7 @@ class MedtrumPump @Inject constructor(
|
|||
return null
|
||||
}
|
||||
basals += ((rate shl 12) + time).toByteArray(3)
|
||||
aapsLogger.debug(LTag.PUMP, "buildMedtrumProfileArray: value: ${item.value} time: ${item.timeAsSeconds}")
|
||||
aapsLogger.debug(LTag.PUMP, "buildMedtrumProfileArray: value: ${item.value} time: ${item.timeAsSeconds}, converted: $rate, $time")
|
||||
}
|
||||
return (list.size).toByteArray(1) + basals
|
||||
}
|
||||
|
|
|
@ -8,9 +8,8 @@ enum class PatchStep {
|
|||
DISCARDED_FROM_ALARM,
|
||||
PREPARE_PATCH,
|
||||
PRIME,
|
||||
ATTACH_INSERT_NEEDLE,
|
||||
BASAL_SCHEDULE,
|
||||
CHECK_CONNECTION,
|
||||
ATTACH_PATCH,
|
||||
ACTIVATE,
|
||||
CANCEL,
|
||||
COMPLETE,
|
||||
BACK_TO_HOME,
|
||||
|
|
|
@ -3,7 +3,7 @@ package info.nightscout.pump.medtrum.comm.enums
|
|||
enum class MedtrumPumpState(val state: Byte) {
|
||||
NONE(0),
|
||||
IDLE(1),
|
||||
FILL(2),
|
||||
FILLED(2),
|
||||
PRIMING(3),
|
||||
PRIMED(4),
|
||||
EJECTING(5),
|
||||
|
|
|
@ -57,14 +57,23 @@ class ActivatePacket(injector: HasAndroidInjector, private val basalProfile: Byt
|
|||
* bytes 15 - end // Basal profile > see MedtrumPump
|
||||
*/
|
||||
|
||||
val autoSuspendEnable: Byte = 0
|
||||
val autoSuspendTime: Byte = 12 // Not sure why, but pump needs this in order to activate
|
||||
|
||||
val patchExpiration: Byte = medtrumPump.desiredPatchExpiration.toByte()
|
||||
val alarmSetting: Byte = medtrumPump.desiredAlarmSetting
|
||||
|
||||
val lowSuspend: Byte = 0
|
||||
val predictiveLowSuspend: Byte = 0
|
||||
val predictiveLowSuspendRange: Byte = 30 // Not sure why, but pump needs this in order to activate
|
||||
|
||||
val hourlyMaxInsulin: Int = round(medtrumPump.desiredHourlyMaxInsulin / 0.05).toInt()
|
||||
val dailyMaxInsulin: Int = round(medtrumPump.desiredDailyMaxInsulin / 0.05).toInt()
|
||||
val currentTDD: Double = tddCalculator.calculateToday()?.totalAmount?.div(0.05) ?: 0.0
|
||||
|
||||
return byteArrayOf(opCode) + 0.toByteArray(2) + patchExpiration + alarmSetting + 0.toByteArray(3) + hourlyMaxInsulin.toByteArray(2) + dailyMaxInsulin.toByteArray(2) + currentTDD.toInt()
|
||||
.toByteArray(2) + 1.toByte() + basalProfile
|
||||
return byteArrayOf(opCode) + autoSuspendEnable + autoSuspendTime + patchExpiration + alarmSetting + lowSuspend + predictiveLowSuspend + predictiveLowSuspendRange + hourlyMaxInsulin.toByteArray(
|
||||
2
|
||||
) + dailyMaxInsulin.toByteArray(2) + currentTDD.toInt().toByteArray(2) + 1.toByte() + basalProfile
|
||||
}
|
||||
|
||||
override fun handleResponse(data: ByteArray): Boolean {
|
||||
|
@ -82,7 +91,9 @@ class ActivatePacket(injector: HasAndroidInjector, private val basalProfile: Byt
|
|||
|
||||
medtrumPump.patchId = patchId
|
||||
medtrumPump.lastTimeReceivedFromPump = time
|
||||
// TODO: Handle basal here, and report to AAPS directly
|
||||
// Update the actual basal profile
|
||||
medtrumPump.actualBasalProfile = basalProfile
|
||||
// TODO: Handle history entry
|
||||
medtrumPump.handleBasalStatusUpdate(basalType, basalValue, basalSequence, basalPatchId, basalStartTime, time)
|
||||
}
|
||||
|
||||
|
|
|
@ -30,13 +30,13 @@ open class MedtrumPacket(protected var injector: HasAndroidInjector) {
|
|||
}
|
||||
|
||||
open fun getRequest(): ByteArray {
|
||||
aapsLogger.debug(LTag.PUMPCOMM, "Get REQUEST TEST")
|
||||
return byteArrayOf(opCode)
|
||||
}
|
||||
|
||||
/** handles a response from the Medtrum pump, returns true if command was successfull, returns false if command failed or waiting for response */
|
||||
open fun handleResponse(data: ByteArray): Boolean {
|
||||
if (expectedMinRespLength > data.size) {
|
||||
// Check for broken packets
|
||||
if (RESP_RESULT_END > data.size) {
|
||||
failed = true
|
||||
aapsLogger.debug(LTag.PUMPCOMM, "handleResponse: Unexpected response length, expected: $expectedMinRespLength got: ${data.size}")
|
||||
return false
|
||||
|
@ -48,11 +48,17 @@ open class MedtrumPacket(protected var injector: HasAndroidInjector) {
|
|||
return when {
|
||||
incomingOpCode != opCode -> {
|
||||
failed = true
|
||||
aapsLogger.debug(LTag.PUMPCOMM, "handleResponse: Unexpected command, expected: $opCode got: $incomingOpCode")
|
||||
aapsLogger.error(LTag.PUMPCOMM, "handleResponse: Unexpected command, expected: $opCode got: $incomingOpCode")
|
||||
false
|
||||
}
|
||||
|
||||
responseCode == 0 -> {
|
||||
// Check if length is what is expected from this type of packet
|
||||
if (expectedMinRespLength > data.size) {
|
||||
failed = true
|
||||
aapsLogger.debug(LTag.PUMPCOMM, "handleResponse: Unexpected response length, expected: $expectedMinRespLength got: ${data.size}")
|
||||
return false
|
||||
}
|
||||
aapsLogger.debug(LTag.PUMPCOMM, "handleResponse: Happy command: $opCode response: $responseCode")
|
||||
true
|
||||
}
|
||||
|
@ -65,7 +71,7 @@ open class MedtrumPacket(protected var injector: HasAndroidInjector) {
|
|||
|
||||
else -> {
|
||||
failed = true
|
||||
aapsLogger.debug(LTag.PUMPCOMM, "handleResponse: Error in command: $opCode response: $responseCode")
|
||||
aapsLogger.warn(LTag.PUMPCOMM, "handleResponse: Error in command: $opCode response: $responseCode")
|
||||
false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,6 +36,7 @@ class NotificationPacket(val injector: HasAndroidInjector) {
|
|||
@Inject lateinit var medtrumPump: MedtrumPump
|
||||
|
||||
companion object {
|
||||
|
||||
private const val NOTIF_STATE_START = 0
|
||||
private const val NOTIF_STATE_END = NOTIF_STATE_START + 1
|
||||
|
||||
|
@ -96,13 +97,13 @@ class NotificationPacket(val injector: HasAndroidInjector) {
|
|||
aapsLogger.debug(LTag.PUMPCOMM, "Normal bolus notification received")
|
||||
var bolusData = data.copyOfRange(offset, offset + 1).toInt()
|
||||
var bolusType = bolusData and 0x7F
|
||||
var bolusCompleted = (bolusData shr 7) and 0x01
|
||||
var bolusCompleted = (bolusData shr 7) and 0x01 // TODO: Check for other flags here :)
|
||||
var bolusDelivered = data.copyOfRange(offset + 1, offset + 3).toInt() / 0.05
|
||||
// TODO Sync bolus flow:
|
||||
// If bolus is known add status
|
||||
// If bolus is not known start read bolus
|
||||
// When bolus is completed, remove bolus from medtrumPump
|
||||
aapsLogger.debug(LTag.PUMPCOMM, "Bolus type: $bolusType, bolus completed: $bolusCompleted, bolus delivered: $bolusDelivered")
|
||||
aapsLogger.debug(LTag.PUMPCOMM, "Bolus type: $bolusType, bolusData: $bolusData bolus completed: $bolusCompleted, bolus delivered: $bolusDelivered")
|
||||
offset += 3
|
||||
}
|
||||
|
||||
|
|
|
@ -3,18 +3,56 @@ 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.SET_BASAL_PROFILE
|
||||
import info.nightscout.pump.medtrum.extension.toInt
|
||||
import info.nightscout.pump.medtrum.extension.toLong
|
||||
import info.nightscout.pump.medtrum.util.MedtrumTimeUtil
|
||||
import javax.inject.Inject
|
||||
|
||||
class SetBasalProfilePacket(injector: HasAndroidInjector, private val basalProfile: ByteArray) : MedtrumPacket(injector) {
|
||||
|
||||
@Inject lateinit var medtrumPump: MedtrumPump
|
||||
|
||||
companion object {
|
||||
|
||||
private const val RESP_BASAL_TYPE_START = 6
|
||||
private const val RESP_BASAL_TYPE_END = RESP_BASAL_TYPE_START + 1
|
||||
private const val RESP_BASAL_VALUE_START = 7
|
||||
private const val RESP_BASAL_VALUE_END = RESP_BASAL_VALUE_START + 2
|
||||
private const val RESP_BASAL_SEQUENCE_START = 9
|
||||
private const val RESP_BASAL_SEQUENCE_END = RESP_BASAL_SEQUENCE_START + 2
|
||||
private const val RESP_BASAL_PATCH_ID_START = 11
|
||||
private const val RESP_BASAL_PATCH_ID_END = RESP_BASAL_PATCH_ID_START + 2
|
||||
private const val RESP_BASAL_START_TIME_START = 13
|
||||
private const val RESP_BASAL_START_TIME_END = RESP_BASAL_START_TIME_START + 4
|
||||
}
|
||||
|
||||
init {
|
||||
opCode = SET_BASAL_PROFILE.code
|
||||
expectedMinRespLength = RESP_BASAL_START_TIME_END
|
||||
|
||||
}
|
||||
|
||||
override fun getRequest(): ByteArray {
|
||||
val basalType: Byte = 1 // Fixed to normal basal
|
||||
return byteArrayOf(opCode) + basalType + basalProfile
|
||||
}
|
||||
|
||||
override fun handleResponse(data: ByteArray): Boolean {
|
||||
val success = super.handleResponse(data)
|
||||
if (success) {
|
||||
val medtrumTimeUtil = MedtrumTimeUtil()
|
||||
val basalType = data.copyOfRange(RESP_BASAL_TYPE_START, RESP_BASAL_TYPE_END).toInt()
|
||||
val basalValue = data.copyOfRange(RESP_BASAL_VALUE_START, RESP_BASAL_VALUE_END).toInt() * 0.05
|
||||
val basalSequence = data.copyOfRange(RESP_BASAL_SEQUENCE_START, RESP_BASAL_SEQUENCE_END).toInt()
|
||||
val basalPatchId = data.copyOfRange(RESP_BASAL_PATCH_ID_START, RESP_BASAL_PATCH_ID_END).toInt()
|
||||
val basalStartTime = medtrumTimeUtil.convertPumpTimeToSystemTimeSeconds(data.copyOfRange(RESP_BASAL_START_TIME_START, RESP_BASAL_START_TIME_END).toLong())
|
||||
|
||||
// Update the actual basal profile
|
||||
medtrumPump.actualBasalProfile = basalProfile
|
||||
// TODO: Do we need to let AAPS know? Maybe depends on where we cancel TBR if we need to
|
||||
// TODO: Handle history entry
|
||||
medtrumPump.handleBasalStatusUpdate(basalType, basalValue, basalSequence, basalPatchId, basalStartTime)
|
||||
}
|
||||
return success
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,8 +46,7 @@ class SynchronizePacket(injector: HasAndroidInjector) : MedtrumPacket(injector)
|
|||
aapsLogger.debug(LTag.PUMPCOMM, "SynchronizePacket: fieldMask: $fieldMask")
|
||||
}
|
||||
|
||||
// Remove bolus fields from fieldMask if fields are present
|
||||
// TODO: Test if this workaround is needed (hence the warning log)
|
||||
// Remove bolus fields from fieldMask if fields are present (we sync bolus trough other commands)
|
||||
if (fieldMask and MASK_SUSPEND != 0) {
|
||||
offset += 4 // If field is present, skip 4 bytes
|
||||
}
|
||||
|
|
|
@ -7,6 +7,8 @@ import dagger.Module
|
|||
import dagger.Provides
|
||||
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.MedtrumPreparePatchFragment
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ui.MedtrumPrimeFragment
|
||||
import info.nightscout.pump.medtrum.services.MedtrumService
|
||||
|
@ -56,6 +58,14 @@ abstract class MedtrumModule {
|
|||
@ContributesAndroidInjector
|
||||
internal abstract fun contributesPrimeFragment(): MedtrumPrimeFragment
|
||||
|
||||
@FragmentScope
|
||||
@ContributesAndroidInjector
|
||||
internal abstract fun contributesAttachPatchFragment(): MedtrumAttachPatchFragment
|
||||
|
||||
@FragmentScope
|
||||
@ContributesAndroidInjector
|
||||
internal abstract fun contributesActivateFragment(): MedtrumActivateFragment
|
||||
|
||||
// ACTIVITIES
|
||||
@ContributesAndroidInjector
|
||||
abstract fun contributesMedtrumActivity(): MedtrumActivity
|
||||
|
@ -63,4 +73,5 @@ abstract class MedtrumModule {
|
|||
// SERVICE
|
||||
@ContributesAndroidInjector
|
||||
abstract fun contributesMedtrumService(): MedtrumService
|
||||
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ import info.nightscout.rx.logging.LTag
|
|||
import info.nightscout.shared.interfaces.ResourceHelper
|
||||
import info.nightscout.shared.sharedPreferences.SP
|
||||
import info.nightscout.shared.utils.DateUtil
|
||||
import info.nightscout.shared.utils.T
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable
|
||||
import io.reactivex.rxjava3.kotlin.plusAssign
|
||||
import java.util.*
|
||||
|
@ -75,29 +76,6 @@ class MedtrumService : DaggerService(), BLECommCallback {
|
|||
// TODO: Stuff like this in a settings class?
|
||||
private var mLastDeviceTime: Long = 0
|
||||
|
||||
companion object {
|
||||
|
||||
private val MASK_SUSPEND = 0x01
|
||||
private val MASK_NORMAL_BOLUS = 0x02
|
||||
private val MASK_EXTENDED_BOLUS = 0x04
|
||||
private val MASK_BASAL = 0x08
|
||||
|
||||
private val MASK_SETUP = 0x10
|
||||
private val MASK_RESERVOIR = 0x20
|
||||
private val MASK_LIFE_TIME = 0x40
|
||||
private val MASK_BATTERY = 0x80
|
||||
|
||||
private val MASK_STORAGE = 0x100
|
||||
private val MASK_ALARM = 0x200
|
||||
private val MASK_START_TIME = 0x400
|
||||
private val MASK_UNKNOWN_1 = 0x800
|
||||
|
||||
private val MASK_UNUSED_CGM = 0x1000
|
||||
private val MASK_UNUSED_COMMAND_CONFIRM = 0x2000
|
||||
private val MASK_UNUSED_AUTO_STATUS = 0x4000
|
||||
private val MASK_UNUSED_LEGACY = 0x8000
|
||||
}
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
bleComm.setCallback(this)
|
||||
|
@ -135,6 +113,18 @@ class MedtrumService : DaggerService(), BLECommCallback {
|
|||
}
|
||||
}
|
||||
|
||||
fun startPrime(): Boolean {
|
||||
val packet = PrimePacket(injector)
|
||||
return sendPacketAndGetResponse(packet)
|
||||
}
|
||||
|
||||
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 stopConnecting() {
|
||||
// TODO proper way for this might need send commands etc
|
||||
bleComm.stopConnecting()
|
||||
|
@ -177,9 +167,8 @@ class MedtrumService : DaggerService(), BLECommCallback {
|
|||
}
|
||||
|
||||
fun updateBasalsInPump(profile: Profile): Boolean {
|
||||
if (!isConnected) return false
|
||||
// TODO
|
||||
return false
|
||||
val packet = medtrumPump.buildMedtrumProfileArray(profile)?.let { SetBasalProfilePacket(injector, it) }
|
||||
return packet?.let { sendPacketAndGetResponse(it) } == true
|
||||
}
|
||||
|
||||
fun changePump() {
|
||||
|
@ -189,9 +178,9 @@ class MedtrumService : DaggerService(), BLECommCallback {
|
|||
} catch (e: NumberFormatException) {
|
||||
aapsLogger.debug(LTag.PUMP, "changePump: Invalid input!")
|
||||
}
|
||||
// TODO: What do we do with active patch here?
|
||||
// TODO: What do we do with active patch here? Getting status should be enough?
|
||||
when (currentState) {
|
||||
// is IdleState -> connect("changePump")
|
||||
is IdleState -> connect("changePump")
|
||||
// is ReadyState -> disconnect("changePump")
|
||||
else -> null // TODO: What to do here? Abort stuff?
|
||||
}
|
||||
|
@ -244,6 +233,19 @@ class MedtrumService : DaggerService(), BLECommCallback {
|
|||
currentState.onEnter()
|
||||
}
|
||||
|
||||
private fun sendPacketAndGetResponse(packet: MedtrumPacket): Boolean {
|
||||
var result = false
|
||||
if (currentState is ReadyState) {
|
||||
toState(CommandState())
|
||||
mPacket = packet
|
||||
mPacket?.getRequest()?.let { bleComm.sendMessage(it) }
|
||||
result = currentState.waitForResponse()
|
||||
} else {
|
||||
aapsLogger.error(LTag.PUMPCOMM, "Send packet attempt when in non Ready state")
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// State class, Can we move this to different file?
|
||||
private abstract inner class State {
|
||||
|
||||
|
@ -265,11 +267,18 @@ class MedtrumService : DaggerService(), BLECommCallback {
|
|||
// TODO: Check flow for this
|
||||
toState(IdleState())
|
||||
}
|
||||
|
||||
open fun waitForResponse(): Boolean {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
private inner class IdleState : State() {
|
||||
|
||||
override fun onEnter() {}
|
||||
override fun onEnter() {
|
||||
aapsLogger.debug(LTag.PUMPCOMM, "Medtrum Service reached IdleState")
|
||||
connect("IdleState onEnter")
|
||||
}
|
||||
|
||||
override fun onConnected() {
|
||||
super.onConnected()
|
||||
|
@ -278,10 +287,11 @@ class MedtrumService : DaggerService(), BLECommCallback {
|
|||
|
||||
override fun onDisconnected() {
|
||||
super.onDisconnected()
|
||||
// TODO replace this by proper connecting state where we can retry
|
||||
connect("IdleState onDisconnected")
|
||||
}
|
||||
}
|
||||
|
||||
// State for connect flow, could be replaced by commandState and steps in connect()
|
||||
private inner class AuthState : State() {
|
||||
|
||||
override fun onEnter() {
|
||||
|
@ -306,6 +316,7 @@ class MedtrumService : DaggerService(), BLECommCallback {
|
|||
}
|
||||
}
|
||||
|
||||
// State for connect flow, could be replaced by commandState and steps in connect()
|
||||
private inner class GetDeviceTypeState : State() {
|
||||
|
||||
override fun onEnter() {
|
||||
|
@ -330,6 +341,7 @@ class MedtrumService : DaggerService(), BLECommCallback {
|
|||
}
|
||||
}
|
||||
|
||||
// State for connect flow, could be replaced by commandState and steps in connect()
|
||||
private inner class GetTimeState : State() {
|
||||
|
||||
override fun onEnter() {
|
||||
|
@ -361,6 +373,7 @@ class MedtrumService : DaggerService(), BLECommCallback {
|
|||
}
|
||||
}
|
||||
|
||||
// State for connect flow, could be replaced by commandState and steps in connect()
|
||||
private inner class SetTimeState : State() {
|
||||
|
||||
override fun onEnter() {
|
||||
|
@ -381,6 +394,7 @@ class MedtrumService : DaggerService(), BLECommCallback {
|
|||
}
|
||||
}
|
||||
|
||||
// State for connect flow, could be replaced by commandState and steps in connect()
|
||||
private inner class SetTimeZoneState : State() {
|
||||
|
||||
override fun onEnter() {
|
||||
|
@ -401,6 +415,7 @@ class MedtrumService : DaggerService(), BLECommCallback {
|
|||
}
|
||||
}
|
||||
|
||||
// State for connect flow, could be replaced by commandState and steps in connect()
|
||||
private inner class SynchronizeState : State() {
|
||||
|
||||
override fun onEnter() {
|
||||
|
@ -422,6 +437,7 @@ class MedtrumService : DaggerService(), BLECommCallback {
|
|||
}
|
||||
}
|
||||
|
||||
// State for connect flow, could be replaced by commandState and steps in connect()
|
||||
private inner class SubscribeState : State() {
|
||||
|
||||
override fun onEnter() {
|
||||
|
@ -442,6 +458,7 @@ class MedtrumService : DaggerService(), BLECommCallback {
|
|||
}
|
||||
}
|
||||
|
||||
// This state is reached when the patch is ready to receive commands (Activation, Bolus, temp basal and whatever)
|
||||
private inner class ReadyState : State() {
|
||||
|
||||
override fun onEnter() {
|
||||
|
@ -451,6 +468,53 @@ class MedtrumService : DaggerService(), BLECommCallback {
|
|||
isConnected = true
|
||||
rxBus.send(EventPumpStatusChanged(EventPumpStatusChanged.Status.CONNECTED))
|
||||
}
|
||||
// Just a placeholder, this state is reached when the patch is ready to receive commands (Bolus, temp basal and whatever)
|
||||
}
|
||||
|
||||
// This state is when a command is send and we wait for a response for that command
|
||||
private inner class CommandState : State() {
|
||||
|
||||
private var responseHandled = false
|
||||
private var responseSuccess = false
|
||||
|
||||
override fun onEnter() {
|
||||
aapsLogger.debug(LTag.PUMPCOMM, "Medtrum Service reached CommandState")
|
||||
}
|
||||
|
||||
override fun onIndication(data: ByteArray) {
|
||||
if (mPacket?.handleResponse(data) == true) {
|
||||
// Succes!
|
||||
responseHandled = true
|
||||
responseSuccess = true
|
||||
toState(ReadyState())
|
||||
} else if (mPacket?.failed == true) {
|
||||
// Failure
|
||||
responseHandled = true
|
||||
responseSuccess = false
|
||||
toState(ReadyState())
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDisconnected() {
|
||||
super.onDisconnected()
|
||||
responseHandled = true
|
||||
responseSuccess = false
|
||||
}
|
||||
|
||||
override fun waitForResponse(): Boolean {
|
||||
val startTime = System.currentTimeMillis()
|
||||
val timeoutMillis = T.secs(45).msecs()
|
||||
while (!responseHandled) {
|
||||
if (System.currentTimeMillis() - startTime > timeoutMillis) {
|
||||
// If we haven't received a response in the specified time, assume the command failed
|
||||
aapsLogger.debug(LTag.PUMPCOMM, "Medtrum Service CommandState timeout")
|
||||
// Disconnect to cancel any outstanding commands and go back to ready state
|
||||
bleComm.disconnect("Timeout")
|
||||
toState(ReadyState())
|
||||
return false
|
||||
}
|
||||
Thread.sleep(100)
|
||||
}
|
||||
return responseSuccess
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
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.FragmentMedtrumActivateBinding
|
||||
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 MedtrumActivateFragment : MedtrumBaseFragment<FragmentMedtrumActivateBinding>() {
|
||||
|
||||
@Inject lateinit var aapsLogger: AAPSLogger
|
||||
|
||||
companion object {
|
||||
|
||||
fun newInstance(): MedtrumActivateFragment = MedtrumActivateFragment()
|
||||
}
|
||||
|
||||
override fun getLayoutId(): Int = R.layout.fragment_medtrum_activate
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
binding.apply {
|
||||
viewModel = ViewModelProvider(requireActivity(), viewModelFactory).get(MedtrumViewModel::class.java)
|
||||
viewModel?.apply {
|
||||
setupStep.observe(viewLifecycleOwner) {
|
||||
when (it) {
|
||||
MedtrumViewModel.SetupStep.PRIMED -> Unit // Nothing to do here, previous state
|
||||
MedtrumViewModel.SetupStep.ACTIVATED -> btnPositive.visibility = View.VISIBLE
|
||||
MedtrumViewModel.SetupStep.ERROR -> {
|
||||
ToastUtils.errorToast(requireContext(), "Error Activating") // TODO: String resource and show error message
|
||||
moveStep(PatchStep.CANCEL)
|
||||
}
|
||||
|
||||
else -> {
|
||||
ToastUtils.errorToast(requireContext(), "Unexpected state: $it") // TODO: String resource and show error message
|
||||
moveStep(PatchStep.CANCEL)
|
||||
}
|
||||
}
|
||||
}
|
||||
startActivate()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,6 +9,8 @@ import android.os.Bundle
|
|||
import android.view.MotionEvent
|
||||
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.MedtrumPreparePatchFragment
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ui.MedtrumPrimeFragment
|
||||
import info.nightscout.core.utils.extensions.safeGetSerializableExtra
|
||||
|
@ -42,6 +44,9 @@ class MedtrumActivity : MedtrumBaseActivity<ActivityMedtrumBinding>() {
|
|||
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
|
||||
}
|
||||
|
|
|
@ -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.FragmentMedtrumAttachPatchBinding
|
||||
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 MedtrumAttachPatchFragment : MedtrumBaseFragment<FragmentMedtrumAttachPatchBinding>() {
|
||||
|
||||
@Inject lateinit var aapsLogger: AAPSLogger
|
||||
|
||||
companion object {
|
||||
|
||||
fun newInstance(): MedtrumAttachPatchFragment = MedtrumAttachPatchFragment()
|
||||
}
|
||||
|
||||
override fun getLayoutId(): Int = R.layout.fragment_medtrum_attach_patch
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
aapsLogger.debug(LTag.PUMP, "MedtrumAttachPatchFragment onViewCreated")
|
||||
binding.apply {
|
||||
viewModel = ViewModelProvider(requireActivity(), viewModelFactory).get(MedtrumViewModel::class.java)
|
||||
viewModel?.apply {
|
||||
setupStep.observe(viewLifecycleOwner) {
|
||||
when (it) {
|
||||
MedtrumViewModel.SetupStep.PRIMED -> Unit // Nothing to do here, previous state
|
||||
MedtrumViewModel.SetupStep.ERROR -> {
|
||||
ToastUtils.errorToast(requireContext(), "Error attach patch") // TODO: String resource and show error message
|
||||
moveStep(PatchStep.CANCEL)
|
||||
}
|
||||
else -> {
|
||||
ToastUtils.errorToast(requireContext(), "Unexpected state: $it") // TODO: String resource and show error message
|
||||
moveStep(PatchStep.CANCEL)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@ import android.view.View
|
|||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import info.nightscout.pump.medtrum.MedtrumPump
|
||||
import info.nightscout.pump.medtrum.databinding.FragmentMedtrumOverviewBinding
|
||||
import info.nightscout.pump.medtrum.ui.viewmodel.MedtrumOverviewViewModel
|
||||
import info.nightscout.pump.medtrum.R
|
||||
|
@ -13,6 +14,7 @@ import info.nightscout.pump.medtrum.code.EventType
|
|||
import info.nightscout.pump.medtrum.code.PatchStep
|
||||
import info.nightscout.rx.AapsSchedulers
|
||||
import info.nightscout.rx.logging.AAPSLogger
|
||||
import info.nightscout.rx.logging.LTag
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -20,6 +22,7 @@ class MedtrumOverviewFragment : MedtrumBaseFragment<FragmentMedtrumOverviewBindi
|
|||
|
||||
@Inject lateinit var aapsSchedulers: AapsSchedulers
|
||||
@Inject lateinit var aapsLogger: AAPSLogger
|
||||
@Inject lateinit var medtrumPump: MedtrumPump
|
||||
private lateinit var resultLauncherForResume: ActivityResultLauncher<Intent>
|
||||
private lateinit var resultLauncherForPause: ActivityResultLauncher<Intent>
|
||||
|
||||
|
@ -40,7 +43,12 @@ class MedtrumOverviewFragment : MedtrumBaseFragment<FragmentMedtrumOverviewBindi
|
|||
viewmodel?.apply {
|
||||
eventHandler.observe(viewLifecycleOwner) { evt ->
|
||||
when (evt.peekContent()) {
|
||||
EventType.ACTIVATION_CLICKED -> requireContext().apply { startActivity(MedtrumActivity.createIntentFromMenu(this, PatchStep.PREPARE_PATCH)) }
|
||||
EventType.ACTIVATION_CLICKED -> requireContext().apply {
|
||||
val step = convertToPatchStep(medtrumPump.pumpState)
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,9 @@ 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.FragmentMedtrumPreparePatchBinding
|
||||
import info.nightscout.pump.medtrum.ui.MedtrumBaseFragment
|
||||
import info.nightscout.pump.medtrum.ui.viewmodel.MedtrumViewModel
|
||||
|
@ -30,9 +32,19 @@ class MedtrumPreparePatchFragment : MedtrumBaseFragment<FragmentMedtrumPreparePa
|
|||
viewModel?.apply {
|
||||
setupStep.observe(viewLifecycleOwner) {
|
||||
when (it) {
|
||||
MedtrumViewModel.SetupStep.INITIAL -> btnPositive.visibility = View.GONE
|
||||
// TODO: Confirmation dialog
|
||||
MedtrumViewModel.SetupStep.CONNECTED -> btnPositive.visibility = View.VISIBLE
|
||||
else -> Unit
|
||||
MedtrumViewModel.SetupStep.FILLED -> btnPositive.visibility = View.VISIBLE
|
||||
|
||||
MedtrumViewModel.SetupStep.ERROR -> {
|
||||
ToastUtils.errorToast(requireContext(), "Error preparing patch") // TODO: String resource and show error message
|
||||
moveStep(PatchStep.CANCEL)
|
||||
}
|
||||
|
||||
else -> {
|
||||
ToastUtils.errorToast(requireContext(), "Unexpected state: $it") // TODO: String resource and show error message
|
||||
moveStep(PatchStep.CANCEL)
|
||||
}
|
||||
}
|
||||
}
|
||||
preparePatch()
|
||||
|
|
|
@ -3,7 +3,9 @@ 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.FragmentMedtrumPrimeBinding
|
||||
import info.nightscout.pump.medtrum.ui.MedtrumBaseFragment
|
||||
import info.nightscout.pump.medtrum.ui.viewmodel.MedtrumViewModel
|
||||
|
@ -26,7 +28,24 @@ class MedtrumPrimeFragment : MedtrumBaseFragment<FragmentMedtrumPrimeBinding>()
|
|||
super.onViewCreated(view, savedInstanceState)
|
||||
binding.apply {
|
||||
viewModel = ViewModelProvider(requireActivity(), viewModelFactory).get(MedtrumViewModel::class.java)
|
||||
// TODO do stuff
|
||||
viewModel?.apply {
|
||||
setupStep.observe(viewLifecycleOwner) {
|
||||
when (it) {
|
||||
MedtrumViewModel.SetupStep.FILLED -> Unit // Nothing to do here, previous state
|
||||
MedtrumViewModel.SetupStep.PRIMED -> btnPositive.visibility = View.VISIBLE
|
||||
MedtrumViewModel.SetupStep.ERROR -> {
|
||||
ToastUtils.errorToast(requireContext(), "Error priming") // TODO: String resource and show error message
|
||||
moveStep(PatchStep.CANCEL)
|
||||
}
|
||||
|
||||
else -> {
|
||||
ToastUtils.errorToast(requireContext(), "Unexpected state: $it") // TODO: String resource and show error message
|
||||
moveStep(PatchStep.CANCEL)
|
||||
}
|
||||
}
|
||||
}
|
||||
startPrime()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package info.nightscout.pump.medtrum.ui.viewmodel
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import info.nightscout.pump.medtrum.code.PatchStep
|
||||
import info.nightscout.pump.medtrum.comm.enums.MedtrumPumpState
|
||||
import info.nightscout.pump.medtrum.ui.MedtrumBaseNavigator
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable
|
||||
import io.reactivex.rxjava3.disposables.Disposable
|
||||
|
@ -28,4 +30,12 @@ abstract class BaseViewModel<N : MedtrumBaseNavigator> : ViewModel() {
|
|||
|
||||
fun Disposable.addTo() = apply { compositeDisposable.add(this) }
|
||||
|
||||
fun convertToPatchStep(pumpState: MedtrumPumpState) = when (pumpState) {
|
||||
MedtrumPumpState.NONE, MedtrumPumpState.IDLE -> PatchStep.PREPARE_PATCH
|
||||
MedtrumPumpState.FILLED -> PatchStep.PREPARE_PATCH
|
||||
MedtrumPumpState.PRIMING -> PatchStep.PRIME
|
||||
MedtrumPumpState.PRIMED, MedtrumPumpState.EJECTED -> PatchStep.ATTACH_PATCH
|
||||
MedtrumPumpState.ACTIVE, MedtrumPumpState.ACTIVE_ALT -> PatchStep.COMPLETE
|
||||
else -> PatchStep.CANCEL
|
||||
}
|
||||
}
|
|
@ -9,6 +9,8 @@ import info.nightscout.pump.medtrum.ui.event.SingleLiveEvent
|
|||
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.comm.enums.MedtrumPumpState
|
||||
import info.nightscout.rx.AapsSchedulers
|
||||
import info.nightscout.rx.bus.RxBus
|
||||
import info.nightscout.rx.events.EventPumpStatusChanged
|
||||
|
@ -16,6 +18,9 @@ import info.nightscout.rx.logging.AAPSLogger
|
|||
import info.nightscout.rx.logging.LTag
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable
|
||||
import io.reactivex.rxjava3.kotlin.plusAssign
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
class MedtrumOverviewViewModel @Inject constructor(
|
||||
|
@ -24,9 +29,11 @@ class MedtrumOverviewViewModel @Inject constructor(
|
|||
private val aapsSchedulers: AapsSchedulers,
|
||||
private val fabricPrivacy: FabricPrivacy,
|
||||
private val profileFunction: ProfileFunction,
|
||||
private val medtrumPump: MedtrumPump
|
||||
) : BaseViewModel<MedtrumBaseNavigator>() {
|
||||
|
||||
private var disposable: CompositeDisposable = CompositeDisposable()
|
||||
private val scope = CoroutineScope(Dispatchers.Default)
|
||||
|
||||
private val _eventHandler = SingleLiveEvent<UIEvent<EventType>>()
|
||||
val eventHandler: LiveData<UIEvent<EventType>>
|
||||
|
@ -36,11 +43,12 @@ class MedtrumOverviewViewModel @Inject constructor(
|
|||
val bleStatus: LiveData<String>
|
||||
get() = _bleStatus
|
||||
|
||||
// TODO make these livedata
|
||||
val isPatchActivated: Boolean
|
||||
get() = false // TODO
|
||||
private val _isPatchActivated = SingleLiveEvent<Boolean>()
|
||||
val isPatchActivated: LiveData<Boolean>
|
||||
get() = _isPatchActivated
|
||||
|
||||
init {
|
||||
// TODO proper connection state from medtrumPump
|
||||
disposable += rxBus
|
||||
.toObservable(EventPumpStatusChanged::class.java)
|
||||
.observeOn(aapsSchedulers.main)
|
||||
|
@ -59,6 +67,16 @@ class MedtrumOverviewViewModel @Inject constructor(
|
|||
""
|
||||
}
|
||||
}, fabricPrivacy::logException)
|
||||
scope.launch {
|
||||
medtrumPump.pumpStateFlow.collect { state ->
|
||||
aapsLogger.debug(LTag.PUMP, "MedtrumViewModel pumpStateFlow: $state")
|
||||
if (state > MedtrumPumpState.EJECTED) {
|
||||
_isPatchActivated.postValue(true)
|
||||
} else {
|
||||
_isPatchActivated.postValue(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun onClickActivation() {
|
||||
|
|
|
@ -4,9 +4,12 @@ import androidx.lifecycle.LiveData
|
|||
import androidx.lifecycle.MutableLiveData
|
||||
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.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.ui.MedtrumBaseNavigator
|
||||
import info.nightscout.pump.medtrum.ui.event.SingleLiveEvent
|
||||
import info.nightscout.pump.medtrum.ui.event.UIEvent
|
||||
|
@ -17,8 +20,9 @@ import info.nightscout.rx.events.EventPumpStatusChanged
|
|||
import info.nightscout.rx.logging.AAPSLogger
|
||||
import info.nightscout.rx.logging.LTag
|
||||
import info.nightscout.shared.sharedPreferences.SP
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable
|
||||
import io.reactivex.rxjava3.kotlin.plusAssign
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
class MedtrumViewModel @Inject constructor(
|
||||
|
@ -28,17 +32,20 @@ class MedtrumViewModel @Inject constructor(
|
|||
private val aapsSchedulers: AapsSchedulers,
|
||||
private val fabricPrivacy: FabricPrivacy,
|
||||
private val medtrumPlugin: MedtrumPlugin,
|
||||
private val medtrumPump: MedtrumPump,
|
||||
private val sp: SP
|
||||
) : BaseViewModel<MedtrumBaseNavigator>() {
|
||||
|
||||
val patchStep = MutableLiveData<PatchStep>()
|
||||
|
||||
val title = "Activation"
|
||||
|
||||
val medtrumService: MedtrumService?
|
||||
get() = medtrumPlugin.getService()
|
||||
|
||||
private var disposable: CompositeDisposable = CompositeDisposable()
|
||||
private val scope = CoroutineScope(Dispatchers.Default)
|
||||
|
||||
private val _title = MutableLiveData<Int>(R.string.step_prepare_patch)
|
||||
val title: LiveData<Int>
|
||||
get() = _title
|
||||
|
||||
private val _eventHandler = SingleLiveEvent<UIEvent<EventType>>()
|
||||
val eventHandler: LiveData<UIEvent<EventType>>
|
||||
|
@ -47,22 +54,37 @@ class MedtrumViewModel @Inject constructor(
|
|||
private var mInitPatchStep: PatchStep? = null
|
||||
|
||||
init {
|
||||
disposable += rxBus
|
||||
.toObservable(EventPumpStatusChanged::class.java)
|
||||
.observeOn(aapsSchedulers.main)
|
||||
.subscribe({
|
||||
when (it.status) {
|
||||
EventPumpStatusChanged.Status.CONNECTING -> {}
|
||||
|
||||
EventPumpStatusChanged.Status.CONNECTED
|
||||
-> if (patchStep.value == PatchStep.PREPARE_PATCH) setupStep.postValue(SetupStep.CONNECTED) else {
|
||||
scope.launch {
|
||||
medtrumPump.pumpStateFlow.collect { state ->
|
||||
aapsLogger.debug(LTag.PUMP, "MedtrumViewModel pumpStateFlow: $state")
|
||||
when (state) {
|
||||
MedtrumPumpState.NONE, MedtrumPumpState.IDLE -> {
|
||||
setupStep.postValue(SetupStep.INITIAL)
|
||||
}
|
||||
|
||||
EventPumpStatusChanged.Status.DISCONNECTED -> {}
|
||||
|
||||
else -> {}
|
||||
MedtrumPumpState.FILLED -> {
|
||||
setupStep.postValue(SetupStep.FILLED)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}, fabricPrivacy::logException)
|
||||
}
|
||||
|
||||
fun moveStep(newPatchStep: PatchStep) {
|
||||
|
@ -71,8 +93,8 @@ class MedtrumViewModel @Inject constructor(
|
|||
if (oldPatchStep != newPatchStep) {
|
||||
when (newPatchStep) {
|
||||
PatchStep.CANCEL -> {
|
||||
if (medtrumService?.isConnected == true || medtrumService?.isConnecting == true) medtrumService?.disconnect("Cancel") else {
|
||||
}
|
||||
// if (medtrumService?.isConnected == true || medtrumService?.isConnecting == true) medtrumService?.disconnect("Cancel") else {
|
||||
// }
|
||||
}
|
||||
|
||||
else -> null
|
||||
|
@ -91,20 +113,49 @@ class MedtrumViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
fun preparePatch() {
|
||||
// TODO: Decide if we want to connect already when user is still filling, or if we want to wait after user is done filling
|
||||
// TODO When we dont need to connect what needs to be done here?
|
||||
medtrumService?.connect("PreparePatch")
|
||||
}
|
||||
|
||||
fun startPrime() {
|
||||
// TODO: Get result from service
|
||||
if (medtrumPump.pumpState == MedtrumPumpState.PRIMING) {
|
||||
aapsLogger.info(LTag.PUMP, "startPrime: already priming!")
|
||||
} else {
|
||||
if (medtrumService?.startPrime() == true) {
|
||||
aapsLogger.info(LTag.PUMP, "startPrime: success!")
|
||||
} else {
|
||||
aapsLogger.info(LTag.PUMP, "startPrime: failure!")
|
||||
setupStep.postValue(SetupStep.ERROR)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun startActivate() {
|
||||
if (medtrumService?.startActivate() == true) {
|
||||
aapsLogger.info(LTag.PUMP, "startActivate: success!")
|
||||
} else {
|
||||
aapsLogger.info(LTag.PUMP, "startActivate: failure!")
|
||||
setupStep.postValue(SetupStep.ERROR)
|
||||
}
|
||||
}
|
||||
|
||||
private fun prepareStep(step: PatchStep?): PatchStep {
|
||||
// TODO Title per screen :) And proper sync with patchstate
|
||||
// (step ?: convertToPatchStep(patchConfig.lifecycleEvent.lifeCycle)).let { newStep ->
|
||||
|
||||
(step ?: PatchStep.SAFE_DEACTIVATION).let { newStep ->
|
||||
(step ?: convertToPatchStep(medtrumPump.pumpState)).let { newStep ->
|
||||
when (newStep) {
|
||||
|
||||
else -> ""
|
||||
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
|
||||
}.let {
|
||||
|
||||
aapsLogger.info(LTag.PUMP, "prepareStep: title before cond: $it")
|
||||
if (_title.value != it) {
|
||||
aapsLogger.info(LTag.PUMP, "prepareStep: title: $it")
|
||||
_title.postValue(it)
|
||||
}
|
||||
}
|
||||
|
||||
patchStep.postValue(newStep)
|
||||
|
@ -114,9 +165,11 @@ class MedtrumViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
enum class SetupStep {
|
||||
CONNECTED,
|
||||
PRIME_READY,
|
||||
ACTIVATED
|
||||
INITIAL,
|
||||
FILLED,
|
||||
PRIMED,
|
||||
ACTIVATED,
|
||||
ERROR
|
||||
}
|
||||
|
||||
val setupStep = MutableLiveData<SetupStep>()
|
||||
|
|
|
@ -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.COMPLETE)}"
|
||||
tools:visibility="visible" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
||||
</layout>
|
|
@ -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_activate"
|
||||
android:text="@string/string_start_activate"
|
||||
android:visibility="visible"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/btn_negative"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:onSafeClick="@{() -> viewModel.moveStep(PatchStep.ACTIVATE)}"
|
||||
tools:visibility="visible" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
||||
</layout>
|
|
@ -25,7 +25,40 @@
|
|||
android:layout_height="wrap_content"
|
||||
tools:context=".ui.MedtrumActivity">
|
||||
|
||||
<!-- TODO some stuff here that we are waiting :) -->
|
||||
<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_next"
|
||||
android:text="@string/string_next"
|
||||
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.ATTACH_PATCH)}"
|
||||
tools:visibility="visible" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
|
|
@ -16,7 +16,16 @@
|
|||
|
||||
<!-- wizard-->
|
||||
<string name="string_change_patch">Discard/Change Patch</string> <!-- TODO check-->
|
||||
<string name="string_next">Next</string>
|
||||
<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 ="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>
|
||||
|
||||
<!-- settings-->
|
||||
<string name="snInput_title">SN</string>
|
||||
|
|
|
@ -70,6 +70,7 @@ class ActivatePacketTest : MedtrumTestBase() {
|
|||
assertEquals(expectedBasalSequence, medtrumPump.lastBasalSequence)
|
||||
assertEquals(expectedBasalPatchId, medtrumPump.lastBasalPatchId)
|
||||
assertEquals(expectedBasalStart, medtrumPump.lastBasalStartTime)
|
||||
assertEquals(basalProfile, medtrumPump.actualBasalProfile)
|
||||
}
|
||||
|
||||
@Test fun handleResponseGivenResponseWhenMessageTooShortThenResultFalse() {
|
||||
|
|
|
@ -32,4 +32,43 @@ class SetBasalProfilePacketTest : MedtrumTestBase() {
|
|||
val expected = byteArrayOf(opCode.toByte()) + 1.toByte() + basalProfile
|
||||
assertEquals(expected.contentToString(), result.contentToString())
|
||||
}
|
||||
|
||||
@Test fun handleResponseGivenPacketWhenValuesSetThenReturnCorrectValues() {
|
||||
// Inputs
|
||||
val repsonse = byteArrayOf(18, 21, 16, 0, 0, 0, 1, 22, 0, 3, 0, -110, 0, -32, -18, 88, 17)
|
||||
val basalProfile = byteArrayOf(8, 2, 3, 4, -1, 0, 0, 0, 0)
|
||||
|
||||
// Call
|
||||
val packet = SetBasalProfilePacket(packetInjector, basalProfile)
|
||||
val result = packet.handleResponse(repsonse)
|
||||
|
||||
// Expected values
|
||||
val expectedBasalType = 1
|
||||
val expectedBasalRate = 1.1
|
||||
val expectedBasalSequence = 3
|
||||
val expectedStartTime = 1679575392L
|
||||
val expectedPatchId = 146
|
||||
|
||||
assertTrue(result)
|
||||
assertEquals(expectedBasalType, medtrumPump.lastBasalType)
|
||||
assertEquals(expectedBasalRate, medtrumPump.lastBasalRate, 0.01)
|
||||
assertEquals(expectedBasalSequence, medtrumPump.lastBasalSequence)
|
||||
assertEquals(expectedStartTime, medtrumPump.lastBasalStartTime)
|
||||
assertEquals(expectedPatchId, medtrumPump.lastBasalPatchId)
|
||||
assertEquals(basalProfile, medtrumPump.actualBasalProfile)
|
||||
}
|
||||
|
||||
@Test fun handleResponseGivenResponseWhenMessageTooShortThenResultFalse() {
|
||||
// Inputs
|
||||
val response = byteArrayOf(18, 21, 16, 0, 0, 0, 1, 22, 0, 3, 0, -110, 0, -32, -18, 88)
|
||||
val basalProfile = byteArrayOf(8, 2, 3, 4, -1, 0, 0, 0, 0)
|
||||
|
||||
// Call
|
||||
val packet = SetBasalProfilePacket(packetInjector, basalProfile)
|
||||
val result = packet.handleResponse(response)
|
||||
|
||||
// Expected values
|
||||
assertFalse(result)
|
||||
assertTrue(packet.failed)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue