EOPatch2 support
This commit is contained in:
parent
479bfc4103
commit
cfaf9f228e
|
@ -163,6 +163,10 @@ android {
|
|||
}
|
||||
|
||||
useLibrary "org.apache.http.legacy"
|
||||
|
||||
dataBinding {
|
||||
enabled = true
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
|
@ -193,6 +197,7 @@ dependencies {
|
|||
implementation project(':omnipod-dash')
|
||||
implementation project(':diaconn')
|
||||
implementation project(':openhumans')
|
||||
implementation project(':eopatch')
|
||||
|
||||
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||
|
||||
|
|
BIN
app/libs/eopatch_core.aar
Normal file
BIN
app/libs/eopatch_core.aar
Normal file
Binary file not shown.
|
@ -37,6 +37,7 @@ import info.nightscout.androidaps.plugins.general.wear.WearPlugin
|
|||
import info.nightscout.androidaps.plugins.general.xdripStatusline.StatusLinePlugin
|
||||
import info.nightscout.androidaps.plugins.insulin.InsulinOrefFreePeakPlugin
|
||||
import info.nightscout.androidaps.plugins.pump.combo.ComboPlugin
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.EopatchPumpPlugin
|
||||
import info.nightscout.androidaps.plugins.pump.insight.LocalInsightPlugin
|
||||
import info.nightscout.androidaps.plugins.pump.medtronic.MedtronicPumpPlugin
|
||||
import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin
|
||||
|
@ -98,6 +99,7 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
|
|||
@Inject lateinit var virtualPumpPlugin: VirtualPumpPlugin
|
||||
@Inject lateinit var wearPlugin: WearPlugin
|
||||
@Inject lateinit var maintenancePlugin: MaintenancePlugin
|
||||
@Inject lateinit var eopatchPumpPlugin: EopatchPumpPlugin
|
||||
|
||||
@Inject lateinit var passwordCheck: PasswordCheck
|
||||
@Inject lateinit var nsSettingStatus: NSSettingsStatus
|
||||
|
@ -180,6 +182,7 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
|
|||
addPreferencesFromResourceIfEnabled(medtronicPumpPlugin, rootKey, config.PUMPDRIVERS)
|
||||
addPreferencesFromResourceIfEnabled(diaconnG8Plugin, rootKey, config.PUMPDRIVERS)
|
||||
addPreferencesFromResource(R.xml.pref_pump, rootKey, config.PUMPDRIVERS)
|
||||
addPreferencesFromResourceIfEnabled(eopatchPumpPlugin, rootKey, config.PUMPDRIVERS)
|
||||
addPreferencesFromResourceIfEnabled(virtualPumpPlugin, rootKey)
|
||||
addPreferencesFromResourceIfEnabled(insulinOrefFreePeakPlugin, rootKey)
|
||||
addPreferencesFromResourceIfEnabled(nsClientPlugin, rootKey)
|
||||
|
|
|
@ -18,6 +18,7 @@ import info.nightscout.androidaps.insight.di.InsightModule
|
|||
import info.nightscout.androidaps.plugin.general.openhumans.dagger.OpenHumansModule
|
||||
import info.nightscout.androidaps.plugins.pump.common.di.PumpCommonModule
|
||||
import info.nightscout.androidaps.plugins.pump.common.di.RileyLinkModule
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.dagger.EopatchModule
|
||||
import info.nightscout.androidaps.plugins.pump.medtronic.di.MedtronicModule
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.dagger.OmnipodDashModule
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.eros.dagger.OmnipodErosModule
|
||||
|
@ -62,6 +63,7 @@ import javax.inject.Singleton
|
|||
WorkersModule::class,
|
||||
DiaconnG8Module::class,
|
||||
OpenHumansModule::class,
|
||||
EopatchModule::class,
|
||||
SharedModule::class
|
||||
]
|
||||
)
|
||||
|
|
|
@ -40,6 +40,7 @@ import info.nightscout.androidaps.plugins.insulin.InsulinOrefUltraRapidActingPlu
|
|||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
|
||||
import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin
|
||||
import info.nightscout.androidaps.plugins.pump.combo.ComboPlugin
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.EopatchPumpPlugin
|
||||
import info.nightscout.androidaps.plugins.pump.insight.LocalInsightPlugin
|
||||
import info.nightscout.androidaps.plugins.pump.mdi.MDIPlugin
|
||||
import info.nightscout.androidaps.plugins.pump.medtronic.MedtronicPumpPlugin
|
||||
|
@ -182,6 +183,12 @@ abstract class PluginsModule {
|
|||
@IntKey(155)
|
||||
abstract fun bindDiaconnG8Plugin(plugin: DiaconnG8Plugin): PluginBase
|
||||
|
||||
@Binds
|
||||
@PumpDriver
|
||||
@IntoMap
|
||||
@IntKey(156)
|
||||
abstract fun bindEopatchPumpPlugin(plugin: EopatchPumpPlugin): PluginBase
|
||||
|
||||
@Binds
|
||||
@NotNSClient
|
||||
@IntoMap
|
||||
|
|
|
@ -34,6 +34,10 @@ buildscript {
|
|||
|
||||
androidx_junit = '1.1.2'
|
||||
androidx_rules = '1.4.0-alpha04'
|
||||
|
||||
timber_version = "4.7.1"
|
||||
rxandroidble_version = '1.12.1'
|
||||
replayshare_version = '2.2.0'
|
||||
}
|
||||
repositories {
|
||||
google()
|
||||
|
|
|
@ -10,5 +10,6 @@ enum class ManufacturerType(val description: String) {
|
|||
Cellnovo("Cellnovo"),
|
||||
Roche("Roche"),
|
||||
Ypsomed("Ypsomed"),
|
||||
G2e("G2e");
|
||||
G2e("G2e"),
|
||||
Eoflow("Eoflow");
|
||||
}
|
|
@ -130,6 +130,7 @@ open class Notification {
|
|||
const val MDT_INVALID_HISTORY_DATA = 76
|
||||
const val IDENTIFICATION_NOT_SET = 77
|
||||
const val PERMISSION_BT = 78
|
||||
const val EOELOW_PATCH_ALERTS = 79
|
||||
|
||||
const val USER_MESSAGE = 1000
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ enum class PumpCapability {
|
|||
OmnipodCapabilities(arrayOf(Bolus, TempBasal, BasalProfileSet, BasalRate30min)),
|
||||
YpsomedCapabilities(arrayOf(Bolus, ExtendedBolus, TempBasal, BasalProfileSet, Refill, ReplaceBattery, TDD, ManualTDDLoad)), // BasalRates (separately grouped)
|
||||
DiaconnCapabilities(arrayOf(Bolus, ExtendedBolus, TempBasal, BasalProfileSet, Refill, ReplaceBattery, TDD, ManualTDDLoad)), //
|
||||
EopatchCapabilities(arrayOf(Bolus, ExtendedBolus, TempBasal, BasalProfileSet, BasalRate30min)),
|
||||
BasalRate_Duration15minAllowed,
|
||||
BasalRate_Duration30minAllowed,
|
||||
BasalRate_Duration15and30minAllowed(arrayOf(BasalRate_Duration15minAllowed, BasalRate_Duration30minAllowed)),
|
||||
|
|
|
@ -311,7 +311,24 @@ enum class PumpType {
|
|||
baseBasalStep = 0.01,
|
||||
baseBasalSpecialSteps = null,
|
||||
pumpCapability = PumpCapability.DanaWithHistoryCapabilities,
|
||||
source = Sources.DiaconnG8);
|
||||
source = Sources.DiaconnG8),
|
||||
|
||||
//EOPatch Pump
|
||||
EOFLOW_EOPATCH2(description = "Eoflow Eopatch2",
|
||||
manufacturer = ManufacturerType.Eoflow,
|
||||
model = "Eopatch",
|
||||
bolusSize = 0.05,
|
||||
specialBolusSize = null,
|
||||
extendedBolusSettings = DoseSettings(0.05, 30, 8 * 60, 0.05, 25.0),
|
||||
pumpTempBasalType = PumpTempBasalType.Absolute,
|
||||
tbrSettings = DoseSettings(0.05, 30, 12 * 60, 0.0, 15.0),
|
||||
specialBasalDurations = PumpCapability.BasalRate_Duration30minAllowed,
|
||||
baseBasalMinValue = 0.05,
|
||||
baseBasalMaxValue = 15.0,
|
||||
baseBasalStep = 0.05,
|
||||
baseBasalSpecialSteps = null,
|
||||
pumpCapability = PumpCapability.EopatchCapabilities,
|
||||
source = Sources.EOPatch2);
|
||||
|
||||
val description: String
|
||||
var manufacturer: ManufacturerType? = null
|
||||
|
@ -393,6 +410,7 @@ enum class PumpType {
|
|||
InterfaceIDs.PumpType.MDI -> MDI
|
||||
InterfaceIDs.PumpType.USER -> USER
|
||||
InterfaceIDs.PumpType.DIACONN_G8 -> DIACONN_G8
|
||||
InterfaceIDs.PumpType.EOPATCH2 -> EOFLOW_EOPATCH2
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -511,5 +529,6 @@ enum class PumpType {
|
|||
MDI -> InterfaceIDs.PumpType.MDI
|
||||
USER -> InterfaceIDs.PumpType.USER
|
||||
DIACONN_G8 -> InterfaceIDs.PumpType.DIACONN_G8
|
||||
EOFLOW_EOPATCH2 -> InterfaceIDs.PumpType.EOPATCH2
|
||||
}
|
||||
}
|
|
@ -137,6 +137,7 @@ class UserEntryMapper {
|
|||
Omnipod (UserEntry.Sources.Omnipod),
|
||||
OmnipodEros (UserEntry.Sources.OmnipodEros),
|
||||
OmnipodDash (UserEntry.Sources.OmnipodDash),
|
||||
EOPatch2 (UserEntry.Sources.EOPatch2),
|
||||
MDI (UserEntry.Sources.MDI),
|
||||
VirtualPump (UserEntry.Sources.VirtualPump),
|
||||
SMS (UserEntry.Sources.SMS),
|
||||
|
|
|
@ -92,6 +92,7 @@ class UserEntryPresentationHelper @Inject constructor(
|
|||
Sources.Omnipod -> R.drawable.ic_pod_128
|
||||
Sources.OmnipodEros -> R.drawable.ic_pod_128
|
||||
Sources.OmnipodDash -> R.drawable.ic_pod_128
|
||||
Sources.EOPatch2 -> R.drawable.ic_eopatch2_128
|
||||
Sources.MDI -> R.drawable.ic_ict
|
||||
Sources.VirtualPump -> R.drawable.ic_virtual_pump
|
||||
Sources.SMS -> R.drawable.ic_sms
|
||||
|
|
43
core/src/main/res/drawable/ic_eopatch2_128.xml
Normal file
43
core/src/main/res/drawable/ic_eopatch2_128.xml
Normal file
|
@ -0,0 +1,43 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="128dp"
|
||||
android:height="128dp"
|
||||
android:viewportWidth="128"
|
||||
android:viewportHeight="128">
|
||||
<path
|
||||
android:pathData="M0,0h128v128h-128z"
|
||||
android:strokeAlpha="0"
|
||||
android:fillColor="#fff"
|
||||
android:fillAlpha="0"/>
|
||||
<path
|
||||
android:pathData="M43.77,113.5C19.18,113.5 4.5,95 4.5,64c0,-41.4 11.62,-49.5 31.69,-49.5H81.77A41.68,41.68 0,0 1,123.5 56V88.17A25.43,25.43 0,0 1,98.05 113.5Z"
|
||||
android:fillColor="#f7f7f7"/>
|
||||
<path
|
||||
android:pathData="M81.77,15A41.18,41.18 0,0 1,123 56V88.17a24.92,24.92 0,0 1,-25 24.83H43.77c-11.64,0 -21.36,-4.36 -28.11,-12.62C8.69,91.85 5,79.27 5,64 5,23 16.43,15 36.19,15H81.77m0,-1H36.19C14.65,14 4,24 4,64c0,32 15.77,50 39.77,50H98.05a25.89,25.89 0,0 0,26 -25.83V56A42.14,42.14 0,0 0,81.77 14Z"
|
||||
android:fillColor="#424242"/>
|
||||
<path
|
||||
android:pathData="M81.59,29.5A26.35,26.35 0,0 1,108 55.72V88.19A10.36,10.36 0,0 1,97.61 98.5h-51c-7.54,0 -13,-1.76 -17.29,-5.53C23.13,87.51 20,77.76 20,64c0,-25.56 1.38,-34.5 16.79,-34.5h44.8m0,-1H36.79C20.58,28.5 19,38.22 19,64c0,31.93 16.49,35.5 27.59,35.5h51A11.35,11.35 0,0 0,109 88.19V55.72A27.32,27.32 0,0 0,81.59 28.5Z"
|
||||
android:fillColor="#424242"/>
|
||||
<path
|
||||
android:pathData="M84.39,57H95.61a0.29,0.29 0,0 0,0.25 -0.26,10.88 10.88,0 0,0 0,-3.48 0.29,0.29 0,0 0,-0.25 -0.26H84.39a0.29,0.29 0,0 0,-0.25 0.26,10.88 10.88,0 0,0 0,3.48A0.29,0.29 0,0 0,84.39 57Z"
|
||||
android:fillColor="#bdbdbd"/>
|
||||
<path
|
||||
android:strokeWidth="1"
|
||||
android:pathData="M90,55m-9,0a9,9 0,1 1,18 0a9,9 0,1 1,-18 0"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#bdbdbd"/>
|
||||
<path
|
||||
android:pathData="M85.5,107.5m-2,0a2,2 0,1 1,4 0a2,2 0,1 1,-4 0"
|
||||
android:strokeWidth="0.97"
|
||||
android:fillColor="#fff"
|
||||
android:strokeColor="#bdbdbd"/>
|
||||
<path
|
||||
android:pathData="M10.45,64m-2,0a2,2 0,1 1,4 0a2,2 0,1 1,-4 0"
|
||||
android:strokeWidth="0.97"
|
||||
android:fillColor="#fff"
|
||||
android:strokeColor="#bdbdbd"/>
|
||||
<path
|
||||
android:pathData="M85.5,20.5m-2,0a2,2 0,1 1,4 0a2,2 0,1 1,-4 0"
|
||||
android:strokeWidth="0.97"
|
||||
android:fillColor="#fff"
|
||||
android:strokeColor="#bdbdbd"/>
|
||||
</vector>
|
|
@ -41,6 +41,8 @@ files:
|
|||
translation: /automation/src/main/res/values-%android_code%/strings.xml
|
||||
- source: /diaconn/src/main/res/values/strings.xml
|
||||
translation: /diaconn/src/main/res/values-%android_code%/strings.xml
|
||||
- source: /eopatch/src/main/res/values/strings.xml
|
||||
translation: /eopatch/src/main/res/values-%android_code%/strings.xml
|
||||
- source: /openhumans/src/main/res/values/strings.xml
|
||||
translation: /openhumans/src/main/res/values-%android_code%/strings.xml
|
||||
translate_attributes: 0
|
||||
|
|
|
@ -42,6 +42,7 @@ data class InterfaceIDs(
|
|||
YPSOPUMP,
|
||||
MDI,
|
||||
DIACONN_G8,
|
||||
EOPATCH2,
|
||||
USER;
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -168,6 +168,7 @@ data class UserEntry(
|
|||
Omnipod, //No entry currently
|
||||
OmnipodEros,
|
||||
OmnipodDash, //No entry currently
|
||||
EOPatch2,
|
||||
MDI,
|
||||
VirtualPump,
|
||||
SMS, //From SMS plugin
|
||||
|
|
1
eopatch/.gitignore
vendored
Normal file
1
eopatch/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/build
|
49
eopatch/build.gradle
Normal file
49
eopatch/build.gradle
Normal file
|
@ -0,0 +1,49 @@
|
|||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
apply plugin: 'kotlin-kapt'
|
||||
apply plugin: 'kotlin-allopen'
|
||||
apply plugin: 'com.hiya.jacoco-android'
|
||||
|
||||
apply from: "${project.rootDir}/gradle/android_dependencies.gradle"
|
||||
apply from: "${project.rootDir}/gradle/android_module_dependencies.gradle"
|
||||
apply from: "${project.rootDir}/gradle/test_dependencies.gradle"
|
||||
apply from: "${project.rootDir}/gradle/jacoco_global.gradle"
|
||||
|
||||
android {
|
||||
dataBinding {
|
||||
enabled = true
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
flatDir {
|
||||
dirs 'libs'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||
implementation (name: 'eopatch_core', ext: 'aar')
|
||||
implementation project(':core')
|
||||
implementation project(':shared')
|
||||
implementation project(':database')
|
||||
|
||||
implementation 'androidx.core:core-ktx:1.6.0'
|
||||
implementation 'androidx.appcompat:appcompat:1.3.1'
|
||||
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
||||
|
||||
// RxJava
|
||||
implementation "io.reactivex.rxjava2:rxjava:$rxjava_version"
|
||||
implementation "io.reactivex.rxjava2:rxkotlin:$rxkotlin_version"
|
||||
implementation "io.reactivex.rxjava2:rxandroid:$rxandroid_version"
|
||||
|
||||
//RxAndroidBle
|
||||
implementation "com.polidea.rxandroidble2:rxandroidble:$rxandroidble_version"
|
||||
implementation "com.jakewharton.rx2:replaying-share:$replayshare_version"
|
||||
|
||||
// Log
|
||||
implementation "com.jakewharton.timber:timber:$timber_version"
|
||||
}
|
BIN
eopatch/libs/eopatch_core.aar
Normal file
BIN
eopatch/libs/eopatch_core.aar
Normal file
Binary file not shown.
21
eopatch/proguard-rules.pro
vendored
Normal file
21
eopatch/proguard-rules.pro
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
|
@ -0,0 +1,25 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
import org.junit.Assert.*
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* See [testing documentation](http://d.android.com/tools/testing).
|
||||
*/
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class ExampleInstrumentedTest {
|
||||
|
||||
@Test
|
||||
fun useAppContext() {
|
||||
// Context of the app under test.
|
||||
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
||||
assertEquals("com.eoflow.patch", appContext.packageName)
|
||||
}
|
||||
}
|
13
eopatch/src/main/AndroidManifest.xml
Normal file
13
eopatch/src/main/AndroidManifest.xml
Normal file
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="info.nightscout.androidaps.plugins.pump.eopatch">
|
||||
|
||||
<application>
|
||||
|
||||
<activity android:name=".ui.EopatchActivity" />
|
||||
<activity android:name=".ui.AlarmHelperActivity" />
|
||||
<activity android:name=".ui.DialogHelperActivity" />
|
||||
<receiver android:name=".OsAlarmReceiver"/>
|
||||
</application>
|
||||
|
||||
</manifest>
|
|
@ -0,0 +1,87 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch
|
||||
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
interface AppConstant {
|
||||
companion object {
|
||||
|
||||
val BASAL_MIN_AMOUNT = 0.05f
|
||||
val CLICK_THROTTLE = 600L
|
||||
|
||||
/**
|
||||
* Bluetooth Connection State
|
||||
*/
|
||||
val BT_STATE_NOT_CONNECT = 1
|
||||
val BT_STATE_CONNECTED = 2
|
||||
|
||||
|
||||
val INSULIN_DECIMAL_PLACE_VAR = 100f // 10.f; 소수점자리수 계산상수 (100.f 는 두자리)
|
||||
|
||||
// 패치 1P = 1 cycle = 0.1U
|
||||
const val INSULIN_UNIT_P = 0.05f // 최소 주입 단위
|
||||
|
||||
val INSULIN_UNIT_MIN_U = 0f
|
||||
val INSULIN_UNIT_STEP_U = INSULIN_UNIT_P
|
||||
|
||||
/**
|
||||
* On/Off
|
||||
*/
|
||||
val OFF = 0
|
||||
val ON = 1
|
||||
|
||||
/**
|
||||
* Pump Duration, Interval
|
||||
*/
|
||||
val PUMP_DURATION_MILLI = TimeUnit.SECONDS.toMillis(4) // 15;
|
||||
val PUMP_RESOLUTION = INSULIN_UNIT_P
|
||||
|
||||
/**
|
||||
* Basal
|
||||
*/
|
||||
val BASAL_RATE_PER_HOUR_MIN = BASAL_MIN_AMOUNT
|
||||
val BASAL_RATE_PER_HOUR_STEP = INSULIN_UNIT_STEP_U
|
||||
val BASAL_RATE_PER_HOUR_MAX = 15.0f // 30.0f; 30.00U/hr
|
||||
|
||||
val SEGMENT_MAX_SIZE_48 = 48
|
||||
val SEGMENT_MAX_SIZE = SEGMENT_MAX_SIZE_48
|
||||
|
||||
val SEGMENT_COUNT_MAX = SEGMENT_MAX_SIZE_48
|
||||
|
||||
/**
|
||||
* Bolus
|
||||
*/
|
||||
val BOLUS_NORMAL_ID = 0x1
|
||||
val BOLUS_EXTENDED_ID = 0x2
|
||||
|
||||
val BOLUS_ACTIVE_OFF = OFF
|
||||
val BOLUS_ACTIVE_NORMAL = 0x01
|
||||
val BOLUS_ACTIVE_EXTENDED_WAIT = 0x2
|
||||
val BOLUS_ACTIVE_EXTENDED = 0x04
|
||||
val BOLUS_ACTIVE_DISCONNECTED = 0x08
|
||||
|
||||
val BOLUS_UNIT_MIN = BASAL_MIN_AMOUNT
|
||||
val BOLUS_UNIT_STEP = INSULIN_UNIT_STEP_U
|
||||
val BOLUS_UNIT_MAX = 25.0f // 30.0f;
|
||||
val BOLUS_DELIVER_MIN = 0.0f
|
||||
|
||||
|
||||
|
||||
/* Wizard */
|
||||
val WIZARD_STEP_MAX = 24
|
||||
|
||||
val INFO_REMINDER_DEFAULT_VALUE = 1
|
||||
|
||||
|
||||
val DAY_START_MINUTE = 0 * 60
|
||||
val DAY_END_MINUTE = 24 * 60
|
||||
|
||||
|
||||
val SNOOZE_INTERVAL_STEP = 5
|
||||
|
||||
/* Insulin Duration */
|
||||
val INSULIN_DURATION_MIN = 2.0f
|
||||
val INSULIN_DURATION_MAX = 8.0f
|
||||
val INSULIN_DURATION_STEP = 0.5f
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,178 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch
|
||||
|
||||
|
||||
import android.content.Context
|
||||
import info.nightscout.androidaps.plugins.pump.common.utils.DateTimeUtil
|
||||
import io.reactivex.disposables.Disposable
|
||||
import java.util.*
|
||||
import java.util.function.Function
|
||||
|
||||
object CommonUtils {
|
||||
val TO_INT = Function<Number, Int> { it.toInt() }
|
||||
val TO_FLOAT = Function<Number, Float> { it.toFloat() }
|
||||
val TO_STRING = Function<Number, String> { it.toString() }
|
||||
val TO_CLOCK = Function<Number, String>{ num -> String.format(Locale.US, "%d:%02d", num.toInt() / 60, num.toInt() % 60) }
|
||||
|
||||
@JvmStatic fun dispose(vararg disposable: Disposable?) {
|
||||
for (d in disposable){
|
||||
d?.let {
|
||||
if (!it.isDisposed()) {
|
||||
it.dispose()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic fun nullSafe(ch: CharSequence?): String {
|
||||
if (ch == null)
|
||||
return ""
|
||||
val str = ch.toString()
|
||||
return str
|
||||
}
|
||||
|
||||
@JvmStatic fun hasText(str: CharSequence?): Boolean {
|
||||
if (str == null || str.length == 0) {
|
||||
return false
|
||||
}
|
||||
val strLen = str.length
|
||||
for (i in 0 until strLen) {
|
||||
if (!Character.isWhitespace(str[i])) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@JvmStatic fun hasText(str: String?): Boolean {
|
||||
return str?.let{hasText(it as CharSequence)}?:false
|
||||
}
|
||||
|
||||
@JvmStatic fun isStringEmpty(cs: CharSequence?): Boolean {
|
||||
return cs == null || cs.length == 0
|
||||
}
|
||||
|
||||
@JvmStatic fun dateString(millis: Long): String {
|
||||
if(millis == 0L) return ""
|
||||
|
||||
val c = Calendar.getInstance()
|
||||
c.timeInMillis = millis
|
||||
return dateString(c)
|
||||
}
|
||||
|
||||
fun dateString(c: Calendar): String {
|
||||
return String.format(Locale.US, "%04d-%02d-%02d %02d:%02d:%02d",
|
||||
c.get(Calendar.YEAR),
|
||||
c.get(Calendar.MONTH) + 1,
|
||||
c.get(Calendar.DAY_OF_MONTH),
|
||||
c.get(Calendar.HOUR_OF_DAY),
|
||||
c.get(Calendar.MINUTE),
|
||||
c.get(Calendar.SECOND))
|
||||
}
|
||||
|
||||
fun getTimeString(millis: Long): String {
|
||||
val c = Calendar.getInstance()
|
||||
c.timeInMillis = millis
|
||||
return getTimeString(c)
|
||||
}
|
||||
|
||||
fun getTimeString(c: Calendar): String {
|
||||
return String.format(Locale.US, "%02d:%02d",
|
||||
c.get(Calendar.HOUR_OF_DAY), c.get(Calendar.MINUTE))
|
||||
}
|
||||
|
||||
fun bytesToStringArray(byteArray: ByteArray?): String {
|
||||
if (byteArray == null || byteArray.size == 0) {
|
||||
return "null"
|
||||
}
|
||||
|
||||
val sb = StringBuilder()
|
||||
for (b in byteArray) {
|
||||
if (sb.length > 0) {
|
||||
sb.append(String.format(" %02x", b))
|
||||
} else {
|
||||
sb.append(String.format("0x%02x", b))
|
||||
}
|
||||
}
|
||||
return sb.toString()
|
||||
}
|
||||
|
||||
fun insulinFormat(): String {
|
||||
if (AppConstant.INSULIN_UNIT_STEP_U == 0.1f) {
|
||||
return "%.1f"
|
||||
}
|
||||
return if (AppConstant.INSULIN_UNIT_STEP_U == 0.05f) {
|
||||
"%.2f"
|
||||
} else "%.2f 인슐린출력형식추가하셈"
|
||||
|
||||
}
|
||||
|
||||
fun convertInsulinFormat(context: Context, strResId: Int): String {
|
||||
if (AppConstant.INSULIN_UNIT_STEP_U == 0.1f) {
|
||||
return context.getString(strResId).replace(".2f", ".1f")
|
||||
}
|
||||
if (AppConstant.INSULIN_UNIT_STEP_U == 0.05f) {
|
||||
return context.getString(strResId)
|
||||
}
|
||||
|
||||
return context.getString(strResId).replace(".2f", ".2f 인슐린출력형식추가하셈")
|
||||
|
||||
}
|
||||
|
||||
fun getRemainHourMin(timeMillis: Long): Pair<Long, Long> {
|
||||
val diffHours: Long
|
||||
var diffMinutes: Long
|
||||
|
||||
if (timeMillis >= 0) {
|
||||
diffMinutes = Math.abs(timeMillis / (60 * 1000) % 60) + 1
|
||||
if (diffMinutes == 60L) {
|
||||
diffMinutes = 0
|
||||
diffHours = Math.abs(timeMillis / (60 * 60 * 1000)) + 1
|
||||
} else {
|
||||
diffHours = Math.abs(timeMillis / (60 * 60 * 1000))
|
||||
}
|
||||
} else {
|
||||
diffMinutes = Math.abs(timeMillis / (60 * 1000) % 60)
|
||||
diffHours = Math.abs(timeMillis / (60 * 60 * 1000))
|
||||
}
|
||||
return Pair(diffHours, diffMinutes)
|
||||
}
|
||||
|
||||
fun getTimeString(minutes: Int): String {
|
||||
return String.format("%d:%02d", minutes / 60, minutes % 60)
|
||||
}
|
||||
|
||||
fun getTimeString_hhmm(minutes: Int): String {
|
||||
return String.format("%02d:%02d", minutes / 60, minutes % 60)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun generatePumpId(date: Long, typeCode: Long = 0): Long {
|
||||
return DateTimeUtil.toATechDate(date) * 100L + typeCode
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun nearlyEqual(a: Float, b: Float, epsilon: Float): Boolean {
|
||||
val absA = Math.abs(a)
|
||||
val absB = Math.abs(b)
|
||||
val diff = Math.abs(a - b)
|
||||
return if (a == b) { // shortcut, handles infinities
|
||||
true
|
||||
} else if (a == 0f || b == 0f || absA + absB < java.lang.Float.MIN_NORMAL) {
|
||||
// a or b is zero or both are extremely close to it
|
||||
// relative error is less meaningful here
|
||||
diff < epsilon * java.lang.Float.MIN_NORMAL
|
||||
} else { // use relative error
|
||||
diff / Math.min(absA + absB, Float.MAX_VALUE) < epsilon
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun nearlyNotEqual(a: Float, b: Float, epsilon: Float): Boolean {
|
||||
return !nearlyEqual(a, b, epsilon)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun <T : Any> clone(src: T): T {
|
||||
return GsonHelper.sharedGson().fromJson(GsonHelper.sharedGson().toJson(src), src.javaClass)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch
|
||||
|
||||
import info.nightscout.shared.logging.AAPSLogger
|
||||
import info.nightscout.androidaps.plugins.general.overview.notifications.Notification
|
||||
import javax.inject.Inject
|
||||
|
||||
class EONotification constructor() : Notification() {
|
||||
|
||||
@Inject lateinit var aapsLogger: AAPSLogger
|
||||
|
||||
constructor(id: Int, text: String, level: Int) : this() {
|
||||
this.id = id
|
||||
date = System.currentTimeMillis()
|
||||
this.text = text
|
||||
this.level = level
|
||||
}
|
||||
|
||||
fun action(buttonText: Int, action: Runnable) {
|
||||
this.buttonText = buttonText
|
||||
this.action = action
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch
|
||||
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.subjects.PublishSubject
|
||||
|
||||
object EoPatchRxBus {
|
||||
private val publishSubject: PublishSubject<Any> = PublishSubject.create()
|
||||
|
||||
fun publish(event: Any) {
|
||||
publishSubject.onNext(event)
|
||||
}
|
||||
|
||||
fun <T> listen(eventType: Class<T>): Observable<T> {
|
||||
return publishSubject.ofType(eventType)
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,588 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch
|
||||
|
||||
import android.content.Context
|
||||
import android.os.SystemClock
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import dagger.android.HasAndroidInjector
|
||||
import info.nightscout.androidaps.data.DetailedBolusInfo
|
||||
import info.nightscout.androidaps.interfaces.Profile
|
||||
import info.nightscout.androidaps.data.PumpEnactResult
|
||||
import info.nightscout.androidaps.events.EventAppInitialized
|
||||
import info.nightscout.androidaps.events.EventPreferenceChange
|
||||
import info.nightscout.androidaps.interfaces.*
|
||||
import info.nightscout.shared.logging.AAPSLogger
|
||||
import info.nightscout.shared.logging.LTag
|
||||
import info.nightscout.androidaps.plugins.bus.RxBus
|
||||
import info.nightscout.androidaps.plugins.common.ManufacturerType
|
||||
import info.nightscout.androidaps.plugins.general.actions.defs.CustomAction
|
||||
import info.nightscout.androidaps.plugins.general.actions.defs.CustomActionType
|
||||
import info.nightscout.androidaps.plugins.general.overview.events.EventOverviewBolusProgress
|
||||
import info.nightscout.androidaps.plugins.pump.common.defs.PumpType
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.alarm.IAlarmManager
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ble.IPatchManager
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ble.IPreferenceManager
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.code.BolusExDuration
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.code.SettingKeys
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.extension.takeOne
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.extension.with
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ui.EopatchOverviewFragment
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.vo.TempBasal
|
||||
import info.nightscout.androidaps.queue.commands.CustomCommand
|
||||
import info.nightscout.androidaps.utils.DateUtil
|
||||
import info.nightscout.androidaps.utils.FabricPrivacy
|
||||
import info.nightscout.androidaps.utils.T
|
||||
import info.nightscout.androidaps.utils.TimeChangeType
|
||||
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
||||
import info.nightscout.shared.sharedPreferences.SP
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import io.reactivex.functions.Consumer
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import io.reactivex.subjects.BehaviorSubject
|
||||
import org.json.JSONObject
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
import kotlin.math.min
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
@Singleton
|
||||
class EopatchPumpPlugin @Inject constructor(
|
||||
injector: HasAndroidInjector,
|
||||
aapsLogger: AAPSLogger,
|
||||
rh: ResourceHelper,
|
||||
commandQueue: CommandQueue,
|
||||
private val context: Context,
|
||||
private val rxBus: RxBus,
|
||||
private val sp: SP,
|
||||
private val profileFunction: ProfileFunction,
|
||||
private val activePlugin: ActivePlugin,
|
||||
private val fabricPrivacy: FabricPrivacy,
|
||||
private val dateUtil: DateUtil,
|
||||
private val pumpSync: PumpSync,
|
||||
private val patchmanager: IPatchManager,
|
||||
private val alarmManager: IAlarmManager,
|
||||
private val preferenceManager: IPreferenceManager
|
||||
):PumpPluginBase(PluginDescription()
|
||||
.mainType(PluginType.PUMP)
|
||||
.fragmentClass(EopatchOverviewFragment::class.java.getName())
|
||||
.pluginIcon(R.drawable.ic_eopatch2_128)
|
||||
.pluginName(R.string.eopatch)
|
||||
.shortName(R.string.eopatch_shortname)
|
||||
.preferencesId(R.xml.pref_eopatch)
|
||||
.description(R.string.eopatch_pump_description), injector, aapsLogger, rh, commandQueue
|
||||
), Pump {
|
||||
|
||||
private val mDisposables = CompositeDisposable()
|
||||
|
||||
var mPumpType: PumpType = PumpType.EOFLOW_EOPATCH2
|
||||
private set
|
||||
private var mLastDataTime: Long = 0
|
||||
private val mPumpDescription = PumpDescription(mPumpType)
|
||||
|
||||
init {
|
||||
if (BuildConfig.DEBUG) {
|
||||
Timber.plant(Timber.DebugTree())
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
mDisposables.add(rxBus
|
||||
.toObservable(EventPreferenceChange::class.java)
|
||||
.observeOn(Schedulers.io())
|
||||
.subscribe({ event: EventPreferenceChange ->
|
||||
if (event.isChanged(rh, SettingKeys.LOW_RESERVIOR_REMINDERS) || event.isChanged(rh, SettingKeys.EXPIRATION_REMINDERS)) {
|
||||
patchmanager.changeReminderSetting()
|
||||
} else if (event.isChanged(rh, SettingKeys.BUZZER_REMINDERS)) {
|
||||
patchmanager.changeBuzzerSetting()
|
||||
}
|
||||
}) { throwable: Throwable -> fabricPrivacy.logException(throwable) }
|
||||
)
|
||||
|
||||
mDisposables.add(rxBus
|
||||
.toObservable(EventAppInitialized::class.java)
|
||||
.observeOn(Schedulers.io())
|
||||
.subscribe({ event: EventAppInitialized? ->
|
||||
aapsLogger.debug(LTag.PUMP,"EventAppInitialized")
|
||||
preferenceManager.init()
|
||||
patchmanager.init()
|
||||
alarmManager.init()
|
||||
}) { throwable: Throwable -> fabricPrivacy.logException(throwable) }
|
||||
)
|
||||
}
|
||||
|
||||
override fun specialEnableCondition(): Boolean {
|
||||
//BG -> FG 시 패치 활성화 재진행 및 미처리 알람 발생
|
||||
if(preferenceManager.isInitDone()) {
|
||||
patchmanager.checkActivationProcess()
|
||||
alarmManager.restartAll()
|
||||
}
|
||||
return super.specialEnableCondition()
|
||||
}
|
||||
|
||||
override fun specialShowInListCondition(): Boolean {
|
||||
return super.specialShowInListCondition()
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
aapsLogger.debug(LTag.PUMP, "EOPatchPumpPlugin onStop()");
|
||||
}
|
||||
|
||||
override fun onStateChange(type: PluginType?, oldState: State?, newState: State?) {
|
||||
super.onStateChange(type, oldState, newState)
|
||||
}
|
||||
|
||||
override fun preprocessPreferences(preferenceFragment: PreferenceFragmentCompat) {
|
||||
super.preprocessPreferences(preferenceFragment)
|
||||
}
|
||||
|
||||
override fun updatePreferenceSummary(pref: Preference) {
|
||||
super.updatePreferenceSummary(pref)
|
||||
}
|
||||
|
||||
override fun isUnreachableAlertTimeoutExceeded(alertTimeoutMilliseconds: Long): Boolean {
|
||||
return super.isUnreachableAlertTimeoutExceeded(alertTimeoutMilliseconds)
|
||||
}
|
||||
|
||||
override fun setNeutralTempAtFullHour(): Boolean {
|
||||
return super.setNeutralTempAtFullHour()
|
||||
}
|
||||
|
||||
override fun isInitialized(): Boolean {
|
||||
val isInit = isConnected() && patchmanager.isActivated()
|
||||
return isInit
|
||||
}
|
||||
|
||||
override fun isSuspended(): Boolean {
|
||||
return patchmanager.patchState.isNormalBasalPaused
|
||||
}
|
||||
|
||||
override fun isBusy(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun isConnected(): Boolean {
|
||||
return patchmanager.patchConnectionState.isConnected
|
||||
}
|
||||
|
||||
override fun isConnecting(): Boolean {
|
||||
return patchmanager.patchConnectionState.isConnecting
|
||||
}
|
||||
|
||||
override fun isHandshakeInProgress(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun finishHandshaking() {
|
||||
}
|
||||
|
||||
override fun connect(reason: String) {
|
||||
aapsLogger.debug(LTag.PUMP,"EOPatch connect - reason:$reason")
|
||||
mLastDataTime = System.currentTimeMillis()
|
||||
}
|
||||
|
||||
override fun disconnect(reason: String) {
|
||||
aapsLogger.debug(LTag.PUMP,"EOPatch disconnect - reason:$reason")
|
||||
}
|
||||
|
||||
override fun stopConnecting() {
|
||||
}
|
||||
|
||||
override fun getPumpStatus(reason: String) {
|
||||
if (patchmanager.isActivated()) {
|
||||
if ("SMS" == reason) {
|
||||
aapsLogger.debug("Acknowledged AAPS getPumpStatus request it was requested through an SMS")
|
||||
}else{
|
||||
aapsLogger.debug("Acknowledged AAPS getPumpStatus request")
|
||||
}
|
||||
patchmanager.updateConnection().subscribe(Consumer {
|
||||
mLastDataTime = System.currentTimeMillis()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
override fun setNewBasalProfile(profile: Profile): PumpEnactResult {
|
||||
mLastDataTime = System.currentTimeMillis()
|
||||
if(patchmanager.isActivated){
|
||||
if(patchmanager.patchState.isTempBasalActive || patchmanager.patchState.isBolusActive){
|
||||
return PumpEnactResult(injector)
|
||||
}else{
|
||||
var isSuccess: Boolean? = null
|
||||
val result: BehaviorSubject<Boolean> = BehaviorSubject.create()
|
||||
val disposable = result.hide()
|
||||
.subscribe {
|
||||
isSuccess = it
|
||||
}
|
||||
|
||||
val nb = preferenceManager.getNormalBasalManager().convertProfileToNormalBasal(profile)
|
||||
patchmanager.startBasal(nb)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe({ response ->
|
||||
if (response.isSuccess) {
|
||||
preferenceManager.getNormalBasalManager().normalBasal = nb
|
||||
preferenceManager.flushNormalBasalManager()
|
||||
}
|
||||
result.onNext(response.isSuccess)
|
||||
}, { throwable ->
|
||||
result.onNext(false)
|
||||
})
|
||||
|
||||
do{
|
||||
SystemClock.sleep(100)
|
||||
}while(isSuccess == null)
|
||||
|
||||
disposable.dispose()
|
||||
aapsLogger.info(LTag.PUMP, "Basal Profile was set: ${isSuccess?:false}");
|
||||
return PumpEnactResult(injector).apply{ success = isSuccess?:false }
|
||||
}
|
||||
}else{
|
||||
preferenceManager.getNormalBasalManager().setNormalBasal(profile)
|
||||
preferenceManager.flushNormalBasalManager()
|
||||
return PumpEnactResult(injector)
|
||||
}
|
||||
}
|
||||
|
||||
override fun isThisProfileSet(profile: Profile): Boolean {
|
||||
if (!patchmanager.isActivated()) {
|
||||
return true
|
||||
}
|
||||
|
||||
val ret = preferenceManager.getNormalBasalManager().isEqual(profile)
|
||||
aapsLogger.info(LTag.PUMP, "Is this profile set? ${ret}");
|
||||
return ret
|
||||
}
|
||||
|
||||
override fun lastDataTime(): Long {
|
||||
return mLastDataTime
|
||||
}
|
||||
|
||||
override val baseBasalRate: Double
|
||||
get() {
|
||||
if (!patchmanager.isActivated || patchmanager.patchState.isNormalBasalPaused) {
|
||||
return 0.0
|
||||
}
|
||||
|
||||
return preferenceManager.getNormalBasalManager().normalBasal.getCurrentSegment()?.doseUnitPerHour?.toDouble()?:0.05
|
||||
}
|
||||
|
||||
override val reservoirLevel: Double
|
||||
get() {
|
||||
if (!patchmanager.isActivated) {
|
||||
return 0.0
|
||||
}
|
||||
val reserviorLevel = patchmanager.patchState.remainedInsulin.toDouble()
|
||||
|
||||
return (reserviorLevel > 50.0).takeOne(50.0, reserviorLevel)
|
||||
}
|
||||
|
||||
override val batteryLevel: Int
|
||||
get() {
|
||||
if(patchmanager.isActivated) {
|
||||
return patchmanager.patchState.batteryLevel()
|
||||
}else{
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
override fun deliverTreatment(detailedBolusInfo: DetailedBolusInfo): PumpEnactResult {
|
||||
|
||||
if (detailedBolusInfo.insulin == 0.0 && detailedBolusInfo.carbs == 0.0) {
|
||||
// neither carbs nor bolus requested
|
||||
aapsLogger.error("deliverTreatment: Invalid input: neither carbs nor insulin are set in treatment")
|
||||
return PumpEnactResult(injector).success(false).enacted(false).bolusDelivered(0.0).carbsDelivered(0.0)
|
||||
.comment(rh.gs(R.string.invalidinput))
|
||||
} else if (detailedBolusInfo.insulin > 0.0) {
|
||||
var isSuccess = true
|
||||
val result = BehaviorSubject.createDefault(true)
|
||||
val disposable = result.hide()
|
||||
.subscribe {
|
||||
isSuccess = it
|
||||
}
|
||||
|
||||
patchmanager.startCalculatorBolus(detailedBolusInfo)
|
||||
.doOnSuccess {
|
||||
mLastDataTime = System.currentTimeMillis()
|
||||
}.subscribe({
|
||||
result.onNext(it.isSuccess)
|
||||
}, {
|
||||
result.onNext(false)
|
||||
})
|
||||
|
||||
do{
|
||||
SystemClock.sleep(100)
|
||||
if(patchmanager.patchConnectionState.isConnected) {
|
||||
var delivering = patchmanager.bolusCurrent.nowBolus.injected
|
||||
rxBus.send(EventOverviewBolusProgress.apply {
|
||||
status = rh.gs(R.string.bolusdelivering, delivering)
|
||||
percent = min((delivering / detailedBolusInfo.insulin * 100).toInt(), 100)
|
||||
})
|
||||
}
|
||||
}while(!patchmanager.bolusCurrent.nowBolus.endTimeSynced && isSuccess)
|
||||
|
||||
rxBus.send(EventOverviewBolusProgress.apply {
|
||||
status = rh.gs(R.string.bolusdelivered, detailedBolusInfo.insulin)
|
||||
percent = 100
|
||||
})
|
||||
|
||||
detailedBolusInfo.insulin = patchmanager.bolusCurrent.nowBolus.injected.toDouble()
|
||||
patchmanager.addBolusToHistory(detailedBolusInfo)
|
||||
|
||||
disposable.dispose()
|
||||
|
||||
return if(isSuccess)
|
||||
PumpEnactResult(injector).success(true)/*.enacted(true)*/.carbsDelivered(detailedBolusInfo.carbs).bolusDelivered(detailedBolusInfo.insulin)
|
||||
else
|
||||
PumpEnactResult(injector).success(false)/*.enacted(false)*/.carbsDelivered(0.0).bolusDelivered(detailedBolusInfo.insulin)
|
||||
|
||||
} else {
|
||||
// no bolus required, carb only treatment
|
||||
patchmanager.addBolusToHistory(detailedBolusInfo);
|
||||
|
||||
return PumpEnactResult(injector).success(true).enacted(true).bolusDelivered(0.0)
|
||||
.carbsDelivered(detailedBolusInfo.carbs).comment(rh.gs(info.nightscout.androidaps.core.R.string.ok));
|
||||
}
|
||||
}
|
||||
|
||||
override fun stopBolusDelivering() {
|
||||
patchmanager.stopNowBolus()
|
||||
.with()
|
||||
.subscribe { it ->
|
||||
rxBus.send(EventOverviewBolusProgress.apply {
|
||||
status = rh.gs(R.string.bolusdelivered, (it.injectedBolusAmount * 0.05f)) //todo stoped 메세지로 변경 필요
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
override fun setTempBasalAbsolute(absoluteRate: Double, durationInMinutes: Int, profile: Profile, enforceNew: Boolean, tbrType: PumpSync.TemporaryBasalType): PumpEnactResult {
|
||||
aapsLogger.info(LTag.PUMP, "setTempBasalAbsolute - absoluteRate: ${absoluteRate.toFloat()}, durationInMinutes: ${durationInMinutes.toLong()}, enforceNew: $enforceNew")
|
||||
if(patchmanager.patchState.isNormalBasalAct){
|
||||
mLastDataTime = System.currentTimeMillis()
|
||||
val tb = TempBasal.createAbsolute(durationInMinutes.toLong(), absoluteRate.toFloat())
|
||||
return patchmanager.startTempBasal(tb)
|
||||
.with()
|
||||
.doOnSuccess {
|
||||
preferenceManager.getTempBasalManager().startedBasal = tb
|
||||
preferenceManager.getTempBasalManager().startedBasal?.startTimestamp = System.currentTimeMillis()
|
||||
pumpSync.syncTemporaryBasalWithPumpId(
|
||||
timestamp = dateUtil.now(),
|
||||
rate = absoluteRate,
|
||||
duration = T.mins(durationInMinutes.toLong()).msecs(),
|
||||
isAbsolute = true,
|
||||
type = tbrType,
|
||||
pumpId = dateUtil.now(),
|
||||
pumpType = PumpType.EOFLOW_EOPATCH2,
|
||||
pumpSerial = serialNumber()
|
||||
)
|
||||
aapsLogger.info(LTag.PUMP,"setTempBasalAbsolute - tbrCurrent:${readTBR()}")
|
||||
}
|
||||
.map { it -> PumpEnactResult(injector).success(true).enacted(true).duration(durationInMinutes).absolute(absoluteRate).isPercent(false).isTempCancel(false) }
|
||||
.onErrorReturnItem(PumpEnactResult(injector).success(false).enacted(false)
|
||||
.comment("Internal error"))
|
||||
.blockingGet()
|
||||
}else{
|
||||
aapsLogger.info(LTag.PUMP,"setTempBasalAbsolute - normal basal is not active")
|
||||
return PumpEnactResult(injector).success(false).enacted(false)
|
||||
}
|
||||
}
|
||||
|
||||
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")
|
||||
if(patchmanager.patchState.isNormalBasalAct && percent != 0){
|
||||
mLastDataTime = System.currentTimeMillis()
|
||||
val tb = TempBasal.createPercent(durationInMinutes.toLong(), percent)
|
||||
return patchmanager.startTempBasal(tb)
|
||||
.with()
|
||||
.doOnSuccess {
|
||||
preferenceManager.getTempBasalManager().startedBasal = tb
|
||||
preferenceManager.getTempBasalManager().startedBasal?.startTimestamp = System.currentTimeMillis()
|
||||
pumpSync.syncTemporaryBasalWithPumpId(
|
||||
timestamp = dateUtil.now(),
|
||||
rate = percent.toDouble(),
|
||||
duration = T.mins(durationInMinutes.toLong()).msecs(),
|
||||
isAbsolute = false,
|
||||
type = tbrType,
|
||||
pumpId = dateUtil.now(),
|
||||
pumpType = PumpType.EOFLOW_EOPATCH2,
|
||||
pumpSerial = serialNumber()
|
||||
)
|
||||
aapsLogger.info(LTag.PUMP,"setTempBasalPercent - tbrCurrent:${readTBR()}")
|
||||
}
|
||||
.map { it -> PumpEnactResult(injector).success(true).enacted(true).duration(durationInMinutes).percent(percent).isPercent(true).isTempCancel(false) }
|
||||
.onErrorReturnItem(PumpEnactResult(injector).success(false).enacted(false)
|
||||
.comment("Internal error"))
|
||||
.blockingGet()
|
||||
}else{
|
||||
aapsLogger.info(LTag.PUMP,"setTempBasalPercent - normal basal is not active")
|
||||
return PumpEnactResult(injector).success(false).enacted(false)
|
||||
}
|
||||
}
|
||||
|
||||
override fun setExtendedBolus(insulin: Double, durationInMinutes: Int): PumpEnactResult {
|
||||
aapsLogger.info(LTag.PUMP,"setExtendedBolus - insulin: $insulin, durationInMinutes: $durationInMinutes")
|
||||
|
||||
return patchmanager.startQuickBolus(0f, insulin.toFloat(), BolusExDuration.ofRaw(durationInMinutes))
|
||||
.doOnSuccess {
|
||||
mLastDataTime = System.currentTimeMillis()
|
||||
pumpSync.syncExtendedBolusWithPumpId(
|
||||
timestamp = dateUtil.now(),
|
||||
amount = insulin,
|
||||
duration = T.mins(durationInMinutes.toLong()).msecs(),
|
||||
isEmulatingTB = false,
|
||||
pumpId = dateUtil.now(),
|
||||
pumpType = PumpType.EOFLOW_EOPATCH2,
|
||||
pumpSerial = serialNumber()
|
||||
)
|
||||
}
|
||||
.map { it -> PumpEnactResult(injector).success(true).enacted(true)}
|
||||
.onErrorReturnItem(PumpEnactResult(injector).success(false).enacted(false).bolusDelivered(0.0)
|
||||
.comment(rh.gs(info.nightscout.androidaps.core.R.string.error)))
|
||||
.blockingGet()
|
||||
}
|
||||
|
||||
override fun cancelTempBasal(enforceNew: Boolean): PumpEnactResult {
|
||||
val tbrCurrent = readTBR()
|
||||
|
||||
if (tbrCurrent == null ) {
|
||||
aapsLogger.debug(LTag.PUMP,"cancelTempBasal - TBR already false.")
|
||||
return PumpEnactResult(injector).success(true).enacted(false)
|
||||
}
|
||||
|
||||
if (!patchmanager.patchState.isTempBasalActive) {
|
||||
return if (pumpSync.expectedPumpState().temporaryBasal != null) {
|
||||
PumpEnactResult(injector).success(true).enacted(true).isTempCancel(true)
|
||||
}else
|
||||
PumpEnactResult(injector).success(true).isTempCancel(true)
|
||||
}
|
||||
|
||||
return patchmanager.stopTempBasal()
|
||||
.doOnSuccess {
|
||||
mLastDataTime = System.currentTimeMillis()
|
||||
aapsLogger.debug(LTag.PUMP,"cancelTempBasal - $it")
|
||||
pumpSync.syncStopTemporaryBasalWithPumpId(
|
||||
timestamp = dateUtil.now(),
|
||||
endPumpId = dateUtil.now(),
|
||||
pumpType = PumpType.EOFLOW_EOPATCH2,
|
||||
pumpSerial = serialNumber()
|
||||
)
|
||||
}
|
||||
.doOnError{
|
||||
aapsLogger.error(LTag.PUMP,"cancelTempBasal() - $it")
|
||||
}
|
||||
.map { it -> PumpEnactResult(injector).success(true).enacted(true).isTempCancel(true)}
|
||||
.onErrorReturnItem(PumpEnactResult(injector).success(false).enacted(false)
|
||||
.comment(rh.gs(info.nightscout.androidaps.core.R.string.error)))
|
||||
.blockingGet()
|
||||
}
|
||||
|
||||
override fun cancelExtendedBolus(): PumpEnactResult {
|
||||
if(patchmanager.patchState.isExtBolusActive){
|
||||
return patchmanager.stopExtBolus()
|
||||
.doOnSuccess {
|
||||
aapsLogger.debug(LTag.PUMP,"cancelExtendedBolus - success")
|
||||
mLastDataTime = System.currentTimeMillis()
|
||||
pumpSync.syncStopExtendedBolusWithPumpId(
|
||||
timestamp = dateUtil.now(),
|
||||
endPumpId = dateUtil.now(),
|
||||
pumpType = PumpType.EOFLOW_EOPATCH2,
|
||||
pumpSerial = serialNumber()
|
||||
)
|
||||
}
|
||||
.map { it -> PumpEnactResult(injector).success(true).enacted(true).isTempCancel(true)}
|
||||
.onErrorReturnItem(PumpEnactResult(injector).success(false).enacted(false)
|
||||
.comment(rh.gs(info.nightscout.androidaps.core.R.string.error)))
|
||||
.blockingGet()
|
||||
}else{
|
||||
aapsLogger.debug(LTag.PUMP,"cancelExtendedBolus - nothing stops")
|
||||
return if (pumpSync.expectedPumpState().extendedBolus != null) {
|
||||
pumpSync.syncStopExtendedBolusWithPumpId(
|
||||
timestamp = dateUtil.now(),
|
||||
endPumpId = dateUtil.now(),
|
||||
pumpType = PumpType.EOFLOW_EOPATCH2,
|
||||
pumpSerial = serialNumber()
|
||||
)
|
||||
PumpEnactResult(injector).success(true).enacted(true).isTempCancel(true)
|
||||
}else
|
||||
PumpEnactResult(injector)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getJSONStatus(profile: Profile, profileName: String, version: String): JSONObject {
|
||||
return JSONObject()
|
||||
}
|
||||
|
||||
override fun manufacturer(): ManufacturerType {
|
||||
return ManufacturerType.Eoflow
|
||||
}
|
||||
|
||||
override fun model(): PumpType {
|
||||
return PumpType.EOFLOW_EOPATCH2
|
||||
}
|
||||
|
||||
override fun serialNumber(): String {
|
||||
return patchmanager.patchConfig.patchSerialNumber
|
||||
}
|
||||
|
||||
override val pumpDescription: PumpDescription
|
||||
get() = mPumpDescription
|
||||
|
||||
override fun shortStatus(veryShort: Boolean): String {
|
||||
if(patchmanager.isActivated) {
|
||||
var ret = ""
|
||||
val activeTemp = pumpSync.expectedPumpState().temporaryBasal
|
||||
if (activeTemp != null)
|
||||
ret += "Temp: ${activeTemp.rate} U/hr"
|
||||
|
||||
val activeExtendedBolus = pumpSync.expectedPumpState().extendedBolus
|
||||
if (activeExtendedBolus != null)
|
||||
ret += "Extended: ${activeExtendedBolus.amount} U\n"
|
||||
|
||||
val reservoirStr = patchmanager.patchState.remainedInsulin.let {
|
||||
when {
|
||||
it > 50f -> "50+ U"
|
||||
it < 1f -> "0 U"
|
||||
else -> "${it.roundToInt()} U"
|
||||
}
|
||||
}
|
||||
|
||||
ret += "Reservoir: $reservoirStr"
|
||||
ret += "Batt: ${patchmanager.patchState.batteryLevel()}"
|
||||
return ret
|
||||
}else{
|
||||
return "EOPatch is not enabled."
|
||||
}
|
||||
}
|
||||
|
||||
override val isFakingTempsByExtendedBoluses: Boolean = false
|
||||
|
||||
override fun loadTDDs(): PumpEnactResult {
|
||||
return PumpEnactResult(injector)
|
||||
}
|
||||
|
||||
override fun canHandleDST(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun getCustomActions(): List<CustomAction>? {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun executeCustomAction(customActionType: CustomActionType) {
|
||||
|
||||
}
|
||||
|
||||
override fun executeCustomCommand(customCommand: CustomCommand): PumpEnactResult? {
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
override fun timezoneOrDSTChanged(timeChangeType: TimeChangeType) {
|
||||
|
||||
}
|
||||
|
||||
private fun readTBR(): PumpSync.PumpState.TemporaryBasal? {
|
||||
return pumpSync.expectedPumpState().temporaryBasal
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch
|
||||
|
||||
import java.util.*
|
||||
import java.util.function.Function
|
||||
|
||||
object FloatFormatters {
|
||||
val INSULIN = Function<Number, String>{ value -> String.format(Locale.US, CommonUtils.insulinFormat(), value.toFloat()) }
|
||||
val FAT = Function<Number, String>{ value -> String.format(Locale.US, "%.1f", value.toFloat()) }
|
||||
val DURATION = Function<Number, String>{ value -> String.format(Locale.US, "%.1f", value.toFloat()) }
|
||||
|
||||
fun insulin(value: Float): String {
|
||||
return INSULIN.apply(value)
|
||||
}
|
||||
|
||||
fun insulin(value: Float, suffix: String?): String {
|
||||
return if (CommonUtils.isStringEmpty(suffix)) {
|
||||
INSULIN.apply(value)
|
||||
} else {
|
||||
INSULIN.apply(value).toString() +" "+ suffix!!
|
||||
}
|
||||
}
|
||||
|
||||
fun fat(value: Float): String {
|
||||
return FAT.apply(value)
|
||||
}
|
||||
|
||||
fun fat(value: Float, suffix: String?): String {
|
||||
return if (CommonUtils.isStringEmpty(suffix)) {
|
||||
FAT.apply(value)
|
||||
} else {
|
||||
FAT.apply(value).toString() + suffix!!
|
||||
}
|
||||
}
|
||||
|
||||
fun duration(value: Float): String {
|
||||
return DURATION.apply(value)
|
||||
}
|
||||
|
||||
fun duration(value: Float, suffix: String?): String {
|
||||
return if (CommonUtils.isStringEmpty(suffix)) {
|
||||
DURATION.apply(value)
|
||||
} else {
|
||||
DURATION.apply(value).toString() +" " + suffix!!
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch
|
||||
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
|
||||
object GsonHelper {
|
||||
private var defaultGson: Gson? = null
|
||||
|
||||
init {
|
||||
defaultGson = GsonBuilder().serializeSpecialFloatingPointValues().create()
|
||||
}
|
||||
|
||||
fun sharedGson(): Gson {
|
||||
if (defaultGson == null) {
|
||||
throw RuntimeException("Not configured gson")
|
||||
}
|
||||
|
||||
return defaultGson!!
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import dagger.android.DaggerBroadcastReceiver
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.alarm.AlarmCode
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.alarm.AlarmCode.Companion.fromIntent
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.event.EventEoPatchAlarm
|
||||
|
||||
class OsAlarmReceiver : DaggerBroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
super.onReceive(context, intent)
|
||||
fromIntent(intent)?.let { alarmCode ->
|
||||
EoPatchRxBus.publish(EventEoPatchAlarm(HashSet<AlarmCode>().apply { add(alarmCode) }))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch;
|
||||
|
||||
import android.app.NotificationManager;
|
||||
import android.app.Service;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.IBinder;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
|
||||
public class OsAlarmService extends Service {
|
||||
|
||||
public static final int FOREGROUND_NOTIFICATION_ID = 34534554;
|
||||
|
||||
private CompositeDisposable compositeDisposable;
|
||||
|
||||
private boolean foreground = false;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
|
||||
compositeDisposable = new CompositeDisposable();
|
||||
|
||||
startForeground();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
startForeground();
|
||||
|
||||
String action = null;
|
||||
|
||||
if (action == null) {
|
||||
return Service.START_NOT_STICKY;
|
||||
}
|
||||
|
||||
return START_STICKY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
((NotificationManager) Objects.requireNonNull(getSystemService(Context.NOTIFICATION_SERVICE))).cancel(FOREGROUND_NOTIFICATION_ID);
|
||||
|
||||
compositeDisposable.dispose();
|
||||
}
|
||||
|
||||
public synchronized void startForeground() {
|
||||
if (!foreground) {
|
||||
//// CommonUtils.dispose(mNotificationDisposable);
|
||||
// Notification builder = getNotification(this);
|
||||
// startForeground(FOREGROUND_NOTIFICATION_ID, builder);
|
||||
// startExerciseOrSleepMode(this);
|
||||
foreground = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void start(Context context) {
|
||||
Intent intent = new Intent(context, OsAlarmService.class);
|
||||
|
||||
// context.startForegroundService(intent);
|
||||
}
|
||||
|
||||
public static void notifyNotification(Context context, boolean isNetworkAvailable) {
|
||||
notifyNotification(context);
|
||||
}
|
||||
|
||||
public static void notifyNotification(Context context) {
|
||||
// Notification builder = getNotification(context);
|
||||
// ((NotificationManager) Objects.requireNonNull(context.getSystemService(Context.NOTIFICATION_SERVICE))).notify(FOREGROUND_NOTIFICATION_ID, builder);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch
|
||||
|
||||
import io.reactivex.*
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import org.reactivestreams.Subscription
|
||||
import timber.log.Timber
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
enum class RxVoid {
|
||||
INSTANCE
|
||||
}
|
||||
|
||||
class SilentObserver<T> : MaybeObserver<T>, SingleObserver<T>, Observer<T>, FlowableSubscriber<T> {
|
||||
override fun onSubscribe(d: Disposable) {}
|
||||
override fun onSuccess(t: T) {}
|
||||
override fun onError(e: Throwable) = Timber.d(e, "SilentObserver.onError() ignore")
|
||||
override fun onComplete() {}
|
||||
override fun onNext(t: T) {}
|
||||
override fun onSubscribe(s: Subscription) {}
|
||||
}
|
||||
|
||||
object RxAction {
|
||||
private fun msleep(millis: Long) {
|
||||
if (millis <= 0)
|
||||
return
|
||||
try {
|
||||
Thread.sleep(millis)
|
||||
} catch (e: InterruptedException) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun delay(delayMs: Long): Single<*> {
|
||||
return if (delayMs <= 0) {
|
||||
Single.just(1)
|
||||
} else Single.timer(delayMs, TimeUnit.MILLISECONDS)
|
||||
|
||||
}
|
||||
|
||||
fun single(action: Runnable, delayMs: Long, scheduler: Scheduler): Single<*> {
|
||||
return delay(delayMs)
|
||||
.observeOn(scheduler)
|
||||
.flatMap { o ->
|
||||
Single.fromCallable {
|
||||
action.run()
|
||||
RxVoid.INSTANCE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun safeSingle(action: Runnable, delayMs: Long, scheduler: Scheduler): Single<*> {
|
||||
return single(action, delayMs, scheduler)
|
||||
}
|
||||
|
||||
@JvmOverloads
|
||||
fun runOnComputationThread(action: Runnable, delayMs: Long = 0) {
|
||||
single(action, delayMs, Schedulers.computation()).subscribe(SilentObserver())
|
||||
}
|
||||
|
||||
@JvmOverloads
|
||||
fun runOnIoThread(action: Runnable, delayMs: Long = 0) {
|
||||
single(action, delayMs, Schedulers.io()).subscribe(SilentObserver())
|
||||
}
|
||||
|
||||
@JvmOverloads
|
||||
fun runOnNewThread(action: Runnable, delayMs: Long = 0) {
|
||||
single(action, delayMs, Schedulers.newThread()).subscribe(SilentObserver())
|
||||
}
|
||||
|
||||
@JvmOverloads
|
||||
fun runOnMainThread(action: Runnable, delayMs: Long = 0) {
|
||||
single(action, delayMs, AndroidSchedulers.mainThread()).subscribe(SilentObserver())
|
||||
}
|
||||
|
||||
@JvmOverloads
|
||||
fun safeRunOnComputationThread(action: Runnable, delayMs: Long = 0) {
|
||||
safeSingle(action, delayMs, Schedulers.computation()).subscribe(SilentObserver())
|
||||
}
|
||||
|
||||
@JvmOverloads
|
||||
fun safeRunOnIoThread(action: Runnable, delayMs: Long = 0) {
|
||||
safeSingle(action, delayMs, Schedulers.io()).subscribe(SilentObserver())
|
||||
}
|
||||
|
||||
@JvmOverloads
|
||||
fun safeRunOnNewThread(action: Runnable, delayMs: Long = 0) {
|
||||
safeSingle(action, delayMs, Schedulers.newThread()).subscribe(SilentObserver())
|
||||
}
|
||||
|
||||
@JvmOverloads
|
||||
fun safeRunOnMainThread(action: Runnable, delayMs: Long = 0) {
|
||||
safeSingle(action, delayMs, AndroidSchedulers.mainThread()).subscribe(SilentObserver())
|
||||
}
|
||||
|
||||
|
||||
fun singleOnMainThread(action: Runnable, delayMs: Long): Single<*> {
|
||||
return single(action, delayMs, AndroidSchedulers.mainThread())
|
||||
}
|
||||
|
||||
fun singleOnComputationThread(action: Runnable, delayMs: Long): Single<*> {
|
||||
return single(action, delayMs, Schedulers.computation())
|
||||
}
|
||||
|
||||
fun singleOnIoThread(action: Runnable, delayMs: Long): Single<*> {
|
||||
return single(action, delayMs, Schedulers.io())
|
||||
}
|
||||
|
||||
fun singleOnNewThread(action: Runnable, delayMs: Long): Single<*> {
|
||||
return single(action, delayMs, Schedulers.newThread())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.alarm
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.R
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.code.AlarmCategory
|
||||
import java.util.*
|
||||
import java.util.function.Function
|
||||
import java.util.stream.Collectors
|
||||
import java.util.stream.Stream
|
||||
|
||||
enum class AlarmCode(defaultName: String, messageResId: Int) {
|
||||
A002("Empty reservoir", R.string.string_a002),
|
||||
A003("Patch expired", R.string.string_a003),
|
||||
A004("Occlusion", R.string.string_a004),
|
||||
A005("Power on self test failure", R.string.string_a005),
|
||||
A007("Inappropriate temperature", R.string.string_a007),
|
||||
A016("Needle insertion Error", R.string.string_a016),
|
||||
A018("Patch battery Error", R.string.string_a018),
|
||||
A019("Patch battery Error", R.string.string_a019),
|
||||
A020("Patch activation Error", R.string.string_a020),
|
||||
A022("Patch Error", R.string.string_a022),
|
||||
A023("Patch Error", R.string.string_a023),
|
||||
A034("Patch Error", R.string.string_a034),
|
||||
A041("Patch Error", R.string.string_a041),
|
||||
A042("Patch Error", R.string.string_a042),
|
||||
A043("Patch Error", R.string.string_a043),
|
||||
A044("Patch Error", R.string.string_a044),
|
||||
A106("Patch Error", R.string.string_a106),
|
||||
A107("Patch Error", R.string.string_a107),
|
||||
A108("Patch Error", R.string.string_a108),
|
||||
A116("Patch Error", R.string.string_a116),
|
||||
A117("Patch Error", R.string.string_a117),
|
||||
A118("Patch Error", R.string.string_a118),
|
||||
B001("End of insulin suspend", R.string.string_b001),
|
||||
B003("Low reservoir", R.string.string_b003),
|
||||
B005("Patch operating life expired", R.string.string_b005),
|
||||
B006("Patch will expire soon", R.string.string_b006),
|
||||
B012("Incomplete Patch activation", R.string.string_b012),
|
||||
B018("Patch battery low", R.string.string_b018);
|
||||
|
||||
val type: Char
|
||||
val code: Int
|
||||
val resId: Int
|
||||
val alarmCategory: AlarmCategory
|
||||
get() = when (type) {
|
||||
TYPE_ALARM -> AlarmCategory.ALARM
|
||||
TYPE_ALERT -> AlarmCategory.ALERT
|
||||
else -> AlarmCategory.NONE
|
||||
}
|
||||
val aeCode: Int
|
||||
get() {
|
||||
when (type) {
|
||||
TYPE_ALARM -> return code + 100
|
||||
TYPE_ALERT -> return code
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
val osAlarmId: Int
|
||||
get() = (when (type) {
|
||||
TYPE_ALARM -> 10000
|
||||
TYPE_ALERT -> 20000
|
||||
else -> 0
|
||||
} + code ) * 1000 + 1
|
||||
|
||||
val isPatchOccurrenceAlert: Boolean
|
||||
get() = this == B003 || this == B005 || this == B006 || this == B018
|
||||
|
||||
val isPatchOccurrenceAlarm: Boolean
|
||||
get() = this == A002 || this == A003 || this == A004 || this == A018 || this == A019 || this == A022
|
||||
|| this == A023 || this == A034 || this == A041 || this == A042 || this == A043 || this == A044 || this == A106
|
||||
|| this == A107 || this == A108 || this == A116 || this == A117 || this == A118
|
||||
|
||||
init {
|
||||
type = name[0]
|
||||
this.code = name.substring(1).toInt()
|
||||
resId = messageResId
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TYPE_ALARM = 'A'
|
||||
const val TYPE_ALERT = 'B'
|
||||
|
||||
private const val SCHEME = "alarmkey"
|
||||
private const val ALARM_KEY_PATH = "alarmkey"
|
||||
private const val QUERY_CODE = "alarmcode"
|
||||
|
||||
private val NAME_MAP = Stream.of(*values())
|
||||
.collect(Collectors.toMap({ obj: AlarmCode -> obj.name }, Function.identity()))
|
||||
|
||||
fun fromStringToCode(name: String): AlarmCode? {
|
||||
return NAME_MAP[name]
|
||||
}
|
||||
|
||||
fun findByPatchAeCode(aeCode: Int): AlarmCode? {
|
||||
return if (aeCode > 100) {
|
||||
fromStringToCode(String.format(Locale.US, "A%03d", aeCode - 100))
|
||||
} else fromStringToCode(String.format(Locale.US, "B%03d", aeCode))
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getUri(alarmCode: AlarmCode): Uri {
|
||||
return Uri.Builder()
|
||||
.scheme(SCHEME)
|
||||
.authority("com.eoflow.eomapp")
|
||||
.path(ALARM_KEY_PATH)
|
||||
.appendQueryParameter(QUERY_CODE, alarmCode.name)
|
||||
.build();
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getAlarmCode(uri: Uri): AlarmCode? {
|
||||
if (SCHEME == uri.scheme && ALARM_KEY_PATH == uri.lastPathSegment) {
|
||||
val code = uri.getQueryParameter(QUERY_CODE)
|
||||
if (code.isNullOrBlank()) {
|
||||
return null
|
||||
}
|
||||
return fromStringToCode(code)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun fromIntent(intent: Intent): AlarmCode? {
|
||||
return intent.data?.let { getAlarmCode(it) }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,171 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.alarm
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import info.nightscout.androidaps.interfaces.ActivePlugin
|
||||
import info.nightscout.androidaps.interfaces.CommandQueue
|
||||
import info.nightscout.shared.logging.AAPSLogger
|
||||
import info.nightscout.shared.logging.LTag
|
||||
import info.nightscout.androidaps.plugins.bus.RxBus
|
||||
import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification
|
||||
import info.nightscout.androidaps.plugins.general.overview.notifications.Notification
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.alarm.AlarmCode.*
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.EONotification
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.EoPatchRxBus
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.R
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ble.IPatchManager
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ble.IPreferenceManager
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.code.AlarmCategory
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.event.EventEoPatchAlarm
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ui.AlarmHelperActivity
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.vo.Alarms
|
||||
import info.nightscout.androidaps.utils.FabricPrivacy
|
||||
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
||||
import info.nightscout.shared.sharedPreferences.SP
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.Single
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
interface IAlarmManager {
|
||||
fun init()
|
||||
fun restartAll()
|
||||
}
|
||||
|
||||
@Singleton
|
||||
class AlarmManager @Inject constructor() : IAlarmManager {
|
||||
@Inject lateinit var patchManager: IPatchManager
|
||||
@Inject lateinit var activePlugin: ActivePlugin
|
||||
@Inject lateinit var commandQueue: CommandQueue
|
||||
@Inject lateinit var aapsLogger: AAPSLogger
|
||||
@Inject lateinit var resourceHelper: ResourceHelper
|
||||
@Inject lateinit var rxBus: RxBus
|
||||
@Inject lateinit var fabricPrivacy: FabricPrivacy
|
||||
@Inject lateinit var sp: SP
|
||||
@Inject lateinit var context: Context
|
||||
|
||||
@Inject lateinit var pm: IPreferenceManager
|
||||
@Inject lateinit var mAlarmRegistry: IAlarmRegistry
|
||||
|
||||
private lateinit var mAlarmProcess: AlarmProcess
|
||||
|
||||
private var compositeDisposable: CompositeDisposable = CompositeDisposable()
|
||||
|
||||
@Inject
|
||||
fun onInit() {
|
||||
mAlarmProcess = AlarmProcess(patchManager, rxBus)
|
||||
}
|
||||
|
||||
override fun init(){
|
||||
EoPatchRxBus.listen(EventEoPatchAlarm::class.java)
|
||||
.map { it -> it.alarmCodes }
|
||||
.doOnNext { aapsLogger.info(LTag.PUMP,"EventEoPatchAlarm Received") }
|
||||
.concatMap {
|
||||
Observable.fromArray(it)
|
||||
.observeOn(Schedulers.io())
|
||||
.subscribeOn(AndroidSchedulers.mainThread())
|
||||
.doOnNext { alarmCodes ->
|
||||
alarmCodes.forEach {
|
||||
aapsLogger.info(LTag.PUMP,"alarmCode: ${it.name}")
|
||||
val valid = isValid(it)
|
||||
if (valid) {
|
||||
if (it.alarmCategory == AlarmCategory.ALARM || it == B012) {
|
||||
showAlarmDialog(it)
|
||||
} else {
|
||||
showNotification(it)
|
||||
}
|
||||
|
||||
updateState(it, AlarmState.FIRED)
|
||||
}else{
|
||||
updateState(it, AlarmState.HANDLE)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
.subscribe({}, { throwable: Throwable -> fabricPrivacy.logException(throwable) })
|
||||
}
|
||||
|
||||
override fun restartAll() {
|
||||
val now = System.currentTimeMillis()
|
||||
val occuredAlarm = pm.getAlarms().occured.clone() as HashMap<AlarmCode, Alarms.AlarmItem>
|
||||
val registeredAlarm = pm.getAlarms().registered.clone() as HashMap<AlarmCode, Alarms.AlarmItem>
|
||||
compositeDisposable.clear()
|
||||
if(occuredAlarm.isNotEmpty()){
|
||||
EoPatchRxBus.publish(EventEoPatchAlarm(occuredAlarm.keys))
|
||||
}
|
||||
|
||||
if(registeredAlarm.isNotEmpty()){
|
||||
registeredAlarm.forEach { raEntry ->
|
||||
compositeDisposable.add(
|
||||
mAlarmRegistry.add(raEntry.key, Math.max(OS_REGISTER_GAP, raEntry.value.triggerTimeMilli - now))
|
||||
.subscribe()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun isValid(code: AlarmCode): Boolean{
|
||||
return when(code){
|
||||
A005, A016, A020, B012 -> {
|
||||
aapsLogger.info(LTag.PUMP,"Is ${code} valid? ${pm.getPatchConfig().hasMacAddress() && pm.getPatchConfig().lifecycleEvent.isSubStepRunning}")
|
||||
pm.getPatchConfig().hasMacAddress() && pm.getPatchConfig().lifecycleEvent.isSubStepRunning
|
||||
}
|
||||
else -> {
|
||||
aapsLogger.info(LTag.PUMP,"Is ${code} valid? ${pm.getPatchConfig().isActivated}")
|
||||
pm.getPatchConfig().isActivated
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showAlarmDialog(alarmCode: AlarmCode){
|
||||
val i = Intent(context, AlarmHelperActivity::class.java)
|
||||
i.putExtra("soundid", R.raw.error)
|
||||
i.putExtra("code", alarmCode.name)
|
||||
i.putExtra("status", resourceHelper.gs(alarmCode.resId))
|
||||
i.putExtra("title", resourceHelper.gs(R.string.string_alarm))
|
||||
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
context.startActivity(i)
|
||||
}
|
||||
|
||||
private fun showNotification(alarmCode: AlarmCode, timeOffset: Long = 0L){
|
||||
var occurredTimestamp: Long = pm.getPatchConfig().patchWakeupTimestamp + TimeUnit.SECONDS.toMillis(timeOffset)
|
||||
val notification = EONotification(Notification.EOELOW_PATCH_ALERTS + (alarmCode.aeCode + 10000), resourceHelper.gs(alarmCode.resId), Notification.URGENT)
|
||||
|
||||
notification.action(R.string.confirm) {
|
||||
Single.just(isValid(alarmCode))
|
||||
.flatMap { isValid ->
|
||||
return@flatMap if(isValid) mAlarmProcess.doAction(context, alarmCode)
|
||||
else Single.just(IAlarmProcess.ALARM_HANDLED)
|
||||
}
|
||||
.subscribe { ret ->
|
||||
if(ret == IAlarmProcess.ALARM_HANDLED){
|
||||
updateState(alarmCode, AlarmState.HANDLE)
|
||||
}else{
|
||||
rxBus.send(EventNewNotification(notification))
|
||||
}
|
||||
}
|
||||
}
|
||||
notification.soundId = R.raw.error
|
||||
notification.date = occurredTimestamp
|
||||
rxBus.send(EventNewNotification(notification))
|
||||
}
|
||||
|
||||
private fun updateState(alarmCode: AlarmCode, state: AlarmState){
|
||||
when(state){
|
||||
AlarmState.REGISTER -> pm.getAlarms().register(alarmCode, 0)
|
||||
AlarmState.FIRED -> pm.getAlarms().occured(alarmCode)
|
||||
AlarmState.HANDLE -> pm.getAlarms().handle(alarmCode)
|
||||
}
|
||||
pm.flushAlarms()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private const val OS_REGISTER_GAP = 3 * 1000L
|
||||
}
|
||||
}
|
|
@ -0,0 +1,135 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.alarm
|
||||
|
||||
import android.content.Context
|
||||
import android.content.DialogInterface
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ui.EopatchActivity.Companion.createIntentForCheckConnection
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ui.EopatchActivity.Companion.createIntentForDiscarded
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ui.EopatchActivity.Companion.createIntentForChangePatch
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ui.EopatchActivity.Companion.createIntentForCanularInsertionError
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ble.IPatchManager
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.alarm.AlarmCode.*
|
||||
import android.content.Intent
|
||||
import info.nightscout.androidaps.plugins.bus.RxBus
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.R
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ui.EopatchActivity
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.response.BaseResponse
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.code.DeactivationStatus
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.response.TemperatureResponse
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.event.EventDialog
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.event.EventProgressDialog
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.extension.takeOne
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ui.dialogs.CommonDialog
|
||||
import io.reactivex.Single
|
||||
import java.lang.Exception
|
||||
import java.util.concurrent.Callable
|
||||
|
||||
interface IAlarmProcess {
|
||||
fun doAction(context: Context, code: AlarmCode): Single<Int>
|
||||
|
||||
companion object {
|
||||
const val ALARM_UNHANDLED = 0
|
||||
const val ALARM_PAUSE = 1
|
||||
const val ALARM_HANDLED = 2
|
||||
}
|
||||
}
|
||||
|
||||
class AlarmProcess(val patchManager: IPatchManager, val rxBus: RxBus) : IAlarmProcess {
|
||||
override fun doAction(context: Context, code: AlarmCode): Single<Int> {
|
||||
return when (code) {
|
||||
B001 -> resumeBasalAction(context)
|
||||
A002, A003, A004, A005, A018, A019,
|
||||
A020, A022, A023, A034, A041, A042,
|
||||
A043, A044, A106, A107, A108, A116,
|
||||
A117, A118 -> patchDeactivationAction(context, true)
|
||||
A007 -> inappropriateTemperatureAction(context)
|
||||
A016 -> needleInsertionErrorAction(context)
|
||||
B003, B018 -> Single.just(IAlarmProcess.ALARM_HANDLED)
|
||||
B005, B006 -> Single.just(IAlarmProcess.ALARM_HANDLED)
|
||||
B012 -> Single.just(IAlarmProcess.ALARM_HANDLED)
|
||||
else -> Single.just(IAlarmProcess.ALARM_HANDLED)
|
||||
}
|
||||
}
|
||||
|
||||
private fun startActivityWithSingleTop(context: Context, intent: Intent) {
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||
context.startActivity(intent)
|
||||
}
|
||||
|
||||
private fun showCommunicationFailedDialog(onConfirmed: Runnable) {
|
||||
var dialog = CommonDialog().apply {
|
||||
title = R.string.patch_communication_failed
|
||||
message = R.string.patch_communication_check_helper_1
|
||||
positiveBtn = R.string.string_communication_check
|
||||
positiveListener = DialogInterface.OnClickListener { dialog, which ->
|
||||
onConfirmed.run()
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
rxBus.send(EventDialog(dialog, true))
|
||||
}
|
||||
|
||||
private fun actionWithPatchCheckConnection(context: Context, action: Callable<Single<Int>>): Single<Int> {
|
||||
return if (patchManager.patchConnectionState.isConnected) {
|
||||
try {
|
||||
action.call()
|
||||
} catch (e: Exception) {
|
||||
Single.just(IAlarmProcess.ALARM_PAUSE)
|
||||
}
|
||||
} else {
|
||||
Single.fromCallable {
|
||||
showCommunicationFailedDialog {
|
||||
startActivityWithSingleTop(context,
|
||||
createIntentForCheckConnection(context, true, true))
|
||||
}
|
||||
IAlarmProcess.ALARM_PAUSE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun resumeBasalAction(context: Context): Single<Int> {
|
||||
return actionWithPatchCheckConnection(context) {
|
||||
patchManager.resumeBasal()
|
||||
.map { obj: BaseResponse -> obj.isSuccess }
|
||||
.flatMap { Single.just(it.takeOne(IAlarmProcess.ALARM_HANDLED, IAlarmProcess.ALARM_UNHANDLED)) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun patchDeactivationAction(context: Context, goHome: Boolean): Single<Int> {
|
||||
return actionWithPatchCheckConnection(context) {
|
||||
rxBus.send(EventProgressDialog(true, R.string.string_in_progress))
|
||||
patchManager.deactivate(6000, true)
|
||||
.doFinally {
|
||||
rxBus.send(EventProgressDialog(false, R.string.string_in_progress))
|
||||
startActivityWithSingleTop(context, createIntentForDiscarded(context, goHome))
|
||||
}
|
||||
.flatMap { ok: DeactivationStatus? -> Single.just(IAlarmProcess.ALARM_HANDLED) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun changPatchAction(context: Context): Single<Int> {
|
||||
return Single.fromCallable {
|
||||
startActivityWithSingleTop(context, createIntentForChangePatch(context))
|
||||
IAlarmProcess.ALARM_HANDLED
|
||||
}
|
||||
}
|
||||
|
||||
private fun needleInsertionErrorAction(context: Context): Single<Int> {
|
||||
return Single.fromCallable {
|
||||
startActivityWithSingleTop(context, createIntentForCanularInsertionError(context))
|
||||
IAlarmProcess.ALARM_HANDLED
|
||||
}
|
||||
}
|
||||
|
||||
private fun inappropriateTemperatureAction(context: Context): Single<Int> {
|
||||
return actionWithPatchCheckConnection(context) {
|
||||
patchManager.temperature
|
||||
.map(TemperatureResponse::getTemperature)
|
||||
.map { temp -> (temp >= EopatchActivity.NORMAL_TEMPERATURE_MIN && temp <= EopatchActivity.NORMAL_TEMPERATURE_MAX) }
|
||||
.filter{ok -> ok}
|
||||
.flatMap { patchManager.resumeBasal().map { it.isSuccess.takeOne(IAlarmProcess.ALARM_HANDLED, IAlarmProcess.ALARM_UNHANDLED) }.toMaybe() }
|
||||
.defaultIfEmpty(IAlarmProcess.ALARM_UNHANDLED)
|
||||
.toSingle()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,164 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.alarm
|
||||
|
||||
import android.app.AlarmManager
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.alarm.AlarmCode.Companion.getUri
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ble.IPreferenceManager
|
||||
import info.nightscout.androidaps.plugins.bus.RxBus
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.event.EventEoPatchAlarm
|
||||
import android.app.PendingIntent
|
||||
import android.app.AlarmManager.AlarmClockInfo
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import info.nightscout.shared.logging.AAPSLogger
|
||||
import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification
|
||||
import info.nightscout.androidaps.plugins.general.overview.notifications.Notification
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.EoPatchRxBus
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.OsAlarmReceiver
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.code.PatchLifecycle
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.code.PatchAeCode
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.extension.observeOnMainThread
|
||||
import io.reactivex.Maybe
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import io.reactivex.disposables.Disposable
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
interface IAlarmRegistry {
|
||||
fun add(alarmCode: AlarmCode, triggerAfter: Long, isFirst: Boolean = false): Maybe<AlarmCode>
|
||||
fun add(patchAeCodes: Set<PatchAeCode>)
|
||||
fun remove(alarmKey: AlarmCode): Maybe<AlarmCode>
|
||||
}
|
||||
|
||||
@Singleton
|
||||
class AlarmRegistry @Inject constructor() : IAlarmRegistry {
|
||||
@Inject lateinit var mContext: Context
|
||||
@Inject lateinit var pm: IPreferenceManager
|
||||
@Inject lateinit var rxBus: RxBus
|
||||
@Inject lateinit var aapsLogger: AAPSLogger
|
||||
|
||||
private lateinit var mOsAlarmManager: AlarmManager
|
||||
private var mDisposable: Disposable? = null
|
||||
private var compositeDisposable: CompositeDisposable = CompositeDisposable()
|
||||
|
||||
@Inject fun onInit() {
|
||||
mOsAlarmManager = mContext.getSystemService(Context.ALARM_SERVICE) as AlarmManager
|
||||
mDisposable = pm.observePatchLifeCycle()
|
||||
.observeOnMainThread()
|
||||
.subscribe {
|
||||
when(it){
|
||||
PatchLifecycle.REMOVE_NEEDLE_CAP -> {
|
||||
val triggerAfter = pm.getPatchConfig().patchWakeupTimestamp + TimeUnit.HOURS.toMillis(1) - System.currentTimeMillis()
|
||||
compositeDisposable.add(add(AlarmCode.A020, triggerAfter).subscribe())
|
||||
}
|
||||
PatchLifecycle.ACTIVATED -> {
|
||||
|
||||
}
|
||||
PatchLifecycle.SHUTDOWN -> {
|
||||
val sources = ArrayList<Maybe<*>>()
|
||||
sources.add(Maybe.just(true))
|
||||
pm.getAlarms().occured.let{
|
||||
if(it.isNotEmpty()){
|
||||
it.keys.forEach {
|
||||
sources.add(
|
||||
Maybe.just(it)
|
||||
.observeOnMainThread()
|
||||
.doOnSuccess { rxBus.send(EventDismissNotification(Notification.EOELOW_PATCH_ALERTS + (it.aeCode + 10000))) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
pm.getAlarms().registered.let{
|
||||
if(it.isNotEmpty()){
|
||||
it.keys.forEach {
|
||||
sources.add(remove(it))
|
||||
}
|
||||
}
|
||||
}
|
||||
Maybe.concat(sources)
|
||||
.subscribe {
|
||||
pm.getAlarms().clear()
|
||||
pm.flushAlarms()
|
||||
}
|
||||
}
|
||||
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun add(alarmCode: AlarmCode, triggerAfter: Long, isFirst: Boolean): Maybe<AlarmCode> {
|
||||
if(pm.getAlarms().occured.containsKey(alarmCode)){
|
||||
return Maybe.just(alarmCode)
|
||||
}else {
|
||||
val triggerTimeMilli = System.currentTimeMillis() + triggerAfter
|
||||
pm.getAlarms().register(alarmCode, triggerAfter)
|
||||
pm.flushAlarms()
|
||||
if (triggerAfter <= 0L) {
|
||||
EoPatchRxBus.publish(EventEoPatchAlarm(HashSet<AlarmCode>().apply { add(alarmCode) }, isFirst))
|
||||
return Maybe.just(alarmCode)
|
||||
}
|
||||
return registerOsAlarm(alarmCode, triggerTimeMilli)
|
||||
}
|
||||
}
|
||||
|
||||
override fun add(patchAeCodes: Set<PatchAeCode>) {
|
||||
compositeDisposable.add(
|
||||
Observable.fromIterable(patchAeCodes)
|
||||
.filter{patchAeCodeItem -> AlarmCode.Companion.findByPatchAeCode(patchAeCodeItem.getAeValue()) != null}
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.filter { patchAeCodes -> AlarmCode.findByPatchAeCode(patchAeCodes.getAeValue()) != null }
|
||||
.flatMapMaybe{aeCodeResponse -> add(AlarmCode.findByPatchAeCode(aeCodeResponse.getAeValue())!!,0L, true)}
|
||||
.subscribe()
|
||||
)
|
||||
}
|
||||
|
||||
private fun registerOsAlarm(alarmCode: AlarmCode, triggerTime: Long): Maybe<AlarmCode> {
|
||||
return Maybe.fromCallable {
|
||||
cancelOsAlarmInternal(alarmCode)
|
||||
val pendingIntent = createPendingIntent(alarmCode, 0)
|
||||
val now = System.currentTimeMillis()
|
||||
mOsAlarmManager.setAlarmClock(AlarmClockInfo(triggerTime, pendingIntent), pendingIntent)
|
||||
alarmCode
|
||||
}
|
||||
}
|
||||
|
||||
override fun remove(alarmCode: AlarmCode): Maybe<AlarmCode> {
|
||||
if(pm.getAlarms().registered.containsKey(alarmCode)) {
|
||||
return cancelOsAlarms(alarmCode)
|
||||
.doOnSuccess {
|
||||
pm.getAlarms().unregister(alarmCode)
|
||||
pm.flushAlarms()
|
||||
}
|
||||
.map { integer: Int? -> alarmCode }
|
||||
}else{
|
||||
return Maybe.just(alarmCode)
|
||||
}
|
||||
}
|
||||
|
||||
private fun cancelOsAlarms(vararg alarmCodes: AlarmCode): Maybe<Int> {
|
||||
return Observable.fromArray(*alarmCodes)
|
||||
.map(this::cancelOsAlarmInternal)
|
||||
.reduce(Integer::sum)
|
||||
}
|
||||
|
||||
private fun cancelOsAlarmInternal(alarmCode: AlarmCode): Int {
|
||||
val old = createPendingIntent(alarmCode, PendingIntent.FLAG_NO_CREATE)
|
||||
return if (old != null) {
|
||||
mOsAlarmManager.cancel(old)
|
||||
old.cancel()
|
||||
aapsLogger.debug("[${alarmCode}] OS Alarm canceled.")
|
||||
1
|
||||
} else {
|
||||
aapsLogger.debug("[${alarmCode}] OS Alarm not canceled, not registered.")
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
private fun createPendingIntent(alarmCode: AlarmCode, flag: Int): PendingIntent {
|
||||
val intent = Intent(mContext, OsAlarmReceiver::class.java).setData(getUri(alarmCode))
|
||||
return PendingIntent.getBroadcast(mContext, 1, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.alarm;
|
||||
|
||||
public enum AlarmState {
|
||||
REGISTER,
|
||||
FIRED,
|
||||
HANDLE
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.bindingadapters
|
||||
|
||||
import android.view.View
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
|
||||
class OnSafeClickListener(
|
||||
private val clickListener: View.OnClickListener,
|
||||
private val intervalMs: Long = MIN_CLICK_INTERVAL
|
||||
) : View.OnClickListener {
|
||||
private var canClick = AtomicBoolean(true)
|
||||
|
||||
override fun onClick(v: View?) {
|
||||
if (canClick.getAndSet(false)) {
|
||||
v?.run {
|
||||
postDelayed({
|
||||
canClick.set(true)
|
||||
}, intervalMs)
|
||||
clickListener.onClick(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
companion object {
|
||||
// 중복 클릭 방지 시간 설정
|
||||
private val MIN_CLICK_INTERVAL: Long = 1000
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.bindingadapters
|
||||
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.ColorRes
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.databinding.BindingAdapter
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.extension.check
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.extension.setVisibleOrGone
|
||||
|
||||
@BindingAdapter("android:visibility")
|
||||
fun setVisibility(view: View, visible: Boolean) {
|
||||
view.setVisibleOrGone(visible)
|
||||
}
|
||||
|
||||
@BindingAdapter("visibleOrGone")
|
||||
fun setVisibleOrGone(view: View, visibleOrGone: Boolean) {
|
||||
view.setVisibleOrGone(visibleOrGone)
|
||||
}
|
||||
|
||||
@BindingAdapter("onSafeClick")
|
||||
fun View.setOnSafeClickListener(clickListener: View.OnClickListener?) {
|
||||
clickListener?.also {
|
||||
setOnClickListener(OnSafeClickListener(it))
|
||||
} ?: setOnClickListener(null)
|
||||
}
|
||||
|
||||
@BindingAdapter("textColor")
|
||||
fun setTextColor(view: TextView, @ColorRes colorResId: Int) {
|
||||
view.setTextColor(view.context.getColor(colorResId))
|
||||
}
|
||||
|
||||
@BindingAdapter("android:text")
|
||||
fun setText(view: TextView, @StringRes resId: Int?) {
|
||||
val text = resId?.let { view.context.getString(it) } ?: ""
|
||||
val oldText = view.text
|
||||
if (text.check(oldText)) {
|
||||
view.text = text
|
||||
}
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.ble;
|
||||
|
||||
|
||||
import info.nightscout.androidaps.data.DetailedBolusInfo;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.scan.BleConnectionState;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.scan.PatchSelfTestResult;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.scan.ScanList;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.code.BolusExDuration;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.code.DeactivationStatus;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.code.PatchLifecycle;
|
||||
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.response.BasalScheduleSetResponse;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.response.BaseResponse;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.response.BolusResponse;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.response.BolusStopResponse;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.response.ComboBolusStopResponse;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.response.PatchBooleanResponse;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.response.TempBasalScheduleSetResponse;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.response.TemperatureResponse;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.vo.BolusCurrent;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.vo.NormalBasal;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.vo.PatchConfig;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.vo.PatchLifecycleEvent;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.vo.PatchState;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.vo.TempBasal;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.Single;
|
||||
|
||||
public interface IPatchManager {
|
||||
|
||||
/*
|
||||
@Deprecated
|
||||
static IPatchManager shared() {
|
||||
return BaseApplication.instance.getDataComponent().getPatchManager();
|
||||
}
|
||||
*/
|
||||
void init();
|
||||
|
||||
IPreferenceManager getPreferenceManager();
|
||||
|
||||
PatchConfig getPatchConfig();
|
||||
|
||||
boolean isActivated();
|
||||
|
||||
Single<? extends BaseResponse> resumeBasal();
|
||||
|
||||
Observable<PatchLifecycle> observePatchLifeCycle();
|
||||
|
||||
Observable<PatchState> observePatchState();
|
||||
|
||||
BleConnectionState getPatchConnectionState();
|
||||
|
||||
void connect();
|
||||
|
||||
void disconnect();
|
||||
|
||||
PatchState getPatchState();
|
||||
|
||||
void updatePatchState(PatchState state);
|
||||
|
||||
BolusCurrent getBolusCurrent();
|
||||
|
||||
Single<DeactivationStatus> deactivate(long timeout, boolean force);
|
||||
|
||||
Observable<BleConnectionState> observePatchConnectionState();
|
||||
|
||||
Observable<BolusCurrent> observeBolusCurrent();
|
||||
|
||||
void setConnection();
|
||||
|
||||
Single<BolusStopResponse> stopNowBolus();
|
||||
|
||||
Single<BolusStopResponse> stopExtBolus();
|
||||
|
||||
Single<ComboBolusStopResponse> stopComboBolus();
|
||||
|
||||
Single<? extends BolusResponse> startQuickBolus(float nowDoseU, float exDoseU,
|
||||
BolusExDuration exDuration);
|
||||
|
||||
Single<? extends BolusResponse> startCalculatorBolus(DetailedBolusInfo detailedBolusInfo);
|
||||
|
||||
|
||||
Single<PatchBooleanResponse> infoReminderSet(boolean infoReminder);
|
||||
|
||||
Single<PatchBooleanResponse> setLowReservoir(int doseUnit, int hours);
|
||||
|
||||
Single<PatchState> updateConnection();
|
||||
|
||||
long getPatchExpiredTime();
|
||||
|
||||
Single<BasalScheduleSetResponse> startBasal(NormalBasal basal);
|
||||
|
||||
void updatePatchLifeCycle(PatchLifecycleEvent event);
|
||||
|
||||
Single<Boolean> startBond(String mac);
|
||||
|
||||
Single<Boolean> getPatchInfo(long timeout);
|
||||
|
||||
Single<PatchSelfTestResult> selfTest(long timeout);
|
||||
|
||||
Observable<Long> startPriming(long timeout, long count);
|
||||
|
||||
Single<Boolean> checkNeedleSensing(long timeout);
|
||||
|
||||
Single<Boolean> patchActivation(long timeout);
|
||||
|
||||
Single<PatchBooleanResponse> stopAeBeep(int aeCode);
|
||||
|
||||
Single<TempBasalScheduleSetResponse> startTempBasal(TempBasal tempBasal);
|
||||
|
||||
Single<? extends BaseResponse> pauseBasal(float pauseDurationHour);
|
||||
|
||||
Single<ScanList> scan(long timeout);
|
||||
|
||||
Single<PatchBooleanResponse> stopTempBasal();
|
||||
|
||||
Single<TemperatureResponse> getTemperature();
|
||||
|
||||
void initBasalSchedule();
|
||||
|
||||
void addBolusToHistory(DetailedBolusInfo originalDetailedBolusInfo);
|
||||
|
||||
void changeBuzzerSetting();
|
||||
|
||||
void changeReminderSetting();
|
||||
|
||||
void checkActivationProcess();
|
||||
}
|
|
@ -0,0 +1,451 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.ble;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import info.nightscout.androidaps.data.DetailedBolusInfo;
|
||||
import info.nightscout.androidaps.events.EventCustomActionsChanged;
|
||||
import info.nightscout.androidaps.events.EventPumpStatusChanged;
|
||||
import info.nightscout.androidaps.events.EventRefreshOverview;
|
||||
import info.nightscout.androidaps.interfaces.ActivePlugin;
|
||||
import info.nightscout.androidaps.interfaces.CommandQueue;
|
||||
import info.nightscout.androidaps.interfaces.ProfileFunction;
|
||||
import info.nightscout.androidaps.interfaces.PumpSync;
|
||||
import info.nightscout.androidaps.plugins.pump.common.defs.PumpType;
|
||||
import info.nightscout.androidaps.utils.DateUtil;
|
||||
import info.nightscout.shared.logging.AAPSLogger;
|
||||
import info.nightscout.shared.logging.LTag;
|
||||
import info.nightscout.androidaps.plugins.bus.RxBus;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.CommonUtils;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.R;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.RxAction;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.alarm.AlarmCode;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.scan.BleConnectionState;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.scan.IPatchScanner;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.scan.PatchScanner;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.scan.PatchSelfTestResult;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.scan.ScanList;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.code.BolusExDuration;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.code.DeactivationStatus;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.code.PatchLifecycle;
|
||||
import com.polidea.rxandroidble2.RxBleClient;
|
||||
import com.polidea.rxandroidble2.exceptions.BleException;
|
||||
import com.polidea.rxandroidble2.internal.RxBleLog;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.response.BasalScheduleSetResponse;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.response.BaseResponse;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.response.BolusResponse;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.response.BolusStopResponse;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.response.ComboBolusStopResponse;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.response.PatchBooleanResponse;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.response.TempBasalScheduleSetResponse;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.response.TemperatureResponse;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.code.SettingKeys;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.event.EventPatchActivationNotComplete;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ui.DialogHelperActivity;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.vo.BolusCurrent;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.vo.NormalBasal;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.vo.PatchConfig;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.vo.PatchLifecycleEvent;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.vo.PatchState;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.vo.TempBasal;
|
||||
import info.nightscout.androidaps.queue.commands.Command;
|
||||
import info.nightscout.androidaps.utils.resources.ResourceHelper;
|
||||
import info.nightscout.shared.sharedPreferences.SP;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.exceptions.OnErrorNotImplementedException;
|
||||
import io.reactivex.exceptions.UndeliverableException;
|
||||
import io.reactivex.plugins.RxJavaPlugins;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
|
||||
@Singleton
|
||||
public class PatchManager implements IPatchManager {
|
||||
|
||||
@Inject PatchManagerImpl patchManager;
|
||||
@Inject IPreferenceManager pm;
|
||||
@Inject ProfileFunction profileFunction;
|
||||
@Inject ActivePlugin activePlugin;
|
||||
@Inject CommandQueue commandQueue;
|
||||
@Inject AAPSLogger aapsLogger;
|
||||
@Inject ResourceHelper resourceHelper;
|
||||
@Inject RxBus rxBus;
|
||||
@Inject Context context;
|
||||
@Inject SP sp;
|
||||
@Inject PumpSync pumpSync;
|
||||
@Inject DateUtil dateUtil;
|
||||
|
||||
private IPatchScanner patchScanner;
|
||||
private CompositeDisposable mCompositeDisposable = new CompositeDisposable();
|
||||
private Disposable mConnectingDisposable = null;
|
||||
|
||||
@Inject
|
||||
public PatchManager() {
|
||||
setupRxAndroidBle();
|
||||
}
|
||||
|
||||
private void setupRxAndroidBle() {
|
||||
RxJavaPlugins.setErrorHandler(throwable -> {
|
||||
if (throwable instanceof UndeliverableException) {
|
||||
if (throwable.getCause() instanceof BleException) {
|
||||
return;
|
||||
}
|
||||
aapsLogger.error(LTag.PUMPBTCOMM, "rx UndeliverableException Error Handler");
|
||||
return;
|
||||
} else if (throwable instanceof OnErrorNotImplementedException) {
|
||||
aapsLogger.error(LTag.PUMPBTCOMM, "rx exception Error Handler");
|
||||
return;
|
||||
}
|
||||
throw new RuntimeException("Unexpected Throwable in RxJavaPlugins error handler", throwable);
|
||||
});
|
||||
}
|
||||
|
||||
@Inject
|
||||
void onInit() {
|
||||
patchScanner = new PatchScanner(context);
|
||||
|
||||
mCompositeDisposable.add(observePatchConnectionState()
|
||||
.subscribe(bleConnectionState -> {
|
||||
switch (bleConnectionState) {
|
||||
case DISCONNECTED:
|
||||
rxBus.send(new EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTED));
|
||||
rxBus.send(new EventRefreshOverview("Eopatch connection state: " + bleConnectionState.name(), true));
|
||||
rxBus.send(new EventCustomActionsChanged());
|
||||
stopObservingConnection();
|
||||
break;
|
||||
|
||||
case CONNECTED:
|
||||
rxBus.send(new EventPumpStatusChanged(EventPumpStatusChanged.Status.CONNECTED));
|
||||
rxBus.send(new EventRefreshOverview("Eopatch connection state: " + bleConnectionState.name(), true));
|
||||
rxBus.send(new EventCustomActionsChanged());
|
||||
stopObservingConnection();
|
||||
break;
|
||||
|
||||
case CONNECTING:
|
||||
mConnectingDisposable = Observable.interval(0, 1, TimeUnit.SECONDS)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.takeUntil(n -> getPatchConnectionState().isConnected() || n > 10 * 60)
|
||||
.subscribe(n -> rxBus.send(new EventPumpStatusChanged(EventPumpStatusChanged.Status.CONNECTING, n.intValue())));
|
||||
break;
|
||||
|
||||
default:
|
||||
stopObservingConnection();
|
||||
}
|
||||
})
|
||||
);
|
||||
mCompositeDisposable.add(rxBus
|
||||
.toObservable(EventPatchActivationNotComplete.class)
|
||||
.observeOn(Schedulers.io())
|
||||
.subscribeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(eventPatchActivationNotComplete -> {
|
||||
Intent i = new Intent(context, DialogHelperActivity.class);
|
||||
i.putExtra("title", resourceHelper.gs(R.string.patch_activate_reminder_title));
|
||||
i.putExtra("message", resourceHelper.gs(R.string.patch_activate_reminder_desc));
|
||||
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
context.startActivity(i);
|
||||
})
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
initBasalSchedule();
|
||||
setConnection();
|
||||
}
|
||||
|
||||
private void stopObservingConnection(){
|
||||
if(mConnectingDisposable != null) {
|
||||
mConnectingDisposable.dispose();
|
||||
mConnectingDisposable = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IPreferenceManager getPreferenceManager() {
|
||||
return pm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PatchConfig getPatchConfig() {
|
||||
return pm.getPatchConfig();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Observable<PatchLifecycle> observePatchLifeCycle() {
|
||||
return pm.observePatchLifeCycle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void updatePatchLifeCycle(PatchLifecycleEvent event) {
|
||||
pm.updatePatchLifeCycle(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BleConnectionState getPatchConnectionState() {
|
||||
return patchManager.getPatchConnectionState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Observable<BleConnectionState> observePatchConnectionState() {
|
||||
return patchManager.observePatchConnectionState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PatchState getPatchState() {
|
||||
return pm.getPatchState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updatePatchState(PatchState state) {
|
||||
pm.getPatchState().update(state);
|
||||
pm.flushPatchState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Observable<PatchState> observePatchState() {
|
||||
return pm.observePatchState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getPatchExpiredTime() {
|
||||
return pm.getPatchConfig().getPatchExpiredTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BolusCurrent getBolusCurrent() {
|
||||
return pm.getBolusCurrent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Observable<BolusCurrent> observeBolusCurrent() {
|
||||
return pm.observeBolusCurrent();
|
||||
}
|
||||
|
||||
|
||||
public void connect() {
|
||||
// Nothing (Auto Connect mode)
|
||||
}
|
||||
|
||||
public void disconnect() {
|
||||
// Nothing (Auto Connect mode)
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setConnection() {
|
||||
if(pm.getPatchConfig().hasMacAddress()){
|
||||
patchManager.updateMacAddress(pm.getPatchConfig().getMacAddress(), false);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isActivated() {
|
||||
return pm.getPatchConfig().isActivated();
|
||||
}
|
||||
|
||||
public Single<Boolean> startBond(String mac) {
|
||||
return patchManager.startBond(mac);
|
||||
}
|
||||
|
||||
public Single<Boolean> getPatchInfo(long timeout) {
|
||||
return patchManager.getPatchInfo(timeout);
|
||||
}
|
||||
|
||||
public Single<PatchSelfTestResult> selfTest(long timeout) {
|
||||
return patchManager.selfTest(timeout);
|
||||
}
|
||||
|
||||
public Single<TemperatureResponse> getTemperature() {
|
||||
return patchManager.getTemperature();
|
||||
}
|
||||
|
||||
public Observable<Long> startPriming(long timeout, long count) {
|
||||
return patchManager.startPriming(timeout, count);
|
||||
}
|
||||
|
||||
public Single<Boolean> checkNeedleSensing(long timeout) {
|
||||
return patchManager.checkNeedleSensing(timeout);
|
||||
}
|
||||
|
||||
public Single<Boolean> patchActivation(long timeout) {
|
||||
return patchManager.patchActivation(timeout);
|
||||
}
|
||||
|
||||
public Single<BasalScheduleSetResponse> startBasal(NormalBasal basal) {
|
||||
return patchManager.startBasal(basal);
|
||||
}
|
||||
|
||||
public Single<? extends BaseResponse> resumeBasal() {
|
||||
return patchManager.resumeBasal();
|
||||
}
|
||||
|
||||
|
||||
public Single<? extends BaseResponse> pauseBasal(float pauseDurationHour) {
|
||||
return patchManager.pauseBasal(pauseDurationHour);
|
||||
}
|
||||
|
||||
//==============================================================================================
|
||||
// IPatchManager interface [TEMP BASAL]
|
||||
//==============================================================================================
|
||||
|
||||
public Single<TempBasalScheduleSetResponse> startTempBasal(TempBasal tempBasal) {
|
||||
return patchManager.startTempBasal(tempBasal);
|
||||
}
|
||||
|
||||
// 템프베이젤 주입 정지
|
||||
// 템프베이젤이 정지되면 자동으로 노멀베이젤이 활성화된다
|
||||
// 외부에서 호출된다. 즉 명시적으로 tempBasal 정지. 이 때는 normalBasal resume 은 PatchState 보고 처리.
|
||||
|
||||
public Single<PatchBooleanResponse> stopTempBasal() {
|
||||
return patchManager.stopTempBasal();
|
||||
}
|
||||
|
||||
|
||||
public Single<? extends BolusResponse> startQuickBolus(float nowDoseU,
|
||||
float exDoseU, BolusExDuration exDuration) {
|
||||
return patchManager.startQuickBolus(nowDoseU, exDoseU, exDuration);
|
||||
}
|
||||
|
||||
|
||||
public Single<? extends BolusResponse> startCalculatorBolus(DetailedBolusInfo detailedBolusInfo) {
|
||||
return patchManager.startCalculatorBolus(detailedBolusInfo);
|
||||
}
|
||||
|
||||
|
||||
public Single<BolusStopResponse> stopNowBolus() {
|
||||
return patchManager.stopNowBolus();
|
||||
}
|
||||
|
||||
|
||||
public Single<BolusStopResponse> stopExtBolus() {
|
||||
return patchManager.stopExtBolus();
|
||||
}
|
||||
|
||||
|
||||
public Single<ComboBolusStopResponse> stopComboBolus(){
|
||||
return patchManager.stopComboBolus();
|
||||
}
|
||||
|
||||
public Single<DeactivationStatus> deactivate(long timeout, boolean force) {
|
||||
return patchManager.deactivate(timeout, force);
|
||||
}
|
||||
|
||||
public Single<PatchBooleanResponse> stopBuzz() {
|
||||
return patchManager.stopBuzz();
|
||||
}
|
||||
|
||||
public Single<PatchBooleanResponse> infoReminderSet(boolean infoReminder) {
|
||||
return patchManager.infoReminderSet(infoReminder);
|
||||
}
|
||||
|
||||
public Single<PatchBooleanResponse> setLowReservoir(int doseUnit, int hours) {
|
||||
return patchManager.setLowReservoir(doseUnit, hours);
|
||||
}
|
||||
|
||||
public Single<PatchState> updateConnection() {
|
||||
return patchManager.updateConnection();
|
||||
}
|
||||
|
||||
public Single<PatchBooleanResponse> stopAeBeep(int aeCode) {
|
||||
return patchManager.stopAeBeep(aeCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Single<ScanList> scan(long timeout) {
|
||||
patchManager.updateMacAddress("", false);
|
||||
pm.getPatchConfig().setMacAddress("");
|
||||
return patchScanner.scan(timeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initBasalSchedule() {
|
||||
if(pm.getNormalBasalManager().getNormalBasal() == null){
|
||||
pm.getNormalBasalManager().setNormalBasal(profileFunction.getProfile());
|
||||
pm.flushNormalBasalManager();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBolusToHistory(DetailedBolusInfo originalDetailedBolusInfo) {
|
||||
DetailedBolusInfo detailedBolusInfo = originalDetailedBolusInfo.copy();
|
||||
|
||||
if(detailedBolusInfo.insulin > 0) {
|
||||
pumpSync.syncBolusWithPumpId(
|
||||
detailedBolusInfo.timestamp,
|
||||
detailedBolusInfo.insulin,
|
||||
detailedBolusInfo.getBolusType(),
|
||||
dateUtil.now(),
|
||||
PumpType.EOFLOW_EOPATCH2,
|
||||
patchManager.pm.getPatchSerial()
|
||||
);
|
||||
}
|
||||
if (detailedBolusInfo.carbs > 0) {
|
||||
pumpSync.syncCarbsWithTimestamp(
|
||||
detailedBolusInfo.getCarbsTimestamp() != null ? detailedBolusInfo.getCarbsTimestamp() : detailedBolusInfo.timestamp,
|
||||
detailedBolusInfo.carbs,
|
||||
null,
|
||||
PumpType.USER,
|
||||
patchManager.pm.getPatchSerial()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changeBuzzerSetting() {
|
||||
boolean buzzer = sp.getBoolean(SettingKeys.Companion.getBUZZER_REMINDERS(), false);
|
||||
if(pm.getPatchConfig().getInfoReminder() != buzzer) {
|
||||
if (isActivated()) {
|
||||
infoReminderSet(buzzer)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(patchBooleanResponse -> {
|
||||
pm.getPatchConfig().setInfoReminder(buzzer);
|
||||
pm.flushPatchConfig();
|
||||
});
|
||||
} else {
|
||||
pm.getPatchConfig().setInfoReminder(buzzer);
|
||||
pm.flushPatchConfig();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changeReminderSetting() {
|
||||
int doseUnit = sp.getInt(SettingKeys.Companion.getLOW_RESERVIOR_REMINDERS(), 0);
|
||||
int hours = sp.getInt(SettingKeys.Companion.getEXPIRATION_REMINDERS(), 0);
|
||||
PatchConfig pc = pm.getPatchConfig();
|
||||
if(pc.getLowReservoirAlertAmount() != doseUnit || pc.getPatchExpireAlertTime() != hours) {
|
||||
if (isActivated()) {
|
||||
setLowReservoir(doseUnit, hours)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(patchBooleanResponse -> {
|
||||
pc.setLowReservoirAlertAmount(doseUnit);
|
||||
pc.setPatchExpireAlertTime(hours);
|
||||
pm.flushPatchConfig();
|
||||
});
|
||||
} else {
|
||||
pc.setLowReservoirAlertAmount(doseUnit);
|
||||
pc.setPatchExpireAlertTime(hours);
|
||||
pm.flushPatchConfig();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkActivationProcess(){
|
||||
if(getPatchConfig().getLifecycleEvent().isSubStepRunning()
|
||||
&& !pm.getAlarms().isOccuring(AlarmCode.A005)
|
||||
&& !pm.getAlarms().isOccuring(AlarmCode.A020)) {
|
||||
RxAction.INSTANCE.runOnMainThread(() -> {
|
||||
rxBus.send(new EventPatchActivationNotComplete());
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,793 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.ble;
|
||||
|
||||
import static android.content.Intent.ACTION_DATE_CHANGED;
|
||||
import static android.content.Intent.ACTION_TIMEZONE_CHANGED;
|
||||
import static android.content.Intent.ACTION_TIME_CHANGED;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import info.nightscout.androidaps.data.DetailedBolusInfo;
|
||||
import info.nightscout.shared.logging.AAPSLogger;
|
||||
import info.nightscout.shared.logging.LTag;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.EoPatchRxBus;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.alarm.AlarmCode;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ble.task.ActivateTask;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ble.task.DeactivateTask;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ble.task.GetPatchInfoTask;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ble.task.InfoReminderTask;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ble.task.NeedleSensingTask;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ble.task.PauseBasalTask;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ble.task.PrimingTask;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ble.task.ResumeBasalTask;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ble.task.SelfTestTask;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ble.task.SetLowReservoirTask;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ble.task.StartBondTask;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ble.task.StartCalcBolusTask;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ble.task.StartNormalBasalTask;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ble.task.StartQuickBolusTask;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ble.task.StartTempBasalTask;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ble.task.StopComboBolusTask;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ble.task.StopExtBolusTask;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ble.task.StopNowBolusTask;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ble.task.StopTempBasalTask;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ble.task.SyncBasalHistoryTask;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ble.task.TaskBase;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ble.task.TaskFunc;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ble.task.UpdateConnectionTask;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.scan.BleConnectionState;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.scan.IBleDevice;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.Patch;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.scan.PatchSelfTestResult;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.api.BasalStop;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.api.BuzzerStop;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.api.GetTemperature;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.api.PublicKeySend;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.api.SequenceGet;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.api.StopAeBeep;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.code.BolusType;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.noti.AlarmNotification;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.noti.BaseNotification;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.noti.InfoNotification;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.response.*;
|
||||
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.code.BolusExDuration;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.code.DeactivationStatus;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.security.AlgorithmParameters;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.interfaces.ECPublicKey;
|
||||
import java.security.spec.ECGenParameterSpec;
|
||||
import java.security.spec.ECParameterSpec;
|
||||
import java.security.spec.ECPoint;
|
||||
import java.security.spec.ECPublicKeySpec;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.InvalidParameterSpecException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.crypto.KeyAgreement;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.util.HexString;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.code.SettingKeys;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.event.EventEoPatchAlarm;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ui.receiver.RxBroadcastReceiver;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.vo.BolusCurrent;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.vo.NormalBasal;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.vo.PatchConfig;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.vo.PatchState;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.vo.TempBasal;
|
||||
import info.nightscout.shared.sharedPreferences.SP;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.Scheduler;
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
import io.reactivex.functions.Consumer;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
|
||||
@Singleton
|
||||
public class PatchManagerImpl/* implements IPatchConstant*/ {
|
||||
@Inject IPreferenceManager pm;
|
||||
@Inject Context context;
|
||||
@Inject SP sp;
|
||||
@Inject AAPSLogger aapsLogger;
|
||||
|
||||
@Inject StartBondTask START_BOND;
|
||||
@Inject GetPatchInfoTask GET_PATCH_INFO;
|
||||
@Inject SelfTestTask SELF_TEST;
|
||||
@Inject PrimingTask START_PRIMING;
|
||||
@Inject NeedleSensingTask START_NEEDLE_CHECK;
|
||||
|
||||
IBleDevice patch;
|
||||
HexString hexString;
|
||||
|
||||
private CompositeDisposable compositeDisposable;
|
||||
|
||||
private Observable<Intent> dateTimeChanged;
|
||||
|
||||
private static final long DEFAULT_API_TIME_OUT = 10; // SECONDS
|
||||
|
||||
private BuzzerStop BUZZER_STOP;
|
||||
private GetTemperature TEMPERATURE_GET;
|
||||
private BasalStop BASAL_STOP;
|
||||
private StopAeBeep ALARM_ALERT_ERROR_BEEP_STOP;
|
||||
private PublicKeySend PUBLIC_KEY_SET;
|
||||
private SequenceGet SEQUENCE_GET;
|
||||
|
||||
@Inject
|
||||
public PatchManagerImpl() {
|
||||
compositeDisposable = new CompositeDisposable();
|
||||
hexString = new HexString();
|
||||
|
||||
BUZZER_STOP = new BuzzerStop();
|
||||
TEMPERATURE_GET = new GetTemperature();
|
||||
BASAL_STOP = new BasalStop();
|
||||
ALARM_ALERT_ERROR_BEEP_STOP = new StopAeBeep();
|
||||
PUBLIC_KEY_SET = new PublicKeySend();
|
||||
SEQUENCE_GET = new SequenceGet();
|
||||
}
|
||||
|
||||
@Inject
|
||||
void onInit() {
|
||||
patch = Patch.getInstance();
|
||||
patch.init(context);
|
||||
patch.setSeq(pm.getPatchConfig().getSeq15());
|
||||
|
||||
IntentFilter filter = new IntentFilter(ACTION_TIME_CHANGED);
|
||||
filter.addAction(ACTION_DATE_CHANGED);
|
||||
filter.addAction(ACTION_TIMEZONE_CHANGED);
|
||||
|
||||
dateTimeChanged = RxBroadcastReceiver.Companion.create(context, filter);
|
||||
|
||||
compositeDisposable.add(
|
||||
Observable.combineLatest(patch.observeConnected(), pm.observePatchLifeCycle(),
|
||||
(connected, lifeCycle) -> (connected && lifeCycle.isActivated()))
|
||||
.subscribeOn(Schedulers.io())
|
||||
.filter(ok -> ok)
|
||||
.observeOn(Schedulers.io())
|
||||
.doOnNext(v -> TaskBase.enqueue(TaskFunc.UPDATE_CONNECTION))
|
||||
.retry()
|
||||
.subscribe());
|
||||
|
||||
compositeDisposable.add(
|
||||
Observable.combineLatest(patch.observeConnected(), pm.observePatchLifeCycle().distinctUntilChanged(), dateTimeChanged.startWith(new Intent()),
|
||||
(connected, lifeCycle, value) -> (connected && lifeCycle.isActivated()))
|
||||
.subscribeOn(Schedulers.io())
|
||||
.doOnNext(v -> aapsLogger.debug(LTag.PUMP,"Has the date or time changed? "+v))
|
||||
.filter(ok -> ok)
|
||||
.doOnNext(v -> TaskBase.enqueue(TaskFunc.SET_GLOBAL_TIME))
|
||||
.doOnError(e -> aapsLogger.error(LTag.PUMP, "Failed to set EOPatch time."))
|
||||
.retry()
|
||||
.subscribe());
|
||||
|
||||
compositeDisposable.add(
|
||||
patch.observeConnected()
|
||||
.doOnNext(it -> onPatchConnected(it))
|
||||
.subscribe());
|
||||
|
||||
compositeDisposable.add(
|
||||
pm.getPatchConfig().observe().doOnNext(config -> {
|
||||
byte[] newKey = config.getSharedKey();
|
||||
patch.updateEncryptionParam(newKey);
|
||||
}).subscribe()
|
||||
);
|
||||
|
||||
compositeDisposable.add(
|
||||
EoPatchRxBus.INSTANCE.listen(EventEoPatchAlarm.class)
|
||||
.filter(it -> it.isFirst())
|
||||
.filter(it -> !pm.getPatchConfig().isDeactivated())
|
||||
.filter(it -> patch.getConnectionState().isConnected())
|
||||
.concatMapIterable(it -> it.getAlarmCodes())
|
||||
.filter(it -> it.isPatchOccurrenceAlert())
|
||||
.flatMap(it -> stopAeBeep(it.getAeCode()).toObservable())
|
||||
.subscribe()
|
||||
);
|
||||
|
||||
compositeDisposable.add(
|
||||
EoPatchRxBus.INSTANCE.listen(EventEoPatchAlarm.class)
|
||||
.filter(it -> it.isFirst())
|
||||
.filter(it -> !pm.getPatchConfig().isDeactivated())
|
||||
.filter(it -> patch.getConnectionState().isConnected())
|
||||
.concatMapIterable(it -> it.getAlarmCodes())
|
||||
.filter(it -> it.isPatchOccurrenceAlarm())
|
||||
.flatMap(it -> pauseBasalImpl(0.0f, System.currentTimeMillis(), it).toObservable())
|
||||
.subscribe()
|
||||
);
|
||||
|
||||
|
||||
monitorPatchNotification();
|
||||
onConnectedUpdateSequence();
|
||||
}
|
||||
|
||||
private void onPatchConnected(boolean connected) {
|
||||
boolean activated = pm.getPatchConfig().isActivated();
|
||||
boolean useEncryption = pm.getPatchConfig().getSharedKey() != null;
|
||||
int doseUnit = sp.getInt(SettingKeys.Companion.getLOW_RESERVIOR_REMINDERS(), 0);
|
||||
int hours = sp.getInt(SettingKeys.Companion.getEXPIRATION_REMINDERS(), 0);
|
||||
boolean buzzer = sp.getBoolean(SettingKeys.Companion.getBUZZER_REMINDERS(), false);
|
||||
PatchConfig pc = pm.getPatchConfig();
|
||||
|
||||
if (connected && activated && useEncryption) {
|
||||
compositeDisposable.add(
|
||||
SEQUENCE_GET.get()
|
||||
.map(KeyResponse::getSequence)
|
||||
.doOnSuccess(sequence -> {
|
||||
if (sequence >= 0) {
|
||||
saveSequence(sequence);
|
||||
}
|
||||
})
|
||||
.flatMap(integer -> {
|
||||
if(pc.getLowReservoirAlertAmount() != doseUnit || pc.getPatchExpireAlertTime() != hours) {
|
||||
return setLowReservoir(doseUnit, hours)
|
||||
.doOnSuccess(patchBooleanResponse -> {
|
||||
pc.setLowReservoirAlertAmount(doseUnit);
|
||||
pc.setPatchExpireAlertTime(hours);
|
||||
pm.flushPatchConfig();
|
||||
}).map(patchBooleanResponse -> true);
|
||||
}
|
||||
return Single.just(true);
|
||||
})
|
||||
.flatMap(ret -> {
|
||||
if(pc.getInfoReminder() != buzzer) {
|
||||
return infoReminderSet(buzzer)
|
||||
.doOnSuccess(patchBooleanResponse -> {
|
||||
pc.setInfoReminder(buzzer);
|
||||
pm.flushPatchConfig();
|
||||
}).map(patchBooleanResponse -> true);
|
||||
}
|
||||
return Single.just(true);
|
||||
})
|
||||
.subscribe());
|
||||
}
|
||||
|
||||
if(connected == false && activated == true){
|
||||
pm.getPatchConfig().updatetDisconnectedTime();
|
||||
}
|
||||
}
|
||||
|
||||
private void monitorPatchNotification() {
|
||||
compositeDisposable.addAll(
|
||||
patch.observeAlarmNotification()
|
||||
.subscribe(
|
||||
this::onAlarmNotification,
|
||||
throwable -> aapsLogger.error(LTag.PUMP, throwable.getMessage())
|
||||
),
|
||||
patch.observeInfoNotification()
|
||||
.filter(state -> pm.getPatchConfig().isActivated())
|
||||
.subscribe(
|
||||
this::onInfoNotification,
|
||||
throwable -> aapsLogger.error(LTag.PUMP, throwable.getMessage())
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
private void onConnectedUpdateSequence() {
|
||||
|
||||
}
|
||||
|
||||
//==============================================================================================
|
||||
// preference database update helper
|
||||
//==============================================================================================
|
||||
|
||||
// synchronized lock
|
||||
private final Object lock = new Object();
|
||||
|
||||
private void updatePatchConfig(Consumer<PatchConfig> consumer, boolean needSave) throws Exception {
|
||||
synchronized (lock) {
|
||||
consumer.accept(pm.getPatchConfig());
|
||||
if (needSave) {
|
||||
pm.flushPatchConfig();
|
||||
} else {
|
||||
pm.flushPatchConfig();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
synchronized void updateBasal() {
|
||||
|
||||
NormalBasal normalBasal = pm.getNormalBasalManager().getNormalBasal();
|
||||
|
||||
if (normalBasal != null) {
|
||||
// 아래 코드를 실행하면 isDoseUChanged 가 false 가 된다.
|
||||
if(normalBasal.updateNormalBasalIndex()) {
|
||||
pm.flushNormalBasalManager();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void connect() {
|
||||
|
||||
}
|
||||
|
||||
public void disconnect() {
|
||||
}
|
||||
|
||||
/**
|
||||
* getPatchConnection() 을 사용해야 한다.
|
||||
* 아직 Life Cycle 이 Activated 가 아님.
|
||||
*
|
||||
* Activation Process task #1 Get Patch Information from Patch
|
||||
* Fragment: fragment_patch_connect_new
|
||||
*/
|
||||
|
||||
public Single<Boolean> startBond(String mac) {
|
||||
return START_BOND.start(mac);
|
||||
}
|
||||
|
||||
public Single<Boolean> getPatchInfo(long timeout) {
|
||||
return GET_PATCH_INFO.get().timeout(timeout, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Activation Process task #2 Check Patch is O.K
|
||||
* Fragment: fragment_patch_connect_new
|
||||
*/
|
||||
public Single<PatchSelfTestResult> selfTest(long timeout) {
|
||||
return SELF_TEST.start().timeout(timeout, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Activation Process task #3 PRIMING
|
||||
* Fragment: fragment_patch_priming
|
||||
*/
|
||||
|
||||
public Single<TemperatureResponse> getTemperature() {
|
||||
return TEMPERATURE_GET.get()
|
||||
.timeout(DEFAULT_API_TIME_OUT, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
public Observable<Long> startPriming(long timeout, long count) {
|
||||
return START_PRIMING.start(count)
|
||||
.timeout(timeout, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Activation Process task #4 NEEDLE SENSING
|
||||
* Fragment: fragment_patch_rotate_knob
|
||||
*/
|
||||
public Single<Boolean> checkNeedleSensing(long timeout) { //TODO: Timeout 추가?
|
||||
return START_NEEDLE_CHECK.start()
|
||||
.timeout(timeout, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Activation Process task #5 Activation Secure Key, Basal writing
|
||||
* Fragment: fragment_patch_check_patch
|
||||
*/
|
||||
@Inject
|
||||
ActivateTask ACTIVATE;
|
||||
|
||||
public Single<Boolean> patchActivation(long timeout) {
|
||||
|
||||
return ACTIVATE.start().timeout(timeout, TimeUnit.MILLISECONDS)
|
||||
.flatMap(success -> sharedKey())
|
||||
.flatMap(success -> getSequence())
|
||||
.doOnSuccess(success -> {
|
||||
if (success) {
|
||||
TaskBase.enqueue(TaskFunc.LOW_RESERVOIR);
|
||||
TaskBase.enqueue(TaskFunc.INFO_REMINDER);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================================
|
||||
// IPatchManager interface [NORMAL BASAL]
|
||||
//==============================================================================================
|
||||
|
||||
@Inject
|
||||
StartNormalBasalTask startNormalBasalTask;
|
||||
|
||||
public Single<BasalScheduleSetResponse> startBasal(NormalBasal basal) {
|
||||
|
||||
return startNormalBasalTask.start(basal, false)
|
||||
.timeout(DEFAULT_API_TIME_OUT, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@Inject
|
||||
ResumeBasalTask resumeBasalTask;
|
||||
|
||||
public Single<? extends BaseResponse> resumeBasal() {
|
||||
return resumeBasalTask.resume()
|
||||
.timeout(DEFAULT_API_TIME_OUT, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
public Single<? extends BaseResponse> pauseBasal(float pauseDurationHour) {
|
||||
return pauseBasalImpl(pauseDurationHour, 0, null)
|
||||
.observeOn(SS)
|
||||
.timeout(DEFAULT_API_TIME_OUT, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
//==============================================================================================
|
||||
// IPatchManager implementation [NORMAL BASAL]
|
||||
//==============================================================================================
|
||||
|
||||
@Inject
|
||||
PauseBasalTask pauseBasalTask;
|
||||
|
||||
private Single<? extends BaseResponse> pauseBasalImpl(float pauseDurationHour, long alarmOccurredTime, @Nullable AlarmCode alarmCode) {
|
||||
return pauseBasalTask.pause(pauseDurationHour, alarmOccurredTime, alarmCode);
|
||||
}
|
||||
|
||||
private Single<BasalStopResponse> stopBasal() {
|
||||
return BASAL_STOP.stop();
|
||||
}
|
||||
|
||||
private void insertBasalStart() throws SQLException {
|
||||
insertBasalStart(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
private void insertBasalStart(long timestamp) throws SQLException {
|
||||
NormalBasal startedBasal = pm.getNormalBasalManager().getNormalBasal();
|
||||
if (startedBasal != null) {
|
||||
startedBasal.updateNormalBasalIndex();
|
||||
pm.flushNormalBasalManager();
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================================
|
||||
// IPatchManager interface [TEMP BASAL]
|
||||
//==============================================================================================
|
||||
|
||||
@Inject
|
||||
StartTempBasalTask startTempBasalTask;
|
||||
|
||||
public Single<TempBasalScheduleSetResponse> startTempBasal(TempBasal tempBasal) {
|
||||
return startTempBasalTask.start(tempBasal).timeout(DEFAULT_API_TIME_OUT, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
// 템프베이젤 주입 정지
|
||||
// 템프베이젤이 정지되면 자동으로 노멀베이젤이 활성화된다
|
||||
// 외부에서 호출된다. 즉 명시적으로 tempBasal 정지. 이 때는 normalBasal resume 은 PatchState 보고 처리.
|
||||
|
||||
@Inject
|
||||
StopTempBasalTask stopTempBasalTask;
|
||||
|
||||
public Single<PatchBooleanResponse> stopTempBasal() {
|
||||
return stopTempBasalTask.stop().timeout(DEFAULT_API_TIME_OUT, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@Inject
|
||||
StartQuickBolusTask startQuickBolusTask;
|
||||
|
||||
@Inject
|
||||
StartCalcBolusTask startCalcBolusTask;
|
||||
|
||||
@Inject
|
||||
StopComboBolusTask stopComboBolusTask;
|
||||
|
||||
@Inject
|
||||
StopNowBolusTask stopNowBolusTask;
|
||||
|
||||
@Inject
|
||||
StopExtBolusTask stopExtBolusTask;
|
||||
|
||||
|
||||
public Single<? extends BolusResponse> startQuickBolus(float nowDoseU, float exDoseU,
|
||||
BolusExDuration exDuration) {
|
||||
return startQuickBolusTask.start(nowDoseU, exDoseU, exDuration)
|
||||
.timeout(DEFAULT_API_TIME_OUT, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
public Single<? extends BolusResponse> startCalculatorBolus(DetailedBolusInfo detailedBolusInfo) {
|
||||
return startCalcBolusTask.start(detailedBolusInfo)
|
||||
.timeout(DEFAULT_API_TIME_OUT, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
public Single<BolusStopResponse> stopNowBolus() {
|
||||
return stopNowBolusTask.stop().timeout(DEFAULT_API_TIME_OUT, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
public Single<BolusStopResponse> stopExtBolus() {
|
||||
return stopExtBolusTask.stop().timeout(DEFAULT_API_TIME_OUT, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
public Single<ComboBolusStopResponse> stopComboBolus(){
|
||||
return stopComboBolusTask.stop().timeout(DEFAULT_API_TIME_OUT, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
private Single<? extends BaseResponse> stopNowAndExtBolus() {
|
||||
|
||||
boolean nowActive = pm.getPatchState().isNowBolusActive();
|
||||
boolean extActive = pm.getPatchState().isExtBolusActive();
|
||||
|
||||
if (nowActive && extActive) {
|
||||
return stopComboBolus();
|
||||
} else if (nowActive) {
|
||||
return stopNowBolus();
|
||||
} else if (extActive) {
|
||||
return stopExtBolus();
|
||||
}
|
||||
|
||||
return Single.just(new PatchBooleanResponse(true));
|
||||
}
|
||||
|
||||
//==============================================================================================
|
||||
// IPatchManager implementation [BOLUS]
|
||||
//==============================================================================================
|
||||
|
||||
public void readBolusStatusFromNotification(InfoNotification noti) {
|
||||
if (noti.isBolusRegAct()) {
|
||||
BolusCurrent bolusCurrent = pm.getBolusCurrent();
|
||||
|
||||
Arrays.asList(BolusType.NOW, BolusType.EXT).forEach(type -> {
|
||||
if (noti.isBolusRegAct(type)) { // 완료되었어도 업데이트 필요.
|
||||
int injectedPumpCount = noti.getInjected(type);
|
||||
int remainPumpCount = noti.getRemain(type);
|
||||
bolusCurrent.updateBolusFromPatch(type, injectedPumpCount, remainPumpCount);
|
||||
}
|
||||
});
|
||||
pm.flushBolusCurrent();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Inject
|
||||
DeactivateTask deactivateTask;
|
||||
// Patch Activation Tasks
|
||||
public Single<DeactivationStatus> deactivate(long timeout, boolean force) {
|
||||
return deactivateTask.run(force, timeout);
|
||||
}
|
||||
|
||||
public Single<? extends BaseResponse> stopAll(){
|
||||
List<Single<? extends BaseResponse>> sources = new ArrayList<>();
|
||||
|
||||
// 노멀볼루스 또는 확장볼루스가 동작중이면 정지
|
||||
|
||||
if (pm.getPatchState().isNowBolusActive() || pm.getPatchState().isExtBolusActive()) {
|
||||
sources.add(stopNowAndExtBolus());
|
||||
}
|
||||
|
||||
// 템프베이젤이 동작중이면 중지
|
||||
if (pm.getPatchState().isTempBasalActive()) {
|
||||
sources.add(stopTempBasal());
|
||||
}
|
||||
|
||||
sources.add(stopBasal());
|
||||
|
||||
return Single.concat(sources).lastOrError();
|
||||
}
|
||||
|
||||
public Single<PatchBooleanResponse> stopBuzz() {
|
||||
return BUZZER_STOP.stop();
|
||||
}
|
||||
|
||||
@Inject
|
||||
InfoReminderTask infoReminderTask;
|
||||
|
||||
public Single<PatchBooleanResponse> infoReminderSet(boolean infoReminder) {
|
||||
return infoReminderTask.set(infoReminder).timeout(DEFAULT_API_TIME_OUT, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@Inject
|
||||
SetLowReservoirTask setLowReservoirTask;
|
||||
public Single<PatchBooleanResponse> setLowReservoir(int doseUnit, int hours) {
|
||||
return setLowReservoirTask.set(doseUnit, hours).timeout(DEFAULT_API_TIME_OUT, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@Inject
|
||||
UpdateConnectionTask updateConnectionTask;
|
||||
public Single<PatchState> updateConnection() {
|
||||
return updateConnectionTask.update();
|
||||
}
|
||||
|
||||
public Single<PatchBooleanResponse> stopAeBeep(int aeCode) {
|
||||
return ALARM_ALERT_ERROR_BEEP_STOP.stop(aeCode);
|
||||
}
|
||||
|
||||
synchronized void fetchPatchState() {
|
||||
updateConnectionTask.enqueue();
|
||||
}
|
||||
|
||||
@Inject
|
||||
PatchStateManager patchStateManager;
|
||||
|
||||
@Inject
|
||||
SyncBasalHistoryTask syncBasalHistoryTask;
|
||||
|
||||
void onAlarmNotification(AlarmNotification notification) throws Exception {
|
||||
patchStateManager.updatePatchState(PatchState.create(notification.patchState, System.currentTimeMillis()));
|
||||
|
||||
if (pm.getPatchConfig().isActivated()) {
|
||||
if(!patch.isSeqReady()){
|
||||
getSequence().subscribe();
|
||||
}
|
||||
updateBasal();
|
||||
updateInjected(notification, true);
|
||||
fetchPatchState();
|
||||
}
|
||||
}
|
||||
|
||||
private void onInfoNotification(InfoNotification notification) throws Exception {
|
||||
readBolusStatusFromNotification(notification);
|
||||
updateInjected(notification, false);
|
||||
if (notification.isBolusDone()) {
|
||||
fetchPatchState();
|
||||
}
|
||||
}
|
||||
|
||||
void updateInjected(BaseNotification notification, boolean needSave) throws Exception {
|
||||
updatePatchConfig(patchConfig -> {
|
||||
patchConfig.setInjectCount(notification.getTotalInjected());
|
||||
patchConfig.setStandardBolusInjectCount(notification.getSB_CNT());
|
||||
patchConfig.setExtendedBolusInjectCount(notification.getEB_CNT());
|
||||
patchConfig.setBasalInjectCount(notification.getBasal_CNT());
|
||||
}, needSave);
|
||||
}
|
||||
|
||||
//==============================================================================================
|
||||
// Security
|
||||
//==============================================================================================
|
||||
private static final String SECP256R1 = "secp256r1";
|
||||
private static final String EC = "EC";
|
||||
private static final String ECDH = "ECDH";
|
||||
|
||||
public Single<Boolean> sharedKey() {
|
||||
return genKeyPair().flatMap(keyPair -> ECPublicToRawBytes(keyPair)
|
||||
.flatMap(bytes -> PUBLIC_KEY_SET.send(bytes)
|
||||
.map(KeyResponse::getPublicKey)
|
||||
.map(bytes2 -> rawToEncodedECPublicKey(SECP256R1, bytes2))
|
||||
.map(publicKey -> generateSharedSecret(keyPair.getPrivate(), publicKey))
|
||||
.doOnSuccess(this::saveShared).map(v2 -> true)))
|
||||
.doOnError(e -> aapsLogger.error(LTag.PUMP, "sharedKey error"));
|
||||
}
|
||||
|
||||
public Single<Boolean> getSequence() {
|
||||
return SEQUENCE_GET.get()
|
||||
.map(KeyResponse::getSequence)
|
||||
.doOnSuccess(sequence -> {
|
||||
if (sequence >= 0) {
|
||||
saveSequence(sequence);
|
||||
}
|
||||
})
|
||||
.flatMap(v -> Single.just(true));
|
||||
}
|
||||
|
||||
private void saveShared(byte[] v) {
|
||||
pm.getPatchConfig().setSharedKey(v);
|
||||
pm.flushPatchConfig();
|
||||
}
|
||||
|
||||
private void saveSequence(int sequence) {
|
||||
patch.setSeq(sequence);
|
||||
pm.getPatchConfig().setSeq15(sequence);
|
||||
pm.flushPatchConfig();
|
||||
}
|
||||
|
||||
public Single<KeyPair> genKeyPair() {
|
||||
return Single.fromCallable(() -> {
|
||||
ECGenParameterSpec ecSpec_named = new ECGenParameterSpec(SECP256R1);
|
||||
KeyPairGenerator kpg = KeyPairGenerator.getInstance(EC);
|
||||
kpg.initialize(ecSpec_named);
|
||||
KeyPair pair = kpg.generateKeyPair();
|
||||
return pair;
|
||||
});
|
||||
}
|
||||
|
||||
public Single<byte[]> ECPublicToRawBytes(KeyPair keyPair) {
|
||||
return Single.just(keyPair.getPublic()).cast(ECPublicKey.class)
|
||||
.map(PatchManagerImpl::encodeECPublicKey);
|
||||
}
|
||||
|
||||
private static byte[] encodeECPublicKey(ECPublicKey pubKey) {
|
||||
int keyLengthBytes = pubKey.getParams().getOrder().bitLength()
|
||||
/ Byte.SIZE;
|
||||
byte[] publicKeyEncoded = new byte[2 * keyLengthBytes];
|
||||
|
||||
int offset = 0;
|
||||
|
||||
BigInteger x = pubKey.getW().getAffineX();
|
||||
byte[] xba = x.toByteArray();
|
||||
if (xba.length > keyLengthBytes + 1 || xba.length == keyLengthBytes + 1
|
||||
&& xba[0] != 0) {
|
||||
throw new IllegalStateException(
|
||||
"X coordinate of EC public key has wrong size");
|
||||
}
|
||||
|
||||
if (xba.length == keyLengthBytes + 1) {
|
||||
System.arraycopy(xba, 1, publicKeyEncoded, offset, keyLengthBytes);
|
||||
} else {
|
||||
System.arraycopy(xba, 0, publicKeyEncoded, offset + keyLengthBytes
|
||||
- xba.length, xba.length);
|
||||
}
|
||||
offset += keyLengthBytes;
|
||||
|
||||
BigInteger y = pubKey.getW().getAffineY();
|
||||
byte[] yba = y.toByteArray();
|
||||
if (yba.length > keyLengthBytes + 1 || yba.length == keyLengthBytes + 1
|
||||
&& yba[0] != 0) {
|
||||
throw new IllegalStateException(
|
||||
"Y coordinate of EC public key has wrong size");
|
||||
}
|
||||
|
||||
if (yba.length == keyLengthBytes + 1) {
|
||||
System.arraycopy(yba, 1, publicKeyEncoded, offset, keyLengthBytes);
|
||||
} else {
|
||||
System.arraycopy(yba, 0, publicKeyEncoded, offset + keyLengthBytes
|
||||
- yba.length, yba.length);
|
||||
}
|
||||
|
||||
return publicKeyEncoded;
|
||||
}
|
||||
|
||||
public static ECPublicKey rawToEncodedECPublicKey(String curveName, byte[] rawBytes) throws
|
||||
NoSuchAlgorithmException, InvalidKeySpecException, InvalidParameterSpecException,
|
||||
InvalidAlgorithmParameterException {
|
||||
KeyFactory kf = KeyFactory.getInstance(EC);
|
||||
int mid = rawBytes.length / 2;
|
||||
byte[] x = Arrays.copyOfRange(rawBytes, 0, mid);
|
||||
byte[] y = Arrays.copyOfRange(rawBytes, mid, rawBytes.length);
|
||||
ECPoint w = new ECPoint(new BigInteger(1, x), new BigInteger(1, y));
|
||||
return (ECPublicKey) kf.generatePublic(new ECPublicKeySpec(w, ecParameterSpecForCurve(curveName)));
|
||||
}
|
||||
|
||||
public static ECParameterSpec ecParameterSpecForCurve(String curveName) throws
|
||||
NoSuchAlgorithmException, InvalidParameterSpecException, InvalidAlgorithmParameterException {
|
||||
AlgorithmParameters params = AlgorithmParameters.getInstance(EC);
|
||||
params.init(new ECGenParameterSpec(curveName));
|
||||
return params.getParameterSpec(ECParameterSpec.class);
|
||||
}
|
||||
|
||||
public static byte[] generateSharedSecret(PrivateKey privateKey,
|
||||
PublicKey publicKey) {
|
||||
try {
|
||||
KeyAgreement keyAgreement = KeyAgreement.getInstance(ECDH);
|
||||
keyAgreement.init(privateKey);
|
||||
keyAgreement.doPhase(publicKey, true);
|
||||
|
||||
return keyAgreement.generateSecret();
|
||||
} catch (InvalidKeyException | NoSuchAlgorithmException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================================
|
||||
// Single Scheduler all callback must be observed on
|
||||
//==============================================================================================
|
||||
|
||||
private static final Scheduler SS = Schedulers.single();
|
||||
|
||||
public BleConnectionState getPatchConnectionState() {
|
||||
BleConnectionState result = patch.getConnectionState();
|
||||
return result;
|
||||
}
|
||||
|
||||
public Observable<BleConnectionState> observePatchConnectionState() {
|
||||
return patch.observeConnectionState();
|
||||
}
|
||||
|
||||
public void updateMacAddress(String mac, boolean b){
|
||||
patch.updateMacAddress(mac, b);
|
||||
}
|
||||
}
|
||||
|
||||
class AlarmFiredEventInfo
|
||||
{
|
||||
public AlarmCode code;
|
||||
public long createTimestamp;
|
||||
|
||||
public AlarmFiredEventInfo(AlarmCode code, long createTimestamp) {
|
||||
this.code = code;
|
||||
this.createTimestamp = createTimestamp;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,290 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.ble;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import info.nightscout.androidaps.interfaces.CommandQueue;
|
||||
import info.nightscout.shared.logging.AAPSLogger;
|
||||
import info.nightscout.shared.logging.LTag;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.code.BolusType;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ble.task.FetchAlarmTask;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ble.task.InternalSuspendedTask;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ble.task.ReadBolusFinishTimeTask;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ble.task.ReadTempBasalFinishTimeTask;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.vo.BolusCurrent;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.vo.NormalBasal;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.vo.PatchState;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.vo.TempBasal;
|
||||
import io.reactivex.Maybe;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
|
||||
|
||||
@Singleton
|
||||
public class PatchStateManager {
|
||||
|
||||
@Inject IPreferenceManager pm;
|
||||
@Inject ReadBolusFinishTimeTask readBolusFinishTimeTask;
|
||||
@Inject ReadTempBasalFinishTimeTask readTempBasalFinishTimeTask;
|
||||
@Inject InternalSuspendedTask internalSuspendedTask;
|
||||
@Inject FetchAlarmTask FETCH_ALARM;
|
||||
@Inject CommandQueue commandQueue;
|
||||
@Inject AAPSLogger aapsLogger;
|
||||
|
||||
@Inject
|
||||
public PatchStateManager() {
|
||||
|
||||
}
|
||||
|
||||
public synchronized void updatePatchState(PatchState newState) {
|
||||
Maybe.fromCallable(() -> newState).observeOn(Schedulers.single())
|
||||
.doOnSuccess(patchState -> updatePatchStateInner(patchState))
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.doOnSuccess(patchState -> aapsLogger.debug(LTag.PUMP, patchState.toString()))
|
||||
.subscribe();
|
||||
}
|
||||
|
||||
/* Schedulers.io() */
|
||||
public synchronized void updatePatchStateInner(PatchState newState) {
|
||||
|
||||
final PatchState oldState = pm.getPatchState();
|
||||
|
||||
int diff = newState.currentTime() - oldState.currentTime();
|
||||
if (0 <= diff && diff < 10) { // TODO 상수로 변경.
|
||||
/* 10초 안에 같은 PatchState update 시 skip */
|
||||
if (oldState.equalState(newState)) {
|
||||
return;
|
||||
}
|
||||
} else if (-5 < diff && diff < 0) {
|
||||
/* 이전 State 가 새로운 State 를 덮어 쓰는 것을 방지 -4초 까지 */
|
||||
return;
|
||||
}
|
||||
|
||||
newState.setUpdatedTimestamp(System.currentTimeMillis());
|
||||
|
||||
if (newState.isNewAlertAlarm()) {
|
||||
FETCH_ALARM.enqueue();
|
||||
}
|
||||
|
||||
if (newState.isPatchInternalSuspended()){
|
||||
onPatchInternalSuspended(newState);
|
||||
}
|
||||
|
||||
/* Normal Basal --------------------------------------------------------------------------------------------- */
|
||||
|
||||
if (newState.isNormalBasalAct()) {
|
||||
if (oldState.isNormalBasalPaused()) {
|
||||
// Resume --> onBasalResume
|
||||
onBasalResumeState();
|
||||
|
||||
} else if (oldState.isNormalBasalAct() == false) {
|
||||
// Start --> onBasalStarted
|
||||
}
|
||||
} else if (oldState.isNormalBasalPaused() == false && newState.isNormalBasalPaused()) {
|
||||
if (newState.isTempBasalAct()) {
|
||||
} else {
|
||||
// pause
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* Temp Basal ------------------------------------------------------------------------------------------- */
|
||||
if (newState.isTempBasalAct()) {
|
||||
if (oldState.isTempBasalAct() == false) {
|
||||
// Start
|
||||
onTempBasalStartState();
|
||||
}
|
||||
}
|
||||
|
||||
boolean tempBasalStopped = false;
|
||||
boolean tempBasalFinished = false;
|
||||
|
||||
if (newState.isTempBasalDone() && !newState.isPatchInternalSuspended()) {
|
||||
tempBasalFinished = true;
|
||||
}
|
||||
|
||||
if (oldState.isTempBasalDone() == false) {
|
||||
if (newState.isTempBasalDone()) {
|
||||
tempBasalStopped = true;
|
||||
|
||||
onTempBasalDoneState();
|
||||
} else if (oldState.isTempBasalAct() && newState.isTempBasalAct() == false) {
|
||||
tempBasalStopped = true;
|
||||
|
||||
onTempBasalCancelState();
|
||||
}
|
||||
}
|
||||
|
||||
if (tempBasalStopped) {
|
||||
if (newState.isNormalBasalAct()) {
|
||||
if (!newState.isPatchInternalSuspended()) {
|
||||
onNormalBasalResumed(tempBasalFinished);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (newState.isTempBasalAct() == false && pm.getTempBasalManager().getStartedBasal() != null) {
|
||||
pm.getTempBasalManager().updateBasalStopped();
|
||||
}
|
||||
|
||||
/* Now Bolus -------------------------------------------------------------------------------------------- */
|
||||
if (oldState.isNowBolusRegAct() == false && newState.isNowBolusRegAct() == true) {
|
||||
// Start
|
||||
} else if (oldState.isNowBolusDone() == false) {
|
||||
if (oldState.isNowBolusRegAct() && newState.isNowBolusRegAct() == false) {
|
||||
// Cancel
|
||||
} else if (newState.isNowBolusDone()) {
|
||||
// Done
|
||||
}
|
||||
}
|
||||
|
||||
BolusCurrent bolusCurrent = pm.getBolusCurrent();
|
||||
|
||||
if (newState.isNowBolusRegAct() == false && bolusCurrent.historyId(BolusType.NOW) > 0
|
||||
&& bolusCurrent.endTimeSynced(BolusType.NOW)) {
|
||||
bolusCurrent.clearBolus(BolusType.NOW);
|
||||
}
|
||||
|
||||
/* Extended Bolus --------------------------------------------------------------------------------------- */
|
||||
if (oldState.isExtBolusRegAct() == false && newState.isExtBolusRegAct() == true) {
|
||||
// Start
|
||||
} else if (oldState.isExtBolusDone() == false) {
|
||||
if (oldState.isExtBolusRegAct() && newState.isExtBolusRegAct() == false) {
|
||||
// Cancel
|
||||
} else if (newState.isExtBolusDone()) {
|
||||
// Done
|
||||
}
|
||||
}
|
||||
|
||||
if (newState.isExtBolusRegAct() == false && bolusCurrent.historyId(BolusType.EXT) > 0
|
||||
&& bolusCurrent.endTimeSynced(BolusType.EXT)) {
|
||||
bolusCurrent.clearBolus(BolusType.EXT);
|
||||
}
|
||||
|
||||
/* Finish Time Sync and remained insulin update*/
|
||||
/* Bolus Done -> update finish time */
|
||||
if (Stream.of(BolusType.NOW, BolusType.EXT).anyMatch(type ->
|
||||
newState.isBolusDone(type) && !bolusCurrent.endTimeSynced(type))) {
|
||||
readBolusFinishTime();
|
||||
}
|
||||
|
||||
/* TempBasal Done -> update finish time */
|
||||
if (tempBasalFinished) {
|
||||
readTempBasalFinishTime();
|
||||
}
|
||||
|
||||
/* Remained Insulin update */
|
||||
if (newState.getRemainedInsulin() != oldState.getRemainedInsulin()) {
|
||||
pm.getPatchConfig().setRemainedInsulin(newState.getRemainedInsulin());
|
||||
pm.flushPatchConfig();
|
||||
}
|
||||
|
||||
pm.getPatchState().update(newState);
|
||||
pm.flushPatchState();
|
||||
}
|
||||
|
||||
private void onTempBasalStartState() {
|
||||
TempBasal tempBasal = pm.getTempBasalManager().getStartedBasal();
|
||||
|
||||
if (tempBasal != null) {
|
||||
pm.getPatchConfig().updateTempBasalStarted();
|
||||
|
||||
NormalBasal normalBasal = pm.getNormalBasalManager().getNormalBasal();
|
||||
|
||||
if (normalBasal != null) {
|
||||
pm.getNormalBasalManager().updateBasalPaused();
|
||||
}
|
||||
|
||||
pm.flushPatchConfig();
|
||||
pm.flushNormalBasalManager();
|
||||
}
|
||||
}
|
||||
|
||||
void onTempBasalDoneState() {
|
||||
TempBasal tempBasal = pm.getTempBasalManager().getStartedBasal();
|
||||
|
||||
if (tempBasal != null) {
|
||||
pm.getTempBasalManager().updateBasalStopped();
|
||||
pm.flushTempBasalManager();
|
||||
}
|
||||
}
|
||||
|
||||
private void onTempBasalCancelState() {
|
||||
TempBasal tempBasal = pm.getTempBasalManager().getStartedBasal();
|
||||
|
||||
if (tempBasal != null) {
|
||||
pm.getTempBasalManager().updateBasalStopped();
|
||||
pm.flushTempBasalManager();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void readBolusFinishTime() {
|
||||
readBolusFinishTimeTask.enqueue();
|
||||
}
|
||||
|
||||
private void readTempBasalFinishTime() {
|
||||
readTempBasalFinishTimeTask.enqueue();
|
||||
}
|
||||
|
||||
private synchronized void onBasalResumeState() {
|
||||
|
||||
if (!pm.getNormalBasalManager().isStarted()) {
|
||||
long timestamp = System.currentTimeMillis();
|
||||
onBasalResumed(timestamp + 1000);
|
||||
}
|
||||
}
|
||||
|
||||
void onNormalBasalResumed(boolean tempBasalFinished) {
|
||||
NormalBasal normalBasal = pm.getNormalBasalManager().getNormalBasal();
|
||||
if (normalBasal != null) {
|
||||
pm.getNormalBasalManager().updateBasalStarted();
|
||||
normalBasal.updateNormalBasalIndex();
|
||||
pm.flushNormalBasalManager();;
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void onBasalResumed(long timestamp) {
|
||||
if (!pm.getNormalBasalManager().isStarted()) {
|
||||
pm.getNormalBasalManager().updateBasalStarted();
|
||||
|
||||
pm.getPatchConfig().updateNormalBasalStarted();
|
||||
pm.getPatchConfig().setNeedSetBasalSchedule(false);
|
||||
|
||||
NormalBasal basal = pm.getNormalBasalManager().getNormalBasal();
|
||||
|
||||
if (basal != null) {
|
||||
basal.updateNormalBasalIndex();
|
||||
}
|
||||
|
||||
pm.flushPatchConfig();
|
||||
pm.flushNormalBasalManager();
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void onBasalStarted(NormalBasal basal, long timestamp) {
|
||||
if (basal != null) {
|
||||
pm.getNormalBasalManager().updateBasalStarted();
|
||||
|
||||
basal.updateNormalBasalIndex(); //normal basal index를 업데이트하여 basal change 이력이 또 발생하는것을 방지(df_356)
|
||||
}
|
||||
|
||||
pm.getPatchConfig().updateNormalBasalStarted(); // updateNormalBasalStarted 도 동일함...
|
||||
pm.getPatchConfig().setNeedSetBasalSchedule(false);
|
||||
|
||||
pm.flushPatchConfig();
|
||||
pm.flushNormalBasalManager();
|
||||
}
|
||||
|
||||
private void onPatchInternalSuspended(PatchState state) {
|
||||
boolean isNowBolusActive = state.isNowBolusActive();
|
||||
boolean isExtBolusActive = state.isExtBolusActive();
|
||||
boolean isTempBasalActive = state.isTempBasalActive();
|
||||
|
||||
if (isNowBolusActive || isExtBolusActive || isTempBasalActive) {
|
||||
internalSuspendedTask.enqueue(isNowBolusActive, isExtBolusActive, isTempBasalActive);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,254 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.ble
|
||||
|
||||
import info.nightscout.shared.logging.AAPSLogger
|
||||
import info.nightscout.shared.logging.LTag
|
||||
import info.nightscout.androidaps.plugins.bus.RxBus
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.GsonHelper
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.code.SettingKeys
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.code.PatchLifecycle
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.vo.*
|
||||
import info.nightscout.shared.sharedPreferences.SP
|
||||
import io.reactivex.Observable
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
interface IPreferenceManager {
|
||||
fun getPatchConfig(): PatchConfig
|
||||
fun getPatchState(): PatchState
|
||||
fun getBolusCurrent(): BolusCurrent
|
||||
fun getNormalBasalManager(): NormalBasalManager
|
||||
fun getTempBasalManager(): TempBasalManager
|
||||
fun getAlarms(): Alarms
|
||||
fun init()
|
||||
fun flushPatchConfig()
|
||||
fun flushPatchState()
|
||||
fun flushBolusCurrent()
|
||||
fun flushNormalBasalManager()
|
||||
fun flushTempBasalManager()
|
||||
fun flushAlarms()
|
||||
fun updatePatchLifeCycle(event: PatchLifecycleEvent)
|
||||
fun updatePatchState(newState: PatchState)
|
||||
fun getPatchSerial(): String
|
||||
fun getPatchMac(): String?
|
||||
fun isActivated(): Boolean
|
||||
fun setMacAddress(mac: String)
|
||||
fun getPatchExpiredTime(): Long
|
||||
fun setSharedKey(bytes: ByteArray?)
|
||||
fun setSeq15(seq15: Int)
|
||||
fun getSeq15(): Int
|
||||
fun increaseSeq15()
|
||||
fun getPatchWakeupTimestamp(): Long
|
||||
fun observePatchLifeCycle(): Observable<PatchLifecycle>
|
||||
fun observePatchConfig(): Observable<PatchConfig>
|
||||
fun observePatchState(): Observable<PatchState>
|
||||
fun observeBolusCurrent(): Observable<BolusCurrent>
|
||||
fun observeAlarm(): Observable<Alarms>
|
||||
fun isInitDone(): Boolean
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* patch2 패키지에서 사용하는 프리퍼런스의 작업을 대신 처리하는 클래스
|
||||
*/
|
||||
@Singleton
|
||||
class PreferenceManager @Inject constructor(): IPreferenceManager {
|
||||
@Inject lateinit var sp: SP
|
||||
@Inject lateinit var rxBus: RxBus
|
||||
@Inject lateinit var aapsLogger: AAPSLogger
|
||||
@Inject lateinit var mPatchConfig: PatchConfig
|
||||
@Inject lateinit var mNormalBasalMgr: NormalBasalManager
|
||||
@Inject lateinit var mTempBasalMgr: TempBasalManager
|
||||
@Inject lateinit var mAlarms: Alarms
|
||||
|
||||
private var mPatchState = PatchState()
|
||||
private var mBolusCurrent = BolusCurrent()
|
||||
private lateinit var observePatchLifeCycle: Observable<PatchLifecycle>
|
||||
private var initialized = false
|
||||
|
||||
@Inject
|
||||
fun onInit() {
|
||||
observePatchLifeCycle = mPatchConfig.observe()
|
||||
.map { patchConfig -> patchConfig.lifecycleEvent.lifeCycle }
|
||||
.distinctUntilChanged()
|
||||
.replay(1).refCount()
|
||||
}
|
||||
|
||||
override fun getPatchConfig(): PatchConfig {
|
||||
return mPatchConfig
|
||||
}
|
||||
|
||||
override fun getPatchState(): PatchState {
|
||||
return mPatchState
|
||||
}
|
||||
|
||||
override fun getBolusCurrent(): BolusCurrent {
|
||||
return mBolusCurrent
|
||||
}
|
||||
|
||||
override fun getNormalBasalManager(): NormalBasalManager {
|
||||
return mNormalBasalMgr
|
||||
}
|
||||
|
||||
override fun getTempBasalManager(): TempBasalManager {
|
||||
return mTempBasalMgr
|
||||
}
|
||||
|
||||
override fun getAlarms(): Alarms {
|
||||
return mAlarms
|
||||
}
|
||||
|
||||
override fun init() {
|
||||
try {
|
||||
val jsonStr = sp.getString(SettingKeys.PATCH_STATE, "")
|
||||
val savedState = GsonHelper.sharedGson().fromJson(jsonStr, PatchState::class.java)
|
||||
mPatchState = savedState
|
||||
} catch (ex: Exception) {
|
||||
mPatchState = PatchState()
|
||||
aapsLogger.error(LTag.PUMP, ex.message?:"PatchState load error")
|
||||
}
|
||||
|
||||
try {
|
||||
val jsonStr = sp.getString(SettingKeys.BOLUS_CURRENT, "")
|
||||
val savedBolusCurrent = GsonHelper.sharedGson().fromJson(jsonStr, BolusCurrent::class.java)
|
||||
mBolusCurrent = savedBolusCurrent
|
||||
} catch (ex: Exception) {
|
||||
mBolusCurrent = BolusCurrent()
|
||||
aapsLogger.error(LTag.PUMP, ex.message?:"BolusCurrent load error")
|
||||
}
|
||||
|
||||
try {
|
||||
val jsonStr = sp.getString(SettingKeys.PATCH_CONFIG, "")
|
||||
val savedConfig = GsonHelper.sharedGson().fromJson(jsonStr, PatchConfig::class.java)
|
||||
mPatchConfig.update(savedConfig)
|
||||
} catch (ex: Exception) {
|
||||
aapsLogger.error(LTag.PUMP, ex.message?:"PatchConfig load error")
|
||||
}
|
||||
|
||||
try {
|
||||
val jsonStr = sp.getString(SettingKeys.NORMAL_BASAL, "")
|
||||
val normalBasalManager = GsonHelper.sharedGson().fromJson(jsonStr, NormalBasalManager::class.java)
|
||||
mNormalBasalMgr.update(normalBasalManager)
|
||||
} catch (ex: Exception) {
|
||||
aapsLogger.error(LTag.PUMP, ex.message?:"NormalBasal load error")
|
||||
}
|
||||
|
||||
try {
|
||||
val jsonStr = sp.getString(SettingKeys.TEMP_BASAL, "")
|
||||
val tempBasalManager = GsonHelper.sharedGson().fromJson(jsonStr, TempBasalManager::class.java)
|
||||
mTempBasalMgr.update(tempBasalManager)
|
||||
} catch (ex: Exception) {
|
||||
aapsLogger.error(LTag.PUMP, ex.message?:"TempBasal load error")
|
||||
}
|
||||
|
||||
try {
|
||||
val jsonStr = sp.getString(SettingKeys.ALARMS, "")
|
||||
val alarms = GsonHelper.sharedGson().fromJson(jsonStr, Alarms::class.java)
|
||||
mAlarms.update(alarms)
|
||||
} catch (ex: Exception) {
|
||||
aapsLogger.error(LTag.PUMP, ex.message?:"Alarms load error")
|
||||
}
|
||||
|
||||
aapsLogger.info(LTag.PUMP,"Load from PatchConfig preference: ${mPatchConfig}")
|
||||
aapsLogger.info(LTag.PUMP,"Load from PatchState preference: ${mPatchState}")
|
||||
aapsLogger.info(LTag.PUMP,"Load from BolusCurrent preference: ${mBolusCurrent}")
|
||||
aapsLogger.info(LTag.PUMP,"Load from NormalBasal preference: ${mNormalBasalMgr}")
|
||||
aapsLogger.info(LTag.PUMP,"Load from TempBasal preference: ${mTempBasalMgr}")
|
||||
aapsLogger.info(LTag.PUMP,"Load from Alarms preference: ${mAlarms}")
|
||||
initialized = true
|
||||
}
|
||||
|
||||
override fun isInitDone() = initialized
|
||||
|
||||
override fun flushPatchConfig() = mPatchConfig.flush(sp)
|
||||
override fun flushPatchState() = mPatchState.flush(sp)
|
||||
override fun flushBolusCurrent() = mBolusCurrent.flush(sp)
|
||||
override fun flushNormalBasalManager() = mNormalBasalMgr.flush(sp)
|
||||
override fun flushTempBasalManager() = mTempBasalMgr.flush(sp)
|
||||
override fun flushAlarms() = mAlarms.flush(sp)
|
||||
|
||||
@Synchronized
|
||||
override fun updatePatchLifeCycle(event: PatchLifecycleEvent) {
|
||||
mPatchConfig.updateLifecycle(event)
|
||||
flushPatchConfig()
|
||||
|
||||
when (event.lifeCycle) {
|
||||
PatchLifecycle.SHUTDOWN -> {
|
||||
mPatchState.clear()
|
||||
flushPatchState()
|
||||
mBolusCurrent.clearAll()
|
||||
flushBolusCurrent()
|
||||
mTempBasalMgr.clear()
|
||||
flushTempBasalManager()
|
||||
// mAlarms.clear()
|
||||
// flushAlarms()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun updatePatchState(newState: PatchState) {
|
||||
mPatchState = newState
|
||||
flushPatchState()
|
||||
}
|
||||
|
||||
override fun getPatchSerial(): String {
|
||||
return mPatchConfig.patchSerialNumber
|
||||
}
|
||||
|
||||
override fun getPatchMac(): String? {
|
||||
return mPatchConfig.macAddress
|
||||
}
|
||||
|
||||
override fun isActivated(): Boolean {
|
||||
return mPatchConfig.isActivated
|
||||
}
|
||||
|
||||
override fun setMacAddress(mac: String) {
|
||||
mPatchConfig.macAddress = mac
|
||||
flushPatchConfig()
|
||||
}
|
||||
|
||||
override fun getPatchExpiredTime(): Long {
|
||||
return mPatchConfig.getPatchExpiredTime()
|
||||
}
|
||||
|
||||
override fun setSharedKey(bytes: ByteArray?) {
|
||||
mPatchConfig.sharedKey = bytes
|
||||
}
|
||||
|
||||
override fun setSeq15(seq15: Int) {
|
||||
mPatchConfig.seq15 = seq15
|
||||
}
|
||||
|
||||
override fun getSeq15(): Int {
|
||||
return mPatchConfig.seq15
|
||||
}
|
||||
|
||||
override fun increaseSeq15() {
|
||||
mPatchConfig.incSeq()
|
||||
}
|
||||
|
||||
override fun getPatchWakeupTimestamp(): Long {
|
||||
return mPatchConfig.patchWakeupTimestamp
|
||||
}
|
||||
|
||||
override fun observePatchLifeCycle(): Observable<PatchLifecycle> {
|
||||
return observePatchLifeCycle
|
||||
}
|
||||
|
||||
override fun observePatchConfig(): Observable<PatchConfig> {
|
||||
return mPatchConfig.observe()
|
||||
}
|
||||
|
||||
override fun observePatchState(): Observable<PatchState> {
|
||||
return mPatchState.observe()
|
||||
}
|
||||
|
||||
override fun observeBolusCurrent(): Observable<BolusCurrent>{
|
||||
return mBolusCurrent.observe()
|
||||
}
|
||||
|
||||
override fun observeAlarm(): Observable<Alarms> {
|
||||
return mAlarms.observe()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.ble.task;
|
||||
|
||||
import info.nightscout.shared.logging.LTag;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.api.BasalScheduleSetBig;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.api.SetKey;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.response.BaseResponse;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.vo.NormalBasal;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.vo.PatchLifecycleEvent;
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
|
||||
@Singleton
|
||||
public class ActivateTask extends TaskBase {
|
||||
@Inject StartNormalBasalTask startBasalTask;
|
||||
|
||||
private SetKey SET_KEY;
|
||||
private BasalScheduleSetBig BASAL_SCHEDULE_SET_BIG;
|
||||
|
||||
|
||||
@Inject
|
||||
public ActivateTask() {
|
||||
super(TaskFunc.ACTIVATE);
|
||||
SET_KEY = new SetKey();
|
||||
BASAL_SCHEDULE_SET_BIG = new BasalScheduleSetBig();
|
||||
}
|
||||
|
||||
public Single<Boolean> start() {
|
||||
NormalBasal enabled = pm.getNormalBasalManager().getNormalBasal();
|
||||
return isReady()
|
||||
.concatMapSingle(v -> SET_KEY.setKey())
|
||||
.doOnNext(this::checkResponse)
|
||||
.firstOrError()
|
||||
.observeOn(Schedulers.io()).doOnSuccess(this::onActivated)
|
||||
.flatMap(v -> startBasalTask.start(enabled, false))
|
||||
.map(BaseResponse::isSuccess)
|
||||
.doOnError(e -> aapsLogger.error(LTag.PUMPCOMM, e.getMessage()));
|
||||
}
|
||||
|
||||
private void onActivated(BaseResponse response) {
|
||||
pm.updatePatchLifeCycle(PatchLifecycleEvent.createActivated());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.ble.task;
|
||||
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.code.BolusExDuration;
|
||||
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.AppConstant;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.code.BolusType;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.vo.BolusCurrent;
|
||||
|
||||
abstract class BolusTask extends TaskBase {
|
||||
|
||||
public BolusTask(TaskFunc func) {
|
||||
super(func);
|
||||
}
|
||||
|
||||
public void onQuickBolusStarted(float nowDoseU, float exDoseU, BolusExDuration exDuration) {
|
||||
boolean now = (nowDoseU > 0);
|
||||
boolean ext = (exDoseU > 0);
|
||||
|
||||
long startTimestamp = now ? System.currentTimeMillis() : 0; // dm_1720
|
||||
long endTimestamp = startTimestamp + getPumpDuration(nowDoseU);
|
||||
|
||||
long nowHistoryID = 1L; //record no
|
||||
long exStartTimestamp = 0;
|
||||
|
||||
if (now) {
|
||||
pm.getBolusCurrent().startNowBolus(nowHistoryID, nowDoseU, startTimestamp, endTimestamp);
|
||||
}
|
||||
if (ext) {
|
||||
long estimatedExStartTimestamp = 0;
|
||||
|
||||
if (now) {
|
||||
exStartTimestamp = 0;
|
||||
}
|
||||
else {
|
||||
estimatedExStartTimestamp = System.currentTimeMillis();
|
||||
exStartTimestamp = estimatedExStartTimestamp;
|
||||
}
|
||||
long exEndTimestamp = exStartTimestamp + exDuration.milli();
|
||||
|
||||
long extHistoryID = 2L; //record no
|
||||
pm.getBolusCurrent().startExtBolus(extHistoryID, exDoseU, exStartTimestamp,
|
||||
exEndTimestamp, exDuration.milli());
|
||||
}
|
||||
|
||||
pm.flushBolusCurrent();
|
||||
}
|
||||
|
||||
|
||||
public void onCalcBolusStarted(float nowDoseU, float correctionBolus, float exDoseU,
|
||||
BolusExDuration exDuration) {
|
||||
|
||||
boolean now = (nowDoseU > 0);
|
||||
boolean ext = (exDoseU > 0);
|
||||
|
||||
|
||||
long startTimestamp = now ? System.currentTimeMillis() : 0; // dm_1720
|
||||
long endTimestamp = startTimestamp + getPumpDuration(nowDoseU);
|
||||
|
||||
long nowHistoryID = 1L; //record no
|
||||
|
||||
if (now) {
|
||||
pm.getBolusCurrent().startNowBolus(nowHistoryID, nowDoseU, startTimestamp, endTimestamp);
|
||||
}
|
||||
|
||||
long exStartTimestamp = 0;
|
||||
|
||||
if (ext) {
|
||||
long estimatedExStartTimestamp = 0;
|
||||
|
||||
if (now) {
|
||||
exStartTimestamp = 0;
|
||||
}
|
||||
else {
|
||||
estimatedExStartTimestamp = System.currentTimeMillis();
|
||||
exStartTimestamp = estimatedExStartTimestamp;
|
||||
}
|
||||
long exEndTimestamp = exStartTimestamp + exDuration.milli();
|
||||
long extHistoryID = 2L; //record no
|
||||
pm.getBolusCurrent().startExtBolus(extHistoryID, exDoseU, exStartTimestamp,
|
||||
exEndTimestamp, exDuration.milli());
|
||||
}
|
||||
|
||||
pm.flushBolusCurrent();
|
||||
}
|
||||
|
||||
public void updateNowBolusStopped(int injected) {
|
||||
updateNowBolusStopped(injected, 0);
|
||||
}
|
||||
|
||||
public void updateNowBolusStopped(int injected, long suspendedTimestamp) {
|
||||
|
||||
BolusCurrent bolusCurrent = pm.getBolusCurrent();
|
||||
long nowID = bolusCurrent.historyId(BolusType.NOW);
|
||||
if (nowID > 0 && !bolusCurrent.endTimeSynced(BolusType.NOW)) {
|
||||
// long stopTime = (suspendedTimestamp > 0) ? suspendedTimestamp : System.currentTimeMillis();
|
||||
// float injectedDoseU = FloatAdjusters.INSTANCE.getFLOOR2_BOLUS().apply(injected * AppConstant.INSULIN_UNIT_P);
|
||||
bolusCurrent.setEndTimeSynced(BolusType.NOW, true);
|
||||
pm.flushBolusCurrent();
|
||||
}
|
||||
}
|
||||
|
||||
public void updateExtBolusStopped(int injected) {
|
||||
updateExtBolusStopped(injected, 0);
|
||||
}
|
||||
|
||||
public void updateExtBolusStopped(int injected, long suspendedTimestamp) {
|
||||
BolusCurrent bolusCurrent = pm.getBolusCurrent();
|
||||
long extID = bolusCurrent.historyId(BolusType.EXT);
|
||||
if (extID > 0 && !bolusCurrent.endTimeSynced(BolusType.EXT)) {
|
||||
// long stopTime = (suspendedTimestamp > 0) ? suspendedTimestamp : System.currentTimeMillis();
|
||||
// float injectedDoseU = FloatAdjusters.INSTANCE.getFLOOR2_BOLUS().apply(injected * AppConstant.INSULIN_UNIT_P);
|
||||
bolusCurrent.setEndTimeSynced(BolusType.EXT, true);
|
||||
pm.flushBolusCurrent();
|
||||
}
|
||||
}
|
||||
|
||||
private long getPumpDuration(float doseU) {
|
||||
if (doseU > 0) {
|
||||
long pumpDuration = pm.getPatchConfig().getPumpDurationSmallMilli(); //todo
|
||||
return (long) ((doseU / AppConstant.Companion.getBOLUS_UNIT_STEP()) * pumpDuration);
|
||||
}
|
||||
return 0L;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,132 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.ble.task;
|
||||
|
||||
import info.nightscout.shared.logging.LTag;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ble.IPreferenceManager;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.code.BolusType;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.code.DeactivationStatus;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.api.DeActivation;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.vo.BolusCurrent;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.vo.PatchLifecycleEvent;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.vo.PatchState;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.vo.TempBasal;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
|
||||
@Singleton
|
||||
public class DeactivateTask extends TaskBase {
|
||||
|
||||
@Inject
|
||||
StopBasalTask stopBasalTask;
|
||||
|
||||
@Inject
|
||||
IPreferenceManager pm;
|
||||
|
||||
private DeActivation DEACTIVATION;
|
||||
|
||||
@Inject
|
||||
public DeactivateTask() {
|
||||
super(TaskFunc.DEACTIVATE);
|
||||
DEACTIVATION = new DeActivation();
|
||||
}
|
||||
|
||||
public Single<DeactivationStatus> run(boolean forced, long timeout) {
|
||||
return isReadyCheckActivated()
|
||||
.timeout(timeout, TimeUnit.MILLISECONDS)
|
||||
.concatMapSingle(v ->
|
||||
DEACTIVATION.start()
|
||||
.doOnSuccess(this::checkResponse)
|
||||
.observeOn(Schedulers.io())
|
||||
.doOnSuccess(response -> onDeactivated(false)))
|
||||
.map(response -> DeactivationStatus.of(response.isSuccess(), forced))
|
||||
.firstOrError()
|
||||
.doOnError(e -> aapsLogger.error(LTag.PUMPCOMM, e.getMessage()))
|
||||
.onErrorResumeNext(e -> {
|
||||
if (forced) {
|
||||
try {
|
||||
onDeactivated(true);
|
||||
} catch (Exception t) {
|
||||
aapsLogger.error(LTag.PUMPCOMM, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return Single.just(DeactivationStatus.of(false, forced));
|
||||
});
|
||||
}
|
||||
|
||||
private Observable<TaskFunc> isReadyCheckActivated() {
|
||||
if (pm.getPatchConfig().isActivated()) {
|
||||
enqueue(TaskFunc.UPDATE_CONNECTION);
|
||||
|
||||
stopBasalTask.enqueue();
|
||||
|
||||
return isReady2();
|
||||
}
|
||||
|
||||
return isReady();
|
||||
}
|
||||
|
||||
/* Schedulers.io() */
|
||||
private void onDeactivated(boolean forced) throws SQLException {
|
||||
synchronized (lock) {
|
||||
patch.updateMacAddress(null, false);
|
||||
|
||||
if (pm.getPatchConfig().getLifecycleEvent().isShutdown()) {
|
||||
return;
|
||||
}
|
||||
|
||||
cleanUpRepository();
|
||||
|
||||
pm.getNormalBasalManager().updateForDeactivation();
|
||||
|
||||
pm.updatePatchLifeCycle(PatchLifecycleEvent.createShutdown());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void cleanUpRepository() throws SQLException {
|
||||
updateNowBolusStopped();
|
||||
updateExtBolusStopped();
|
||||
updateTempBasalStopped();
|
||||
}
|
||||
|
||||
private void updateTempBasalStopped() throws SQLException {
|
||||
TempBasal tempBasal = pm.getTempBasalManager().getStartedBasal();
|
||||
|
||||
if (tempBasal != null) {
|
||||
pm.getTempBasalManager().updateBasalStopped();
|
||||
pm.flushTempBasalManager();
|
||||
}
|
||||
}
|
||||
|
||||
/* copied from BolusTask. */
|
||||
private void updateNowBolusStopped() {
|
||||
BolusCurrent bolusCurrent = pm.getBolusCurrent();
|
||||
long nowID = bolusCurrent.historyId(BolusType.NOW);
|
||||
|
||||
if (nowID > 0 && !bolusCurrent.endTimeSynced(BolusType.NOW)) {
|
||||
bolusCurrent.setEndTimeSynced(BolusType.NOW, true);
|
||||
pm.flushBolusCurrent();
|
||||
}
|
||||
}
|
||||
|
||||
/* copied from BolusTask. */
|
||||
private void updateExtBolusStopped() {
|
||||
BolusCurrent bolusCurrent = pm.getBolusCurrent();
|
||||
long extID = bolusCurrent.historyId(BolusType.EXT);
|
||||
|
||||
if (extID > 0 && !bolusCurrent.endTimeSynced(BolusType.EXT)) {
|
||||
bolusCurrent.setEndTimeSynced(BolusType.EXT, true);
|
||||
pm.flushBolusCurrent();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.ble.task;
|
||||
|
||||
import info.nightscout.shared.logging.LTag;
|
||||
import info.nightscout.androidaps.plugins.bus.RxBus;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.alarm.AlarmCode;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.alarm.IAlarmRegistry;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.api.GetErrorCodes;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.response.AeCodeResponse;
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
|
||||
@Singleton
|
||||
public class FetchAlarmTask extends TaskBase {
|
||||
|
||||
@Inject
|
||||
RxBus rxBus;
|
||||
|
||||
@Inject
|
||||
IAlarmRegistry alarmRegistry;
|
||||
|
||||
private GetErrorCodes ALARM_ALERT_ERROR_CODE_GET;
|
||||
|
||||
@Inject
|
||||
public FetchAlarmTask() {
|
||||
super(TaskFunc.FETCH_ALARM);
|
||||
ALARM_ALERT_ERROR_CODE_GET = new GetErrorCodes();
|
||||
}
|
||||
|
||||
public Single<AeCodeResponse> getPatchAlarm() {
|
||||
return isReady()
|
||||
.concatMapSingle(v -> ALARM_ALERT_ERROR_CODE_GET.get())
|
||||
.doOnNext(this::checkResponse)
|
||||
.firstOrError()
|
||||
.doOnSuccess(aeCodeResponse -> alarmRegistry.add(aeCodeResponse.getAlarmCodes()))
|
||||
.doOnError(e -> aapsLogger.error(LTag.PUMPCOMM, e.getMessage()));
|
||||
}
|
||||
|
||||
public synchronized void enqueue() {
|
||||
boolean ready = (disposable == null || disposable.isDisposed());
|
||||
|
||||
if (ready) {
|
||||
disposable = getPatchAlarm()
|
||||
.timeout(TASK_ENQUEUE_TIME_OUT, TimeUnit.SECONDS)
|
||||
.subscribe();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.ble.task;
|
||||
|
||||
import info.nightscout.shared.logging.LTag;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.api.GetFirmwareVersion;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.api.GetLOT;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.api.GetModelName;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.api.GetPumpDuration;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.api.GetSerialNumber;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.api.GetWakeUpTime;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.api.SetGlobalTime;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.response.BaseResponse;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.response.FirmwareVersionResponse;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.response.LotNumberResponse;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.response.ModelNameResponse;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.response.PumpDurationResponse;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.response.SerialNumberResponse;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.response.WakeUpTimeResponse;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
|
||||
@Singleton
|
||||
public class GetPatchInfoTask extends TaskBase {
|
||||
|
||||
@Inject
|
||||
UpdateConnectionTask updateConnectionTask;
|
||||
|
||||
private SetGlobalTime SET_GLOBAL_TIME;
|
||||
private GetSerialNumber SERIAL_NUMBER_GET;
|
||||
private GetLOT LOT_NUMBER_GET;
|
||||
private GetFirmwareVersion FIRMWARE_VERSION_GET;
|
||||
private GetWakeUpTime WAKE_UP_TIME_GET;
|
||||
private GetPumpDuration PUMP_DURATION_GET;
|
||||
private GetModelName GET_MODEL_NAME;
|
||||
|
||||
@Inject
|
||||
public GetPatchInfoTask() {
|
||||
super(TaskFunc.GET_PATCH_INFO);
|
||||
|
||||
SET_GLOBAL_TIME = new SetGlobalTime();
|
||||
SERIAL_NUMBER_GET = new GetSerialNumber();
|
||||
LOT_NUMBER_GET = new GetLOT();
|
||||
FIRMWARE_VERSION_GET = new GetFirmwareVersion();
|
||||
WAKE_UP_TIME_GET = new GetWakeUpTime();
|
||||
PUMP_DURATION_GET = new GetPumpDuration();
|
||||
GET_MODEL_NAME = new GetModelName();
|
||||
}
|
||||
|
||||
public Single<Boolean> get() {
|
||||
Single<Boolean> tasks = Single.concat(Arrays.asList(
|
||||
SET_GLOBAL_TIME.set(),
|
||||
SERIAL_NUMBER_GET.get().doOnSuccess(this::onSerialNumberResponse),
|
||||
LOT_NUMBER_GET.get().doOnSuccess(this::onLotNumberResponse),
|
||||
FIRMWARE_VERSION_GET.get().doOnSuccess(this::onFirmwareResponse),
|
||||
WAKE_UP_TIME_GET.get().doOnSuccess(this::onWakeupTimeResponse),
|
||||
PUMP_DURATION_GET.get().doOnSuccess(this::onPumpDurationResponse),
|
||||
GET_MODEL_NAME.get().doOnSuccess(this::onModelNameResponse)))
|
||||
.map(BaseResponse::isSuccess)
|
||||
.filter(v -> !v) // fail 시 false 가 아래로 내려간다.
|
||||
.first(true);
|
||||
|
||||
return isReady()
|
||||
.concatMapSingle(it -> tasks)
|
||||
.firstOrError()
|
||||
// .flatMap(v -> updateConnectionTask.update()).map(v -> true)
|
||||
.observeOn(Schedulers.io())
|
||||
.doOnSuccess(this::onPatchWakeupSuccess)
|
||||
.doOnError(this::onPatchWakeupFailed)
|
||||
.doOnError(e -> aapsLogger.error(LTag.PUMPCOMM, e.getMessage()));
|
||||
}
|
||||
|
||||
private void onSerialNumberResponse(SerialNumberResponse v) {
|
||||
pm.getPatchConfig().setPatchSerialNumber(v.getSerialNumber());
|
||||
}
|
||||
|
||||
private void onLotNumberResponse(LotNumberResponse v) {
|
||||
pm.getPatchConfig().setPatchLotNumber(v.getLotNumber());
|
||||
}
|
||||
|
||||
private void onFirmwareResponse(FirmwareVersionResponse v) {
|
||||
pm.getPatchConfig().setPatchFirmwareVersion(v.getFirmwareVersionString());
|
||||
}
|
||||
|
||||
private void onWakeupTimeResponse(WakeUpTimeResponse v) {
|
||||
pm.getPatchConfig().setPatchWakeupTimestamp(v.getTimeInMillis());
|
||||
}
|
||||
|
||||
private void onPumpDurationResponse(PumpDurationResponse v) {
|
||||
pm.getPatchConfig().setPumpDurationLargeMilli(v.getDurationL() * 100); // 0.1 초 단위
|
||||
pm.getPatchConfig().setPumpDurationMediumMilli(v.getDurationM() * 100);
|
||||
pm.getPatchConfig().setPumpDurationSmallMilli(v.getDurationS() * 100);
|
||||
}
|
||||
|
||||
private void onModelNameResponse(ModelNameResponse modelNameResponse) {
|
||||
pm.getPatchConfig().setPatchModelName(modelNameResponse.getModelName());
|
||||
}
|
||||
|
||||
/* Schedulers.io() */
|
||||
private void onPatchWakeupSuccess(Boolean result) {
|
||||
synchronized (lock) {
|
||||
pm.flushPatchConfig();
|
||||
}
|
||||
}
|
||||
|
||||
/* Schedulers.io() */
|
||||
private void onPatchWakeupFailed(Throwable e) {
|
||||
patch.setSeq(-1);
|
||||
pm.getPatchConfig().updateDeactivated();
|
||||
pm.flushPatchConfig();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.ble.task;
|
||||
|
||||
import info.nightscout.shared.logging.LTag;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ble.IPreferenceManager;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.api.InfoReminderSet;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.response.PatchBooleanResponse;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import io.reactivex.Single;
|
||||
|
||||
@Singleton
|
||||
public class InfoReminderTask extends TaskBase {
|
||||
|
||||
@Inject
|
||||
IPreferenceManager pm;
|
||||
|
||||
private InfoReminderSet INFO_REMINDER_SET;
|
||||
|
||||
@Inject
|
||||
public InfoReminderTask() {
|
||||
super(TaskFunc.INFO_REMINDER);
|
||||
INFO_REMINDER_SET = new InfoReminderSet();
|
||||
}
|
||||
|
||||
/* alert delay 사용안함 */
|
||||
public Single<PatchBooleanResponse> set(boolean infoReminder) {
|
||||
return isReady()
|
||||
.concatMapSingle(v -> INFO_REMINDER_SET.set(infoReminder))
|
||||
.doOnNext(this::checkResponse)
|
||||
.firstOrError()
|
||||
.doOnError(e -> aapsLogger.error(LTag.PUMPCOMM, e.getMessage()));
|
||||
}
|
||||
|
||||
public synchronized void enqueue() {
|
||||
|
||||
boolean ready = (disposable == null || disposable.isDisposed());
|
||||
|
||||
if (ready) {
|
||||
disposable = set(pm.getPatchConfig().getInfoReminder())
|
||||
.timeout(TASK_ENQUEUE_TIME_OUT, TimeUnit.SECONDS)
|
||||
.subscribe();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,183 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.ble.task;
|
||||
|
||||
import android.os.SystemClock;
|
||||
|
||||
import info.nightscout.androidaps.interfaces.PumpSync;
|
||||
import info.nightscout.androidaps.logging.UserEntryLogger;
|
||||
import info.nightscout.androidaps.utils.userEntry.UserEntryMapper;
|
||||
import info.nightscout.shared.logging.LTag;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.define.IPatchConstant;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import info.nightscout.androidaps.interfaces.CommandQueue;
|
||||
import info.nightscout.shared.logging.AAPSLogger;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.api.BolusStop;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.api.GetInternalSuspendTime;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.api.TempBasalScheduleStop;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.response.PatchInternalSuspendTimeResponse;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.vo.PatchState;
|
||||
import info.nightscout.androidaps.queue.Callback;
|
||||
import info.nightscout.androidaps.queue.commands.Command;
|
||||
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.subjects.BehaviorSubject;
|
||||
|
||||
@Singleton
|
||||
public class InternalSuspendedTask extends BolusTask {
|
||||
|
||||
@Inject CommandQueue commandQueue;
|
||||
@Inject AAPSLogger aapsLogger;
|
||||
@Inject PumpSync pumpSync;
|
||||
@Inject UserEntryLogger uel;
|
||||
|
||||
private GetInternalSuspendTime INTERNAL_SUSPEND_TIME_GET;
|
||||
private BolusStop BOLUS_STOP;
|
||||
private TempBasalScheduleStop TEMP_BASAL_SCHEDULE_STOP;
|
||||
private BehaviorSubject<Boolean> bolusCheckSubject = BehaviorSubject.create();
|
||||
private BehaviorSubject<Boolean> exbolusCheckSubject = BehaviorSubject.create();
|
||||
private BehaviorSubject<Boolean> basalCheckSubject = BehaviorSubject.create();
|
||||
|
||||
@Inject
|
||||
public InternalSuspendedTask() {
|
||||
super(TaskFunc.INTERNAL_SUSPEND);
|
||||
|
||||
INTERNAL_SUSPEND_TIME_GET = new GetInternalSuspendTime();
|
||||
BOLUS_STOP = new BolusStop();
|
||||
TEMP_BASAL_SCHEDULE_STOP = new TempBasalScheduleStop();
|
||||
}
|
||||
|
||||
private Observable<Boolean> getBolusSebject(){
|
||||
return bolusCheckSubject.hide();
|
||||
}
|
||||
|
||||
private Observable<Boolean> getExbolusSebject(){
|
||||
return exbolusCheckSubject.hide();
|
||||
}
|
||||
|
||||
private Observable<Boolean> getBasalSebject(){
|
||||
return basalCheckSubject.hide();
|
||||
}
|
||||
|
||||
public Single<Long> start(boolean isNowBolusActive, boolean isExtBolusActive, boolean isTempBasalActive) {
|
||||
PatchState patchState = pm.getPatchState();
|
||||
|
||||
if (isNowBolusActive || isExtBolusActive) {
|
||||
enqueue(TaskFunc.READ_BOLUS_FINISH_TIME);
|
||||
}
|
||||
|
||||
if (commandQueue.isRunning(Command.CommandType.BOLUS)) {
|
||||
uel.log(UserEntryMapper.Action.CANCEL_BOLUS, UserEntryMapper.Sources.EOPatch2);
|
||||
commandQueue.cancelAllBoluses();
|
||||
SystemClock.sleep(650);
|
||||
}
|
||||
bolusCheckSubject.onNext(true);
|
||||
|
||||
if (pumpSync.expectedPumpState().getExtendedBolus() != null) {
|
||||
uel.log(UserEntryMapper.Action.CANCEL_EXTENDED_BOLUS, UserEntryMapper.Sources.EOPatch2);
|
||||
commandQueue.cancelExtended(new Callback() {
|
||||
@Override
|
||||
public void run() {
|
||||
exbolusCheckSubject.onNext(true);
|
||||
}
|
||||
});
|
||||
}else{
|
||||
exbolusCheckSubject.onNext(true);
|
||||
}
|
||||
|
||||
if (pumpSync.expectedPumpState().getTemporaryBasal() != null) {
|
||||
uel.log(UserEntryMapper.Action.CANCEL_TEMP_BASAL, UserEntryMapper.Sources.EOPatch2);
|
||||
commandQueue.cancelTempBasal(true, new Callback() {
|
||||
@Override
|
||||
public void run() {
|
||||
basalCheckSubject.onNext(true);
|
||||
}
|
||||
});
|
||||
}else{
|
||||
basalCheckSubject.onNext(true);
|
||||
}
|
||||
|
||||
return Observable.zip(getBolusSebject(), getExbolusSebject(), getBasalSebject(), (bolusReady, exbolusReady, basalReady) -> {
|
||||
return (bolusReady && exbolusReady && basalReady);
|
||||
})
|
||||
.filter(ready -> ready)
|
||||
.flatMap(v -> isReady())
|
||||
.concatMapSingle(v -> getInternalSuspendTime())
|
||||
.firstOrError()
|
||||
.doOnError(e -> aapsLogger.error(LTag.PUMPCOMM, e.getMessage()));
|
||||
}
|
||||
|
||||
private Single<Long> getInternalSuspendTime() {
|
||||
return INTERNAL_SUSPEND_TIME_GET.get()
|
||||
.doOnSuccess(this::checkResponse)
|
||||
.map(PatchInternalSuspendTimeResponse::getTotalSeconds);
|
||||
}
|
||||
|
||||
private Single<Long> stopNowBolus(long suspendTime, boolean isNowBolusActive) {
|
||||
if (isNowBolusActive) {
|
||||
long suspendedTimestamp = pm.getPatchConfig().getPatchWakeupTimestamp() + suspendTime;
|
||||
|
||||
return BOLUS_STOP.stop(IPatchConstant.NOW_BOLUS_ID)
|
||||
.doOnSuccess(this::checkResponse)
|
||||
.doOnSuccess(v -> onNowBolusStopped(v.getInjectedBolusAmount(), suspendedTimestamp))
|
||||
.map(v -> suspendTime);
|
||||
}
|
||||
|
||||
return Single.just(suspendTime);
|
||||
}
|
||||
|
||||
private Single<Long> stopExtBolus(long suspendTime, boolean isExtBolusActive) {
|
||||
if (isExtBolusActive) {
|
||||
long suspendedTimestamp = pm.getPatchConfig().getPatchWakeupTimestamp() + suspendTime;
|
||||
|
||||
return BOLUS_STOP.stop(IPatchConstant.EXT_BOLUS_ID)
|
||||
.doOnSuccess(this::checkResponse)
|
||||
.doOnSuccess(v -> onExtBolusStopped(v.getInjectedBolusAmount(), suspendedTimestamp))
|
||||
.map(v -> suspendTime);
|
||||
}
|
||||
|
||||
return Single.just(suspendTime);
|
||||
}
|
||||
|
||||
private Single<Long> stopTempBasal(long suspendTime, boolean isTempBasalActive) {
|
||||
if (isTempBasalActive) {
|
||||
return TEMP_BASAL_SCHEDULE_STOP.stop()
|
||||
.doOnSuccess(this::checkResponse)
|
||||
.doOnSuccess(v -> onTempBasalCanceled())
|
||||
.map(v -> suspendTime);
|
||||
}
|
||||
|
||||
return Single.just(suspendTime);
|
||||
}
|
||||
|
||||
private void onNowBolusStopped(int injectedBolusAmount, long suspendedTimestamp) {
|
||||
updateNowBolusStopped(injectedBolusAmount, suspendedTimestamp);
|
||||
}
|
||||
|
||||
private void onExtBolusStopped(int injectedBolusAmount, long suspendedTimestamp) {
|
||||
updateExtBolusStopped(injectedBolusAmount, suspendedTimestamp);
|
||||
}
|
||||
|
||||
private void onTempBasalCanceled() {
|
||||
pm.getTempBasalManager().updateBasalStopped();
|
||||
pm.flushTempBasalManager();
|
||||
}
|
||||
|
||||
public synchronized void enqueue(boolean isNowBolusActive, boolean isExtBolusActive, boolean isTempBasalActive) {
|
||||
boolean ready = (disposable == null || disposable.isDisposed());
|
||||
|
||||
if (ready) {
|
||||
disposable = start(isNowBolusActive, isExtBolusActive, isTempBasalActive)
|
||||
.timeout(TASK_ENQUEUE_TIME_OUT, TimeUnit.SECONDS)
|
||||
.subscribe(v -> {
|
||||
bolusCheckSubject.onNext(false);
|
||||
exbolusCheckSubject.onNext(false);
|
||||
basalCheckSubject.onNext(false);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.ble.task;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.alarm.AlarmCode;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.alarm.IAlarmRegistry;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.api.StartNeedleCheck;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.api.UpdateConnection;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.vo.PatchState;
|
||||
import info.nightscout.shared.logging.LTag;
|
||||
import io.reactivex.Single;
|
||||
|
||||
@Singleton
|
||||
public class NeedleSensingTask extends TaskBase {
|
||||
|
||||
@Inject
|
||||
IAlarmRegistry alarmRegistry;
|
||||
|
||||
StartNeedleCheck START_NEEDLE_CHECK;
|
||||
UpdateConnection UPDATE_CONNECTION;
|
||||
|
||||
@Inject
|
||||
public NeedleSensingTask() {
|
||||
super(TaskFunc.NEEDLE_SENSING);
|
||||
START_NEEDLE_CHECK = new StartNeedleCheck();
|
||||
UPDATE_CONNECTION = new UpdateConnection();
|
||||
}
|
||||
|
||||
public Single<Boolean> start() {
|
||||
|
||||
return isReady()
|
||||
.concatMapSingle(v -> START_NEEDLE_CHECK.start())
|
||||
.doOnNext(this::checkResponse)
|
||||
.concatMapSingle(v -> UPDATE_CONNECTION.get())
|
||||
.doOnNext(this::checkResponse)
|
||||
.map(updateConnectionResponse -> PatchState.Companion.create(updateConnectionResponse.getPatchState(), System.currentTimeMillis()))
|
||||
.doOnNext(this::onResponse)
|
||||
.map(patchState -> !patchState.isNeedNeedleSensing())
|
||||
.firstOrError()
|
||||
.doOnError(e -> aapsLogger.error(LTag.PUMPCOMM, e.getMessage()));
|
||||
}
|
||||
|
||||
private void onResponse(PatchState v) {
|
||||
if (v.isNeedNeedleSensing()) {
|
||||
alarmRegistry.add(AlarmCode.A016, 0, false).subscribe();
|
||||
} else {
|
||||
alarmRegistry.remove(AlarmCode.A016).subscribe();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,225 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.ble.task;
|
||||
|
||||
|
||||
import android.os.SystemClock;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import info.nightscout.androidaps.interfaces.CommandQueue;
|
||||
import info.nightscout.androidaps.interfaces.PumpSync;
|
||||
import info.nightscout.androidaps.logging.UserEntryLogger;
|
||||
import info.nightscout.androidaps.utils.userEntry.UserEntryMapper;
|
||||
import info.nightscout.shared.logging.AAPSLogger;
|
||||
import info.nightscout.shared.logging.LTag;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.alarm.AlarmCode;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.alarm.IAlarmRegistry;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ble.IPreferenceManager;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.define.IPatchConstant;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.vo.PatchState;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.api.BasalPause;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.api.BolusStop;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.api.TempBasalScheduleStop;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.response.PatchBooleanResponse;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.vo.TempBasal;
|
||||
import info.nightscout.androidaps.queue.Callback;
|
||||
import info.nightscout.androidaps.queue.commands.Command;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.subjects.BehaviorSubject;
|
||||
|
||||
@Singleton
|
||||
public class PauseBasalTask extends BolusTask {
|
||||
@Inject IAlarmRegistry alarmRegistry;
|
||||
@Inject IPreferenceManager pm;
|
||||
@Inject CommandQueue commandQueue;
|
||||
@Inject AAPSLogger aapsLogger;
|
||||
@Inject PumpSync pumpSync;
|
||||
@Inject UserEntryLogger uel;
|
||||
|
||||
private BasalPause BASAL_PAUSE;
|
||||
private BolusStop BOLUS_STOP;
|
||||
private TempBasalScheduleStop TEMP_BASAL_SCHEDULE_STOP;
|
||||
|
||||
private BehaviorSubject<Boolean> bolusCheckSubject = BehaviorSubject.create();
|
||||
private BehaviorSubject<Boolean> exbolusCheckSubject = BehaviorSubject.create();
|
||||
private BehaviorSubject<Boolean> basalCheckSubject = BehaviorSubject.create();
|
||||
|
||||
|
||||
@Inject
|
||||
public PauseBasalTask() {
|
||||
super(TaskFunc.PAUSE_BASAL);
|
||||
|
||||
BASAL_PAUSE = new BasalPause();
|
||||
BOLUS_STOP = new BolusStop();
|
||||
TEMP_BASAL_SCHEDULE_STOP = new TempBasalScheduleStop();
|
||||
}
|
||||
|
||||
private Observable<Boolean> getBolusSebject(){
|
||||
return bolusCheckSubject.hide();
|
||||
}
|
||||
|
||||
private Observable<Boolean> getExbolusSebject(){
|
||||
return exbolusCheckSubject.hide();
|
||||
}
|
||||
|
||||
private Observable<Boolean> getBasalSebject(){
|
||||
return basalCheckSubject.hide();
|
||||
}
|
||||
|
||||
public Single<PatchBooleanResponse> pause(float pauseDurationHour, long pausedTimestamp, @Nullable AlarmCode alarmCode) {
|
||||
PatchState patchState = pm.getPatchState();
|
||||
|
||||
if(patchState.isNormalBasalPaused())
|
||||
return Single.just(new PatchBooleanResponse(true));
|
||||
|
||||
enqueue(TaskFunc.UPDATE_CONNECTION);
|
||||
|
||||
if (commandQueue.isRunning(Command.CommandType.BOLUS)) {
|
||||
uel.log(UserEntryMapper.Action.CANCEL_BOLUS, UserEntryMapper.Sources.EOPatch2);
|
||||
commandQueue.cancelAllBoluses();
|
||||
SystemClock.sleep(650);
|
||||
}
|
||||
bolusCheckSubject.onNext(true);
|
||||
|
||||
if (pumpSync.expectedPumpState().getExtendedBolus() != null) {
|
||||
uel.log(UserEntryMapper.Action.CANCEL_EXTENDED_BOLUS, UserEntryMapper.Sources.EOPatch2);
|
||||
commandQueue.cancelExtended(new Callback() {
|
||||
@Override
|
||||
public void run() {
|
||||
exbolusCheckSubject.onNext(true);
|
||||
}
|
||||
});
|
||||
}else{
|
||||
exbolusCheckSubject.onNext(true);
|
||||
}
|
||||
|
||||
if (pumpSync.expectedPumpState().getTemporaryBasal() != null) {
|
||||
uel.log(UserEntryMapper.Action.CANCEL_TEMP_BASAL, UserEntryMapper.Sources.EOPatch2);
|
||||
commandQueue.cancelTempBasal(true, new Callback() {
|
||||
@Override
|
||||
public void run() {
|
||||
basalCheckSubject.onNext(true);
|
||||
}
|
||||
});
|
||||
}else{
|
||||
basalCheckSubject.onNext(true);
|
||||
}
|
||||
|
||||
return Observable.zip(getBolusSebject(), getExbolusSebject(), getBasalSebject(), (bolusReady, exbolusReady, basalReady) -> {
|
||||
return (bolusReady && exbolusReady && basalReady);
|
||||
})
|
||||
.filter(ready -> ready)
|
||||
.flatMap(v -> isReady())
|
||||
.concatMapSingle(v -> getSuspendedTime(pausedTimestamp, alarmCode))
|
||||
.concatMapSingle(suspendedTimestamp -> pauseBasal(pauseDurationHour, suspendedTimestamp, alarmCode))
|
||||
.firstOrError()
|
||||
.doOnError(e -> aapsLogger.error(LTag.PUMPCOMM, e.getMessage()));
|
||||
}
|
||||
|
||||
private Single<Long> getSuspendedTime(long pausedTimestamp, @Nullable AlarmCode alarmCode) {
|
||||
return Single.just(pausedTimestamp);
|
||||
}
|
||||
|
||||
private Single<Long> stopNowBolus(long pausedTimestamp, boolean isNowBolusActive) {
|
||||
if (isNowBolusActive) {
|
||||
return BOLUS_STOP.stop(IPatchConstant.NOW_BOLUS_ID)
|
||||
.doOnSuccess(this::checkResponse)
|
||||
.doOnSuccess(v -> onNowBolusStopped(v.getInjectedBolusAmount(), pausedTimestamp))
|
||||
.map(v -> pausedTimestamp);
|
||||
}
|
||||
|
||||
return Single.just(pausedTimestamp);
|
||||
}
|
||||
|
||||
private Single<Long> stopExtBolus(long pausedTimestamp, boolean isExtBolusActive) {
|
||||
if (isExtBolusActive) {
|
||||
return BOLUS_STOP.stop(IPatchConstant.EXT_BOLUS_ID)
|
||||
.doOnSuccess(this::checkResponse)
|
||||
.doOnSuccess(v -> onExtBolusStopped(v.getInjectedBolusAmount(), pausedTimestamp))
|
||||
.map(v -> pausedTimestamp);
|
||||
}
|
||||
|
||||
return Single.just(pausedTimestamp);
|
||||
}
|
||||
|
||||
private Single<Long> stopTempBasal(long pausedTimestamp, boolean isTempBasalActive) {
|
||||
if (isTempBasalActive) {
|
||||
return TEMP_BASAL_SCHEDULE_STOP.stop()
|
||||
.doOnSuccess(this::checkResponse)
|
||||
.doOnSuccess(v -> onTempBasalCanceled(pausedTimestamp))
|
||||
.map(v -> pausedTimestamp);
|
||||
}
|
||||
|
||||
return Single.just(pausedTimestamp);
|
||||
}
|
||||
|
||||
private Single<PatchBooleanResponse> pauseBasal(float pauseDurationHour, long suspendedTimestamp, @Nullable AlarmCode alarmCode) {
|
||||
if(alarmCode == null) {
|
||||
return BASAL_PAUSE.pause(pauseDurationHour)
|
||||
.doOnSuccess(this::checkResponse)
|
||||
.doOnSuccess(v -> onBasalPaused(pauseDurationHour, suspendedTimestamp, null));
|
||||
}
|
||||
|
||||
// 정지 알람 발생 시 basal pause 커맨드 전달하지 않음 - 주입 정지 이력만 생성
|
||||
onBasalPaused(pauseDurationHour, suspendedTimestamp, alarmCode);
|
||||
|
||||
return Single.just(new PatchBooleanResponse(true));
|
||||
}
|
||||
|
||||
private void onBasalPaused(float pauseDurationHour, long suspendedTimestamp, @Nullable AlarmCode alarmCode) {
|
||||
if (!pm.getNormalBasalManager().isSuspended()) {
|
||||
String strCode = (alarmCode != null) ? alarmCode.name() : null;
|
||||
|
||||
if (alarmCode != null) {
|
||||
pm.getPatchConfig().updateNormalBasalPausedSilently();
|
||||
}
|
||||
else {
|
||||
pm.getPatchConfig().updateNormalBasalPaused(pauseDurationHour);
|
||||
}
|
||||
pm.getNormalBasalManager().updateBasalSuspended();
|
||||
|
||||
pm.flushNormalBasalManager();
|
||||
pm.flushPatchConfig();
|
||||
|
||||
if((alarmCode == null || alarmCode.getType() == AlarmCode.TYPE_ALERT) && pauseDurationHour != 0)
|
||||
alarmRegistry.add(AlarmCode.B001, TimeUnit.MINUTES.toMillis((long)(pauseDurationHour * 60)), false).subscribe();
|
||||
}
|
||||
|
||||
enqueue(TaskFunc.UPDATE_CONNECTION);
|
||||
}
|
||||
|
||||
private void onNowBolusStopped(int injectedBolusAmount, long suspendedTimestamp) {
|
||||
updateNowBolusStopped(injectedBolusAmount, suspendedTimestamp);
|
||||
}
|
||||
|
||||
private void onExtBolusStopped(int injectedBolusAmount, long suspendedTimestamp) {
|
||||
updateExtBolusStopped(injectedBolusAmount, suspendedTimestamp);
|
||||
}
|
||||
|
||||
private void onTempBasalCanceled(long suspendedTimestamp) {
|
||||
TempBasal tempBasal = pm.getTempBasalManager().getStartedBasal();
|
||||
|
||||
if (tempBasal != null) {
|
||||
pm.getTempBasalManager().updateBasalStopped();
|
||||
pm.flushTempBasalManager();
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void enqueue(float pauseDurationHour, long pausedTime, @Nullable AlarmCode alarmCode) {
|
||||
boolean ready = (disposable == null || disposable.isDisposed());
|
||||
|
||||
if (ready) {
|
||||
disposable = pause(pauseDurationHour, pausedTime, alarmCode)
|
||||
.timeout(TASK_ENQUEUE_TIME_OUT, TimeUnit.SECONDS)
|
||||
.subscribe(v -> {
|
||||
bolusCheckSubject.onNext(false);
|
||||
exbolusCheckSubject.onNext(false);
|
||||
basalCheckSubject.onNext(false);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.ble.task;
|
||||
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import info.nightscout.shared.logging.LTag;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.api.StartPriming;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.api.UpdateConnection;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.vo.PatchState;
|
||||
import io.reactivex.Observable;
|
||||
|
||||
@Singleton
|
||||
public class PrimingTask extends TaskBase {
|
||||
|
||||
private UpdateConnection UPDATE_CONNECTION;
|
||||
private StartPriming START_PRIMING;
|
||||
|
||||
@Inject
|
||||
public PrimingTask() {
|
||||
super(TaskFunc.PRIMING);
|
||||
|
||||
UPDATE_CONNECTION = new UpdateConnection();
|
||||
START_PRIMING = new StartPriming();
|
||||
}
|
||||
|
||||
public Observable<Long> start(long count) {
|
||||
return isReady().concatMapSingle(v -> START_PRIMING.start())
|
||||
.doOnNext(this::checkResponse)
|
||||
.flatMap(v -> observePrimingSuccess(count))
|
||||
.takeUntil(value -> (value == count))
|
||||
.doOnError(e -> aapsLogger.error(LTag.PUMPCOMM, e.getMessage()));
|
||||
}
|
||||
|
||||
private Observable<Long> observePrimingSuccess(long count) {
|
||||
|
||||
return Observable.merge(
|
||||
Observable.interval(1, TimeUnit.SECONDS).take(count + 10)
|
||||
.map(v -> v * 3) // 현재 20초 니깐 60 정도에서 꽉 채워짐. *4 도 괜찮을 듯.
|
||||
.doOnNext(v -> {
|
||||
if (v >= count) {
|
||||
throw new Exception("Priming failed");
|
||||
}
|
||||
}), // 프로그래스바 용.
|
||||
|
||||
Observable.interval(3, TimeUnit.SECONDS)
|
||||
.concatMapSingle(v -> UPDATE_CONNECTION.get())
|
||||
.map(response -> PatchState.Companion.create(response.getPatchState(), System.currentTimeMillis()))
|
||||
.filter(patchState -> patchState.isPrimingSuccess())
|
||||
.map(result -> count) // 프라이밍 체크 용 성공시 count 값 리턴
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.ble.task;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import info.nightscout.shared.logging.LTag;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.api.BolusFinishTimeGet;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.response.BolusFinishTimeResponse;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.code.BolusType;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.vo.BolusCurrent;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.vo.PatchState;
|
||||
import io.reactivex.Single;
|
||||
|
||||
@Singleton
|
||||
public class ReadBolusFinishTimeTask extends BolusTask {
|
||||
|
||||
private BolusFinishTimeGet BOLUS_FINISH_TIME_GET;
|
||||
|
||||
@Inject
|
||||
public ReadBolusFinishTimeTask() {
|
||||
super(TaskFunc.READ_BOLUS_FINISH_TIME);
|
||||
BOLUS_FINISH_TIME_GET = new BolusFinishTimeGet();
|
||||
}
|
||||
|
||||
Single<BolusFinishTimeResponse> read() {
|
||||
return isReady()
|
||||
.concatMapSingle(v -> BOLUS_FINISH_TIME_GET.get())
|
||||
.firstOrError()
|
||||
.doOnSuccess(this::checkResponse)
|
||||
.doOnSuccess(this::onResponse)
|
||||
.doOnError(e -> aapsLogger.error(LTag.PUMPCOMM, e.getMessage()));
|
||||
}
|
||||
|
||||
void onResponse(BolusFinishTimeResponse response) {
|
||||
PatchState patchState = pm.getPatchState();
|
||||
BolusCurrent bolusCurrent = pm.getBolusCurrent();
|
||||
long nowHistoryID = bolusCurrent.historyId(BolusType.NOW);
|
||||
long extHistoryID = bolusCurrent.historyId(BolusType.EXT);
|
||||
|
||||
if (nowHistoryID > 0 && patchState.isBolusDone(BolusType.NOW) && response.getNowBolusFinishTime() > 0) {
|
||||
bolusCurrent.setEndTimeSynced(BolusType.NOW, true);
|
||||
enqueue(TaskFunc.STOP_NOW_BOLUS);
|
||||
}
|
||||
|
||||
if (extHistoryID > 0 && patchState.isBolusDone(BolusType.EXT) && response.getExtBolusFinishTime() > 0) {
|
||||
bolusCurrent.setEndTimeSynced(BolusType.EXT, true);
|
||||
enqueue(TaskFunc.STOP_EXT_BOLUS);
|
||||
}
|
||||
|
||||
pm.flushBolusCurrent();
|
||||
}
|
||||
|
||||
public synchronized void enqueue() {
|
||||
boolean ready = (disposable == null || disposable.isDisposed());
|
||||
|
||||
if (ready) {
|
||||
disposable = read()
|
||||
.timeout(TASK_ENQUEUE_TIME_OUT, TimeUnit.SECONDS)
|
||||
.subscribe();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.ble.task;
|
||||
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import info.nightscout.shared.logging.LTag;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.api.TempBasalFinishTimeGet;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.response.TempBasalFinishTimeResponse;
|
||||
import io.reactivex.Single;
|
||||
|
||||
@Singleton
|
||||
public class ReadTempBasalFinishTimeTask extends TaskBase {
|
||||
|
||||
private TempBasalFinishTimeGet TEMP_BASAL_FINISH_TIME_GET;
|
||||
|
||||
@Inject
|
||||
public ReadTempBasalFinishTimeTask() {
|
||||
super(TaskFunc.READ_TEMP_BASAL_FINISH_TIME);
|
||||
TEMP_BASAL_FINISH_TIME_GET = new TempBasalFinishTimeGet();
|
||||
}
|
||||
|
||||
public Single<TempBasalFinishTimeResponse> read() {
|
||||
return isReady()
|
||||
.concatMapSingle(v -> TEMP_BASAL_FINISH_TIME_GET.get())
|
||||
.firstOrError()
|
||||
.doOnSuccess(this::checkResponse)
|
||||
.doOnSuccess(this::onResponse)
|
||||
.doOnError(e -> aapsLogger.error(LTag.PUMPCOMM, e.getMessage()));
|
||||
}
|
||||
|
||||
private void onResponse(TempBasalFinishTimeResponse response) {
|
||||
}
|
||||
|
||||
public synchronized void enqueue() {
|
||||
boolean ready = (disposable == null || disposable.isDisposed());
|
||||
|
||||
if (ready) {
|
||||
disposable = read()
|
||||
.timeout(TASK_ENQUEUE_TIME_OUT, TimeUnit.SECONDS)
|
||||
.subscribe();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.ble.task;
|
||||
|
||||
import info.nightscout.shared.logging.AAPSLogger;
|
||||
import info.nightscout.shared.logging.LTag;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ble.PatchStateManager;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.api.BasalResume;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.response.BaseResponse;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.response.PatchBooleanResponse;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.vo.NormalBasal;
|
||||
import io.reactivex.Single;
|
||||
|
||||
@Singleton
|
||||
public class ResumeBasalTask extends TaskBase {
|
||||
@Inject
|
||||
StartNormalBasalTask startNormalBasalTask;
|
||||
|
||||
@Inject
|
||||
PatchStateManager patchStateManager;
|
||||
|
||||
private BasalResume BASAL_RESUME;
|
||||
|
||||
@Inject
|
||||
public ResumeBasalTask() {
|
||||
super(TaskFunc.RESUME_BASAL);
|
||||
BASAL_RESUME = new BasalResume();
|
||||
}
|
||||
|
||||
public synchronized Single<? extends BaseResponse> resume() {
|
||||
|
||||
if (pm.getPatchConfig().getNeedSetBasalSchedule()) {
|
||||
NormalBasal normalBasal = pm.getNormalBasalManager().getNormalBasal();
|
||||
|
||||
if (normalBasal != null) {
|
||||
return startNormalBasalTask.start(normalBasal, true);
|
||||
}
|
||||
}
|
||||
|
||||
return isReady().concatMapSingle(v -> BASAL_RESUME.resume())
|
||||
.doOnNext(this::checkResponse)
|
||||
.firstOrError()
|
||||
.doOnSuccess(v -> onResumeResponse(v))
|
||||
.doOnError(e -> aapsLogger.error(LTag.PUMPCOMM, e.getMessage()));
|
||||
}
|
||||
|
||||
private void onResumeResponse(PatchBooleanResponse v) throws SQLException {
|
||||
if (v.isSuccess()) {
|
||||
patchStateManager.onBasalResumed(v.getTimestamp() + 1000);
|
||||
}
|
||||
enqueue(TaskFunc.UPDATE_CONNECTION);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void preCondition() throws Exception {
|
||||
checkPatchActivated();
|
||||
checkPatchConnected();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.ble.task;
|
||||
|
||||
import info.nightscout.shared.logging.LTag;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.scan.PatchSelfTestResult;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.api.GetGlobalTime;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.api.GetTemperature;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.api.GetVoltageLevelB4Priming;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.response.BatteryVoltageLevelPairingResponse;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.response.GlobalTimeResponse;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.response.TemperatureResponse;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import io.reactivex.Single;
|
||||
|
||||
@Singleton
|
||||
public class SelfTestTask extends TaskBase {
|
||||
|
||||
private GetTemperature TEMPERATURE_GET;
|
||||
private GetVoltageLevelB4Priming BATTERY_LEVEL_GET_BEFORE_PRIMING;
|
||||
private GetGlobalTime GET_GLOBAL_TIME;
|
||||
|
||||
@Inject
|
||||
public SelfTestTask() {
|
||||
super(TaskFunc.SELF_TEST);
|
||||
|
||||
TEMPERATURE_GET = new GetTemperature();
|
||||
BATTERY_LEVEL_GET_BEFORE_PRIMING = new GetVoltageLevelB4Priming();
|
||||
GET_GLOBAL_TIME = new GetGlobalTime();
|
||||
}
|
||||
|
||||
public Single<PatchSelfTestResult> start() {
|
||||
Single<PatchSelfTestResult> tasks = Single.concat(Arrays.asList(
|
||||
TEMPERATURE_GET.get()
|
||||
.map(TemperatureResponse::getResult)
|
||||
.doOnSuccess(this::onTemperatureResult),
|
||||
BATTERY_LEVEL_GET_BEFORE_PRIMING.get()
|
||||
.map(BatteryVoltageLevelPairingResponse::getResult)
|
||||
.doOnSuccess(this::onBatteryResult),
|
||||
GET_GLOBAL_TIME.get(false)
|
||||
.map(GlobalTimeResponse::getResult)
|
||||
.doOnSuccess(this::onTimeResult)))
|
||||
.filter(result -> result != PatchSelfTestResult.TEST_SUCCESS)
|
||||
.first(PatchSelfTestResult.TEST_SUCCESS);
|
||||
|
||||
return isReady()
|
||||
.concatMapSingle(v -> tasks)
|
||||
.firstOrError()
|
||||
.doOnError(e -> aapsLogger.error(LTag.PUMPCOMM, e.getMessage()));
|
||||
}
|
||||
|
||||
private void onTemperatureResult(PatchSelfTestResult patchSelfTestResult) {
|
||||
if (patchSelfTestResult != PatchSelfTestResult.TEST_SUCCESS) {
|
||||
}
|
||||
}
|
||||
|
||||
private void onBatteryResult(PatchSelfTestResult patchSelfTestResult) {
|
||||
if (patchSelfTestResult != PatchSelfTestResult.TEST_SUCCESS) {
|
||||
}
|
||||
}
|
||||
|
||||
private void onTimeResult(PatchSelfTestResult patchSelfTestResult) {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.ble.task;
|
||||
|
||||
import info.nightscout.shared.logging.LTag;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.api.GetGlobalTime;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.api.SetGlobalTime;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.response.GlobalTimeResponse;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.response.PatchBooleanResponse;
|
||||
|
||||
import java.util.TimeZone;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import io.reactivex.Single;
|
||||
|
||||
@Singleton
|
||||
public class SetGlobalTimeTask extends TaskBase {
|
||||
|
||||
private SetGlobalTime SET_GLOBAL_TIME;
|
||||
private GetGlobalTime GET_GLOBAL_TIME;
|
||||
|
||||
@Inject
|
||||
public SetGlobalTimeTask() {
|
||||
super(TaskFunc.SET_GLOBAL_TIME);
|
||||
|
||||
SET_GLOBAL_TIME = new SetGlobalTime();
|
||||
GET_GLOBAL_TIME = new GetGlobalTime();
|
||||
}
|
||||
|
||||
public Single<PatchBooleanResponse> set() {
|
||||
return isReady()
|
||||
.concatMapSingle(v -> GET_GLOBAL_TIME.get(false))
|
||||
.doOnNext(this::checkResponse)
|
||||
.doOnNext(this::checkPatchTime)
|
||||
.concatMapSingle(v -> SET_GLOBAL_TIME.set())
|
||||
.doOnNext(this::checkResponse)
|
||||
.firstOrError()
|
||||
.doOnSuccess(v -> onSuccess())
|
||||
.doOnError(e -> aapsLogger.error(LTag.PUMPCOMM, e.getMessage()));
|
||||
}
|
||||
|
||||
private boolean checkPatchTime(GlobalTimeResponse response) throws Exception {
|
||||
|
||||
long newMilli = System.currentTimeMillis();
|
||||
long oldMilli = response.getGlobalTimeInMilli();
|
||||
long oldOffset = response.getTimeZoneOffset();
|
||||
int offset = TimeZone.getDefault().getOffset(newMilli);
|
||||
int minutes = (int) TimeUnit.MILLISECONDS.toMinutes(offset);
|
||||
// TimeZoneOffset (8bit / signed): 타임존 offset 15분 단위을 1로 환산, Korea 의 경우 36값(+9:00)
|
||||
int newOffset = minutes / 15;
|
||||
|
||||
long diff = Math.abs(oldMilli - newMilli);
|
||||
|
||||
if (diff > 60000 || oldOffset != newOffset) {
|
||||
aapsLogger.debug(LTag.PUMPCOMM, String.format("checkPatchTime %s %s %s", diff, oldOffset, newOffset));
|
||||
return true;
|
||||
}
|
||||
|
||||
throw new Exception("No time set required");
|
||||
}
|
||||
|
||||
public synchronized void enqueue() {
|
||||
boolean ready = (disposable == null || disposable.isDisposed());
|
||||
|
||||
if (ready) {
|
||||
disposable = set()
|
||||
.timeout(TASK_ENQUEUE_TIME_OUT, TimeUnit.SECONDS)
|
||||
.subscribe(v -> {}, e -> {}); // Exception 을 사용하기에...
|
||||
}
|
||||
}
|
||||
|
||||
private void onSuccess() {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.ble.task;
|
||||
|
||||
import info.nightscout.shared.logging.LTag;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ble.IPreferenceManager;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.api.SetLowReservoirLevelAndExpireAlert;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.response.PatchBooleanResponse;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import io.reactivex.Single;
|
||||
|
||||
@Singleton
|
||||
public class SetLowReservoirTask extends TaskBase {
|
||||
|
||||
@Inject
|
||||
IPreferenceManager pm;
|
||||
|
||||
private SetLowReservoirLevelAndExpireAlert SET_LOW_RESERVOIR_N_EXPIRE_ALERT;
|
||||
|
||||
@Inject
|
||||
public SetLowReservoirTask() {
|
||||
super(TaskFunc.LOW_RESERVOIR);
|
||||
SET_LOW_RESERVOIR_N_EXPIRE_ALERT = new SetLowReservoirLevelAndExpireAlert();
|
||||
}
|
||||
|
||||
public Single<PatchBooleanResponse> set(int doseUnit, int hours) {
|
||||
return isReady()
|
||||
.concatMapSingle(v -> SET_LOW_RESERVOIR_N_EXPIRE_ALERT.set(
|
||||
doseUnit,
|
||||
hours))
|
||||
.doOnNext(this::checkResponse)
|
||||
.firstOrError()
|
||||
.doOnError(e -> aapsLogger.error(LTag.PUMPCOMM, e.getMessage()));
|
||||
}
|
||||
|
||||
public synchronized void enqueue() {
|
||||
|
||||
int alertTime = pm.getPatchConfig().getPatchExpireAlertTime();
|
||||
int alertSetting = pm.getPatchConfig().getLowReservoirAlertAmount();
|
||||
|
||||
boolean ready = (disposable == null || disposable.isDisposed());
|
||||
|
||||
if (ready) {
|
||||
disposable = set(alertSetting, alertTime)
|
||||
.timeout(TASK_ENQUEUE_TIME_OUT, TimeUnit.SECONDS)
|
||||
.subscribe();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void preCondition() throws Exception {
|
||||
checkPatchConnected();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.ble.task;
|
||||
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
|
||||
import info.nightscout.shared.logging.LTag;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.api.StartBonding;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import io.reactivex.Single;
|
||||
|
||||
import static info.nightscout.androidaps.plugins.pump.eopatch.core.api.StartBonding.OPTION_NUMERIC;
|
||||
|
||||
/**
|
||||
* (주의) API 호출 후 본딩을 위해서 밑단 연결이 끊어짐.
|
||||
*/
|
||||
@Singleton
|
||||
public class StartBondTask extends TaskBase {
|
||||
private StartBonding START_BOND;
|
||||
|
||||
@Inject
|
||||
public StartBondTask() {
|
||||
super(TaskFunc.START_BOND);
|
||||
START_BOND = new StartBonding();
|
||||
}
|
||||
|
||||
public Single<Boolean> start(String mac) {
|
||||
prefSetMacAddress(mac);
|
||||
patch.updateMacAddress(mac, false);
|
||||
|
||||
return isReady()
|
||||
.concatMapSingle(v -> START_BOND.start(OPTION_NUMERIC))
|
||||
.doOnNext(this::checkResponse)
|
||||
.concatMap(response -> patch.observeBondState())
|
||||
.doOnNext(state -> {
|
||||
if(state == BluetoothDevice.BOND_NONE) throw new Exception();
|
||||
})
|
||||
.filter(result -> result == BluetoothDevice.BOND_BONDED)
|
||||
.map(result -> true)
|
||||
.timeout(60, TimeUnit.SECONDS)
|
||||
.doOnNext(v -> prefSetMacAddress(mac))
|
||||
.doOnError(e -> {
|
||||
prefSetMacAddress("");
|
||||
aapsLogger.error(LTag.PUMPCOMM, e.getMessage());
|
||||
})
|
||||
.firstOrError();
|
||||
}
|
||||
|
||||
private synchronized void prefSetMacAddress(String mac) {
|
||||
pm.getPatchConfig().setMacAddress(mac);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.ble.task;
|
||||
|
||||
import info.nightscout.androidaps.data.DetailedBolusInfo;
|
||||
import info.nightscout.shared.logging.LTag;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.code.BolusExDuration;
|
||||
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.api.BolusStart;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.api.ComboBolusStart;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.api.ExtBolusStart;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.response.BolusResponse;
|
||||
import io.reactivex.Single;
|
||||
|
||||
@Singleton
|
||||
public class StartCalcBolusTask extends BolusTask {
|
||||
|
||||
private BolusStart NOW_BOLUS_START;
|
||||
private ExtBolusStart EXT_BOLUS_START;
|
||||
private ComboBolusStart COMBO_BOLUS_START;
|
||||
|
||||
@Inject
|
||||
public StartCalcBolusTask() {
|
||||
super(TaskFunc.START_CALC_BOLUS);
|
||||
|
||||
NOW_BOLUS_START = new BolusStart();
|
||||
EXT_BOLUS_START = new ExtBolusStart();
|
||||
COMBO_BOLUS_START = new ComboBolusStart();
|
||||
}
|
||||
|
||||
public Single<? extends BolusResponse> start(DetailedBolusInfo detailedBolusInfo) {
|
||||
return isReady().concatMapSingle(v -> startBolusImpl((float)detailedBolusInfo.insulin, 0f, BolusExDuration.OFF))
|
||||
.doOnNext(this::checkResponse)
|
||||
.firstOrError()
|
||||
.doOnSuccess(v -> onSuccess((float)detailedBolusInfo.insulin, (float)detailedBolusInfo.insulin, 0f, BolusExDuration.OFF))
|
||||
.doOnError(e -> aapsLogger.error(LTag.PUMPCOMM, e.getMessage()));
|
||||
}
|
||||
|
||||
private Single<? extends BolusResponse> startBolusImpl(float nowDoseU, float exDoseU,
|
||||
BolusExDuration exDuration) {
|
||||
if (nowDoseU > 0 && exDoseU > 0) {
|
||||
return COMBO_BOLUS_START.start(nowDoseU, exDoseU, exDuration.getMinute());
|
||||
} else if (exDoseU > 0) {
|
||||
return EXT_BOLUS_START.start(exDoseU, exDuration.getMinute());
|
||||
} else {
|
||||
return NOW_BOLUS_START.start(nowDoseU);
|
||||
}
|
||||
}
|
||||
|
||||
private void onSuccess(float nowDoseU, float correctionBolus, float exDoseU, BolusExDuration exDuration) {
|
||||
onCalcBolusStarted(nowDoseU, correctionBolus, exDoseU, exDuration);
|
||||
enqueue(TaskFunc.UPDATE_CONNECTION);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void preCondition() throws Exception {
|
||||
//checkPatchActivated();
|
||||
checkPatchConnected();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.ble.task;
|
||||
|
||||
import info.nightscout.shared.logging.LTag;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ble.PatchStateManager;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.api.BasalScheduleSetBig;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.response.BasalScheduleSetResponse;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.vo.NormalBasal;
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
|
||||
@Singleton
|
||||
public class StartNormalBasalTask extends TaskBase {
|
||||
|
||||
private BasalScheduleSetBig BASAL_SCHEDULE_SET_BIG;
|
||||
|
||||
@Inject
|
||||
PatchStateManager patchStateManager;
|
||||
|
||||
@Inject
|
||||
public StartNormalBasalTask() {
|
||||
super(TaskFunc.START_NORMAL_BASAL);
|
||||
BASAL_SCHEDULE_SET_BIG = new BasalScheduleSetBig();
|
||||
}
|
||||
|
||||
public Single<BasalScheduleSetResponse> start(NormalBasal basal, boolean resume) {
|
||||
return isReady().concatMapSingle(v -> startJob(basal, resume)).firstOrError();
|
||||
}
|
||||
|
||||
public Single<BasalScheduleSetResponse> startJob(NormalBasal basal, boolean resume) {
|
||||
return BASAL_SCHEDULE_SET_BIG.set(basal.getDoseUnitPerSegmentArray())
|
||||
.doOnSuccess(this::checkResponse)
|
||||
.observeOn(Schedulers.io())
|
||||
.doOnSuccess(v -> onStartNormalBasalResponse(v, basal, resume))
|
||||
.doOnError(e -> aapsLogger.error(LTag.PUMPCOMM, e.getMessage()));
|
||||
}
|
||||
|
||||
private void onStartNormalBasalResponse(BasalScheduleSetResponse response,
|
||||
NormalBasal basal, boolean resume) throws SQLException {
|
||||
|
||||
long timeStamp = response.getTimestamp();
|
||||
patchStateManager.onBasalStarted(basal, timeStamp+1000);
|
||||
enqueue(TaskFunc.UPDATE_CONNECTION);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void preCondition() throws Exception {
|
||||
checkPatchConnected();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.ble.task;
|
||||
|
||||
import info.nightscout.shared.logging.LTag;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.code.BolusExDuration;
|
||||
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.api.BolusStart;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.api.ComboBolusStart;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.api.ExtBolusStart;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.response.BolusResponse;
|
||||
import io.reactivex.Single;
|
||||
|
||||
@Singleton
|
||||
public class StartQuickBolusTask extends BolusTask {
|
||||
|
||||
private BolusStart NOW_BOLUS_START;
|
||||
private ExtBolusStart EXT_BOLUS_START;
|
||||
private ComboBolusStart COMBO_BOLUS_START;
|
||||
|
||||
@Inject
|
||||
public StartQuickBolusTask() {
|
||||
super(TaskFunc.START_QUICK_BOLUS);
|
||||
|
||||
NOW_BOLUS_START = new BolusStart();
|
||||
EXT_BOLUS_START = new ExtBolusStart();
|
||||
COMBO_BOLUS_START = new ComboBolusStart();
|
||||
}
|
||||
|
||||
public Single<? extends BolusResponse> start(float nowDoseU, float exDoseU,
|
||||
BolusExDuration exDuration) {
|
||||
return isReady().concatMapSingle(v -> startBolusImpl(nowDoseU, exDoseU, exDuration))
|
||||
.doOnNext(this::checkResponse)
|
||||
.firstOrError()
|
||||
.doOnSuccess(v -> onSuccess(nowDoseU, exDoseU, exDuration))
|
||||
.doOnError(e -> aapsLogger.error(LTag.PUMPCOMM, e.getMessage()));
|
||||
}
|
||||
|
||||
private Single<? extends BolusResponse> startBolusImpl(float nowDoseU, float exDoseU,
|
||||
BolusExDuration exDuration) {
|
||||
if (nowDoseU > 0 && exDoseU > 0) {
|
||||
return COMBO_BOLUS_START.start(nowDoseU, exDoseU, exDuration.getMinute());
|
||||
} else if (exDoseU > 0) {
|
||||
return EXT_BOLUS_START.start(exDoseU, exDuration.getMinute());
|
||||
} else {
|
||||
return NOW_BOLUS_START.start(nowDoseU);
|
||||
}
|
||||
}
|
||||
|
||||
private void onSuccess(float nowDoseU, float exDoseU, BolusExDuration exDuration) {
|
||||
onQuickBolusStarted(nowDoseU, exDoseU, exDuration);
|
||||
enqueue(TaskFunc.UPDATE_CONNECTION);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void preCondition() throws Exception {
|
||||
//checkPatchActivated();
|
||||
checkPatchConnected();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.ble.task;
|
||||
|
||||
import info.nightscout.shared.logging.LTag;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.api.TempBasalScheduleStart;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.response.TempBasalScheduleSetResponse;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.vo.TempBasal;
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
|
||||
@Singleton
|
||||
public class StartTempBasalTask extends TaskBase {
|
||||
|
||||
private TempBasalScheduleStart TEMP_BASAL_SCHEDULE_START;
|
||||
|
||||
@Inject
|
||||
public StartTempBasalTask() {
|
||||
super(TaskFunc.START_TEMP_BASAL);
|
||||
|
||||
TEMP_BASAL_SCHEDULE_START = new TempBasalScheduleStart();
|
||||
}
|
||||
|
||||
public Single<TempBasalScheduleSetResponse> start(TempBasal tempBasal) {
|
||||
return isReady()
|
||||
.concatMapSingle(v -> TEMP_BASAL_SCHEDULE_START.start(tempBasal.getDurationMinutes(), tempBasal.getDoseUnitPerHour(), tempBasal.getPercent()))
|
||||
.doOnNext(this::checkResponse)
|
||||
.firstOrError()
|
||||
.observeOn(Schedulers.io())
|
||||
.doOnSuccess(v -> onTempBasalStarted(tempBasal))
|
||||
.doOnError(e -> aapsLogger.error(LTag.PUMPCOMM, e.getMessage()));
|
||||
}
|
||||
|
||||
private void onTempBasalStarted(TempBasal tempBasal) {
|
||||
enqueue(TaskFunc.UPDATE_CONNECTION);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void preCondition() throws Exception {
|
||||
checkPatchConnected();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,126 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.ble.task;
|
||||
|
||||
import android.os.SystemClock;
|
||||
|
||||
import info.nightscout.androidaps.interfaces.PumpSync;
|
||||
import info.nightscout.androidaps.logging.UserEntryLogger;
|
||||
import info.nightscout.androidaps.utils.userEntry.UserEntryMapper;
|
||||
import info.nightscout.shared.logging.LTag;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ble.IPreferenceManager;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.api.BasalStop;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.response.BasalStopResponse;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import info.nightscout.androidaps.interfaces.CommandQueue;
|
||||
import info.nightscout.shared.logging.AAPSLogger;
|
||||
import info.nightscout.androidaps.queue.Callback;
|
||||
import info.nightscout.androidaps.queue.commands.Command;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.subjects.BehaviorSubject;
|
||||
|
||||
@Singleton
|
||||
public class StopBasalTask extends TaskBase {
|
||||
|
||||
@Inject IPreferenceManager pm;
|
||||
@Inject CommandQueue commandQueue;
|
||||
@Inject AAPSLogger aapsLogger;
|
||||
@Inject PumpSync pumpSync;
|
||||
@Inject UserEntryLogger uel;
|
||||
|
||||
private BasalStop BASAL_STOP;
|
||||
private BehaviorSubject<Boolean> bolusCheckSubject = BehaviorSubject.create();
|
||||
private BehaviorSubject<Boolean> exbolusCheckSubject = BehaviorSubject.create();
|
||||
private BehaviorSubject<Boolean> basalCheckSubject = BehaviorSubject.create();
|
||||
|
||||
@Inject
|
||||
public StopBasalTask() {
|
||||
super(TaskFunc.STOP_BASAL);
|
||||
|
||||
BASAL_STOP = new BasalStop();
|
||||
}
|
||||
|
||||
private Observable<Boolean> getBolusSebject(){
|
||||
return bolusCheckSubject.hide();
|
||||
}
|
||||
|
||||
private Observable<Boolean> getExbolusSebject(){
|
||||
return exbolusCheckSubject.hide();
|
||||
}
|
||||
|
||||
private Observable<Boolean> getBasalSebject(){
|
||||
return basalCheckSubject.hide();
|
||||
}
|
||||
|
||||
public Single<BasalStopResponse> stop() {
|
||||
|
||||
if (commandQueue.isRunning(Command.CommandType.BOLUS)) {
|
||||
uel.log(UserEntryMapper.Action.CANCEL_BOLUS, UserEntryMapper.Sources.EOPatch2);
|
||||
commandQueue.cancelAllBoluses();
|
||||
SystemClock.sleep(650);
|
||||
}
|
||||
bolusCheckSubject.onNext(true);
|
||||
|
||||
if (pumpSync.expectedPumpState().getExtendedBolus() != null) {
|
||||
uel.log(UserEntryMapper.Action.CANCEL_EXTENDED_BOLUS, UserEntryMapper.Sources.EOPatch2);
|
||||
commandQueue.cancelExtended(new Callback() {
|
||||
@Override
|
||||
public void run() {
|
||||
exbolusCheckSubject.onNext(true);
|
||||
}
|
||||
});
|
||||
}else{
|
||||
exbolusCheckSubject.onNext(true);
|
||||
}
|
||||
|
||||
if (pumpSync.expectedPumpState().getTemporaryBasal() != null) {
|
||||
uel.log(UserEntryMapper.Action.CANCEL_TEMP_BASAL, UserEntryMapper.Sources.EOPatch2);
|
||||
commandQueue.cancelTempBasal(true, new Callback() {
|
||||
@Override
|
||||
public void run() {
|
||||
basalCheckSubject.onNext(true);
|
||||
}
|
||||
});
|
||||
}else{
|
||||
basalCheckSubject.onNext(true);
|
||||
}
|
||||
|
||||
return Observable.zip(getBolusSebject(), getExbolusSebject(), getBasalSebject(), (bolusReady, exbolusReady, basalReady) -> {
|
||||
return (bolusReady && exbolusReady && basalReady);
|
||||
})
|
||||
.filter(ready -> ready)
|
||||
.flatMap(v -> isReady())
|
||||
.concatMapSingle(v -> BASAL_STOP.stop())
|
||||
.doOnNext(this::checkResponse)
|
||||
.firstOrError()
|
||||
.doOnSuccess(this::onBasalStopped)
|
||||
.doOnError(e -> aapsLogger.error(LTag.PUMPCOMM, e.getMessage()));
|
||||
}
|
||||
|
||||
private void onBasalStopped(BasalStopResponse response) {
|
||||
enqueue(TaskFunc.UPDATE_CONNECTION);
|
||||
}
|
||||
|
||||
public synchronized void enqueue() {
|
||||
boolean ready = (disposable == null || disposable.isDisposed());
|
||||
|
||||
if (ready) {
|
||||
disposable = stop()
|
||||
.timeout(TASK_ENQUEUE_TIME_OUT, TimeUnit.SECONDS)
|
||||
.subscribe(v -> {
|
||||
bolusCheckSubject.onNext(false);
|
||||
exbolusCheckSubject.onNext(false);
|
||||
basalCheckSubject.onNext(false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void preCondition() throws Exception {
|
||||
checkPatchConnected();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.ble.task;
|
||||
|
||||
import info.nightscout.shared.logging.LTag;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.define.IPatchConstant;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.code.PatchBleResultCode;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.api.BolusStop;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.response.BolusStopResponse;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.response.ComboBolusStopResponse;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import io.reactivex.Single;
|
||||
|
||||
@Singleton
|
||||
public class StopComboBolusTask extends BolusTask {
|
||||
|
||||
private BolusStop BOLUS_STOP;
|
||||
|
||||
@Inject
|
||||
public StopComboBolusTask() {
|
||||
super(TaskFunc.STOP_COMBO_BOLUS);
|
||||
BOLUS_STOP = new BolusStop();
|
||||
}
|
||||
|
||||
public Single<ComboBolusStopResponse> stop() {
|
||||
return isReady()
|
||||
.concatMapSingle(v -> stopJob())
|
||||
.firstOrError()
|
||||
.doOnSuccess(this::checkResponse)
|
||||
.doOnSuccess(this::onComboBolusStopped)
|
||||
.doOnError(e -> aapsLogger.error(LTag.PUMPCOMM, e.getMessage()));
|
||||
}
|
||||
|
||||
public Single<ComboBolusStopResponse> stopJob() {
|
||||
return Single.zip(
|
||||
BOLUS_STOP.stop(IPatchConstant.EXT_BOLUS_ID),
|
||||
BOLUS_STOP.stop(IPatchConstant.NOW_BOLUS_ID),
|
||||
(ext, now) -> createStopComboBolusResponse(now, ext));
|
||||
}
|
||||
|
||||
private ComboBolusStopResponse createStopComboBolusResponse(BolusStopResponse now, BolusStopResponse ext) {
|
||||
int idNow = now.isSuccess() ? IPatchConstant.NOW_BOLUS_ID : 0;
|
||||
int idExt = ext.isSuccess() ? IPatchConstant.EXT_BOLUS_ID : 0;
|
||||
|
||||
int injectedAmount = now.getInjectedBolusAmount();
|
||||
int injectingAmount = now.getInjectingBolusAmount();
|
||||
|
||||
int injectedExAmount = ext.getInjectedBolusAmount();
|
||||
int injectingExAmount = ext.getInjectingBolusAmount();
|
||||
|
||||
if (idNow == 0 && idExt == 0) {
|
||||
return new ComboBolusStopResponse(IPatchConstant.NOW_BOLUS_ID, PatchBleResultCode.BOLUS_UNKNOWN_ID);
|
||||
}
|
||||
|
||||
return new ComboBolusStopResponse(idNow, injectedAmount, injectingAmount, idExt, injectedExAmount, injectingExAmount);
|
||||
}
|
||||
|
||||
private void onComboBolusStopped(ComboBolusStopResponse response) {
|
||||
if (response.getId() != 0)
|
||||
updateNowBolusStopped(response.getInjectedBolusAmount());
|
||||
|
||||
if (response.getExtId() != 0)
|
||||
updateExtBolusStopped(response.getInjectedExBolusAmount());
|
||||
|
||||
enqueue(TaskFunc.UPDATE_CONNECTION);
|
||||
}
|
||||
|
||||
public synchronized void enqueue() {
|
||||
boolean ready = (disposable == null || disposable.isDisposed());
|
||||
|
||||
if (ready) {
|
||||
disposable = stop()
|
||||
.timeout(TASK_ENQUEUE_TIME_OUT, TimeUnit.SECONDS)
|
||||
.subscribe();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void preCondition() throws Exception {
|
||||
checkPatchConnected();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.ble.task;
|
||||
|
||||
import info.nightscout.shared.logging.LTag;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.define.IPatchConstant;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.api.BolusStop;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.response.BolusStopResponse;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import io.reactivex.Single;
|
||||
|
||||
@Singleton
|
||||
public class StopExtBolusTask extends BolusTask {
|
||||
|
||||
private BolusStop BOLUS_STOP;
|
||||
|
||||
@Inject
|
||||
public StopExtBolusTask() {
|
||||
super(TaskFunc.STOP_EXT_BOLUS);
|
||||
|
||||
BOLUS_STOP = new BolusStop();
|
||||
}
|
||||
|
||||
public Single<BolusStopResponse> stop() {
|
||||
return isReady().concatMapSingle(v -> stopJob()).firstOrError()
|
||||
.doOnError(e -> aapsLogger.error(LTag.PUMPCOMM, e.getMessage()));
|
||||
}
|
||||
|
||||
public Single<BolusStopResponse> stopJob() {
|
||||
return BOLUS_STOP.stop(IPatchConstant.EXT_BOLUS_ID)
|
||||
.doOnSuccess(this::checkResponse)
|
||||
.doOnSuccess(this::onExtBolusStopped);
|
||||
}
|
||||
|
||||
|
||||
private void onExtBolusStopped(BolusStopResponse response) {
|
||||
updateExtBolusStopped(response.getInjectedBolusAmount());
|
||||
enqueue(TaskFunc.UPDATE_CONNECTION);
|
||||
}
|
||||
|
||||
public synchronized void enqueue() {
|
||||
boolean ready = (disposable == null || disposable.isDisposed());
|
||||
|
||||
if (ready) {
|
||||
disposable = stop()
|
||||
.timeout(TASK_ENQUEUE_TIME_OUT, TimeUnit.SECONDS)
|
||||
.subscribe();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void preCondition() throws Exception {
|
||||
//checkPatchActivated();
|
||||
checkPatchConnected();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.ble.task;
|
||||
|
||||
import info.nightscout.shared.logging.LTag;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.define.IPatchConstant;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.api.BolusStop;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.response.BolusStopResponse;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
|
||||
@Singleton
|
||||
public class StopNowBolusTask extends BolusTask {
|
||||
|
||||
private BolusStop BOLUS_STOP;
|
||||
|
||||
@Inject
|
||||
public StopNowBolusTask() {
|
||||
super(TaskFunc.STOP_NOW_BOLUS);
|
||||
|
||||
BOLUS_STOP = new BolusStop();
|
||||
}
|
||||
|
||||
public Single<BolusStopResponse> stop() {
|
||||
return isReady()
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.concatMapSingle(v -> stopJob()).firstOrError()
|
||||
.doOnError(e -> aapsLogger.error(LTag.PUMPCOMM, e.getMessage()));
|
||||
}
|
||||
|
||||
public Single<BolusStopResponse> stopJob() {
|
||||
return BOLUS_STOP.stop(IPatchConstant.NOW_BOLUS_ID)
|
||||
.doOnSuccess(this::checkResponse)
|
||||
.doOnSuccess(this::onNowBolusStopped);
|
||||
}
|
||||
|
||||
private void onNowBolusStopped(BolusStopResponse response) {
|
||||
updateNowBolusStopped(response.getInjectedBolusAmount());
|
||||
enqueue(TaskFunc.UPDATE_CONNECTION);
|
||||
}
|
||||
|
||||
public synchronized void enqueue() {
|
||||
boolean ready = (disposable == null || disposable.isDisposed());
|
||||
|
||||
if (ready) {
|
||||
disposable = stop()
|
||||
.timeout(TASK_ENQUEUE_TIME_OUT, TimeUnit.SECONDS)
|
||||
.subscribe();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void preCondition() throws Exception {
|
||||
checkPatchConnected();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.ble.task;
|
||||
|
||||
import info.nightscout.shared.logging.LTag;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.api.TempBasalScheduleStop;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.response.PatchBooleanResponse;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import io.reactivex.Single;
|
||||
|
||||
@Singleton
|
||||
public class StopTempBasalTask extends TaskBase {
|
||||
|
||||
private TempBasalScheduleStop TEMP_BASAL_SCHEDULE_STOP;
|
||||
|
||||
@Inject
|
||||
public StopTempBasalTask() {
|
||||
super(TaskFunc.STOP_TEMP_BASAL);
|
||||
|
||||
TEMP_BASAL_SCHEDULE_STOP = new TempBasalScheduleStop();
|
||||
}
|
||||
|
||||
public Single<PatchBooleanResponse> stop() {
|
||||
return isReady().concatMapSingle(v -> stopJob()).firstOrError()
|
||||
.doOnError(e -> aapsLogger.error(LTag.PUMPCOMM, e.getMessage()));
|
||||
}
|
||||
|
||||
public Single<PatchBooleanResponse> stopJob() {
|
||||
return TEMP_BASAL_SCHEDULE_STOP.stop()
|
||||
.doOnSuccess(this::checkResponse)
|
||||
.doOnSuccess(v -> onTempBasalCanceled());
|
||||
}
|
||||
|
||||
private void onTempBasalCanceled() {
|
||||
enqueue(TaskFunc.UPDATE_CONNECTION);
|
||||
}
|
||||
|
||||
public synchronized void enqueue() {
|
||||
boolean ready = (disposable == null || disposable.isDisposed());
|
||||
|
||||
if (ready) {
|
||||
disposable = stop()
|
||||
.timeout(TASK_ENQUEUE_TIME_OUT, TimeUnit.SECONDS)
|
||||
.subscribe();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void preCondition() throws Exception {
|
||||
//checkPatchActivated();
|
||||
checkPatchConnected();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,151 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.ble.task;
|
||||
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ble.IPreferenceManager;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.api.BasalHistoryGetExBig;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.api.BasalHistoryIndexGet;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.api.TempBasalHistoryGetExBig;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.response.BasalHistoryIndexResponse;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.response.BasalHistoryResponse;
|
||||
import io.reactivex.Single;
|
||||
|
||||
import static info.nightscout.androidaps.plugins.pump.eopatch.core.define.IPatchConstant.BASAL_HISTORY_SIZE_BIG;
|
||||
|
||||
@Singleton
|
||||
public class SyncBasalHistoryTask extends TaskBase {
|
||||
|
||||
@Inject
|
||||
IPreferenceManager pm;
|
||||
|
||||
private BasalHistoryIndexGet BASAL_HISTORY_INDEX_GET;
|
||||
private BasalHistoryGetExBig BASAL_HISTORY_GET_EX_BIG;
|
||||
private TempBasalHistoryGetExBig TEMP_BASAL_HISTORY_GET_EX_BIG;
|
||||
|
||||
@Inject
|
||||
public SyncBasalHistoryTask() {
|
||||
super(TaskFunc.SYNC_BASAL_HISTORY);
|
||||
|
||||
BASAL_HISTORY_INDEX_GET = new BasalHistoryIndexGet();
|
||||
BASAL_HISTORY_GET_EX_BIG = new BasalHistoryGetExBig();
|
||||
TEMP_BASAL_HISTORY_GET_EX_BIG = new TempBasalHistoryGetExBig();
|
||||
}
|
||||
|
||||
public Single<Integer> sync(int end) {
|
||||
return Single.just(1); // 베이젤 싱크 사용 안함
|
||||
// return isReady()
|
||||
// .concatMapSingle(v -> syncBoth(pm.getPatchConfig().getLastIndex(), end))
|
||||
// .firstOrError()
|
||||
// .doOnSuccess(this::updatePatchLastIndex);
|
||||
}
|
||||
|
||||
public Single<Integer> sync() {
|
||||
return Single.just(1); // 베이젤 싱크 사용 안함
|
||||
// return isReady()
|
||||
// .concatMapSingle(v -> getLastIndex())
|
||||
// .concatMapSingle(end -> syncBoth(pm.getPatchConfig().getLastIndex(), end))
|
||||
// .firstOrError()
|
||||
// .doOnSuccess(this::updatePatchLastIndex);
|
||||
}
|
||||
|
||||
private Single<Integer> getLastIndex() {
|
||||
return BASAL_HISTORY_INDEX_GET.get()
|
||||
.doOnSuccess(this::checkResponse)
|
||||
.map(BasalHistoryIndexResponse::getLastFinishedIndex);
|
||||
}
|
||||
|
||||
private Single<Integer> syncBoth(int start, int end) {
|
||||
int count = end - start + 1;
|
||||
|
||||
if (count > 0) {
|
||||
return Single.zip(
|
||||
BASAL_HISTORY_GET_EX_BIG.get(start, count),
|
||||
TEMP_BASAL_HISTORY_GET_EX_BIG.get(start, count),
|
||||
(normal, temp) -> onBasalHistoryResponse(normal, temp, start, end));
|
||||
} else {
|
||||
return Single.just(-1);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void enqueue(int end) {
|
||||
|
||||
boolean ready = (disposable == null || disposable.isDisposed());
|
||||
|
||||
if (ready) {
|
||||
disposable = sync(end)
|
||||
.timeout(TASK_ENQUEUE_TIME_OUT, TimeUnit.SECONDS)
|
||||
.subscribe();
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void enqueue() {
|
||||
boolean ready = (disposable == null || disposable.isDisposed());
|
||||
|
||||
if (ready) {
|
||||
disposable = sync()
|
||||
.timeout(TASK_ENQUEUE_TIME_OUT, TimeUnit.SECONDS)
|
||||
.subscribe();
|
||||
}
|
||||
}
|
||||
|
||||
private int onBasalHistoryResponse(BasalHistoryResponse n, BasalHistoryResponse t,
|
||||
int startRequested, int end) throws SQLException {
|
||||
|
||||
if (!n.isSuccess() || !t.isSuccess() || n.getSeq() != t.getSeq()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int start = n.getSeq();
|
||||
|
||||
float[] normal = n.getInjectedDoseValues();
|
||||
float[] temp = t.getInjectedDoseValues();
|
||||
|
||||
int count = Math.min(end - start + 1, BASAL_HISTORY_SIZE_BIG);
|
||||
count = Math.min(count, normal.length);
|
||||
count = Math.min(count, temp.length);
|
||||
|
||||
return updateInjected(normal, temp, start, end);
|
||||
}
|
||||
|
||||
public synchronized int updateInjected(float[] normal, float[] temp, int start, int end) throws SQLException {
|
||||
if (pm.getPatchState().isPatchInternalSuspended() && pm.getPatchConfig().isInBasalPausedTime() == false) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int lastUpdatedIndex = -1;
|
||||
int count = end - start + 1;
|
||||
|
||||
if (count > normal.length) {
|
||||
count = normal.length;
|
||||
}
|
||||
|
||||
if (count > 0) {
|
||||
int lastSyncIndex = pm.getPatchConfig().getLastIndex();
|
||||
for (int i = 0;i < count;i++) {
|
||||
int seq = start + i;
|
||||
if (seq < lastSyncIndex)
|
||||
continue;
|
||||
|
||||
if (start <= seq && seq <= end) {
|
||||
lastUpdatedIndex = seq;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return lastUpdatedIndex;
|
||||
}
|
||||
|
||||
private void updatePatchLastIndex(int newIndex) {
|
||||
int lastIndex = pm.getPatchConfig().getLastIndex();
|
||||
|
||||
if (lastIndex < newIndex) {
|
||||
pm.getPatchConfig().setLastIndex(newIndex);
|
||||
pm.flushPatchConfig();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.ble.task;
|
||||
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ble.IPreferenceManager;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.scan.BleConnectionState;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.scan.IBleDevice;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.Patch;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.exception.NoActivatedPatchException;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.exception.PatchDisconnectedException;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.response.BaseResponse;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import info.nightscout.shared.logging.AAPSLogger;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
|
||||
@Singleton
|
||||
public class TaskBase {
|
||||
protected IBleDevice patch;
|
||||
|
||||
@Inject AAPSLogger aapsLogger;
|
||||
@Inject protected IPreferenceManager pm;
|
||||
@Inject TaskQueue taskQueue;
|
||||
|
||||
TaskFunc func;
|
||||
|
||||
static HashMap<TaskFunc, TaskBase> maps = new HashMap<>();
|
||||
|
||||
/* enqueue 시 사용 */
|
||||
protected Disposable disposable;
|
||||
|
||||
protected final Object lock = new Object();
|
||||
|
||||
protected static final long TASK_ENQUEUE_TIME_OUT = 60; // SECONDS
|
||||
|
||||
@Inject
|
||||
public TaskBase(TaskFunc func) {
|
||||
this.func = func;
|
||||
maps.put(func, this);
|
||||
patch = Patch.getInstance();
|
||||
}
|
||||
|
||||
/* Task 들의 작업 순서 및 조건 체크 */
|
||||
protected Observable<TaskFunc> isReady() {
|
||||
return taskQueue.isReady(func).doOnNext(v -> preCondition());
|
||||
}
|
||||
|
||||
protected Observable<TaskFunc> isReady2() {
|
||||
return taskQueue.isReady2(func).doOnNext(v -> preCondition());
|
||||
}
|
||||
|
||||
protected void checkResponse(BaseResponse response) throws Exception {
|
||||
if (!response.isSuccess()) {
|
||||
throw new Exception("Response failed! - "+response.resultCode.name());
|
||||
}
|
||||
}
|
||||
|
||||
public static void enqueue(TaskFunc func) {
|
||||
TaskBase task = maps.get(func);
|
||||
|
||||
if (task != null) {
|
||||
task.enqueue();
|
||||
}
|
||||
}
|
||||
|
||||
public static void enqueue(TaskFunc func, Boolean flag) {
|
||||
TaskBase task = maps.get(func);
|
||||
|
||||
if (task != null) {
|
||||
task.enqueue(flag);
|
||||
}
|
||||
}
|
||||
|
||||
protected synchronized void enqueue() {
|
||||
}
|
||||
|
||||
protected synchronized void enqueue(Boolean flag) {
|
||||
}
|
||||
|
||||
protected void preCondition() throws Exception {
|
||||
|
||||
}
|
||||
|
||||
protected void checkPatchConnected() throws Exception {
|
||||
if (patch.getConnectionState() == BleConnectionState.DISCONNECTED) {
|
||||
throw new PatchDisconnectedException();
|
||||
}
|
||||
}
|
||||
|
||||
protected void checkPatchActivated() throws Exception {
|
||||
if (pm.getPatchConfig().isDeactivated()) {
|
||||
throw new NoActivatedPatchException();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.ble.task;
|
||||
|
||||
public enum TaskFunc {
|
||||
START_BOND,
|
||||
GET_PATCH_INFO,
|
||||
SELF_TEST,
|
||||
PRIMING,
|
||||
NEEDLE_SENSING,
|
||||
ACTIVATE,
|
||||
DEACTIVATE,
|
||||
UPDATE_CONNECTION,
|
||||
START_NORMAL_BASAL,
|
||||
START_TEMP_BASAL,
|
||||
STOP_TEMP_BASAL,
|
||||
RESUME_BASAL,
|
||||
PAUSE_BASAL,
|
||||
STOP_BASAL,
|
||||
STOP_NOW_BOLUS,
|
||||
STOP_EXT_BOLUS,
|
||||
STOP_COMBO_BOLUS,
|
||||
START_QUICK_BOLUS,
|
||||
START_CALC_BOLUS,
|
||||
SYNC_BASAL_HISTORY,
|
||||
READ_BOLUS_FINISH_TIME,
|
||||
READ_TEMP_BASAL_FINISH_TIME,
|
||||
FETCH_ALARM,
|
||||
LOW_RESERVOIR,
|
||||
SET_GLOBAL_TIME,
|
||||
INFO_REMINDER,
|
||||
INTERNAL_SUSPEND
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.ble.task;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Queue;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import info.nightscout.shared.logging.AAPSLogger;
|
||||
import info.nightscout.shared.logging.LTag;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import io.reactivex.subjects.BehaviorSubject;
|
||||
|
||||
@Singleton
|
||||
public class TaskQueue {
|
||||
@Inject AAPSLogger aapsLogger;
|
||||
|
||||
Queue<PatchTask> queue = new LinkedList<>();
|
||||
|
||||
private int sequence = 0;
|
||||
private final BehaviorSubject<PatchTask> ticketSubject = BehaviorSubject.create();
|
||||
private final BehaviorSubject<Integer> sizeSubject = BehaviorSubject.createDefault(0);
|
||||
|
||||
@Inject
|
||||
public TaskQueue() {
|
||||
}
|
||||
|
||||
protected Observable<Integer> observeQueue() {
|
||||
return sizeSubject.distinctUntilChanged();
|
||||
}
|
||||
|
||||
protected synchronized Observable<TaskFunc> isReady(final TaskFunc function) {
|
||||
return Observable.fromCallable(() -> publishTicket(function))
|
||||
.concatMap(v -> {
|
||||
return ticketSubject
|
||||
.takeUntil(it -> it.number > v)
|
||||
.filter(it -> it.number == v);
|
||||
})
|
||||
.doOnNext(v -> aapsLogger.debug(LTag.PUMPCOMM, String.format("Task #:%s started func:%s", v.number, v.func.name())))
|
||||
.observeOn(Schedulers.io())
|
||||
.map(it -> it.func)
|
||||
.doFinally(this::done);
|
||||
}
|
||||
|
||||
protected synchronized Observable<TaskFunc> isReady2(final TaskFunc function) {
|
||||
return observeQueue()
|
||||
.filter(size -> size == 0).concatMap(v -> isReady(function));
|
||||
}
|
||||
|
||||
private synchronized int publishTicket(final TaskFunc function) {
|
||||
int turn = sequence++;
|
||||
aapsLogger.debug(LTag.PUMPCOMM, String.format("publishTicket() Task #:%s is assigned func:%s", turn, function.name()));
|
||||
|
||||
PatchTask task = new PatchTask(turn, function);
|
||||
addQueue(task);
|
||||
return turn;
|
||||
}
|
||||
|
||||
private synchronized void addQueue(PatchTask task) {
|
||||
queue.add(task);
|
||||
int size = queue.size();
|
||||
sizeSubject.onNext(size);
|
||||
|
||||
if (size == 1) {
|
||||
ticketSubject.onNext(task);
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void done() {
|
||||
if (queue.size() > 0) {
|
||||
PatchTask done = queue.remove();
|
||||
aapsLogger.debug(LTag.PUMPCOMM, String.format("done() Task #:%s completed func:%s task remaining:%s",
|
||||
done.number, done.func.name(), queue.size()));
|
||||
}
|
||||
|
||||
int size = queue.size();
|
||||
sizeSubject.onNext(size);
|
||||
|
||||
PatchTask next = queue.peek();
|
||||
if (next != null) {
|
||||
ticketSubject.onNext(next);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized boolean has(TaskFunc func) {
|
||||
if (queue.size() > 1) {
|
||||
Iterator<PatchTask> itor = queue.iterator();
|
||||
|
||||
/* remove 1st queue */
|
||||
itor.next();
|
||||
|
||||
while (itor.hasNext()) {
|
||||
PatchTask item = itor.next();
|
||||
if (item.func == func) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static class PatchTask {
|
||||
|
||||
int number;
|
||||
TaskFunc func;
|
||||
|
||||
PatchTask(int number, TaskFunc func) {
|
||||
this.number = number;
|
||||
this.func = func;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.ble.task;
|
||||
|
||||
import info.nightscout.shared.logging.LTag;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ble.PatchStateManager;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.vo.PatchState;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.api.UpdateConnection;
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.core.response.UpdateConnectionResponse;
|
||||
import io.reactivex.Single;
|
||||
|
||||
@Singleton
|
||||
public class UpdateConnectionTask extends TaskBase {
|
||||
|
||||
@Inject
|
||||
PatchStateManager patchStateManager;
|
||||
|
||||
private UpdateConnection UPDATE_CONNECTION;
|
||||
|
||||
@Inject
|
||||
public UpdateConnectionTask() {
|
||||
super(TaskFunc.UPDATE_CONNECTION);
|
||||
|
||||
UPDATE_CONNECTION = new UpdateConnection();
|
||||
}
|
||||
|
||||
public Single<PatchState> update() {
|
||||
return isReady().concatMapSingle(v -> updateJob()).firstOrError();
|
||||
}
|
||||
|
||||
public Single<PatchState> updateJob() {
|
||||
return UPDATE_CONNECTION.get()
|
||||
.doOnSuccess(this::checkResponse)
|
||||
.map(UpdateConnectionResponse::getPatchState)
|
||||
.map(bytes -> PatchState.Companion.create(bytes, System.currentTimeMillis()))
|
||||
.doOnSuccess(state -> onUpdateConnection(state))
|
||||
.doOnError(e -> aapsLogger.error(LTag.PUMPCOMM, e.getMessage()));
|
||||
}
|
||||
|
||||
private void onUpdateConnection(PatchState patchState) {
|
||||
patchStateManager.updatePatchState(patchState);
|
||||
}
|
||||
|
||||
public synchronized void enqueue() {
|
||||
boolean ready = (disposable == null || disposable.isDisposed());
|
||||
|
||||
if (ready) {
|
||||
disposable = update()
|
||||
.timeout(TASK_ENQUEUE_TIME_OUT, TimeUnit.SECONDS)
|
||||
.subscribe();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.code
|
||||
|
||||
import com.google.android.gms.common.internal.Preconditions
|
||||
|
||||
enum class AlarmCategory private constructor(val rawValue: Int) {
|
||||
NONE(0),
|
||||
ALARM(1),
|
||||
ALERT(2);
|
||||
|
||||
companion object {
|
||||
|
||||
|
||||
/**
|
||||
* rawValue로 값을 찾기, 못찾으면 null을 리턴
|
||||
*/
|
||||
fun ofRaw(rawValue: Int?): AlarmCategory? {
|
||||
if (rawValue == null) {
|
||||
return null
|
||||
}
|
||||
|
||||
for (t in AlarmCategory.values()) {
|
||||
if (t.rawValue == rawValue) {
|
||||
return t
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* rawValue로 값을 찾기, 못찾으면 defaultValue을 리턴
|
||||
*/
|
||||
fun ofRaw(rawValue: Int?, defaultValue: AlarmCategory): AlarmCategory {
|
||||
Preconditions.checkNotNull(defaultValue)
|
||||
if (rawValue == null) {
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
for (t in AlarmCategory.values()) {
|
||||
if (t.rawValue == rawValue) {
|
||||
return t
|
||||
}
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.code
|
||||
|
||||
|
||||
import com.google.android.gms.common.internal.Preconditions
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
enum class AlarmSource private constructor(val rawValue: Int) {
|
||||
@SerializedName("0")
|
||||
NONE(0),
|
||||
@SerializedName("1")
|
||||
PATCH(1),
|
||||
@SerializedName("2")
|
||||
ADM(2),
|
||||
@SerializedName("3")
|
||||
CGM(3),
|
||||
@SerializedName("9")
|
||||
TEST(9);
|
||||
|
||||
val isTest: Boolean
|
||||
get() = this == TEST
|
||||
|
||||
val isCgm: Boolean
|
||||
get() = this == CGM
|
||||
|
||||
companion object {
|
||||
|
||||
/**
|
||||
* rawValue로 값을 찾기, 못찾으면 null을 리턴
|
||||
*/
|
||||
@JvmStatic
|
||||
fun ofRaw(rawValue: Int?): AlarmSource {
|
||||
if (rawValue == null) {
|
||||
return NONE
|
||||
}
|
||||
|
||||
for (t in AlarmSource.values()) {
|
||||
if (t.rawValue == rawValue) {
|
||||
return t
|
||||
}
|
||||
}
|
||||
return NONE
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun toRaw(source: AlarmSource?): Int {
|
||||
return if (source != null) source.rawValue else NONE.rawValue
|
||||
}
|
||||
|
||||
/**
|
||||
* rawValue로 값을 찾기, 못찾으면 defaultValue을 리턴
|
||||
*/
|
||||
fun ofRaw(rawValue: Int?, defaultValue: AlarmSource): AlarmSource {
|
||||
Preconditions.checkNotNull(defaultValue)
|
||||
if (rawValue == null) {
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
for (t in AlarmSource.values()) {
|
||||
if (t.rawValue == rawValue) {
|
||||
return t
|
||||
}
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.code
|
||||
|
||||
enum class BasalStatus constructor(val rawValue: Int) {
|
||||
STOPPED(0),
|
||||
PAUSED(1), //템프베이젤 주입중
|
||||
SUSPENDED(2), //주입 정지
|
||||
STARTED(3), //주입중
|
||||
SELECTED(4); //패치 폐기
|
||||
|
||||
val isStarted: Boolean
|
||||
get() = this == STARTED
|
||||
|
||||
val isPaused: Boolean
|
||||
get() = this == PAUSED
|
||||
|
||||
val isSuspended: Boolean
|
||||
get() = this == SUSPENDED
|
||||
|
||||
val isStopped: Boolean
|
||||
get() = this == STOPPED
|
||||
|
||||
val isSelected: Boolean
|
||||
get() = this == SELECTED
|
||||
|
||||
val isSelectedGroup: Boolean
|
||||
get() = isStarted || isPaused || isSuspended || isSelected
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun ofRaw(rawValue: Int?): BasalStatus {
|
||||
if (rawValue == null) {
|
||||
return STOPPED
|
||||
}
|
||||
|
||||
for (t in values()) {
|
||||
if (t.rawValue == rawValue) {
|
||||
return t
|
||||
}
|
||||
}
|
||||
return STOPPED
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.code
|
||||
|
||||
enum class BgUnit private constructor(val rawValue: Int, val unitStr: String) {
|
||||
GRAM(1, "mg/dL"),
|
||||
MMOL(2, "mmol/L");
|
||||
|
||||
fun isGram() = GRAM == this
|
||||
fun isMmol() = MMOL == this
|
||||
|
||||
fun getUnit() = unitStr
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* rawValue로 값을 찾기, 못찾으면 null을 리턴
|
||||
*/
|
||||
fun ofRaw(rawValue: Int?): BgUnit {
|
||||
for (t in BgUnit.values()) {
|
||||
if (t.rawValue == rawValue) {
|
||||
return t
|
||||
}
|
||||
}
|
||||
return GRAM
|
||||
}
|
||||
|
||||
/**
|
||||
* rawValue로 값을 찾기, 못찾으면 defaultValue을 리턴
|
||||
*/
|
||||
fun ofRaw(rawValue: Int, defaultValue: BgUnit): BgUnit {
|
||||
for (t in BgUnit.values()) {
|
||||
if (t.rawValue == rawValue) {
|
||||
return t
|
||||
}
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.code
|
||||
|
||||
import android.content.Context
|
||||
import androidx.collection.SparseArrayCompat
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.CommonUtils
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.R
|
||||
import java.util.*
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
enum class BolusExDuration constructor(val index: Int, val minute: Int, val hour: Float) {
|
||||
OFF(0, 0, 0f),
|
||||
MINUTE_30(1, 30, 0.5f),
|
||||
MINUTE_60(2, 60, 1.0f),
|
||||
MINUTE_90(3, 90, 1.5f),
|
||||
MINUTE_120(4, 120, 2.0f),
|
||||
MINUTE_150(5, 150, 2.5f),
|
||||
MINUTE_180(6, 180, 3.0f),
|
||||
MINUTE_210(7, 210, 3.5f),
|
||||
MINUTE_240(8, 240, 4.0f),
|
||||
MINUTE_270(9, 270, 4.5f),
|
||||
MINUTE_300(10, 300, 5.0f),
|
||||
MINUTE_330(11, 330, 5.5f),
|
||||
MINUTE_360(12, 360, 6.0f),
|
||||
MINUTE_390(13, 390, 6.5f),
|
||||
MINUTE_420(14, 420, 7.0f),
|
||||
MINUTE_450(15, 450, 7.5f),
|
||||
MINUTE_480(16, 480, 8.0f);
|
||||
|
||||
val isOff: Boolean
|
||||
get() = this == OFF
|
||||
|
||||
val isNotOff: Boolean
|
||||
get() = this != OFF
|
||||
|
||||
fun milli(): Long {
|
||||
return TimeUnit.MINUTES.toMillis(this.minute.toLong())
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun getItemFromIndex(index: Int): BolusExDuration {
|
||||
var reverseIndices = SparseArrayCompat<BolusExDuration>()
|
||||
for (duration in values()) {
|
||||
reverseIndices.put(duration.index, duration)
|
||||
}
|
||||
val result = reverseIndices.get(index)
|
||||
return result ?: OFF
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun ofRaw(rawValue: Int): BolusExDuration {
|
||||
if (rawValue == null) {
|
||||
return OFF
|
||||
}
|
||||
|
||||
for (t in BolusExDuration.values()) {
|
||||
if (t.minute == rawValue) {
|
||||
return t
|
||||
}
|
||||
}
|
||||
return OFF
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.code
|
||||
|
||||
|
||||
enum class DeactivationStatus constructor(val rawValue: Int) {
|
||||
DEACTIVATION_FAILED(0),
|
||||
NORMAL_DEACTIVATED(1),
|
||||
FORCE_DEACTIVATED(2);
|
||||
|
||||
val isDeactivated: Boolean
|
||||
get() = this == NORMAL_DEACTIVATED || this == FORCE_DEACTIVATED
|
||||
|
||||
val isNormalSuccess: Boolean
|
||||
get() = this == NORMAL_DEACTIVATED
|
||||
|
||||
val isNormalFailed: Boolean
|
||||
get() = this == DEACTIVATION_FAILED || this == FORCE_DEACTIVATED
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun of(isSuccess: Boolean, forced: Boolean): DeactivationStatus {
|
||||
return when {
|
||||
isSuccess -> NORMAL_DEACTIVATED
|
||||
forced -> FORCE_DEACTIVATED
|
||||
else -> DEACTIVATION_FAILED
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.code
|
||||
|
||||
enum class Dummy {
|
||||
INSTANCE
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.code
|
||||
|
||||
enum class EventType {
|
||||
ACTIVTION_CLICKED,
|
||||
DEACTIVTION_CLICKED,
|
||||
SUSPEND_CLICKED,
|
||||
RESUME_CLICKED,
|
||||
INVALID_BASAL_RATE,
|
||||
PROFILE_NOT_SET,
|
||||
SHOW_PATCH_COMM_DIALOG,
|
||||
DISMISS_PATCH_COMM_DIALOG,
|
||||
SHOW_PATCH_COMM_ERROR_DIALOG,
|
||||
SHOW_BONDED_DIALOG,
|
||||
SHOW_CHANGE_PATCH_DIALOG,
|
||||
SHOW_PROGRESS_DIALOG,
|
||||
DISMISS_PROGRESS_DIALOG,
|
||||
FINISH_ACTIVITY,
|
||||
SHOW_DISCARD_DIALOG,
|
||||
PAUSE_BASAL_FAILED,
|
||||
RESUME_BASAL_FAILED,
|
||||
CALCULATED_BOLUS_CLICKED,
|
||||
EXTENDED_BOLUS_SETTINGS_CLICKED,
|
||||
SAVE_CLICKED
|
||||
;
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.code
|
||||
|
||||
import android.content.Context
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.R
|
||||
|
||||
enum class PatchExpireAlertTime private constructor(val index: Int, val hour: Int) {
|
||||
HOUR_1(0, 1),
|
||||
HOUR_2(1, 2),
|
||||
HOUR_3(2, 3),
|
||||
HOUR_4(3, 4),
|
||||
HOUR_5(4, 5),
|
||||
HOUR_6(5, 6),
|
||||
HOUR_7(6, 7),
|
||||
HOUR_8(7, 8),
|
||||
HOUR_9(8, 9),
|
||||
HOUR_10(9, 10),
|
||||
HOUR_11(10, 11),
|
||||
HOUR_12(11, 12),
|
||||
HOUR_13(12, 13),
|
||||
HOUR_14(13, 14),
|
||||
HOUR_15(14, 15),
|
||||
HOUR_16(15, 16),
|
||||
HOUR_17(16, 17),
|
||||
HOUR_18(17, 18),
|
||||
HOUR_19(18, 19),
|
||||
HOUR_20(19, 20),
|
||||
HOUR_21(20, 21),
|
||||
HOUR_22(21, 22),
|
||||
HOUR_23(22, 23),
|
||||
HOUR_24(23, 24);
|
||||
|
||||
companion object {
|
||||
|
||||
fun atIndex(index: Int): PatchExpireAlertTime {
|
||||
for (i in values()) {
|
||||
if (i.index == index) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return HOUR_1
|
||||
}
|
||||
|
||||
fun atHour(hour: Int): PatchExpireAlertTime {
|
||||
for (i in values()) {
|
||||
if (i.hour == hour) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return HOUR_1
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.code
|
||||
|
||||
enum class PatchLifecycle private constructor(val rawValue: Int) {
|
||||
SHUTDOWN(1),
|
||||
BONDED(2),
|
||||
SAFETY_CHECK(3),
|
||||
REMOVE_NEEDLE_CAP(4),
|
||||
REMOVE_PROTECTION_TAPE(5),
|
||||
ROTATE_KNOB(6),
|
||||
BASAL_SETTING(7),
|
||||
ACTIVATED(8);
|
||||
|
||||
val isShutdown: Boolean
|
||||
get() = this == SHUTDOWN
|
||||
|
||||
val isActivated: Boolean
|
||||
get() = this == ACTIVATED
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun ofRaw(rawValue: Int): PatchLifecycle {
|
||||
for (type in values()) {
|
||||
if (type.rawValue == rawValue) {
|
||||
return type
|
||||
}
|
||||
}
|
||||
return SHUTDOWN
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.code
|
||||
|
||||
enum class PatchStep {
|
||||
SAFE_DEACTIVATION,
|
||||
MANUALLY_TURNING_OFF_ALARM,
|
||||
DISCARDED,
|
||||
DISCARDED_FOR_CHANGE,
|
||||
DISCARDED_FROM_ALARM,
|
||||
WAKE_UP,
|
||||
CONNECT_NEW,
|
||||
REMOVE_NEEDLE_CAP,
|
||||
REMOVE_PROTECTION_TAPE,
|
||||
SAFETY_CHECK,
|
||||
ROTATE_KNOB,
|
||||
ROTATE_KNOB_NEEDLE_INSERTION_ERROR,
|
||||
BASAL_SCHEDULE,
|
||||
SETTING_REMINDER_TIME,
|
||||
CHECK_CONNECTION,
|
||||
CANCEL,
|
||||
COMPLETE,
|
||||
BACK_TO_HOME,
|
||||
FINISH;
|
||||
|
||||
val isConnectNew: Boolean
|
||||
get() = this == CONNECT_NEW
|
||||
|
||||
val isSafeDeactivation: Boolean
|
||||
get() = this == SAFE_DEACTIVATION
|
||||
|
||||
val isCheckConnection: Boolean
|
||||
get() = this == CHECK_CONNECTION
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.code
|
||||
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.R
|
||||
|
||||
class SettingKeys {
|
||||
companion object{
|
||||
val LOW_RESERVIOR_REMINDERS: Int = R.string.key_eopatch_low_reservior_reminders
|
||||
val EXPIRATION_REMINDERS: Int = R.string.key_eopatch_expiration_reminders
|
||||
val BUZZER_REMINDERS: Int = R.string.key_eopatch_patch_buzzer_reminders
|
||||
|
||||
val PATCH_CONFIG: Int = R.string.key_eopatch_patch_config
|
||||
val PATCH_STATE: Int = R.string.key_eopatch_patch_state
|
||||
val BOLUS_CURRENT: Int = R.string.key_eopatch_bolus_current
|
||||
val NORMAL_BASAL: Int = R.string.key_eopatch_normal_basal
|
||||
val TEMP_BASAL: Int = R.string.key_eopatch_temp_basal
|
||||
val ALARMS: Int = R.string.key_eopatch_bolus_current
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.code
|
||||
|
||||
import com.google.android.gms.common.internal.Preconditions
|
||||
|
||||
enum class UnitOrPercent private constructor(val rawValue: Int, val symbol: String) {
|
||||
P(0, "%"),
|
||||
U(1, "U");
|
||||
|
||||
|
||||
fun isPercentage() = this == P
|
||||
fun isU() = this == U
|
||||
|
||||
companion object {
|
||||
|
||||
val U_PER_HOUR = "U/hr"
|
||||
val U_PER_DAY = "U/day"
|
||||
|
||||
|
||||
/**
|
||||
* rawValue로 값을 찾기, 못찾으면 null을 리턴
|
||||
*/
|
||||
fun ofRaw(rawValue: Int?): UnitOrPercent? {
|
||||
if (rawValue == null) {
|
||||
return null
|
||||
}
|
||||
|
||||
for (t in UnitOrPercent.values()) {
|
||||
if (t.rawValue == rawValue) {
|
||||
return t
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* rawValue로 값을 찾기, 못찾으면 defaultValue을 리턴
|
||||
*/
|
||||
fun ofRaw(rawValue: Int?, defaultValue: UnitOrPercent): UnitOrPercent {
|
||||
Preconditions.checkNotNull(defaultValue)
|
||||
if (rawValue == null) {
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
for (t in UnitOrPercent.values()) {
|
||||
if (t.rawValue == rawValue) {
|
||||
return t
|
||||
}
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.dagger
|
||||
|
||||
import javax.inject.Qualifier
|
||||
import javax.inject.Scope
|
||||
|
||||
@Qualifier
|
||||
annotation class EopatchPluginQualifier
|
||||
|
||||
@MustBeDocumented
|
||||
@Scope
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class ActivityScope
|
||||
|
||||
@MustBeDocumented
|
||||
@Scope
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class FragmentScope
|
|
@ -0,0 +1,140 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.dagger
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.android.ContributesAndroidInjector
|
||||
import dagger.multibindings.IntoMap
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.OsAlarmReceiver
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.alarm.AlarmManager
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.alarm.AlarmRegistry
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.alarm.IAlarmManager
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.alarm.IAlarmRegistry
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ble.IPatchManager
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ble.IPreferenceManager
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ble.PatchManager
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ble.PreferenceManager
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ui.*
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ui.dialogs.AlarmDialog
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ui.dialogs.ActivationNotCompleteDialog
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ui.dialogs.CommonDialog
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ui.viewmodel.EopatchOverviewViewModel
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ui.viewmodel.EopatchViewModel
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ui.viewmodel.ViewModelFactory
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ui.viewmodel.ViewModelKey
|
||||
import javax.inject.Provider
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module(includes = [EopatchPrefModule::class])
|
||||
@Suppress("unused")
|
||||
abstract class EopatchModule {
|
||||
companion object {
|
||||
@Provides
|
||||
@EopatchPluginQualifier
|
||||
fun providesViewModelFactory(@EopatchPluginQualifier viewModels: MutableMap<Class<out ViewModel>, @JvmSuppressWildcards Provider<ViewModel>>): ViewModelProvider.Factory {
|
||||
return ViewModelFactory(viewModels)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
abstract fun bindPatchManager(patchManager: PatchManager): IPatchManager
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
abstract fun bindAlarmManager(alarmManager: AlarmManager): IAlarmManager
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
abstract fun bindAlarmRegistry(alarmRegistry: AlarmRegistry): IAlarmRegistry
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
abstract fun bindPreferenceManager(preferenceManager: PreferenceManager): IPreferenceManager
|
||||
|
||||
// #### VIEW MODELS ############################################################################
|
||||
@Binds
|
||||
@IntoMap
|
||||
@EopatchPluginQualifier
|
||||
@ViewModelKey(EopatchOverviewViewModel::class)
|
||||
internal abstract fun bindsEopatchOverviewViewmodel(viewModel: EopatchOverviewViewModel): ViewModel
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@EopatchPluginQualifier
|
||||
@ViewModelKey(EopatchViewModel::class)
|
||||
internal abstract fun bindsEopatchViewModel(viewModel: EopatchViewModel): ViewModel
|
||||
|
||||
// #### FRAGMENTS ##############################################################################
|
||||
@FragmentScope
|
||||
@ContributesAndroidInjector
|
||||
internal abstract fun contributesEopatchOverviewFragment(): EopatchOverviewFragment
|
||||
|
||||
@FragmentScope
|
||||
@ContributesAndroidInjector
|
||||
internal abstract fun contributesEopatchSafeDeactivationFragment(): EopatchSafeDeactivationFragment
|
||||
|
||||
@FragmentScope
|
||||
@ContributesAndroidInjector
|
||||
internal abstract fun contributesEopatchTurningOffAlarmFragment(): EopatchTurningOffAlarmFragment
|
||||
|
||||
@FragmentScope
|
||||
@ContributesAndroidInjector
|
||||
internal abstract fun contributesEopatchRemoveFragment(): EopatchRemoveFragment
|
||||
|
||||
@FragmentScope
|
||||
@ContributesAndroidInjector
|
||||
internal abstract fun contributesEopatchWakeUpFragment(): EopatchWakeUpFragment
|
||||
|
||||
@FragmentScope
|
||||
@ContributesAndroidInjector
|
||||
internal abstract fun contributesEopatchConnectNewFragment(): EopatchConnectNewFragment
|
||||
|
||||
@FragmentScope
|
||||
@ContributesAndroidInjector
|
||||
internal abstract fun contributesEopatchRemoveNeedleCapFragment(): EopatchRemoveNeedleCapFragment
|
||||
|
||||
@FragmentScope
|
||||
@ContributesAndroidInjector
|
||||
internal abstract fun contributesEopatchRemoveProtectionTapeFragment(): EopatchRemoveProtectionTapeFragment
|
||||
|
||||
@FragmentScope
|
||||
@ContributesAndroidInjector
|
||||
internal abstract fun contributesEopatchSafetyCheckFragment(): EopatchSafetyCheckFragment
|
||||
|
||||
@FragmentScope
|
||||
@ContributesAndroidInjector
|
||||
internal abstract fun contributesEopatchRotateKnobFragment(): EopatchRotateKnobFragment
|
||||
|
||||
@FragmentScope
|
||||
@ContributesAndroidInjector
|
||||
internal abstract fun contributesEopatchBasalScheduleFragment(): EopatchBasalScheduleFragment
|
||||
|
||||
@FragmentScope
|
||||
@ContributesAndroidInjector
|
||||
internal abstract fun contributesAlarmDialog(): AlarmDialog
|
||||
|
||||
@FragmentScope
|
||||
@ContributesAndroidInjector
|
||||
internal abstract fun contributesCommonDialog(): ActivationNotCompleteDialog
|
||||
|
||||
// Activities
|
||||
@ContributesAndroidInjector
|
||||
abstract fun contributesEopatchActivity(): EopatchActivity
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun contributesAlarmHelperActivity(): AlarmHelperActivity
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun contributesDialogHelperActivity(): DialogHelperActivity
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun contributesEoDialog(): CommonDialog
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun contributesOsAlarmReceiver(): OsAlarmReceiver
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.dagger
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.ble.*
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.vo.Alarms
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.vo.NormalBasalManager
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.vo.PatchConfig
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.vo.PatchState
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.vo.TempBasalManager
|
||||
import info.nightscout.shared.sharedPreferences.SP
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
class EopatchPrefModule {
|
||||
@Provides
|
||||
@Singleton
|
||||
internal fun providePatchConfig(): PatchConfig {
|
||||
return PatchConfig()
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
internal fun provideNormalBasalManager(sp: SP): NormalBasalManager {
|
||||
return NormalBasalManager()
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
internal fun provideTempBasalManager(sp: SP): TempBasalManager {
|
||||
return TempBasalManager()
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
internal fun provideAlarms(): Alarms {
|
||||
return Alarms()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.event
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import info.nightscout.androidaps.events.Event
|
||||
import info.nightscout.androidaps.plugins.pump.eopatch.alarm.AlarmCode
|
||||
|
||||
class EventEoPatchAlarm(var alarmCodes: Set<AlarmCode>, var isFirst: Boolean = false) : Event()
|
||||
class EventDialog(val dialog: DialogFragment, val show: Boolean) : Event()
|
||||
class EventProgressDialog(val show: Boolean, @StringRes val resId: Int = 0) : Event()
|
||||
class EventPatchActivationNotComplete() : Event()
|
|
@ -0,0 +1,62 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.extension
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.fragment.app.FragmentTransaction
|
||||
import java.io.Serializable
|
||||
|
||||
fun AppCompatActivity.replaceFragmentInActivity(fragment: Fragment, frameId: Int, addToBackStack: Boolean = false) {
|
||||
supportFragmentManager.transact {
|
||||
replace(frameId, fragment)
|
||||
if (addToBackStack) addToBackStack(null)
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun FragmentManager.transact(action: FragmentTransaction.() -> Unit) {
|
||||
beginTransaction().apply {
|
||||
action()
|
||||
}.commit()
|
||||
}
|
||||
|
||||
fun Intent.fillExtras(params: Array<out Pair<String, Any?>>){
|
||||
fillIntentArguments(this, params)
|
||||
}
|
||||
|
||||
private fun fillIntentArguments(intent: Intent, params: Array<out Pair<String, Any?>>) {
|
||||
params.forEach {
|
||||
when (val value = it.second) {
|
||||
null -> intent.putExtra(it.first, null as Serializable?)
|
||||
is Int -> intent.putExtra(it.first, value)
|
||||
is Long -> intent.putExtra(it.first, value)
|
||||
is CharSequence -> intent.putExtra(it.first, value)
|
||||
is String -> intent.putExtra(it.first, value)
|
||||
is Float -> intent.putExtra(it.first, value)
|
||||
is Double -> intent.putExtra(it.first, value)
|
||||
is Char -> intent.putExtra(it.first, value)
|
||||
is Short -> intent.putExtra(it.first, value)
|
||||
is Boolean -> intent.putExtra(it.first, value)
|
||||
is Serializable -> intent.putExtra(it.first, value)
|
||||
is Bundle -> intent.putExtra(it.first, value)
|
||||
is Parcelable -> intent.putExtra(it.first, value)
|
||||
is Array<*> -> when {
|
||||
value.isArrayOf<CharSequence>() -> intent.putExtra(it.first, value)
|
||||
value.isArrayOf<String>() -> intent.putExtra(it.first, value)
|
||||
value.isArrayOf<Parcelable>() -> intent.putExtra(it.first, value)
|
||||
else -> throw Exception("Intent extra ${it.first} has wrong type ${value.javaClass.name}")
|
||||
}
|
||||
is IntArray -> intent.putExtra(it.first, value)
|
||||
is LongArray -> intent.putExtra(it.first, value)
|
||||
is FloatArray -> intent.putExtra(it.first, value)
|
||||
is DoubleArray -> intent.putExtra(it.first, value)
|
||||
is CharArray -> intent.putExtra(it.first, value)
|
||||
is ShortArray -> intent.putExtra(it.first, value)
|
||||
is BooleanArray -> intent.putExtra(it.first, value)
|
||||
else -> throw Exception("Intent extra ${it.first} has wrong type ${value.javaClass.name}")
|
||||
}
|
||||
return@forEach
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.extension
|
||||
|
||||
fun <T> Boolean.takeOne(whenTrue: T, whenFalse: T): T {
|
||||
return if(this) whenTrue else whenFalse
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.extension
|
||||
|
||||
import android.text.Spanned
|
||||
|
||||
fun CharSequence?.check(oldText: CharSequence?): Boolean {
|
||||
val text = this
|
||||
if (text === oldText || text == null && oldText?.length == 0) {
|
||||
return false
|
||||
}
|
||||
if (text is Spanned) {
|
||||
if (text == oldText) {
|
||||
return false // No change in the spans, so don't set anything.
|
||||
}
|
||||
} else if (!text.haveContentsChanged(oldText)) {
|
||||
return false // No content changes, so don't set anything.
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
fun CharSequence?.haveContentsChanged(str2: CharSequence?): Boolean {
|
||||
val str1: CharSequence? = this
|
||||
if (str1 == null != (str2 == null)) {
|
||||
return true
|
||||
} else if (str1 == null) {
|
||||
return false
|
||||
}
|
||||
val length = str1.length
|
||||
if (length != str2!!.length) {
|
||||
return true
|
||||
}
|
||||
for (i in 0 until length) {
|
||||
if (str1[i] != str2[i]) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.extension
|
||||
|
||||
import io.reactivex.Completable
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import timber.log.Timber
|
||||
|
||||
fun Completable.observeOnMainThread(): Completable = observeOn(AndroidSchedulers.mainThread())
|
||||
|
||||
fun Completable.observeOnComputation(): Completable = observeOn(Schedulers.computation())
|
||||
|
||||
fun Completable.observeOnIo(): Completable = observeOn(Schedulers.io())
|
||||
|
||||
fun Completable.subscribeEmpty(): Disposable {
|
||||
return subscribe({}, {})
|
||||
}
|
||||
|
||||
fun Completable.subscribeEmpty(onComplete: () -> Unit, onError: (Throwable) -> Unit): Disposable {
|
||||
return subscribe(onComplete, onError)
|
||||
}
|
||||
|
||||
fun Completable.subscribeDefault(): Disposable {
|
||||
return subscribe({ Timber.d("onComplete") }, { Timber.e(it, "onError") })
|
||||
}
|
||||
|
||||
fun Completable.subscribeDefault(onComplete: () -> Unit): Disposable {
|
||||
return subscribe(onComplete, { Timber.e(it, "onError") })
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.extension
|
||||
|
||||
fun Float.nearlyEqual(b: Float, epsilon: Float): Boolean {
|
||||
val absA = Math.abs(this)
|
||||
val absB = Math.abs(b)
|
||||
val diff = Math.abs(this - b)
|
||||
return if (this == b) {
|
||||
true
|
||||
} else if (this == 0f || b == 0f || absA + absB < java.lang.Float.MIN_NORMAL) {
|
||||
diff < epsilon * java.lang.Float.MIN_NORMAL
|
||||
} else {
|
||||
diff / Math.min(absA + absB, Float.MAX_VALUE) < epsilon
|
||||
}
|
||||
}
|
||||
|
||||
fun Float.nearlyNotEqual(b: Float, epsilon: Float): Boolean {
|
||||
return !nearlyEqual(b, epsilon)
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.extension
|
||||
|
||||
import java.util.*
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
internal val Long.date: Date
|
||||
get() = Calendar.getInstance().also { it.timeInMillis = this }.time
|
||||
|
||||
fun Long.getDiffTime(isRelative: Boolean = false): Triple<Long, Long, Long> {
|
||||
val inputTimeMillis = this
|
||||
val currentTimeMillis = System.currentTimeMillis()
|
||||
val diffTimeMillis = if (inputTimeMillis > currentTimeMillis) inputTimeMillis - currentTimeMillis else isRelative.takeOne(currentTimeMillis - inputTimeMillis, 0)
|
||||
|
||||
val hours = TimeUnit.MILLISECONDS.toHours(diffTimeMillis)
|
||||
val minutes = TimeUnit.MILLISECONDS.toMinutes(diffTimeMillis - TimeUnit.HOURS.toMillis(hours))
|
||||
val seconds = TimeUnit.MILLISECONDS.toSeconds(diffTimeMillis - TimeUnit.HOURS.toMillis(hours) - TimeUnit.MINUTES.toMillis(minutes))
|
||||
|
||||
return Triple(hours, minutes, seconds)
|
||||
}
|
||||
|
||||
fun Long.getDiffTime(startTimeMillis: Long): Triple<Long, Long, Long> {
|
||||
val inputTimeMillis = this
|
||||
val diffTimeMillis = if (inputTimeMillis > startTimeMillis) inputTimeMillis - startTimeMillis else 0
|
||||
|
||||
val hours = TimeUnit.MILLISECONDS.toHours(diffTimeMillis)
|
||||
val minutes = TimeUnit.MILLISECONDS.toMinutes(diffTimeMillis - TimeUnit.HOURS.toMillis(hours))
|
||||
val seconds = TimeUnit.MILLISECONDS.toSeconds(diffTimeMillis - TimeUnit.HOURS.toMillis(hours) - TimeUnit.MINUTES.toMillis(minutes))
|
||||
|
||||
return Triple(hours, minutes, seconds)
|
||||
}
|
||||
|
||||
fun Long.getDiffDays(isRelative: Boolean = false): Long {
|
||||
val inputTimeMillis = this
|
||||
val currentTimeMillis = System.currentTimeMillis()
|
||||
val diffTimeMillis = if (inputTimeMillis > currentTimeMillis) inputTimeMillis - currentTimeMillis else isRelative.takeOne(currentTimeMillis - inputTimeMillis, 0)
|
||||
|
||||
return TimeUnit.MILLISECONDS.toDays(diffTimeMillis)
|
||||
}
|
||||
|
||||
fun Long.getSeconds() : Long {
|
||||
return (this/1000)*1000
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.extension
|
||||
|
||||
import io.reactivex.Maybe
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import timber.log.Timber
|
||||
|
||||
fun <T> Maybe<T>.observeOnMainThread(): Maybe<T> = observeOn(AndroidSchedulers.mainThread())
|
||||
|
||||
fun <T> Maybe<T>.observeOnComputation(): Maybe<T> = observeOn(Schedulers.computation())
|
||||
|
||||
fun <T> Maybe<T>.observeOnIo(): Maybe<T> = observeOn(Schedulers.io())
|
||||
|
||||
fun <T> Maybe<T>.subscribeEmpty(): Disposable = subscribe({}, {}, {})
|
||||
|
||||
fun <T> Maybe<T>.subscribeEmpty(onSuccess: (T) -> Unit): Disposable = subscribe(onSuccess, {}, {})
|
||||
|
||||
fun <T> Maybe<T>.subscribeEmpty(onSuccess: (T) -> Unit, onError: (Throwable) -> Unit): Disposable = subscribe(onSuccess, onError, {})
|
||||
|
||||
fun <T> Maybe<T>.subscribeDefault(): Disposable = subscribe({ Timber.d("onSuccess") }, { Timber.e(it, "onError") }, { Timber.d("onComplete") })
|
||||
|
||||
fun <T> Maybe<T>.subscribeDefault(onSuccess: (T) -> Unit): Disposable = subscribe(onSuccess, { Timber.e(it, "onError") }, { Timber.d("onComplete") })
|
||||
|
||||
fun <T> Maybe<T>.subscribeDefault(onSuccess: (T) -> Unit, onError: (Throwable) -> Unit): Disposable = subscribe(onSuccess, onError, { Timber.d("onComplete") })
|
||||
|
||||
fun <T> Maybe<T>.with(): Maybe<T> = subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
|
|
@ -0,0 +1,27 @@
|
|||
package info.nightscout.androidaps.plugins.pump.eopatch.extension
|
||||
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import timber.log.Timber
|
||||
|
||||
fun <T> Observable<T>.observeOnMainThread(): Observable<T> = observeOn(AndroidSchedulers.mainThread())
|
||||
|
||||
fun <T> Observable<T>.observeOnComputation(): Observable<T> = observeOn(Schedulers.computation())
|
||||
|
||||
fun <T> Observable<T>.observeOnIo(): Observable<T> = observeOn(Schedulers.io())
|
||||
|
||||
fun <T> Observable<T>.subscribeEmpty(): Disposable = subscribe({}, {}, {})
|
||||
|
||||
fun <T> Observable<T>.subscribeEmpty(onSuccess: (T) -> Unit): Disposable = subscribe(onSuccess, {}, {})
|
||||
|
||||
fun <T> Observable<T>.subscribeEmpty(onSuccess: (T) -> Unit, onError: (Throwable) -> Unit): Disposable = subscribe(onSuccess, onError, {})
|
||||
|
||||
fun <T> Observable<T>.subscribeDefault(): Disposable = subscribe({ Timber.d("onSuccess") }, { Timber.e(it, "onError") }, { Timber.d("onComplete") })
|
||||
|
||||
fun <T> Observable<T>.subscribeDefault(onSuccess: (T) -> Unit): Disposable = subscribe(onSuccess, { Timber.e(it, "onError") }, { Timber.d("onComplete") })
|
||||
|
||||
fun <T> Observable<T>.subscribeDefault(onSuccess: (T) -> Unit, onError: (Throwable) -> Unit): Disposable = subscribe(onSuccess, onError, { Timber.d("onComplete") })
|
||||
|
||||
fun <T> Observable<T>.with(): Observable<T> = subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue