diff --git a/.circleci/config.yml b/.circleci/config.yml index 4593de71fc5..d54942a43f5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -4,35 +4,58 @@ version: 2.1 # Orbs are reusable packages of CircleCI configuration that you may share across projects, enabling you to create encapsulated, parameterized commands, jobs, and executors that can be used across multiple projects. orbs: - android: circleci/android@2.3.0 + android: circleci/android@2.4.0 codecov: codecov/codecov@3.3.0 jobs: # Below is the definition of your job to build and test your app, you can rename and customize it as you want. build-and-test: - # These next lines define the Android machine image executor: https://circleci.com/docs/2.0/executor-types/ - executor: - name: android/android-machine - resource-class: large - tag: 2023.11.1 + machine: true + resource_class: nightscout/android steps: - checkout - - android/change-java-version: - java-version: 17 + - run: + name: Create avd + command: | + echo "no" | /opt/android-sdk/cmdline-tools/latest/bin/avdmanager --verbose create avd -n citest -k "system-images;android-29;google_apis_playstore;x86" --force + + - run: + name: Launch emulator + command: | + export ANDROID_SDK_ROOT=/opt/android-sdk + export ANDROID_HOME=/opt/android-sdk + emulator -avd citest -delay-adb -verbose -no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim + background: true + + - run: + name: Run connectedFullDebugAndroidTest + command: | + export ANDROID_SDK_ROOT=/opt/android-sdk + export ANDROID_HOME=/opt/android-sdk + env + ./gradlew -Dorg.gradle.jvmargs=-Xmx6g connectedFullDebugAndroidTest - - android/start-emulator-and-run-tests: - system-image: system-images;android-29;google_apis;x86 - # Compile while the emulator starts to use the time. - post-emulator-launch-assemble-command: ./gradlew compileFullDebugUnitTestSources compileFullDebugAndroidTestSources - test-command: ./gradlew connectedFullDebugAndroidTest + - run: + name: Kill emulators + command: | + echo "Killing emulators" + adb devices | grep emulator | cut -f1 | while read -r line; do adb -s $line emu kill; done - - android/run-tests: - test-command: ./gradlew testFullDebugUnitTest + - run: + name: Run testFullDebugUnitTest + command: | + export ANDROID_SDK_ROOT=/opt/android-sdk + export ANDROID_HOME=/opt/android-sdk + ./gradlew -Dorg.gradle.jvmargs=-Xmx6g testFullDebugUnitTest - - android/run-tests: - test-command: ./gradlew --stacktrace jacocoAllDebugReport + - run: + run: Run jacocoAllDebugReport + command: | + export ANDROID_SDK_ROOT=/opt/android-sdk + export ANDROID_HOME=/opt/android-sdk + ./gradlew --stacktrace jacocoAllDebugReport - run: name: Save test results diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 8db9f1ace1c..c3c3d2b4655 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -198,6 +198,7 @@ dependencies { implementation(project(":pump:danar")) implementation(project(":pump:diaconn")) implementation(project(":pump:eopatch")) + implementation(project(":pump:equil")) implementation(project(":pump:medtrum")) implementation(project(":insight")) implementation(project(":pump:medtronic")) diff --git a/app/src/main/kotlin/app/aaps/di/AppComponent.kt b/app/src/main/kotlin/app/aaps/di/AppComponent.kt index c395f47632a..646f6d89e4f 100644 --- a/app/src/main/kotlin/app/aaps/di/AppComponent.kt +++ b/app/src/main/kotlin/app/aaps/di/AppComponent.kt @@ -14,6 +14,7 @@ import app.aaps.plugins.main.di.PluginsModule import app.aaps.plugins.source.di.SourceModule import app.aaps.plugins.sync.di.OpenHumansModule import app.aaps.plugins.sync.di.SyncModule +import app.aaps.pump.equil.di.EquilModule import app.aaps.pump.virtual.di.VirtualPumpModule import app.aaps.shared.impl.di.SharedImplModule import app.aaps.ui.di.UiModule @@ -76,6 +77,7 @@ import javax.inject.Singleton DanaRSModule::class, DiaconnG8Module::class, EopatchModule::class, + EquilModule::class, InsightModule::class, InsightDatabaseModule::class, MedtronicModule::class, diff --git a/app/src/main/kotlin/app/aaps/di/PluginsListModule.kt b/app/src/main/kotlin/app/aaps/di/PluginsListModule.kt index 36c1cd22c0a..5c1e29f7c9e 100644 --- a/app/src/main/kotlin/app/aaps/di/PluginsListModule.kt +++ b/app/src/main/kotlin/app/aaps/di/PluginsListModule.kt @@ -53,6 +53,7 @@ import app.aaps.plugins.sync.nsclientV3.NSClientV3Plugin import app.aaps.plugins.sync.openhumans.OpenHumansUploaderPlugin import app.aaps.plugins.sync.tidepool.TidepoolPlugin import app.aaps.plugins.sync.xdrip.XdripPlugin +import app.aaps.pump.equil.EquilPumpPlugin import app.aaps.pump.virtual.VirtualPumpPlugin import dagger.Binds import dagger.Module @@ -221,9 +222,15 @@ abstract class PluginsListModule { abstract fun bindMedtrumPlugin(plugin: MedtrumPlugin): PluginBase @Binds - @AllConfigs + @PumpDriver @IntoMap @IntKey(170) + abstract fun bindEquilPumpPlugin(plugin: EquilPumpPlugin): PluginBase + + @Binds + @AllConfigs + @IntoMap + @IntKey(180) abstract fun bindVirtualPumpPlugin(plugin: VirtualPumpPlugin): PluginBase @Binds diff --git a/build.gradle.kts b/build.gradle.kts index e973d9c30e4..e242b0f99da 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,8 +8,8 @@ buildscript { mavenCentral() } dependencies { - classpath("com.android.tools.build:gradle:8.2.1") - classpath("com.google.gms:google-services:4.4.0") + classpath("com.android.tools.build:gradle:8.4.0") + classpath("com.google.gms:google-services:4.4.1") classpath("com.google.firebase:firebase-crashlytics-gradle:2.9.9") // NOTE: Do not place your application dependencies here; they belong @@ -58,5 +58,5 @@ allprojects { apply(from = "jacoco_aggregation.gradle.kts") tasks.register("clean").configure { - delete(rootProject.buildDir) + delete(rootProject.layout.buildDirectory) } diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 223da698c98..c0c3eadb253 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -1,7 +1,7 @@ object KtsBuildVersions { - const val gradle = "8.2.0" - const val kotlin = "1.9.10" + const val gradle = "8.4.0" + const val kotlin = "1.9.23" } plugins { diff --git a/buildSrc/src/main/kotlin/Libs.kt b/buildSrc/src/main/kotlin/Libs.kt index ecc70d9727b..0bf141f13d9 100644 --- a/buildSrc/src/main/kotlin/Libs.kt +++ b/buildSrc/src/main/kotlin/Libs.kt @@ -3,7 +3,7 @@ object Libs { object Kotlin { - const val kotlin = "1.9.10" + const val kotlin = "1.9.23" const val stdlibJdk8 = "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin" const val reflect = "org.jetbrains.kotlin:kotlin-reflect:$kotlin" diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index 340ef9ae8aa..4dd9dc712a8 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -2,7 +2,7 @@ import org.gradle.api.JavaVersion object Versions { - const val appVersion = "3.2.0.4" + const val appVersion = "3.2.1.0" const val versionCode = 1500 const val ndkVersion = "21.1.6352462" diff --git a/buildSrc/src/main/kotlin/jacoco-app-dependencies.gradle.kts b/buildSrc/src/main/kotlin/jacoco-app-dependencies.gradle.kts index db9a293e9ef..a5178e24c7f 100644 --- a/buildSrc/src/main/kotlin/jacoco-app-dependencies.gradle.kts +++ b/buildSrc/src/main/kotlin/jacoco-app-dependencies.gradle.kts @@ -117,11 +117,11 @@ fun Project.registerCodeCoverageTask( description = "Generate Jacoco coverage reports on the ${sourceName.replaceFirstChar(Char::titlecase)} build." val javaDirectories = fileTree( - "${project.buildDir}/intermediates/classes/${sourcePath}" + layout.buildDirectory.dir("intermediates/classes/${sourcePath}") ) { exclude(excludedFiles) } val kotlinDirectories = fileTree( - "${project.buildDir}/tmp/kotlin-classes/${sourcePath}" + layout.buildDirectory.dir("tmp/kotlin-classes/${sourcePath}") ) { exclude(excludedFiles) } val coverageSrcDirectories = listOf( @@ -137,7 +137,7 @@ fun Project.registerCodeCoverageTask( additionalClassDirs.setFrom(files(coverageSrcDirectories)) sourceDirectories.setFrom(files(coverageSrcDirectories)) executionData.setFrom( - files("${project.buildDir}/jacoco/${testTaskName}.exec") + files(layout.buildDirectory.dir("jacoco/${testTaskName}.exec")) ) reports { diff --git a/buildSrc/src/main/kotlin/jacoco-module-dependencies.gradle.kts b/buildSrc/src/main/kotlin/jacoco-module-dependencies.gradle.kts index e199e8ff78e..11818442b32 100644 --- a/buildSrc/src/main/kotlin/jacoco-module-dependencies.gradle.kts +++ b/buildSrc/src/main/kotlin/jacoco-module-dependencies.gradle.kts @@ -117,11 +117,11 @@ fun Project.registerCodeCoverageTask( description = "Generate Jacoco coverage reports on the ${sourceName.replaceFirstChar(Char::titlecase)} build." val javaDirectories = fileTree( - "${project.buildDir}/intermediates/classes/${sourcePath}" + layout.buildDirectory.dir("intermediates/classes/${sourcePath}") ) { exclude(excludedFiles) } val kotlinDirectories = fileTree( - "${project.buildDir}/tmp/kotlin-classes/${sourcePath}" + layout.buildDirectory.dir("tmp/kotlin-classes/${sourcePath}") ) { exclude(excludedFiles) } val coverageSrcDirectories = listOf( @@ -137,7 +137,7 @@ fun Project.registerCodeCoverageTask( additionalClassDirs.setFrom(files(coverageSrcDirectories)) sourceDirectories.setFrom(files(coverageSrcDirectories)) executionData.setFrom( - files("${project.buildDir}/jacoco/${testTaskName}.exec") + files(layout.buildDirectory.dir("jacoco/${testTaskName}.exec")) ) reports { diff --git a/core/interfaces/src/main/kotlin/app/aaps/core/interfaces/pump/defs/ManufacturerType.kt b/core/interfaces/src/main/kotlin/app/aaps/core/interfaces/pump/defs/ManufacturerType.kt index 71125404a22..5197b92b02d 100644 --- a/core/interfaces/src/main/kotlin/app/aaps/core/interfaces/pump/defs/ManufacturerType.kt +++ b/core/interfaces/src/main/kotlin/app/aaps/core/interfaces/pump/defs/ManufacturerType.kt @@ -12,5 +12,6 @@ enum class ManufacturerType(val description: String) { Roche("Roche"), Ypsomed("Ypsomed"), G2e("G2e"), - Eoflow("Eoflow"); + Eoflow("Eoflow"), + Equil("Equil"); } \ No newline at end of file diff --git a/core/interfaces/src/main/kotlin/app/aaps/core/interfaces/pump/defs/PumpType.kt b/core/interfaces/src/main/kotlin/app/aaps/core/interfaces/pump/defs/PumpType.kt index ebf385556e1..59bbd60ddfd 100644 --- a/core/interfaces/src/main/kotlin/app/aaps/core/interfaces/pump/defs/PumpType.kt +++ b/core/interfaces/src/main/kotlin/app/aaps/core/interfaces/pump/defs/PumpType.kt @@ -434,6 +434,24 @@ enum class PumpType { description = "Medtrum untested", model = "untested", parent = MEDTRUM_NANO + ), + EQUIL( + description = "Equil", + manufacturer = ManufacturerType.Equil, + model = "Equil", + bolusSize = 0.05, + specialBolusSize = null, + extendedBolusSettings = DoseSettings(0.05, 30, 5 * 60, 0.05), + pumpTempBasalType = PumpTempBasalType.Absolute, + tbrSettings = DoseSettings(0.05, 30, 24 * 60, 0.0, 35.0), + specialBasalDurations = PumpCapability.BasalRate_Duration30minAllowed, + baseBasalMinValue = 0.05, + baseBasalMaxValue = 3.0, + baseBasalStep = 0.01, + baseBasalSpecialSteps = null, + pumpCapability = PumpCapability.DiaconnCapabilities, + source = Source.EQuil, + useHardwareLink = true, ); val description: String @@ -506,7 +524,8 @@ enum class PumpType { Medtrum, MDI, VirtualPump, - Unknown + Unknown, + EQuil } companion object { diff --git a/core/main/src/main/kotlin/app/aaps/core/main/pump/PumpTypeExtension.kt b/core/main/src/main/kotlin/app/aaps/core/main/pump/PumpTypeExtension.kt index 17676bbfcca..16f95355dbd 100644 --- a/core/main/src/main/kotlin/app/aaps/core/main/pump/PumpTypeExtension.kt +++ b/core/main/src/main/kotlin/app/aaps/core/main/pump/PumpTypeExtension.kt @@ -59,6 +59,7 @@ fun PumpType.Companion.fromDbPumpType(pt: InterfaceIDs.PumpType): PumpType = InterfaceIDs.PumpType.USER -> PumpType.USER InterfaceIDs.PumpType.DIACONN_G8 -> PumpType.DIACONN_G8 InterfaceIDs.PumpType.EOPATCH2 -> PumpType.EOFLOW_EOPATCH2 + InterfaceIDs.PumpType.EQUIL -> PumpType.EQUIL InterfaceIDs.PumpType.MEDTRUM -> PumpType.MEDTRUM_NANO InterfaceIDs.PumpType.MEDTRUM_300U -> PumpType.MEDTRUM_300U InterfaceIDs.PumpType.MEDTRUM_UNTESTED -> PumpType.MEDTRUM_UNTESTED @@ -84,6 +85,7 @@ fun PumpType.Source.toDbSource(): UserEntry.Sources = PumpType.Source.Medtrum -> UserEntry.Sources.Medtrum PumpType.Source.MDI -> UserEntry.Sources.MDI PumpType.Source.VirtualPump -> UserEntry.Sources.VirtualPump + PumpType.Source.EQuil -> UserEntry.Sources.Equil else -> UserEntry.Sources.Unknown } @@ -121,6 +123,7 @@ fun PumpType.toDbPumpType(): InterfaceIDs.PumpType = PumpType.USER -> InterfaceIDs.PumpType.USER PumpType.DIACONN_G8 -> InterfaceIDs.PumpType.DIACONN_G8 PumpType.EOFLOW_EOPATCH2 -> InterfaceIDs.PumpType.EOPATCH2 + PumpType.EQUIL -> InterfaceIDs.PumpType.EQUIL PumpType.MEDTRUM_NANO -> InterfaceIDs.PumpType.MEDTRUM PumpType.MEDTRUM_300U -> InterfaceIDs.PumpType.MEDTRUM_300U PumpType.MEDTRUM_UNTESTED -> InterfaceIDs.PumpType.MEDTRUM_UNTESTED diff --git a/core/ui/src/main/res/drawable/ic_equil_128.png b/core/ui/src/main/res/drawable/ic_equil_128.png new file mode 100644 index 00000000000..3944569d7b9 Binary files /dev/null and b/core/ui/src/main/res/drawable/ic_equil_128.png differ diff --git a/crowdin.yml b/crowdin.yml index ef7bc6e2a5c..db318b516ae 100644 --- a/crowdin.yml +++ b/crowdin.yml @@ -63,6 +63,8 @@ files: translation: /pump/danar/src/main/res/values-%android_code%/strings.xml - source: /pump/danars/src/main/res/values/strings.xml translation: /pump/danars/src/main/res/values-%android_code%/strings.xml + - source: /pump/equil/src/main/res/values/strings.xml + translation: /pump/equil/src/main/res/values-%android_code%/strings.xml - source: /pump/medtronic/src/main/res/values/strings.xml translation: /pump/medtronic/src/main/res/values-%android_code%/strings.xml - source: /pump/medtrum/src/main/res/values/strings.xml diff --git a/database/entities/src/main/kotlin/app/aaps/database/entities/UserEntry.kt b/database/entities/src/main/kotlin/app/aaps/database/entities/UserEntry.kt index d336cbfd114..e80354b0cf1 100644 --- a/database/entities/src/main/kotlin/app/aaps/database/entities/UserEntry.kt +++ b/database/entities/src/main/kotlin/app/aaps/database/entities/UserEntry.kt @@ -176,6 +176,7 @@ data class UserEntry( OmnipodEros, OmnipodDash, //No entry currently EOPatch2, + Equil, Medtrum, MDI, VirtualPump, diff --git a/database/entities/src/main/kotlin/app/aaps/database/entities/embedments/InterfaceIDs.kt b/database/entities/src/main/kotlin/app/aaps/database/entities/embedments/InterfaceIDs.kt index 5bc81af5552..d26c806c498 100644 --- a/database/entities/src/main/kotlin/app/aaps/database/entities/embedments/InterfaceIDs.kt +++ b/database/entities/src/main/kotlin/app/aaps/database/entities/embedments/InterfaceIDs.kt @@ -48,6 +48,7 @@ data class InterfaceIDs @Ignore constructor( MDI, DIACONN_G8, EOPATCH2, + EQUIL, MEDTRUM, MEDTRUM_300U, MEDTRUM_UNTESTED, diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e2968a868fe..1f33f874378 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Sun Sep 18 18:21:09 CEST 2022 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME diff --git a/implementation/src/main/kotlin/app/aaps/implementation/userEntry/UserEntryPresentationHelperImpl.kt b/implementation/src/main/kotlin/app/aaps/implementation/userEntry/UserEntryPresentationHelperImpl.kt index 35c42443a3f..256f8323645 100644 --- a/implementation/src/main/kotlin/app/aaps/implementation/userEntry/UserEntryPresentationHelperImpl.kt +++ b/implementation/src/main/kotlin/app/aaps/implementation/userEntry/UserEntryPresentationHelperImpl.kt @@ -97,6 +97,7 @@ class UserEntryPresentationHelperImpl @Inject constructor( Sources.OmnipodEros -> R.drawable.ic_patch_pump_outline Sources.OmnipodDash -> R.drawable.ic_patch_pump_outline Sources.EOPatch2 -> app.aaps.core.ui.R.drawable.ic_eopatch2_128 + Sources.Equil -> app.aaps.core.ui.R.drawable.ic_equil_128 Sources.Medtrum -> app.aaps.core.ui.R.drawable.ic_medtrum_128 Sources.MDI -> R.drawable.ic_ict Sources.VirtualPump -> R.drawable.ic_virtual_pump diff --git a/jacoco_aggregation.gradle.kts b/jacoco_aggregation.gradle.kts index 23a2da05317..9c0e3550fab 100644 --- a/jacoco_aggregation.gradle.kts +++ b/jacoco_aggregation.gradle.kts @@ -42,8 +42,8 @@ project.afterEvaluate { val classesDirectories = mutableListOf().also { subprojects.forEach { proj -> variants.forEach { variant -> - it.add("${proj.buildDir}/intermediates/javac/$variant/classes") - it.add("${proj.buildDir}/tmp/kotlin-classes/$variant") + it.add("${proj.layout.buildDirectory}/intermediates/javac/$variant/classes") + it.add("${proj.layout.buildDirectory}/tmp/kotlin-classes/$variant") } } } @@ -71,12 +71,12 @@ project.afterEvaluate { val executions = mutableListOf().also { subprojects.forEach { proj -> variants.forEach { variant -> - val path = "${proj.buildDir}/outputs/unit_test_code_coverage/${variant}UnitTest/test${variant.replaceFirstChar(Char::titlecase)}UnitTest.exec" + val path = "${proj.layout.buildDirectory}/outputs/unit_test_code_coverage/${variant}UnitTest/test${variant.replaceFirstChar(Char::titlecase)}UnitTest.exec" if ((File(path)).exists()) { it.add(path) println("Collecting execution data from: $path") } - val androidPath = "${proj.buildDir}/outputs/code_coverage/${variant}AndroidTest/connected/" + val androidPath = "${proj.layout.buildDirectory}/outputs/code_coverage/${variant}AndroidTest/connected/" val androidFiles = fileTree(androidPath) androidFiles.forEach { file -> it.add(file.path) diff --git a/plugins/constraints/src/main/res/xml/pref_safety.xml b/plugins/constraints/src/main/res/xml/pref_safety.xml index 180e13c3b55..04ac7dd49f2 100644 --- a/plugins/constraints/src/main/res/xml/pref_safety.xml +++ b/plugins/constraints/src/main/res/xml/pref_safety.xml @@ -21,7 +21,7 @@ android:inputType="numberDecimal" android:key="@string/key_treatmentssafety_maxbolus" android:title="@string/max_bolus_title" - validate:floatmaxNumber="25.0" + validate:floatmaxNumber="60.0" validate:floatminNumber="0.1" validate:testType="floatNumericRange" /> diff --git a/plugins/constraints/src/test/kotlin/app/aaps/plugins/constraints/signatureVerifier/SignatureVerifierPluginTest.kt b/plugins/constraints/src/test/kotlin/app/aaps/plugins/constraints/signatureVerifier/SignatureVerifierPluginTest.kt index e57455dfdb3..00098dc9112 100644 --- a/plugins/constraints/src/test/kotlin/app/aaps/plugins/constraints/signatureVerifier/SignatureVerifierPluginTest.kt +++ b/plugins/constraints/src/test/kotlin/app/aaps/plugins/constraints/signatureVerifier/SignatureVerifierPluginTest.kt @@ -25,6 +25,7 @@ class SignatureVerifierPluginTest : TestBase() { fun singleCharUnMapTest() { @Suppress("SpellCheckingInspection") val key = "2ΙšÄΠΒϨÒÇeЄtЄЗž-*Ж*ZcHijЊÄœ<|x\"Ε" val unmapped = singleCharUnMap(key) + assertThat(key.length).isEqualTo(32) assertThat(unmapped).isEqualTo("32:99:61:C4:A0:92:E8:D2:C7:65:04:74:04:17:7E:2D:2A:16:2A:5A:63:48:69:6A:0A:C4:53:3C:7C:78:22:95") } } diff --git a/plugins/main/src/main/kotlin/app/aaps/plugins/main/general/smsCommunicator/SmsCommunicatorPlugin.kt b/plugins/main/src/main/kotlin/app/aaps/plugins/main/general/smsCommunicator/SmsCommunicatorPlugin.kt index 9726e1bd4a2..e5b13915b66 100644 --- a/plugins/main/src/main/kotlin/app/aaps/plugins/main/general/smsCommunicator/SmsCommunicatorPlugin.kt +++ b/plugins/main/src/main/kotlin/app/aaps/plugins/main/general/smsCommunicator/SmsCommunicatorPlugin.kt @@ -129,7 +129,7 @@ class SmsCommunicatorPlugin @Inject constructor( val commands = mapOf( "BG" to "BG", "LOOP" to "LOOP STOP/DISABLE/START/ENABLE/RESUME/STATUS/CLOSED/LGS\nLOOP SUSPEND 20", - "NSCLIENT" to "NSCLIENT RESTART", + "AAPSCLIENT" to "AAPSCLIENT RESTART", "PUMP" to "PUMP\nPUMP CONNECT\nPUMP DISCONNECT 30\n", "BASAL" to "BASAL STOP/CANCEL\nBASAL 0.3\nBASAL 0.3 20\nBASAL 30%\nBASAL 30% 20\n", "BOLUS" to "BOLUS 1.2\nBOLUS 1.2 MEAL", @@ -280,7 +280,7 @@ class SmsCommunicatorPlugin @Inject constructor( else if (divided.size == 2 || divided.size == 3) processLOOP(divided, receivedSms) else sendSMS(Sms(receivedSms.phoneNumber, rh.gs(R.string.wrong_format))) - "NSCLIENT" -> + "AAPSCLIENT" -> if (divided.size == 2) processNSCLIENT(divided, receivedSms) else sendSMS(Sms(receivedSms.phoneNumber, rh.gs(R.string.wrong_format))) @@ -542,7 +542,7 @@ class SmsCommunicatorPlugin @Inject constructor( private fun processNSCLIENT(divided: Array, receivedSms: Sms) { if (divided[1].uppercase(Locale.getDefault()) == "RESTART") { rxBus.send(EventNSClientRestart()) - sendSMS(Sms(receivedSms.phoneNumber, "NSCLIENT RESTART SENT")) + sendSMS(Sms(receivedSms.phoneNumber, "AAPSCLIENT RESTART SENT")) receivedSms.processed = true } else sendSMS(Sms(receivedSms.phoneNumber, rh.gs(R.string.wrong_format))) diff --git a/plugins/main/src/test/kotlin/app/aaps/plugins/main/general/smsCommunicator/SmsCommunicatorPluginTest.kt b/plugins/main/src/test/kotlin/app/aaps/plugins/main/general/smsCommunicator/SmsCommunicatorPluginTest.kt index e59e2047603..898cc276765 100644 --- a/plugins/main/src/test/kotlin/app/aaps/plugins/main/general/smsCommunicator/SmsCommunicatorPluginTest.kt +++ b/plugins/main/src/test/kotlin/app/aaps/plugins/main/general/smsCommunicator/SmsCommunicatorPluginTest.kt @@ -531,34 +531,34 @@ class SmsCommunicatorPluginTest : TestBaseWithProfile() { assertThat(smsCommunicatorPlugin.messages[2].text).isEqualTo(passCode) assertThat(smsCommunicatorPlugin.messages[3].text).isEqualTo("Current loop mode: $modeLgs") - //NSCLIENT RESTART + //AAPSCLIENT RESTART `when`(loop.isEnabled()).thenReturn(true) `when`(loop.isSuspended).thenReturn(false) smsCommunicatorPlugin.messages = ArrayList() - sms = Sms("1234", "NSCLIENT RESTART") + sms = Sms("1234", "AAPSCLIENT RESTART") smsCommunicatorPlugin.processSms(sms) assertThat(sms.ignored).isFalse() - assertThat(smsCommunicatorPlugin.messages[0].text).isEqualTo("NSCLIENT RESTART") - assertThat(smsCommunicatorPlugin.messages[1].text).contains("NSCLIENT RESTART") + assertThat(smsCommunicatorPlugin.messages[0].text).isEqualTo("AAPSCLIENT RESTART") + assertThat(smsCommunicatorPlugin.messages[1].text).contains("AAPSCLIENT RESTART") - //NSCLIENT BLA BLA + //AAPSCLIENT BLA BLA `when`(loop.isEnabled()).thenReturn(true) `when`(loop.isSuspended).thenReturn(false) smsCommunicatorPlugin.messages = ArrayList() - sms = Sms("1234", "NSCLIENT BLA BLA") + sms = Sms("1234", "AAPSCLIENT BLA BLA") smsCommunicatorPlugin.processSms(sms) assertThat(sms.ignored).isFalse() - assertThat(smsCommunicatorPlugin.messages[0].text).isEqualTo("NSCLIENT BLA BLA") + assertThat(smsCommunicatorPlugin.messages[0].text).isEqualTo("AAPSCLIENT BLA BLA") assertThat(smsCommunicatorPlugin.messages[1].text).isEqualTo("Wrong format") - //NSCLIENT BLABLA + //AAPSCLIENT BLABLA `when`(loop.isEnabled()).thenReturn(true) `when`(loop.isSuspended).thenReturn(false) smsCommunicatorPlugin.messages = ArrayList() - sms = Sms("1234", "NSCLIENT BLABLA") + sms = Sms("1234", "AAPSCLIENT BLABLA") smsCommunicatorPlugin.processSms(sms) assertThat(sms.ignored).isFalse() - assertThat(smsCommunicatorPlugin.messages[0].text).isEqualTo("NSCLIENT BLABLA") + assertThat(smsCommunicatorPlugin.messages[0].text).isEqualTo("AAPSCLIENT BLABLA") assertThat(smsCommunicatorPlugin.messages[1].text).isEqualTo("Wrong format") //PUMP diff --git a/pump/equil/.gitignore b/pump/equil/.gitignore new file mode 100644 index 00000000000..42afabfd2ab --- /dev/null +++ b/pump/equil/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/pump/equil/build.gradle.kts b/pump/equil/build.gradle.kts new file mode 100644 index 00000000000..3fbfcab163a --- /dev/null +++ b/pump/equil/build.gradle.kts @@ -0,0 +1,44 @@ +plugins { + id("com.android.library") + id("kotlin-android") + id("kotlin-kapt") + id("android-module-dependencies") + id("test-module-dependencies") + id("jacoco-module-dependencies") +} + + +android { + + namespace = "app.aaps.pump.equil" + defaultConfig { + kapt { + arguments { + arg("room.incremental", "true") + arg("room.schemaLocation", "$projectDir/schemas") + } + } + } +} + +dependencies { + implementation(project(":core:interfaces")) + implementation(project(":core:libraries")) + implementation(project(":core:main")) + implementation(project(":core:utils")) + implementation(project(":core:ui")) + implementation(project(":core:validators")) + implementation(project(":pump:pump-common")) + + api(Libs.AndroidX.fragment) + api(Libs.AndroidX.navigationFragment) + + api(Libs.AndroidX.Room.room) + api(Libs.AndroidX.Room.runtime) + api(Libs.AndroidX.Room.rxJava3) + kapt(Libs.AndroidX.Room.compiler) + kapt(Libs.Dagger.compiler) + kapt(Libs.Dagger.androidProcessor) + + implementation("com.github.bumptech.glide:glide:4.16.0") +} diff --git a/pump/equil/proguard-rules.pro b/pump/equil/proguard-rules.pro new file mode 100644 index 00000000000..481bb434814 --- /dev/null +++ b/pump/equil/proguard-rules.pro @@ -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 \ No newline at end of file diff --git a/pump/equil/schemas/app.aaps.pump.equil.database.EquilHistoryDatabase/11.json b/pump/equil/schemas/app.aaps.pump.equil.database.EquilHistoryDatabase/11.json new file mode 100644 index 00000000000..b314448bcb4 --- /dev/null +++ b/pump/equil/schemas/app.aaps.pump.equil.database.EquilHistoryDatabase/11.json @@ -0,0 +1,234 @@ +{ + "formatVersion": 1, + "database": { + "version": 11, + "identityHash": "f31b53d9fb1aa34b993fc11767a645d5", + "entities": [ + { + "tableName": "equilHistoryPump", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `timestamp` INTEGER NOT NULL, `code` INTEGER NOT NULL, `battery` INTEGER NOT NULL, `insulin` INTEGER NOT NULL, `rate` INTEGER NOT NULL, `largeRate` INTEGER NOT NULL, `type` INTEGER NOT NULL, `eventIndex` INTEGER NOT NULL, `level` INTEGER NOT NULL, `parm` INTEGER NOT NULL, `port` INTEGER NOT NULL, `eventTimestamp` INTEGER NOT NULL, `serialNumber` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "code", + "columnName": "code", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "battery", + "columnName": "battery", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "insulin", + "columnName": "insulin", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "rate", + "columnName": "rate", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "largeRate", + "columnName": "largeRate", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "eventIndex", + "columnName": "eventIndex", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "level", + "columnName": "level", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "parm", + "columnName": "parm", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "port", + "columnName": "port", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "eventTimestamp", + "columnName": "eventTimestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "serialNumber", + "columnName": "serialNumber", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_equilHistoryPump_code_timestamp_eventTimestamp_eventIndex", + "unique": false, + "columnNames": [ + "code", + "timestamp", + "eventTimestamp", + "eventIndex" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_equilHistoryPump_code_timestamp_eventTimestamp_eventIndex` ON `${TABLE_NAME}` (`code`, `timestamp`, `eventTimestamp`, `eventIndex`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "equilHistoryRecord", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `type` TEXT, `timestamp` INTEGER NOT NULL, `serialNumber` TEXT NOT NULL, `resolvedStatus` TEXT, `resolvedAt` INTEGER, `note` TEXT, `tempBasalRecord_duration` INTEGER, `tempBasalRecord_rate` REAL, `tempBasalRecord_startTime` INTEGER, `bolusRecord_amount` REAL, `bolusRecord_bolusType` TEXT, `bolusRecord_startTime` INTEGER, `basalprofile_segments` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "serialNumber", + "columnName": "serialNumber", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "resolvedStatus", + "columnName": "resolvedStatus", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "resolvedAt", + "columnName": "resolvedAt", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "note", + "columnName": "note", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "tempBasalRecord.duration", + "columnName": "tempBasalRecord_duration", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "tempBasalRecord.rate", + "columnName": "tempBasalRecord_rate", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "tempBasalRecord.startTime", + "columnName": "tempBasalRecord_startTime", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "bolusRecord.amount", + "columnName": "bolusRecord_amount", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "bolusRecord.bolusType", + "columnName": "bolusRecord_bolusType", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "bolusRecord.startTime", + "columnName": "bolusRecord_startTime", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "basalValuesRecord.segments", + "columnName": "basalprofile_segments", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_equilHistoryRecord_type_timestamp", + "unique": false, + "columnNames": [ + "type", + "timestamp" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_equilHistoryRecord_type_timestamp` ON `${TABLE_NAME}` (`type`, `timestamp`)" + } + ], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'f31b53d9fb1aa34b993fc11767a645d5')" + ] + } +} \ No newline at end of file diff --git a/pump/equil/src/main/AndroidManifest.xml b/pump/equil/src/main/AndroidManifest.xml new file mode 100644 index 00000000000..0eaea1cfe44 --- /dev/null +++ b/pump/equil/src/main/AndroidManifest.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/EquilConst.kt b/pump/equil/src/main/java/app/aaps/pump/equil/EquilConst.kt new file mode 100644 index 00000000000..eefdec15ac2 --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/EquilConst.kt @@ -0,0 +1,25 @@ +package app.aaps.pump.equil + +object EquilConst { + + const val EQUIL_CMD_TIME_OUT: Long = 300000 + const val EQUIL_BLE_WRITE_TIME_OUT: Long = 20 + const val EQUIL_BLE_NEXT_CMD: Long = 150 + const val EQUIL_SUPPORT_LEVEL = 5.3f + const val EQUIL_BLOUS_THRESHOLD_STEP = 1600 + const val EQUIL_STEP_MAX = 32000 + const val EQUIL_STEP_FILL = 160 + const val EQUIL_STEP_AIR = 120 + object Prefs { + + val EQUIL_DEVICES = R.string.key_equil_devices + val EQUIL_PASSWORD = R.string.key_equil_password + val Equil_ALARM_BATTERY_10 = R.string.key_equil_alarm_battery_10 + val EQUIL_ALARM_INSULIN_10 = R.string.key_equil_alarm_insulin_10 + val EQUIL_ALARM_INSULIN_5 = R.string.key_equil_alarm_insulin_5 + val EQUIL_BASAL_SET = R.string.key_equil_basal_set + val EQUIL_STATE = R.string.key_equil_state + val EQUIL_ALARM_BATTERY = R.string.key_equil_alarm_battery + val EQUIL_ALARM_INSULIN = R.string.key_equil_alarm_insulin + } +} diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/EquilFragment.kt b/pump/equil/src/main/java/app/aaps/pump/equil/EquilFragment.kt new file mode 100644 index 00000000000..371f861431f --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/EquilFragment.kt @@ -0,0 +1,394 @@ +package app.aaps.pump.equil + +import android.annotation.SuppressLint +import android.content.Intent +import android.graphics.Color +import android.os.Bundle +import android.os.Handler +import android.os.HandlerThread +import android.text.TextUtils +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.DialogFragment +import app.aaps.core.interfaces.extensions.runOnUiThread +import app.aaps.core.interfaces.logging.AAPSLogger +import app.aaps.core.interfaces.logging.LTag +import app.aaps.core.interfaces.logging.UserEntryLogger +import app.aaps.core.interfaces.plugin.ActivePlugin +import app.aaps.core.interfaces.protection.ProtectionCheck +import app.aaps.core.interfaces.pump.WarnColors +import app.aaps.core.interfaces.queue.Callback +import app.aaps.core.interfaces.queue.CommandQueue +import app.aaps.core.interfaces.resources.ResourceHelper +import app.aaps.core.interfaces.rx.AapsSchedulers +import app.aaps.core.interfaces.rx.bus.RxBus +import app.aaps.core.interfaces.rx.events.EventPumpStatusChanged +import app.aaps.core.interfaces.sharedPreferences.SP +import app.aaps.core.interfaces.utils.DateUtil +import app.aaps.core.interfaces.utils.T +import app.aaps.core.interfaces.utils.fabric.FabricPrivacy +import app.aaps.core.ui.UIRunnable +import app.aaps.core.ui.toast.ToastUtils +import app.aaps.pump.equil.data.RunMode +import app.aaps.pump.equil.databinding.EquilFraBinding +import app.aaps.pump.equil.events.EventEquilDataChanged +import app.aaps.pump.equil.events.EventEquilModeChanged +import app.aaps.pump.equil.manager.EquilManager +import app.aaps.pump.equil.manager.command.* +import app.aaps.pump.equil.ui.* +import app.aaps.pump.equil.ui.dlg.LoadingDlg +import app.aaps.pump.equil.ui.pair.EquilPairActivity +import dagger.android.support.DaggerFragment +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.kotlin.plusAssign +import java.time.Duration +import javax.inject.Inject + +class EquilFragment : DaggerFragment() { + + @Inject lateinit var rxBus: RxBus + @Inject lateinit var aapsLogger: AAPSLogger + @Inject lateinit var fabricPrivacy: FabricPrivacy + @Inject lateinit var commandQueue: CommandQueue + @Inject lateinit var activePlugin: ActivePlugin + @Inject lateinit var rh: ResourceHelper + @Inject lateinit var sp: SP + @Inject lateinit var warnColors: WarnColors + @Inject lateinit var dateUtil: DateUtil + @Inject lateinit var aapsSchedulers: AapsSchedulers + @Inject lateinit var equilPumpPlugin: EquilPumpPlugin + @Inject lateinit var protectionCheck: ProtectionCheck + @Inject lateinit var uel: UserEntryLogger + @Inject lateinit var equilManager: EquilManager + + private var disposable: CompositeDisposable = CompositeDisposable() + + private val handler = Handler(HandlerThread(this::class.simpleName + "Handler").also { it.start() }.looper) + private lateinit var refreshLoop: Runnable + + private var _binding: EquilFraBinding? = null + + // This property is only valid between onCreateView and + // onDestroyView. + private val binding get() = _binding!! + + init { + refreshLoop = Runnable { + activity?.runOnUiThread { updateGUI() } + handler.postDelayed(refreshLoop, T.mins(1).msecs()) + } + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + _binding = EquilFraBinding.inflate(inflater, container, false) + return binding.root + } + + @Synchronized + override fun onResume() { + super.onResume() + handler.postDelayed(refreshLoop, T.mins(1).msecs()) + + disposable += rxBus + .toObservable(EventPumpStatusChanged::class.java) + .observeOn(aapsSchedulers.main) + .subscribe({ updateGUI() }, fabricPrivacy::logException) + disposable += rxBus + .toObservable(EventEquilDataChanged::class.java) + .observeOn(aapsSchedulers.main) + .subscribe({ updateGUI() }, fabricPrivacy::logException) + disposable += rxBus + .toObservable(EventEquilModeChanged::class.java) + .observeOn(aapsSchedulers.main) + .subscribe({ updateModel() }, fabricPrivacy::logException) + + updateGUI() + } + + @Synchronized + override fun onPause() { + super.onPause() + disposable.clear() + handler.removeCallbacks(refreshLoop) + } + + @Synchronized + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } + + @SuppressLint("SetTextI18n") + @Synchronized + fun updateGUI() { + if (_binding == null) return + val devName = equilManager.serialNumber + if (!TextUtils.isEmpty(devName)) { + binding.battery.text = equilManager.battery.toString() + "%" + binding.insulinReservoir.text = equilManager.currentInsulin.toString() + binding.basalSpeed.text = String.format(rh.gs(R.string.equil_unit_u_hours), equilPumpPlugin.baseBasalRate) + binding.firmwareVersion.text = equilManager.firmwareVersion?.toString() + equilManager.startInsulin.let { + if (it == -1) { + binding.totalDelivered.text = "-" + } else { + val totalDelivered = it - equilManager.currentInsulin + binding.totalDelivered.text = String.format( + rh.gs(R.string.equil_unit_u), + totalDelivered.toString() + ) + } + } + + binding.timeDevice.text = dateUtil.dateAndTimeAndSecondsString(equilManager.lastDataTime) + val runMode = equilManager.runMode + binding.mode.setTextColor(Color.WHITE) + if (equilManager.isActivationCompleted) { + when (runMode) { + RunMode.RUN -> { + binding.mode.text = rh.gs(R.string.equil_mode_running) + binding.btnResumeDelivery.visibility = View.GONE + binding.btnSuspendDelivery.visibility = View.VISIBLE + } + + RunMode.STOP -> { + binding.mode.text = rh.gs(R.string.equil_mode_stopped) + binding.btnResumeDelivery.visibility = View.GONE + binding.btnSuspendDelivery.visibility = View.GONE + } + + RunMode.SUSPEND -> { + binding.mode.text = rh.gs(R.string.equil_mode_suspended) + binding.btnResumeDelivery.visibility = View.VISIBLE + binding.btnSuspendDelivery.visibility = View.GONE + } + + else -> { + binding.btnResumeDelivery.visibility = View.VISIBLE + binding.btnSuspendDelivery.visibility = View.GONE + binding.mode.text = "" + } + } + } else { + binding.btnResumeDelivery.visibility = View.GONE + binding.btnSuspendDelivery.visibility = View.GONE + binding.mode.text = rh.gs(R.string.equil_init_insulin_error) + binding.mode.setTextColor(Color.RED) + } + + binding.serialNumber.text = devName + updateTempBasal() + runOnUiThread { + equilManager.bolusRecord.let { + if (it == null) { + binding.lastBolus.text = "-" + } else { + val text = + rh.gs( + R.string.equil_common_overview_last_bolus_value, + it.amount, + rh.gs(app.aaps.core.ui.R.string.insulin_unit_shortname), + readableDuration(Duration.ofMillis(System.currentTimeMillis() - it.startTime)) + ) + binding.lastBolus.text = text + } + + } + } + + binding.btnBind.visibility = View.GONE + binding.btnOpDressing.visibility = View.VISIBLE + binding.btnUnbind.visibility = View.VISIBLE + binding.btnHistory.visibility = View.VISIBLE + + } else { + binding.tempBasal.text = "-" + binding.lastBolus.text = '-'.toString() + binding.battery.text = '-'.toString() + binding.basalSpeed.text = '-'.toString() + binding.timeDevice.text = '-'.toString() + binding.mode.text = '-'.toString() + binding.serialNumber.text = "-" + binding.firmwareVersion.text = "-" + binding.insulinReservoir.text = "-" + binding.totalDelivered.text = "-" + binding.btnBind.visibility = View.VISIBLE + binding.btnOpDressing.visibility = View.GONE + binding.btnUnbind.visibility = View.GONE + binding.btnHistory.visibility = View.GONE + binding.btnResumeDelivery.visibility = View.GONE + binding.btnSuspendDelivery.visibility = View.GONE + binding.btnBind.setOnClickListener { + val intent = Intent(context, EquilPairActivity::class.java) + intent.putExtra(EquilPairActivity.KEY_TYPE, EquilPairActivity.Type.PAIR) + startActivity(intent) + } + } + binding.imv.setOnClickListener { + // commandQueue.customCommand(CmdStatusGet(), null) + } + binding.progressDeviceTime.visibility = View.GONE + binding.btnHistory.setOnClickListener { + if (TextUtils.isEmpty(devName)) { + ToastUtils.errorToast(context, rh.gs(R.string.equil_error_no_devices)) + } else { + startActivity(Intent(context, EquilHistoryRecordActivity::class.java)) + } + } + + binding.btnResumeDelivery.setOnClickListener { + showSetModeDialog() + } + binding.btnSuspendDelivery.setOnClickListener { + showSetModeDialog() + } + binding.btnUnbind.setOnClickListener { + if (TextUtils.isEmpty(devName)) { + ToastUtils.errorToast(context, rh.gs(R.string.equil_error_no_devices)) + } else + startActivity(Intent(context, EquilUnPairDetachActivity::class.java)) + + } + binding.btnOpDressing.setOnClickListener { + if (TextUtils.isEmpty(devName)) { + ToastUtils.errorToast(context, rh.gs(R.string.equil_error_no_devices)) + } else + changeInsulin() + + } + } + + + private fun showSetModeDialog() { + val runMode = equilManager.runMode + var tempMode = RunMode.RUN + if (runMode == RunMode.RUN) { + tempMode = RunMode.SUSPEND + } + showLoading() + commandQueue.customCommand(CmdModelSet(tempMode.command), object : Callback() { + override fun run() { + dismissLoading() + aapsLogger.debug(LTag.PUMPCOMM, "result====" + result.success) + if (result.success) { + equilManager.runMode = tempMode + runOnUiThread { updateGUI() } + } + } + }) + + } + + private fun updateTempBasal() { + val tempBasal = equilManager.tempBasal + if (tempBasal != null && equilManager.isTempBasalRunning) { + val startTime = tempBasal.startTime + val rate = tempBasal.rate + val duration = tempBasal.duration / 60 / 1000 + val minutesRunning = Duration.ofMillis(System.currentTimeMillis() - startTime).toMinutes() + binding.tempBasal.text = rh.gs( + R.string.equil_common_overview_temp_basal_value, + rate, + dateUtil.timeString(startTime), + minutesRunning, + duration + ) + } else { + binding.tempBasal.text = "-" + } + } + + + + @SuppressLint("SetTextI18n") + @Synchronized + fun updateModel() { + // binding.mode.text = equilPumpPlugin.equilManager.equilServiceData.mode.toString() + } + + private fun showLoading() { + LoadingDlg().show(childFragmentManager, "loading") + } + + private fun dismissLoading() { + val fragment = childFragmentManager.findFragmentByTag("loading") + if (fragment is DialogFragment) { + fragment.dismiss() + } + } + + @SuppressLint("SetTextI18n") + @Synchronized + fun changeInsulin() { + + activity?.let { activity -> + context?.let { context -> + protectionCheck.queryProtection( + activity, ProtectionCheck.Protection.PREFERENCES, + UIRunnable { + val intent = Intent(context, EquilPairActivity::class.java) + intent.putExtra(EquilPairActivity.KEY_TYPE, EquilPairActivity.Type.CHANGE_INSULIN) + startActivity(intent) + } + ) + } + } + } + + private fun readableDuration(duration: Duration): String { + val hours = duration.toHours().toInt() + val minutes = duration.toMinutes().toInt() + val seconds = duration.seconds + when { + seconds < 10 -> return rh.gs(R.string.equil_common_moments_ago) + + seconds < 60 -> { + return rh.gs(R.string.equil_common_less_than_a_minute_ago) + } + + seconds < 60 * 60 -> { // < 1 hour + return rh.gs( + R.string.equil_common_time_ago, + rh.gq(R.plurals.equil_common_minutes, minutes, minutes) + ) + } + + seconds < 24 * 60 * 60 -> { // < 1 day + val minutesLeft = minutes % 60 + if (minutesLeft > 0) + return rh.gs( + R.string.equil_common_time_ago, + rh.gs( + R.string.equil_common_composite_time, + rh.gq(R.plurals.equil_common_hours, hours, hours), + rh.gq(R.plurals.equil_common_minutes, minutesLeft, minutesLeft) + ) + ) + return rh.gs( + R.string.equil_common_time_ago, + rh.gq(R.plurals.equil_common_hours, hours, hours) + ) + } + + else -> { + val days = hours / 24 + val hoursLeft = hours % 24 + if (hoursLeft > 0) + return rh.gs( + R.string.equil_common_time_ago, + rh.gs( + R.string.equil_common_composite_time, + rh.gq(R.plurals.equil_common_days, days, days), + rh.gq(R.plurals.equil_common_hours, hoursLeft, hoursLeft) + ) + ) + return rh.gs( + R.string.equil_common_time_ago, + rh.gq(R.plurals.equil_common_days, days, days) + ) + } + } + } + +} diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/EquilPumpPlugin.kt b/pump/equil/src/main/java/app/aaps/pump/equil/EquilPumpPlugin.kt new file mode 100644 index 00000000000..5e1a1349301 --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/EquilPumpPlugin.kt @@ -0,0 +1,480 @@ +package app.aaps.pump.equil + +import android.content.Context +import android.os.Handler +import android.os.HandlerThread +import android.os.SystemClock +import android.text.format.DateFormat +import app.aaps.core.interfaces.logging.AAPSLogger +import app.aaps.core.interfaces.logging.LTag +import app.aaps.core.interfaces.notifications.Notification +import app.aaps.core.interfaces.plugin.PluginDescription +import app.aaps.core.interfaces.plugin.PluginType +import app.aaps.core.interfaces.profile.Profile +import app.aaps.core.interfaces.pump.DetailedBolusInfo +import app.aaps.core.interfaces.pump.Pump +import app.aaps.core.interfaces.pump.PumpEnactResult +import app.aaps.core.interfaces.pump.PumpPluginBase +import app.aaps.core.interfaces.pump.PumpSync +import app.aaps.core.interfaces.pump.PumpSync.TemporaryBasalType +import app.aaps.core.interfaces.pump.defs.ManufacturerType +import app.aaps.core.interfaces.pump.defs.PumpDescription +import app.aaps.core.interfaces.pump.defs.PumpType +import app.aaps.core.interfaces.queue.Callback +import app.aaps.core.interfaces.queue.CommandQueue +import app.aaps.core.interfaces.queue.CustomCommand +import app.aaps.core.interfaces.resources.ResourceHelper +import app.aaps.core.interfaces.rx.AapsSchedulers +import app.aaps.core.interfaces.rx.bus.RxBus +import app.aaps.core.interfaces.rx.events.EventPreferenceChange +import app.aaps.core.interfaces.sharedPreferences.SP +import app.aaps.core.interfaces.utils.DateUtil +import app.aaps.core.interfaces.utils.DecimalFormatter +import app.aaps.core.interfaces.utils.TimeChangeType +import app.aaps.core.interfaces.utils.fabric.FabricPrivacy +import app.aaps.core.ui.toast.ToastUtils +import app.aaps.pump.equil.data.AlarmMode +import app.aaps.pump.equil.data.BolusProfile +import app.aaps.pump.equil.data.RunMode +import app.aaps.pump.equil.driver.definition.ActivationProgress +import app.aaps.pump.equil.driver.definition.BasalSchedule +import app.aaps.pump.equil.events.EventEquilDataChanged +import app.aaps.pump.equil.manager.EquilManager +import app.aaps.pump.equil.manager.command.BaseCmd +import app.aaps.pump.equil.manager.command.CmdAlarmSet +import app.aaps.pump.equil.manager.command.CmdBasalSet +import app.aaps.pump.equil.manager.command.CmdSettingSet +import app.aaps.pump.equil.manager.command.CmdStatusGet +import app.aaps.pump.equil.manager.command.CmdTimeSet +import app.aaps.pump.equil.manager.command.PumpEvent +import dagger.android.HasAndroidInjector +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.kotlin.plusAssign +import org.joda.time.DateTime +import org.joda.time.Duration +import org.json.JSONException +import org.json.JSONObject +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton class EquilPumpPlugin @Inject constructor( + injector: HasAndroidInjector, + aapsLogger: AAPSLogger, + rh: ResourceHelper, + commandQueue: CommandQueue, + private val aapsSchedulers: AapsSchedulers, + private val rxBus: RxBus, + private val context: Context, + private val sp: SP, + private val fabricPrivacy: FabricPrivacy, + private val dateUtil: DateUtil, + private val pumpSync: PumpSync, + private val equilManager: EquilManager, + private val decimalFormatter: DecimalFormatter +) : PumpPluginBase( + PluginDescription() + .mainType(PluginType.PUMP) + .fragmentClass(EquilFragment::class.java.name) + .pluginIcon(app.aaps.core.ui.R.drawable.ic_equil_128) + .pluginName(R.string.equil_name) + .shortName(R.string.equil_name_short) + .preferencesId(R.xml.pref_equil) + .description(R.string.equil_pump_description), injector, aapsLogger, rh, commandQueue +), Pump { + + override val pumpDescription: PumpDescription + private val pumpType = PumpType.EQUIL + private val bolusProfile: BolusProfile = BolusProfile() + + private val disposable = CompositeDisposable() + val handler = Handler(HandlerThread(this::class.java.simpleName + "Handler").also { it.start() }.looper) + private lateinit var statusChecker: Runnable + override fun onStart() { + super.onStart() + handler.postDelayed(statusChecker, STATUS_CHECK_INTERVAL_MILLIS) + disposable += rxBus + .toObservable(EventEquilDataChanged::class.java) + .observeOn(aapsSchedulers.io) + .subscribe({ playAlarm() }, fabricPrivacy::logException) + disposable += rxBus + .toObservable(EventPreferenceChange::class.java) + .observeOn(aapsSchedulers.io) + .subscribe({ event -> + if (event.isChanged(rh.gs(R.string.key_equil_tone))) { + val mode = AlarmMode.fromInt(sp.getString(R.string.key_equil_tone, "3").toInt()) + commandQueue.customCommand(CmdAlarmSet(mode.command), object : Callback() { + override fun run() { + if (result.success) ToastUtils.infoToast(context, rh.gs(R.string.equil_pump_updated)) + else ToastUtils.infoToast(context, rh.gs(R.string.equil_error)) + } + }) + } else if (event.isChanged(rh.gs(R.string.key_equil_maxbolus))) { + val data = sp.getDouble(R.string.key_equil_maxbolus, 10.0) + commandQueue.customCommand(CmdSettingSet(data), object : Callback() { + override fun run() { + if (result.success) ToastUtils.infoToast(context, rh.gs(R.string.equil_pump_updated)) + else ToastUtils.infoToast(context, rh.gs(R.string.equil_error)) + } + }) + } + }, fabricPrivacy::logException) + } + + var tempActivationProgress = ActivationProgress.NONE + + init { + pumpDescription = PumpDescription(pumpType) + statusChecker = Runnable { + if (commandQueue.size() == 0 && commandQueue.performing() == null) { + if (equilManager.isActivationCompleted) commandQueue.customCommand(CmdStatusGet(), null) + } else { + aapsLogger.debug(LTag.PUMPCOMM, "Skipping Pod status check because command queue is not empty") + } + handler.postDelayed(statusChecker, STATUS_CHECK_INTERVAL_MILLIS) + } + PumpEvent.init(rh) + } + + override fun onStop() { + super.onStop() + aapsLogger.debug(LTag.PUMPCOMM, "EquilPumpPlugin.onStop()") + handler.removeCallbacksAndMessages(null) + disposable.clear() + } + + override fun isInitialized(): Boolean = true + override fun isConnected(): Boolean = true + override fun isConnecting(): Boolean = false + override fun isBusy(): Boolean = false + + override fun isHandshakeInProgress(): Boolean = false + override fun connect(reason: String) {} + + override fun isSuspended(): Boolean { + val runMode = equilManager.runMode + return if (equilManager.isActivationCompleted) { + runMode == RunMode.SUSPEND || runMode == RunMode.STOP + } else true + } + + override fun getPumpStatus(reason: String) {} + override fun setNewBasalProfile(profile: Profile): PumpEnactResult { + aapsLogger.debug(LTag.PUMPCOMM, "setNewBasalProfile") + val mode = equilManager.runMode + if (mode === RunMode.RUN || mode === RunMode.SUSPEND) { + val basalSchedule = BasalSchedule.mapProfileToBasalSchedule(profile) + val pumpEnactResult = equilManager.executeCmd(CmdBasalSet(basalSchedule, profile)) + if (pumpEnactResult.success) { + equilManager.basalSchedule = basalSchedule + } + return pumpEnactResult + } + return PumpEnactResult(injector).enacted(false).success(false).comment(rh.gs(R.string.equil_pump_not_run)) + } + + override fun isThisProfileSet(profile: Profile): Boolean { + return if (!equilManager.isActivationCompleted) { + // When no Pod is active, return true here in order to prevent AAPS from setting a profile + // When we activate a new Pod, we just use ProfileFunction to set the currently active profile + true + } else equilManager.basalSchedule == BasalSchedule.mapProfileToBasalSchedule(profile) + } + + override fun lastDataTime(): Long { + aapsLogger.debug(LTag.PUMPCOMM, "lastDataTime: ${dateUtil.dateAndTimeAndSecondsString(equilManager.lastDataTime)}") + return equilManager.lastDataTime + } + + override val baseBasalRate: Double + get() = if (isSuspended()) 0.0 else equilManager.basalSchedule?.rateAt(toDuration(DateTime.now())) ?: 0.0 + override val reservoirLevel: Double + get() = equilManager.currentInsulin.toDouble() + override val batteryLevel: Int + get() = equilManager.battery + + override fun deliverTreatment(detailedBolusInfo: DetailedBolusInfo): PumpEnactResult { + if (detailedBolusInfo.insulin == 0.0) { + // 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).comment("Invalid input") + } + val maxBolus = sp.getDouble(R.string.key_equil_maxbolus, 10.0) + if (detailedBolusInfo.insulin > maxBolus) { + val formattedValue = "%.2f".format(maxBolus) + val comment = rh.gs(R.string.equil_maxbolus_tips, formattedValue) + return PumpEnactResult(injector).success(false).enacted(false).bolusDelivered(0.0).comment(comment) + + } + val mode = equilManager.runMode + if (mode !== RunMode.RUN) { + return PumpEnactResult(injector).enacted(false).success(false).bolusDelivered(0.0).comment(rh.gs(R.string.equil_pump_not_run)) + } + val lastInsulin = equilManager.currentInsulin + return if (detailedBolusInfo.insulin > lastInsulin) { + PumpEnactResult(injector).success(false).enacted(false).bolusDelivered(0.0).comment(R.string.equil_not_enough_insulin) + } else deliverBolus(detailedBolusInfo) + } + + override fun stopBolusDelivering() { + equilManager.stopBolus(bolusProfile) + aapsLogger.debug(LTag.PUMPCOMM, "stopBolusDelivering=====") + } + + override fun setTempBasalAbsolute( + absoluteRate: Double, durationInMinutes: Int, profile: Profile, enforceNew: Boolean, tbrType: TemporaryBasalType + ): PumpEnactResult { + aapsLogger.debug(LTag.PUMPCOMM, "setTempBasalAbsolute=====$absoluteRate====$durationInMinutes===$enforceNew") + if (durationInMinutes <= 0 || durationInMinutes % BASAL_STEP_DURATION.standardMinutes != 0L) { + return PumpEnactResult(injector).success(false).comment(rh.gs(R.string.equil_error_set_temp_basal_failed_validation, BASAL_STEP_DURATION.standardMinutes)) + } + val mode = equilManager.runMode + if (mode !== RunMode.RUN) { + return PumpEnactResult(injector).enacted(false).success(false).comment(rh.gs(R.string.equil_pump_not_run)) + } + var pumpEnactResult = PumpEnactResult(injector) + pumpEnactResult.success(false) + pumpEnactResult = equilManager.tempBasalPump + if (pumpEnactResult.success) { + if (pumpEnactResult.enacted) { + pumpEnactResult = cancelTempBasal(true) + } + if (pumpEnactResult.success) { + SystemClock.sleep(EquilConst.EQUIL_BLE_NEXT_CMD) + pumpEnactResult = equilManager.setTempBasal( + absoluteRate, durationInMinutes, false + ) + if (pumpEnactResult.success) { + pumpEnactResult.isTempCancel = false + pumpEnactResult.duration = durationInMinutes + pumpEnactResult.isPercent = false + pumpEnactResult.absolute = absoluteRate + } + } + } + return pumpEnactResult + } + + override fun cancelTempBasal(enforceNew: Boolean): PumpEnactResult { + aapsLogger.debug(LTag.PUMPCOMM, "cancelTempBasal=====$enforceNew") + val pumpEnactResult = equilManager.setTempBasal(0.0, 0, true) + if (pumpEnactResult.success) { + pumpEnactResult.isTempCancel = true + } + return pumpEnactResult + } + + override fun getJSONStatus(profile: Profile, profileName: String, version: String): JSONObject { + if (!isConnected()) return JSONObject().put("status", JSONObject().put("status", "no active Pod")) + + val json = JSONObject() + val battery = JSONObject() + val status = JSONObject() + val extended = JSONObject() + return try { + battery.put("percent", batteryLevel) + status.put("status", if (isSuspended()) "suspended" else "normal") + status.put("timestamp", dateUtil.toISOString(lastDataTime())) + extended.put("Version", version) + pumpSync.expectedPumpState().bolus?.let { bolus -> + extended.put("LastBolus", dateUtil.dateAndTimeString(bolus.timestamp)) + extended.put("LastBolusAmount", bolus.amount) + } + pumpSync.expectedPumpState().temporaryBasal?.let { temporaryBasal -> + extended.put("TempBasalAbsoluteRate", temporaryBasal.convertedToAbsolute(dateUtil.now(), profile)) + extended.put("TempBasalStart", dateUtil.dateAndTimeString(temporaryBasal.timestamp)) + extended.put("TempBasalRemaining", temporaryBasal.plannedRemainingMinutes) + } + pumpSync.expectedPumpState().extendedBolus?.let { extendedBolus -> + extended.put("ExtendedBolusAbsoluteRate", extendedBolus.rate) + extended.put("ExtendedBolusStart", dateUtil.dateAndTimeString(extendedBolus.timestamp)) + extended.put("ExtendedBolusRemaining", extendedBolus.plannedRemainingMinutes) + } + extended.put("BaseBasalRate", baseBasalRate) + extended.put("ActiveProfile", profileName) + json.put("battery", battery) + json.put("status", status) + json.put("extended", extended) + json.put("reservoir", reservoirLevel) + json.put("clock", dateUtil.toISOString(dateUtil.now())) + json + } catch (e: JSONException) { + json.put("status", JSONObject().put("status", "error" + e.message)) + aapsLogger.error("Unhandled exception", e) + json + } + } + + override fun manufacturer(): ManufacturerType = ManufacturerType.Equil + override fun model(): PumpType = PumpType.EQUIL + override fun serialNumber(): String = equilManager.serialNumber + + override fun shortStatus(veryShort: Boolean): String { + if (!equilManager.isActivationCompleted) { + return rh.gs(R.string.equil_init_insulin_error) + } + var ret = "" + if (lastDataTime() != 0L) { + val agoMsec = System.currentTimeMillis() - lastDataTime() + val agoMin = (agoMsec / 60.0 / 1000.0).toInt() + ret += rh.gs(R.string.equil_common_short_status_last_connection, agoMin) + "\n" + } + if (equilManager.bolusRecord != null) { + ret += rh.gs( + R.string.equil_common_short_status_last_bolus, decimalFormatter.to2Decimal(equilManager.bolusRecord.amount), DateFormat.format( + "HH:mm", equilManager.bolusRecord.startTime + ) + ) + "\n" + } + val (temporaryBasal, extendedBolus, _, profile) = pumpSync.expectedPumpState() + if (temporaryBasal != null && profile != null) { + ret += rh.gs(R.string.equil_common_short_status_temp_basal, temporaryBasal.toStringFull(dateUtil, decimalFormatter) + "\n") + } + if (extendedBolus != null) { + ret += rh.gs(R.string.equil_common_short_status_extended_bolus, extendedBolus.toStringFull(dateUtil, decimalFormatter) + "\n") + } + ret += rh.gs(R.string.equil_common_short_status_reservoir, reservoirLevel) + return ret.trim { it <= ' ' } + } + + override fun executeCustomCommand(customCommand: CustomCommand): PumpEnactResult? { + aapsLogger.debug(LTag.PUMPCOMM, "executeCustomCommand $customCommand") + var pumpEnactResult: PumpEnactResult? = null + + if (customCommand is BaseCmd) { + pumpEnactResult = equilManager.executeCmd(customCommand) + } + if (customCommand is CmdStatusGet) { + pumpEnactResult = equilManager.readEquilStatus() + } + return pumpEnactResult + } + + override fun timezoneOrDSTChanged(timeChangeType: TimeChangeType) { + aapsLogger.debug(LTag.PUMP, "DST and/or TimeZone changed event will be consumed by driver") + commandQueue.customCommand(CmdTimeSet(), null) + } + + override val isFakingTempsByExtendedBoluses: Boolean = false + override fun canHandleDST(): Boolean = false + override fun disconnect(reason: String) { + aapsLogger.info(LTag.PUMPCOMM, "disconnect reason=$reason") + equilManager.closeBleAuto() + } + + override fun stopConnecting() {} + + override fun setTempBasalPercent(percent: Int, durationInMinutes: Int, profile: Profile, enforceNew: Boolean, tbrType: TemporaryBasalType): PumpEnactResult { + aapsLogger.debug(LTag.PUMPCOMM, "setTempBasalPercent $percent $durationInMinutes ") + return if (percent == 0) { + setTempBasalAbsolute(0.0, durationInMinutes, profile, enforceNew, tbrType) + } else { + var absoluteValue = profile.getBasal() * (percent / 100.0) + absoluteValue = pumpDescription.pumpType.determineCorrectBasalSize(absoluteValue) + setTempBasalAbsolute(absoluteValue, durationInMinutes, profile, enforceNew, tbrType) + } + } + + override fun setExtendedBolus(insulin: Double, durationInMinutes: Int): PumpEnactResult { + aapsLogger.debug(LTag.PUMPCOMM, "setExtendedBolus $insulin, $durationInMinutes") + val pumpEnactResult = equilManager.setExtendedBolus(insulin, durationInMinutes, false) + if (pumpEnactResult.success) { + pumpEnactResult.isTempCancel = false + pumpEnactResult.duration = durationInMinutes + pumpEnactResult.isPercent = false + pumpEnactResult.absolute = insulin + } + return pumpEnactResult + } + + override fun cancelExtendedBolus(): PumpEnactResult { + aapsLogger.debug(LTag.PUMPCOMM, "cancelExtendedBolus") + return equilManager.setExtendedBolus(0.0, 0, true) + } + + override fun loadTDDs(): PumpEnactResult { + aapsLogger.debug(LTag.PUMPCOMM, "loadTDDs") + return PumpEnactResult(injector).success(false).enacted(false) + } + + override fun isBatteryChangeLoggingEnabled(): Boolean = false + + private fun deliverBolus(detailedBolusInfo: DetailedBolusInfo): PumpEnactResult { + aapsLogger.debug(LTag.PUMPCOMM, "deliverBolus") + bolusProfile.insulin = detailedBolusInfo.insulin + return equilManager.bolus(detailedBolusInfo, bolusProfile) + } + + fun showToast(s: String) { + ToastUtils.showToastInUiThread(context, s) + } + + fun resetData() { + sp.putBoolean(EquilConst.Prefs.Equil_ALARM_BATTERY_10, false) + sp.putBoolean(EquilConst.Prefs.EQUIL_ALARM_INSULIN_10, false) + sp.putBoolean(EquilConst.Prefs.EQUIL_ALARM_INSULIN_5, false) + sp.putBoolean(EquilConst.Prefs.EQUIL_ALARM_INSULIN_5, false) + sp.putBoolean(EquilConst.Prefs.EQUIL_BASAL_SET, false) + } + + fun clearData() { + resetData() + equilManager.clearPodState() + sp.putString(EquilConst.Prefs.EQUIL_DEVICES, "") + sp.putString(EquilConst.Prefs.EQUIL_PASSWORD, "") + } + + private fun playAlarm() { + val battery = equilManager.battery + val insulin = equilManager.currentInsulin + val alarmBattery = sp.getBoolean(EquilConst.Prefs.EQUIL_ALARM_BATTERY, true) + val alarmInsulin = sp.getBoolean(EquilConst.Prefs.EQUIL_ALARM_INSULIN, true) + if (battery <= 10 && alarmBattery) { + val alarmBattery10 = sp.getBoolean(EquilConst.Prefs.Equil_ALARM_BATTERY_10, false) + if (!alarmBattery10) { + equilManager.showNotification( + Notification.FAILED_UPDATE_PROFILE, rh.gs(R.string.equil_low_battery) + battery + "%", Notification.NORMAL, app.aaps.core.ui.R.raw.alarm + ) + sp.putBoolean(EquilConst.Prefs.Equil_ALARM_BATTERY_10, true) + } else { + if (battery < 5) { + equilManager.showNotification( + Notification.FAILED_UPDATE_PROFILE, rh.gs(R.string.equil_low_battery) + battery + "%", Notification.URGENT, app.aaps.core.ui.R.raw.alarm + ) + } + } + } + if (equilManager.runMode === RunMode.RUN && alarmInsulin && equilManager.isActivationCompleted) { + when { + insulin in 6..10 -> { + val alarmInsulin10 = sp.getBoolean(EquilConst.Prefs.EQUIL_ALARM_INSULIN_10, false) + if (!alarmInsulin10) { + equilManager.showNotification(Notification.FAILED_UPDATE_PROFILE, rh.gs(R.string.equil_low_insulin) + insulin + "U", Notification.NORMAL, app.aaps.core.ui.R.raw.alarm) + sp.putBoolean(EquilConst.Prefs.EQUIL_ALARM_INSULIN_10, true) + } + } + + insulin in 3..5 -> { + val alarmInsulin5 = sp.getBoolean(EquilConst.Prefs.EQUIL_ALARM_INSULIN_5, false) + if (!alarmInsulin5) { + equilManager.showNotification(Notification.FAILED_UPDATE_PROFILE, rh.gs(R.string.equil_low_insulin) + insulin + "U", Notification.NORMAL, app.aaps.core.ui.R.raw.alarm) + sp.putBoolean(EquilConst.Prefs.EQUIL_ALARM_INSULIN_5, true) + } + } + + insulin <= 2 -> { + equilManager.showNotification(Notification.FAILED_UPDATE_PROFILE, rh.gs(R.string.equil_low_insulin) + insulin + "U", Notification.URGENT, app.aaps.core.ui.R.raw.alarm) + } + } + } + } + + companion object { + + private const val STATUS_CHECK_INTERVAL_MILLIS = 60 * 3000L // 1 minute + private val BASAL_STEP_DURATION: Duration = Duration.standardMinutes(30) + fun toDuration(dateTime: DateTime?): Duration { + requireNotNull(dateTime) { "dateTime can not be null" } + return Duration(dateTime.toLocalTime().millisOfDay.toLong()) + } + } +} diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/ble/EquilBLE.kt b/pump/equil/src/main/java/app/aaps/pump/equil/ble/EquilBLE.kt new file mode 100644 index 00000000000..b493156f53b --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/ble/EquilBLE.kt @@ -0,0 +1,438 @@ +package app.aaps.pump.equil.ble + +import android.annotation.SuppressLint +import android.bluetooth.BluetoothAdapter +import android.bluetooth.BluetoothDevice +import android.bluetooth.BluetoothGatt +import android.bluetooth.BluetoothGattCallback +import android.bluetooth.BluetoothGattCharacteristic +import android.bluetooth.BluetoothGattDescriptor +import android.bluetooth.BluetoothManager +import android.bluetooth.BluetoothProfile +import android.bluetooth.le.ScanCallback +import android.bluetooth.le.ScanFilter +import android.bluetooth.le.ScanResult +import android.bluetooth.le.ScanSettings +import android.content.Context +import android.os.Handler +import android.os.HandlerThread +import android.os.Looper +import android.os.Message +import android.os.SystemClock +import android.text.TextUtils +import app.aaps.core.interfaces.logging.AAPSLogger +import app.aaps.core.interfaces.logging.LTag +import app.aaps.core.interfaces.rx.bus.RxBus +import app.aaps.core.interfaces.rx.events.EventPumpStatusChanged +import app.aaps.core.utils.notifyAll +import app.aaps.pump.equil.EquilConst +import app.aaps.pump.equil.ble.GattAttributes.characteristicConfigDescriptor +import app.aaps.pump.equil.database.ResolvedResult +import app.aaps.pump.equil.driver.definition.BluetoothConnectionState +import app.aaps.pump.equil.manager.EquilManager +import app.aaps.pump.equil.manager.EquilResponse +import app.aaps.pump.equil.manager.Utils +import app.aaps.pump.equil.manager.command.BaseCmd +import app.aaps.pump.equil.manager.command.CmdDevicesOldGet +import app.aaps.pump.equil.manager.command.CmdHistoryGet +import app.aaps.pump.equil.manager.command.CmdInsulinGet +import app.aaps.pump.equil.manager.command.CmdModelGet +import app.aaps.pump.equil.manager.command.CmdPair +import java.util.UUID +import javax.inject.Inject +import javax.inject.Singleton + +@SuppressLint("MissingPermission") +@Singleton +class EquilBLE @Inject constructor( + private val aapsLogger: AAPSLogger, + private val context: Context, + private val rxBus: RxBus +) { + + var equilManager: EquilManager? = null + private var mGattCallback: BluetoothGattCallback? = null + var notifyChara: BluetoothGattCharacteristic? = null + var writeChara: BluetoothGattCharacteristic? = null + + private var bluetoothGatt: BluetoothGatt? = null + private val bluetoothAdapter: BluetoothAdapter? get() = (context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager?)?.adapter + var isConnected = false + var connecting = false + var macAddress: String? = null + private var bleHandler = Handler(HandlerThread(this::class.simpleName + "Handler").also { it.start() }.looper) + @Synchronized + fun unBond(transmitterMAC: String?) { + if (transmitterMAC == null) return + try { + val pairedDevices = bluetoothAdapter?.bondedDevices ?: return + if (pairedDevices.isNotEmpty()) { + for (device in pairedDevices) { + if (device.address == transmitterMAC) { + try { + val method = device.javaClass.getMethod("removeBond") + method.invoke(device) + } catch (e: Exception) { + aapsLogger.error(LTag.PUMPCOMM, "Error", e) + } + } + } + } + } catch (e: Exception) { + aapsLogger.error(LTag.PUMPCOMM, "Error", e) + } + } + + private fun bleConnectErrorForResult() { + baseCmd?.let { baseCmd -> + synchronized(baseCmd) { + baseCmd.isCmdStatus = false + baseCmd.notifyAll() + } + } + } + + @Suppress("deprecation", "OVERRIDE_DEPRECATION") + fun init(equilManager: EquilManager) { + macAddress = equilManager.address + this.equilManager = equilManager + aapsLogger.debug(LTag.PUMPBTCOMM, "initGatt======= ") + mGattCallback = object : BluetoothGattCallback() { + @Synchronized + override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, i2: Int) { + super.onConnectionStateChange(gatt, status, i2) + val str = if (i2 == BluetoothProfile.STATE_CONNECTED) "CONNECTED" else "DISCONNECTED" + val sb = "onConnectionStateChange called with status:$status, state:$str, i2: $i2, error133: " + aapsLogger.debug(LTag.PUMPBTCOMM, "onConnectionStateChange $sb") + connecting = false + if (status == 133) { + unBond(macAddress) + SystemClock.sleep(50) + aapsLogger.debug(LTag.PUMPCOMM, "error133 ") + baseCmd?.setResolvedResult(ResolvedResult.CONNECT_ERROR) + bleConnectErrorForResult() + disconnect() + return + } + if (i2 == BluetoothProfile.STATE_CONNECTED) { + isConnected = true + equilManager.bluetoothConnectionState = BluetoothConnectionState.CONNECTED + handler.removeMessages(TIME_OUT_CONNECT_WHAT) + bluetoothGatt?.discoverServices() + updateCmdStatus(ResolvedResult.FAILURE) + // rxBus.send(new EventPumpStatusChanged(EventPumpStatusChanged.Status.CONNECTED)); + } else if (i2 == BluetoothProfile.STATE_DISCONNECTED) { + bleConnectErrorForResult() + disconnect() + } + } + + @Synchronized + override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) { + if (status != BluetoothGatt.GATT_SUCCESS) { + aapsLogger.debug(LTag.PUMPBTCOMM, "onServicesDiscovered received: $status") + return + } + val service = gatt.getService(UUID.fromString(GattAttributes.SERVICE_RADIO)) + if (service != null) { + notifyChara = service.getCharacteristic(UUID.fromString(GattAttributes.NRF_UART_NOTIFY)) + writeChara = service.getCharacteristic(UUID.fromString(GattAttributes.NRF_UART_WRITE)) + // rxBus.send(new EventPumpStatusChanged(EventPumpStatusChanged.Status.CONNECTED)); + openNotification() + requestHighPriority() + } + } + + override fun onCharacteristicWrite(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int) { + try { + SystemClock.sleep(EquilConst.EQUIL_BLE_WRITE_TIME_OUT) + writeData() + } catch (e: Exception) { + aapsLogger.error(LTag.PUMPBTCOMM, "Error", e) + } + } + + override fun onCharacteristicRead(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int) { + onCharacteristicChanged(gatt, characteristic) + } + + override fun onCharacteristicChanged(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) { + requestHighPriority() + decode(characteristic.value) + } + + @Synchronized override fun onDescriptorWrite(gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int) { + aapsLogger.debug(LTag.PUMPBTCOMM, "onDescriptorWrite received: $status") + if (status == BluetoothGatt.GATT_SUCCESS) { + aapsLogger.debug(LTag.PUMPBTCOMM, "onDescriptorWrite: Wrote GATT Descriptor successfully.") + ready() + } + } + } + } + + @Suppress("deprecation") fun openNotification() { + aapsLogger.debug(LTag.PUMPBTCOMM, "openNotification: $isConnected") + val r0 = bluetoothGatt?.setCharacteristicNotification(notifyChara, true) + if (r0 == true) { + val descriptor = notifyChara?.getDescriptor(characteristicConfigDescriptor) + val v = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE + descriptor?.setValue(v) + val flag = bluetoothGatt?.writeDescriptor(descriptor) + aapsLogger.debug(LTag.PUMPBTCOMM, "openNotification: $flag") + } + } + + @SuppressLint("MissingPermission") + fun requestHighPriority() { + bluetoothGatt?.requestConnectionPriority(BluetoothGatt.CONNECTION_PRIORITY_HIGH) + } + + fun ready() { + aapsLogger.debug(LTag.PUMPBTCOMM, "ready: ===$baseCmd") + dataList = ArrayList() + baseCmd?.let { baseCmd -> + equilResponse = baseCmd.equilResponse + indexData = 0 + writeData() + } + } + + private fun nextCmd2() { + dataList = ArrayList() + aapsLogger.debug(LTag.PUMPCOMM, "nextCmd===== ${baseCmd?.isEnd}====") + baseCmd?.let { baseCmd -> + equilResponse = baseCmd.nextEquilResponse + aapsLogger.debug(LTag.PUMPCOMM, "nextCmd===== $baseCmd===${equilResponse?.send}") + if ((equilResponse?.send?.size ?: 0) == 0) { + aapsLogger.debug(LTag.PUMPCOMM, "equilResponse is null") + return + } + indexData = 0 + writeData() + } + } + + fun disconnect() { + isConnected = false + startTrue = false + autoScan = false + equilManager?.bluetoothConnectionState = BluetoothConnectionState.DISCONNECTED + aapsLogger.debug(LTag.PUMPBTCOMM, "Closing GATT connection") + bluetoothGatt?.disconnect() + bluetoothGatt?.close() + bluetoothGatt = null + baseCmd = null + preCmd = null + rxBus.send(EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTED)) + } + fun closeBleAuto(){ + handler.postDelayed({ + disconnect() + }, EquilConst.EQUIL_BLE_NEXT_CMD) + } + var autoScan = false + private fun findEquil(mac: String) { + if (mac.isEmpty()) return + if (isConnected) return + val equilDevice: BluetoothDevice? = bluetoothAdapter?.getRemoteDevice(mac) + if (autoScan) startScan() + else connectEquil(equilDevice) + } + + fun connectEquil(device: BluetoothDevice?) { +// disconnect(); + handler.postDelayed({ + if (device != null) { + aapsLogger.debug(LTag.PUMPCOMM, "connectEquil======") + bluetoothGatt = device.connectGatt(context, false, mGattCallback, BluetoothDevice.TRANSPORT_LE) + connecting = true + } + }, 500) + } + + private var baseCmd: BaseCmd? = null + private var preCmd: BaseCmd? = null + fun writeCmd(baseCmd: BaseCmd) { + aapsLogger.debug(LTag.PUMPCOMM, "writeCmd {}", baseCmd) + this.baseCmd = baseCmd + val mac: String = when (baseCmd) { + is CmdPair -> baseCmd.getAddress() + is CmdDevicesOldGet -> baseCmd.getAddress() + else -> equilManager?.address ?: error("Unknown MAC address") + } + autoScan = baseCmd is CmdModelGet || baseCmd is CmdInsulinGet + if (isConnected && baseCmd.isPairStep) { + ready() + } else if (isConnected) { + preCmd?.let { preCmd -> + baseCmd.setRunCode(preCmd.getRunCode()) + baseCmd.setRunPwd(preCmd.getRunPwd()) + nextCmd2() + } + } else { + findEquil(mac) + handler.sendEmptyMessageDelayed(TIME_OUT_CONNECT_WHAT, baseCmd.connectTimeOut.toLong()) + } + preCmd = baseCmd + } + + fun readHistory(baseCmd: CmdHistoryGet) { + if (isConnected && preCmd != null) { + baseCmd.setRunCode(preCmd!!.getRunCode()) + baseCmd.setRunPwd(preCmd!!.getRunPwd()) + this.baseCmd = baseCmd + nextCmd2() + preCmd = baseCmd + } else { + aapsLogger.debug(LTag.PUMPCOMM, "readHistory error") + } + } + + private var equilResponse: EquilResponse? = null + private var indexData = 0 + fun writeData() { + equilResponse?.let { equilResponse -> + val diff = System.currentTimeMillis() - equilResponse.cmdCreateTime + if (diff < EquilConst.EQUIL_CMD_TIME_OUT) { + if (indexData < equilResponse.send.size) { + val data = equilResponse.send[indexData].array() + write(data) + indexData++ + } else aapsLogger.debug(LTag.PUMPCOMM, "indexData error ") + } else aapsLogger.debug(LTag.PUMPCOMM, "equil cmd time out ") + } + } + + @Suppress("deprecation") + private fun write(bytes: ByteArray) { + if (writeChara == null || bluetoothGatt == null) { + aapsLogger.debug(LTag.PUMPCOMM, "write disconnect ") + disconnect() + return + } + writeChara?.setValue(bytes) + aapsLogger.debug(LTag.PUMPBTCOMM, "write: ${Utils.bytesToHex(bytes)}") + bluetoothGatt?.writeCharacteristic(writeChara) + } + + private var dataList: List = ArrayList() + @Synchronized fun decode(buffer: ByteArray?) { + val str = Utils.bytesToHex(buffer) + aapsLogger.debug(LTag.PUMPBTCOMM, "decode=====$str") + val response = baseCmd?.decodeEquilPacket(buffer) + if (response != null) { + writeConf(response) + dataList = ArrayList() + } + } + + private fun writeConf(equilResponse: EquilResponse?) { + try { + dataList = ArrayList() + this.equilResponse = equilResponse + indexData = 0 + writeData() + } catch (e: Exception) { + e.printStackTrace() + } + } + + var handler: Handler = object : Handler(Looper.getMainLooper()) { + override fun handleMessage(msg: Message) { + super.handleMessage(msg) + when (msg.what) { + TIME_OUT_WHAT -> stopScan() + + TIME_OUT_CONNECT_WHAT -> { + stopScan() + aapsLogger.debug(LTag.PUMPCOMM, "TIME_OUT_CONNECT_WHAT====") + baseCmd?.setResolvedResult(ResolvedResult.CONNECT_ERROR) + bleConnectErrorForResult() + disconnect() + } + } + } + } + private var startTrue = false + private fun startScan() { + macAddress = equilManager?.address + if (macAddress.isNullOrEmpty()) return + aapsLogger.debug(LTag.PUMPCOMM, "startScan====$startTrue====$macAddress===") + if (startTrue) { + return + } + val bluetoothLeScanner = bluetoothAdapter?.bluetoothLeScanner + if (bluetoothLeScanner != null) { + updateCmdStatus(ResolvedResult.NOT_FOUNT) + bluetoothLeScanner.startScan(buildScanFilters(), buildScanSettings(), scanCallback) + } + } + + private fun updateCmdStatus(result: ResolvedResult) { + baseCmd?.setResolvedResult(result) + } + + val equilStatus: Unit + get() { + aapsLogger.debug(LTag.PUMPCOMM, "getEquilStatus====$startTrue====$isConnected") + if (startTrue || isConnected) { + return + } + autoScan = false + baseCmd = null + startScan() + } + + private fun buildScanFilters(): List { + val scanFilterList = ArrayList() + if (TextUtils.isEmpty(macAddress)) { + return scanFilterList + } + val scanFilterBuilder = ScanFilter.Builder() + scanFilterBuilder.setDeviceAddress(macAddress) + scanFilterList.add(scanFilterBuilder.build()) + return scanFilterList + } + + private fun buildScanSettings(): ScanSettings { + val builder = ScanSettings.Builder() + builder.setReportDelay(0) + return builder.build() + } + + private var scanCallback: ScanCallback = object : ScanCallback() { + override fun onScanResult(callbackType: Int, result: ScanResult) { + super.onScanResult(callbackType, result) + val name = result.device.name + if (name.isNotEmpty()) { + try { + bleHandler.post { equilManager?.decodeData(result.scanRecord!!.bytes) } + stopScan() + if (autoScan) { + updateCmdStatus(ResolvedResult.CONNECT_ERROR) + connectEquil(result.device) + } + } catch (e: Exception) { + e.printStackTrace() + } + } + } + } + + fun stopScan() { + startTrue = false + handler.removeMessages(TIME_OUT_WHAT) + val bluetoothLeScanner = bluetoothAdapter?.bluetoothLeScanner + if (isBluetoothAvailable) bluetoothLeScanner?.stopScan(scanCallback) + } + + private val isBluetoothAvailable: Boolean + get() = bluetoothAdapter?.isEnabled == true && bluetoothAdapter?.state == BluetoothAdapter.STATE_ON + + companion object { + + const val TIME_OUT_WHAT = 0x12 + const val TIME_OUT_CONNECT_WHAT = 0x13 + } +} diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/ble/GattAttributes.kt b/pump/equil/src/main/java/app/aaps/pump/equil/ble/GattAttributes.kt new file mode 100644 index 00000000000..46268a212cd --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/ble/GattAttributes.kt @@ -0,0 +1,11 @@ +package app.aaps.pump.equil.ble + +import java.util.UUID + +object GattAttributes { + + const val SERVICE_RADIO = "0000f000-0000-1000-8000-00805f9b34fb" + const val NRF_UART_NOTIFY = "0000f001-0000-1000-8000-00805f9b34fb" + const val NRF_UART_WRITE = "0000f001-0000-1000-8000-00805f9b34fb" + val characteristicConfigDescriptor: UUID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb") +} diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/data/AlarmMode.kt b/pump/equil/src/main/java/app/aaps/pump/equil/data/AlarmMode.kt new file mode 100644 index 00000000000..3cfdb7bfcad --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/data/AlarmMode.kt @@ -0,0 +1,12 @@ +package app.aaps.pump.equil.data + +enum class AlarmMode(val command: Int) { + MUTE(0), + TONE(1), + SHAKE(2), + TONE_AND_SHAKE(3); + + companion object { + fun fromInt(number: Int) = entries.firstOrNull { it.command == number } ?: TONE_AND_SHAKE + } +} \ No newline at end of file diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/data/BolusProfile.kt b/pump/equil/src/main/java/app/aaps/pump/equil/data/BolusProfile.kt new file mode 100644 index 00000000000..c41e94d58c5 --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/data/BolusProfile.kt @@ -0,0 +1,8 @@ +package app.aaps.pump.equil.data + + +class BolusProfile { + var timestamp: Long = 0 + var stop: Boolean = false + var insulin: Double = 0.0 +} \ No newline at end of file diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/data/RunMode.kt b/pump/equil/src/main/java/app/aaps/pump/equil/data/RunMode.kt new file mode 100644 index 00000000000..4cabd59e68f --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/data/RunMode.kt @@ -0,0 +1,8 @@ +package app.aaps.pump.equil.data + +enum class RunMode(val command: Int) { + RUN(1), + STOP(2), + SUSPEND(0), + NONE(-1) +} \ No newline at end of file diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/database/Converters.kt b/pump/equil/src/main/java/app/aaps/pump/equil/database/Converters.kt new file mode 100644 index 00000000000..01147ac84e6 --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/database/Converters.kt @@ -0,0 +1,27 @@ +package app.aaps.pump.equil.database + +import androidx.room.TypeConverter +import app.aaps.core.interfaces.profile.Profile +import com.google.gson.GsonBuilder + +class Converters { + + @TypeConverter + fun toBolusType(s: String) = enumValueOf(s) + + @TypeConverter + fun fromBolusType(bolusType: BolusType) = bolusType.name + + @TypeConverter + fun toSegments(s: String?): List { + s ?: return emptyList() + val gson = GsonBuilder().create() + return gson.fromJson(s, Array::class.java).toList() + } + + @TypeConverter + fun fromBasalValues(segments: List): String { + val gson = GsonBuilder().create() + return gson.toJson(segments) + } +} diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/database/EquilHistoryDatabase.kt b/pump/equil/src/main/java/app/aaps/pump/equil/database/EquilHistoryDatabase.kt new file mode 100644 index 00000000000..099952d6a65 --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/database/EquilHistoryDatabase.kt @@ -0,0 +1,31 @@ +package app.aaps.pump.equil.database + +import android.content.Context +import androidx.room.Database +import androidx.room.Room +import androidx.room.RoomDatabase +import androidx.room.TypeConverters + +const val TABLE_EQUIL_HISTORY_PUMP = "equilHistoryPump" +const val TABLE_EQUIL_HISTORY_RECORD = "equilHistoryRecord" + +@Database( + entities = [EquilHistoryPump::class, EquilHistoryRecord::class], + exportSchema = true, + version = EquilHistoryDatabase.VERSION +) +@TypeConverters(Converters::class) +abstract class EquilHistoryDatabase : RoomDatabase() { + + abstract fun historyRecordDao(): EquilHistoryRecordDao + abstract fun historyPumpDao(): EquilHistoryPumpDao + + companion object { + + const val VERSION = 11 + fun build(context: Context) = + Room.databaseBuilder(context.applicationContext, EquilHistoryDatabase::class.java, "equil_database.db") + .fallbackToDestructiveMigration() + .build() + } +} diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/database/EquilHistoryPump.kt b/pump/equil/src/main/java/app/aaps/pump/equil/database/EquilHistoryPump.kt new file mode 100644 index 00000000000..6c9b287e795 --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/database/EquilHistoryPump.kt @@ -0,0 +1,29 @@ +package app.aaps.pump.equil.database + +import androidx.room.Entity +import androidx.room.Index +import androidx.room.PrimaryKey + +@Entity( + tableName = TABLE_EQUIL_HISTORY_PUMP, + indices = [Index("code", "timestamp", "eventTimestamp", "eventIndex")] +) +class EquilHistoryPump { + + @PrimaryKey(autoGenerate = true) + var id: Long = 0 + var timestamp: Long = 0 + var code: Int = 0 + var battery: Int = 0 + var insulin: Int = 0 + var rate: Int = 0 + var largeRate: Int = 0 + var type: Int = 0 + var eventIndex: Int = 0 + var level: Int = 0 + var parm: Int = 0 + var port: Int = 0 + var eventTimestamp: Long = 0 + var serialNumber: String = "" + +} diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/database/EquilHistoryPumpDao.kt b/pump/equil/src/main/java/app/aaps/pump/equil/database/EquilHistoryPumpDao.kt new file mode 100644 index 00000000000..0abea40d220 --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/database/EquilHistoryPumpDao.kt @@ -0,0 +1,32 @@ +package app.aaps.pump.equil.database + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query +import io.reactivex.rxjava3.core.Single + +@Dao +abstract class EquilHistoryPumpDao { + + @Query("SELECT * from $TABLE_EQUIL_HISTORY_PUMP WHERE timestamp >= :timestamp ORDER BY eventTimestamp DESC") + abstract fun allFromByType(timestamp: Long): Single> + + @Query("SELECT * from $TABLE_EQUIL_HISTORY_PUMP WHERE serialNumber=:serialNumber and eventTimestamp >= :startTime and eventTimestamp<=:endTime ORDER BY eventTimestamp DESC") + abstract fun allFromByType(startTime: Long, endTime: Long, serialNumber: String): Single> + + @Query("SELECT * from $TABLE_EQUIL_HISTORY_PUMP WHERE eventTimestamp = :timestamp AND eventIndex = :eventIndex LIMIT 0,1") + abstract fun findByIndexAndEventTime(timestamp: Long, eventIndex: Int): EquilHistoryPump + + @Query("SELECT * from $TABLE_EQUIL_HISTORY_PUMP WHERE eventIndex = :eventIndex LIMIT 0,1") + abstract fun findByEventIndex(eventIndex: Int): EquilHistoryPump + + @Query("SELECT * from $TABLE_EQUIL_HISTORY_PUMP WHERE serialNumber=:serialNumber ORDER BY eventTimestamp DESC LIMIT 0,1") + abstract fun last(serialNumber: String): EquilHistoryPump + + @Insert(onConflict = OnConflictStrategy.REPLACE) + abstract fun createOrUpdate(danaHistoryRecord: EquilHistoryPump) + + @Insert(onConflict = OnConflictStrategy.REPLACE) + abstract fun insert(danaHistoryRecord: EquilHistoryPump): Long +} diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/database/EquilHistoryRecord.kt b/pump/equil/src/main/java/app/aaps/pump/equil/database/EquilHistoryRecord.kt new file mode 100644 index 00000000000..151df240571 --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/database/EquilHistoryRecord.kt @@ -0,0 +1,64 @@ +package app.aaps.pump.equil.database + +import androidx.room.Embedded +import androidx.room.Entity +import androidx.room.Index +import androidx.room.PrimaryKey +import app.aaps.pump.equil.R + +@Entity( + tableName = TABLE_EQUIL_HISTORY_RECORD, + indices = [Index("type", "timestamp")] +) +data class EquilHistoryRecord( + @PrimaryKey(autoGenerate = true) + var id: Long = 0, + @Embedded(prefix = "tempBasalRecord_") var tempBasalRecord: EquilTempBasalRecord? = null, + @Embedded(prefix = "bolusRecord_") var bolusRecord: EquilBolusRecord? = null, + @Embedded(prefix = "basalprofile_") var basalValuesRecord: EquilBasalValuesRecord? = null, + var type: EventType? = null, + var timestamp: Long = 0, + var serialNumber: String, + var resolvedStatus: ResolvedResult? = null, + var resolvedAt: Long? = null, + var note: String? = null + +) { + + constructor( + type: EventType, eventTimestamp: Long, serialNumber: String + ) : this(0, null, null, null, type, eventTimestamp, serialNumber, null, null, null) + + constructor( + eventTimestamp: Long, serialNumber: String + ) : this(0, null, null, null, null, eventTimestamp, serialNumber, null, null, null) + + fun isSuccess(): Boolean = resolvedStatus == ResolvedResult.SUCCESS + + enum class EventType(val resourceId: Int) { + INITIALIZE_EQUIL(R.string.equil_common_cmd_pair), // First step of Pod activation + INSERT_CANNULA(R.string.equil_common_cmd_insert_cannula), // Second step of Pod activation + FILL(R.string.equil_common_cmd_fill), + SET_BASAL_PROFILE(R.string.equil_common_cmd_set_basal_schedule), // + SET_BOLUS(R.string.equil_common_cmd_set_bolus), // + CANCEL_BOLUS(R.string.equil_common_cmd_cancel_bolus), // + SET_EXTENDED_BOLUS(R.string.equil_common_cmd_set_extended_bolus), // + CANCEL_EXTENDED_BOLUS(R.string.equil_common_cmd_cancel_extended_bolus), // + SET_TEMPORARY_BASAL(R.string.equil_common_cmd_set_tbr), // + CANCEL_TEMPORARY_BASAL(R.string.equil_common_cmd_cancel_tbr), // + SET_TIME(R.string.equil_common_cmd_set_time), // + SUSPEND_DELIVERY(R.string.equil_common_cmd_suspend_delivery), + RESUME_DELIVERY(R.string.equil_common_cmd_resume_delivery), + UNPAIR_EQUIL(R.string.equil_common_cmd_unpair), + CHANGE_INSULIN(R.string.equil_common_change_insulin), + SET_ALARM_MUTE(R.string.equil_common_set_alarm_mute), + SET_ALARM_SHAKE(R.string.equil_common_set_alarm_shake), + SET_ALARM_TONE(R.string.equil_common_set_alarm_tone), + SET_ALARM_TONE_AND_SHAK(R.string.equil_common_set_alarm_tone_and_shake), + READ_DEVICES(R.string.equil_common_read_devices), + EQUIL_ALARM(R.string.equil_common_cmd_alarm) + } +} + + + diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/database/EquilHistoryRecordDao.kt b/pump/equil/src/main/java/app/aaps/pump/equil/database/EquilHistoryRecordDao.kt new file mode 100644 index 00000000000..defc6c44d36 --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/database/EquilHistoryRecordDao.kt @@ -0,0 +1,27 @@ +package app.aaps.pump.equil.database + +import androidx.room.* +import io.reactivex.rxjava3.core.Single + +@Dao +abstract class EquilHistoryRecordDao { + + @Insert(onConflict = OnConflictStrategy.REPLACE) + abstract fun insert(equilHistoryRecord: EquilHistoryRecord): Long + + @Update + abstract fun update(equilHistoryRecord: EquilHistoryRecord): Int + + @Query("SELECT * FROM $TABLE_EQUIL_HISTORY_RECORD WHERE id = :id") + abstract fun getEquilHistoryRecordById(id: Long): EquilHistoryRecord? + + @Transaction + @Query("UPDATE $TABLE_EQUIL_HISTORY_RECORD SET resolvedStatus = :resolvedResult, resolvedAt = :resolvedAt WHERE id = :id ") + abstract fun markResolved(id: Long, resolvedResult: ResolvedResult, resolvedAt: Long) + + @Query("SELECT * from $TABLE_EQUIL_HISTORY_RECORD WHERE type=:eventType ORDER BY timestamp DESC LIMIT 0,1") + abstract fun lastRecord(eventType: EquilHistoryRecord.EventType): EquilHistoryRecord + + @Query("SELECT * from $TABLE_EQUIL_HISTORY_RECORD WHERE timestamp >= :startTime and timestamp<=:endTime ORDER BY timestamp DESC") + abstract fun allSince(startTime: Long, endTime: Long): Single> +} diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/database/EquilRecord.kt b/pump/equil/src/main/java/app/aaps/pump/equil/database/EquilRecord.kt new file mode 100644 index 00000000000..e6154aad829 --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/database/EquilRecord.kt @@ -0,0 +1,13 @@ +package app.aaps.pump.equil.database + +import app.aaps.core.interfaces.profile.Profile + +sealed class EquilRecord + +data class EquilBolusRecord(val amount: Double, val bolusType: BolusType, var startTime: Long) : EquilRecord() + +data class EquilTempBasalRecord(val duration: Int, val rate: Double, var startTime: Long) : EquilRecord() + +data class EquilBasalValuesRecord(val segments: List) : EquilRecord() +enum class ResolvedResult { NONE, SUCCESS, FAILURE, CONNECT_ERROR, NOT_FOUNT } +enum class BolusType { DEFAULT, SMB; } diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/di/EquilActivitiesModule.kt b/pump/equil/src/main/java/app/aaps/pump/equil/di/EquilActivitiesModule.kt new file mode 100644 index 00000000000..0c9f590d782 --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/di/EquilActivitiesModule.kt @@ -0,0 +1,48 @@ +package app.aaps.pump.equil.di + +import app.aaps.pump.equil.EquilFragment +import app.aaps.pump.equil.ui.EquilHistoryRecordActivity +import app.aaps.pump.equil.ui.EquilUnPairActivity +import app.aaps.pump.equil.ui.EquilUnPairDetachActivity +import app.aaps.pump.equil.ui.dlg.EquilAutoDressingDlg +import app.aaps.pump.equil.ui.dlg.EquilChangeInsulinDlg +import app.aaps.pump.equil.ui.dlg.EquilPairConfigDlg +import app.aaps.pump.equil.ui.dlg.EquilUnPairDlg +import app.aaps.pump.equil.ui.dlg.LoadingDlg +import app.aaps.pump.equil.ui.pair.EquilChangeInsulinFragment +import app.aaps.pump.equil.ui.pair.EquilPairActivity +import app.aaps.pump.equil.ui.pair.EquilPairAirFragment +import app.aaps.pump.equil.ui.pair.EquilPairAssembleFragment +import app.aaps.pump.equil.ui.pair.EquilPairAttachFragment +import app.aaps.pump.equil.ui.pair.EquilPairConfirmFragment +import app.aaps.pump.equil.ui.pair.EquilPairFillFragment +import app.aaps.pump.equil.ui.pair.EquilPairFragmentBase +import app.aaps.pump.equil.ui.pair.EquilPairSerialNumberFragment +import dagger.Module +import dagger.android.ContributesAndroidInjector + +@Module +@Suppress("unused") +abstract class EquilActivitiesModule { + + @ContributesAndroidInjector abstract fun contributesEquilFragment(): EquilFragment + @ContributesAndroidInjector abstract fun contributesEquilChangeInsulinDlg(): EquilChangeInsulinDlg + @ContributesAndroidInjector abstract fun contributesLoadingDlg(): LoadingDlg + @ContributesAndroidInjector abstract fun contributesEquilUnPairDlg(): EquilUnPairDlg + @ContributesAndroidInjector abstract fun contributesEquilPairConfigDlg(): EquilPairConfigDlg + @ContributesAndroidInjector abstract fun contributesEquilAutoDressingDlg(): EquilAutoDressingDlg + + @ContributesAndroidInjector abstract fun contributesEquilUnPairDetachActivity(): EquilUnPairDetachActivity + @ContributesAndroidInjector abstract fun contributesEquilUnPairActivity(): EquilUnPairActivity + @ContributesAndroidInjector abstract fun contributesEquilPairActivity(): EquilPairActivity + @ContributesAndroidInjector abstract fun contributesEquilPairFragment(): EquilPairFragmentBase + @ContributesAndroidInjector abstract fun contributesEquilPairAssembleFragment(): EquilPairAssembleFragment + @ContributesAndroidInjector abstract fun contributesEquilPairSerialNumberFragment(): EquilPairSerialNumberFragment + @ContributesAndroidInjector abstract fun contributesEquilPairFillFragment(): EquilPairFillFragment + @ContributesAndroidInjector abstract fun contributesEquilPairConfirmFragment(): EquilPairConfirmFragment + @ContributesAndroidInjector abstract fun contributesEquilPairAttachFragment(): EquilPairAttachFragment + @ContributesAndroidInjector abstract fun contributesEquilPairAirFragment(): EquilPairAirFragment + @ContributesAndroidInjector abstract fun contributesEquilChangeInsulinFragment(): EquilChangeInsulinFragment + + @ContributesAndroidInjector abstract fun contributesEquilHistoryRecordActivity(): EquilHistoryRecordActivity +} \ No newline at end of file diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/di/EquilHistoryModule.kt b/pump/equil/src/main/java/app/aaps/pump/equil/di/EquilHistoryModule.kt new file mode 100644 index 00000000000..66bf63099bd --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/di/EquilHistoryModule.kt @@ -0,0 +1,28 @@ +package app.aaps.pump.equil.di + +import android.content.Context +import app.aaps.pump.equil.database.EquilHistoryDatabase +import app.aaps.pump.equil.database.EquilHistoryPumpDao +import app.aaps.pump.equil.database.EquilHistoryRecordDao +import dagger.Module +import dagger.Provides +import javax.inject.Singleton + +@Module +@Suppress("unused") +class EquilHistoryModule { + + @Provides + @Singleton + internal fun provideDatabase(context: Context): EquilHistoryDatabase = EquilHistoryDatabase.build(context) + + @Provides + @Singleton + internal fun provideHistoryRecordDao(equilHistoryDatabase: EquilHistoryDatabase): EquilHistoryRecordDao = + equilHistoryDatabase.historyRecordDao() + + @Provides + @Singleton + internal fun provideHistoryPumpDao(equilHistoryDatabase: EquilHistoryDatabase): EquilHistoryPumpDao = + equilHistoryDatabase.historyPumpDao() +} diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/di/EquilModule.kt b/pump/equil/src/main/java/app/aaps/pump/equil/di/EquilModule.kt new file mode 100644 index 00000000000..c80fcd7f7de --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/di/EquilModule.kt @@ -0,0 +1,13 @@ +package app.aaps.pump.equil.di + +import dagger.Module + +@Module( + includes = [ + EquilActivitiesModule::class, + EquilServicesModule::class, + EquilHistoryModule::class + ] +) + +open class EquilModule \ No newline at end of file diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/di/EquilServicesModule.kt b/pump/equil/src/main/java/app/aaps/pump/equil/di/EquilServicesModule.kt new file mode 100644 index 00000000000..02353d5ad96 --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/di/EquilServicesModule.kt @@ -0,0 +1,12 @@ +package app.aaps.pump.equil.di + +import app.aaps.pump.equil.manager.EquilManager +import dagger.Module +import dagger.android.ContributesAndroidInjector + +@Module +@Suppress("unused") +abstract class EquilServicesModule { + + @ContributesAndroidInjector abstract fun contributesEquilManager(): EquilManager +} \ No newline at end of file diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/driver/definition/ActivationProgress.kt b/pump/equil/src/main/java/app/aaps/pump/equil/driver/definition/ActivationProgress.kt new file mode 100644 index 00000000000..711ded5380b --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/driver/definition/ActivationProgress.kt @@ -0,0 +1,21 @@ +package app.aaps.pump.equil.driver.definition + +enum class ActivationProgress { + NONE, + PRIMING, + CANNULA_CHANGE, + CANNULA_INSERTED, + COMPLETED; + + open fun isBefore(other: ActivationProgress): Boolean { + return ordinal < other.ordinal + } + + open fun isAtLeast(other: ActivationProgress): Boolean { + return ordinal >= other.ordinal + } + + open fun isAfter(other: ActivationProgress): Boolean { + return ordinal > other.ordinal + } +} \ No newline at end of file diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/driver/definition/BasalSchedule.kt b/pump/equil/src/main/java/app/aaps/pump/equil/driver/definition/BasalSchedule.kt new file mode 100644 index 00000000000..fdd9d6ddae0 --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/driver/definition/BasalSchedule.kt @@ -0,0 +1,52 @@ +package app.aaps.pump.equil.driver.definition + +import app.aaps.core.interfaces.profile.Profile +import org.joda.time.Duration +import java.util.Objects + +class BasalSchedule(private val entries: List) { + + init { + require(entries.isNotEmpty()) { "Entries can not be empty" } + require(entries[0].startTime.isEqual(Duration.ZERO)) { "First basal schedule entry should have 0 offset" } + } + + fun rateAt(offset: Duration): Double = lookup(offset).basalScheduleEntry.rate + fun getEntries(): List = ArrayList(entries) + + private fun lookup(offset: Duration): BasalScheduleLookupResult { + require(!(offset.isLongerThan(Duration.standardHours(24)) || offset.isShorterThan(Duration.ZERO))) { "Invalid duration" } + val reversedBasalScheduleEntries = reversedBasalScheduleEntries() + var last = Duration.standardHours(24) + for ((index, entry) in reversedBasalScheduleEntries.withIndex()) { + if (entry.startTime.isShorterThan(offset) || entry.startTime == offset) + return BasalScheduleLookupResult(reversedBasalScheduleEntries.size - (index + 1), entry, entry.startTime, last.minus(entry.startTime)) + last = entry.startTime + } + throw IllegalArgumentException("Basal schedule incomplete") + } + + private fun reversedBasalScheduleEntries(): List = ArrayList(entries).toMutableList().also { it.reverse() } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || javaClass != other.javaClass) return false + val that = other as BasalSchedule + return entries == that.entries + } + + override fun hashCode(): Int = Objects.hash(entries) + class BasalScheduleLookupResult internal constructor(val index: Int, val basalScheduleEntry: BasalScheduleEntry, val startTime: Duration, val duration: Duration) + companion object { + + fun mapProfileToBasalSchedule(profile: Profile): BasalSchedule { + val entries = ArrayList() + for (i in 0..23) { + val value = profile.getBasalTimeFromMidnight(i * 60 * 60) + val basalScheduleEntry = BasalScheduleEntry(value, Duration.standardSeconds((i * 60 * 60L))) + entries.add(basalScheduleEntry) + } + return BasalSchedule(entries) + } + } +} diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/driver/definition/BasalScheduleEntry.kt b/pump/equil/src/main/java/app/aaps/pump/equil/driver/definition/BasalScheduleEntry.kt new file mode 100644 index 00000000000..658bdc685e8 --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/driver/definition/BasalScheduleEntry.kt @@ -0,0 +1,5 @@ +package app.aaps.pump.equil.driver.definition + +import org.joda.time.Duration + +data class BasalScheduleEntry(val rate: Double, val startTime: Duration) \ No newline at end of file diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/driver/definition/BluetoothConnectionState.kt b/pump/equil/src/main/java/app/aaps/pump/equil/driver/definition/BluetoothConnectionState.kt new file mode 100644 index 00000000000..506c90d0e54 --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/driver/definition/BluetoothConnectionState.kt @@ -0,0 +1,5 @@ +package app.aaps.pump.equil.driver.definition + +enum class BluetoothConnectionState { + CONNECTING, CONNECTED, DISCONNECTED +} \ No newline at end of file diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/driver/definition/EquilHistoryEntryGroup.kt b/pump/equil/src/main/java/app/aaps/pump/equil/driver/definition/EquilHistoryEntryGroup.kt new file mode 100644 index 00000000000..20ff63dcc12 --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/driver/definition/EquilHistoryEntryGroup.kt @@ -0,0 +1,37 @@ +package app.aaps.pump.equil.driver.definition + +import app.aaps.core.interfaces.resources.ResourceHelper +import app.aaps.pump.equil.R + +enum class EquilHistoryEntryGroup(val resourceId: Int) { + + All(R.string.equil_history_group_all), + Pair(R.string.equil_history_group_pair), + Bolus(R.string.equil_history_group_bolus), + Basal(R.string.equil_history_group_basal), + Configuration(R.string.equil_history_group_configuration), + ; + + var translated: String? = null + private set + + override fun toString(): String { + return translated!! + } + + companion object { + + private var translatedList: MutableList = mutableListOf() + + fun getTranslatedList(rh: ResourceHelper): List { + if (translatedList.isEmpty()) { + for (pumpHistoryEntryGroup in entries) { + pumpHistoryEntryGroup.translated = rh.gs(pumpHistoryEntryGroup.resourceId) + translatedList.add(pumpHistoryEntryGroup) + } + } + return translatedList + } + } +} + diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/events/EventEquilDataChanged.kt b/pump/equil/src/main/java/app/aaps/pump/equil/events/EventEquilDataChanged.kt new file mode 100644 index 00000000000..d078f40a5a6 --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/events/EventEquilDataChanged.kt @@ -0,0 +1,5 @@ +package app.aaps.pump.equil.events + +import app.aaps.core.interfaces.rx.events.Event + +class EventEquilDataChanged : Event() diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/events/EventEquilInsulinChanged.kt b/pump/equil/src/main/java/app/aaps/pump/equil/events/EventEquilInsulinChanged.kt new file mode 100644 index 00000000000..988c3283cc4 --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/events/EventEquilInsulinChanged.kt @@ -0,0 +1,5 @@ +package app.aaps.pump.equil.events + +import app.aaps.core.interfaces.rx.events.Event + +class EventEquilInsulinChanged : Event() diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/events/EventEquilModeChanged.kt b/pump/equil/src/main/java/app/aaps/pump/equil/events/EventEquilModeChanged.kt new file mode 100644 index 00000000000..3ff6d592d4f --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/events/EventEquilModeChanged.kt @@ -0,0 +1,5 @@ +package app.aaps.pump.equil.events + +import app.aaps.core.interfaces.rx.events.Event + +class EventEquilModeChanged : Event() diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/events/EventEquilUnPairChanged.kt b/pump/equil/src/main/java/app/aaps/pump/equil/events/EventEquilUnPairChanged.kt new file mode 100644 index 00000000000..28b6d132381 --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/events/EventEquilUnPairChanged.kt @@ -0,0 +1,5 @@ +package app.aaps.pump.equil.events + +import app.aaps.core.interfaces.rx.events.Event + +class EventEquilUnPairChanged : Event() diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/manager/AESUtil.java b/pump/equil/src/main/java/app/aaps/pump/equil/manager/AESUtil.java new file mode 100644 index 00000000000..7bcddeabf0f --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/manager/AESUtil.java @@ -0,0 +1,87 @@ +package app.aaps.pump.equil.manager; + + +import android.util.Log; + +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.SecureRandom; +import java.util.Arrays; + +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import javax.crypto.spec.GCMParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +import app.aaps.core.interfaces.logging.LTag; + + +public class AESUtil { + private static byte[] generateAESKeyFromPassword(String password) { + try { + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + byte[] inputBytes = password.getBytes(StandardCharsets.UTF_8); + byte[] hashBytes = digest.digest(inputBytes); + byte[] extractedBytes = new byte[16]; + System.arraycopy(hashBytes, 2, extractedBytes, 0, 16); + return extractedBytes; + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + public static byte[] getEquilPassWord(String password) { + String plaintextDefault = "Equil"; // + byte[] defaultKey = generateAESKeyFromPassword(plaintextDefault); + byte[] aesKey = Utils.concat(defaultKey, generateAESKeyFromPassword(password)); + Log.e(LTag.PUMPCOMM.toString(), Utils.bytesToHex(aesKey) + "===" + aesKey.length); + return aesKey; + } + + public static byte[] generateRandomIV(int length) { + try { + SecureRandom secureRandom = new SecureRandom(); + byte[] ivBytes = new byte[length]; + secureRandom.nextBytes(ivBytes); + return ivBytes; + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + public static EquilCmdModel aesEncrypt(byte[] pwd, byte[] data) throws Exception { + byte[] iv = generateRandomIV(12); + SecretKey key = new SecretKeySpec(pwd, "AES"); + Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); + GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(128, iv); + cipher.init(Cipher.ENCRYPT_MODE, key, gcmParameterSpec); + byte[] ciphertext = cipher.doFinal(data); + byte[] authenticationTag = Arrays.copyOfRange(ciphertext, ciphertext.length - 16, ciphertext.length); + byte[] encryptedData = Arrays.copyOfRange(ciphertext, 0, ciphertext.length - 16); + EquilCmdModel equilcmdmodel = new EquilCmdModel(); + equilcmdmodel.setTag(Utils.bytesToHex(authenticationTag)); + equilcmdmodel.setIv(Utils.bytesToHex(iv)); + equilcmdmodel.setCiphertext(Utils.bytesToHex(encryptedData)); + return equilcmdmodel; + } + + + public static String decrypt(EquilCmdModel equilCmdModel, byte[] keyBytes) throws Exception { + String iv = equilCmdModel.getIv(); + String ciphertext = equilCmdModel.getCiphertext(); + String authenticationTag = equilCmdModel.getTag(); + byte[] ivBytes = Utils.hexStringToBytes(iv); + SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES"); + byte[] decodedCiphertext = Utils.hexStringToBytes(ciphertext); + byte[] decodedAuthenticationTag = Utils.hexStringToBytes(authenticationTag); + Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); + GCMParameterSpec parameterSpec = new GCMParameterSpec(128, ivBytes); + cipher.init(Cipher.DECRYPT_MODE, keySpec, parameterSpec); + cipher.update(decodedCiphertext); + byte[] decrypted = cipher.doFinal(decodedAuthenticationTag); + String content = Utils.bytesToHex(decrypted); + return content; + } +} \ No newline at end of file diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/manager/Crc.java b/pump/equil/src/main/java/app/aaps/pump/equil/manager/Crc.java new file mode 100644 index 00000000000..c1b5a5d12a0 --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/manager/Crc.java @@ -0,0 +1,49 @@ +package app.aaps.pump.equil.manager; + +public class Crc { + + public static int CRC8_MAXIM(byte[] source) { + int offset = 0; + int length = source.length; + int wCRCin = 0x00; + // Integer.reverse(0x31) >>> 24 + int wCPoly = 0x8C; + for (int i = offset, cnt = offset + length; i < cnt; i++) { + wCRCin ^= ((long) source[i] & 0xFF); + for (int j = 0; j < 8; j++) { + if ((wCRCin & 0x01) != 0) { + wCRCin >>= 1; + wCRCin ^= wCPoly; + } else { + wCRCin >>= 1; + } + } + } + return wCRCin ^= 0x00; + } + + + public static byte[] getCRC(byte[] bytes) { + int CRC = 0x0000ffff; + int POLYNOMIAL = 0x0000a001; + int i, j; + for (i = 0; i < bytes.length; i++) { + CRC ^= ((int) bytes[i] & 0x000000ff); + for (j = 0; j < 8; j++) { + if ((CRC & 0x00000001) != 0) { + CRC >>= 1; + CRC ^= POLYNOMIAL; + } else { + CRC >>= 1; + } + } + } + String result = Integer.toHexString(CRC).toUpperCase(); + if (result.length() != 4) { + StringBuffer sb = new StringBuffer("0000"); + result = sb.replace(4 - result.length(), 4, result).toString(); + } + return Utils.hexStringToBytes(result); + } + +} diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/manager/EquilCmdModel.java b/pump/equil/src/main/java/app/aaps/pump/equil/manager/EquilCmdModel.java new file mode 100644 index 00000000000..6e3f332f7b7 --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/manager/EquilCmdModel.java @@ -0,0 +1,51 @@ +package app.aaps.pump.equil.manager; + +import androidx.annotation.NonNull; + +public class EquilCmdModel { + private String code; + private String iv; + private String tag; + private String ciphertext; + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getIv() { + return iv; + } + + public void setIv(String iv) { + this.iv = iv; + } + + public String getTag() { + return tag; + } + + public void setTag(String tag) { + this.tag = tag; + } + + public String getCiphertext() { + return ciphertext; + } + + public void setCiphertext(String ciphertext) { + this.ciphertext = ciphertext; + } + + @NonNull @Override public String toString() { + return "EquilCmdModel{" + + "code='" + code + '\'' + + ", iv='" + iv + '\'' + + ", tag='" + tag + '\'' + + ", ciphertext='" + ciphertext + '\'' + + '}'; + } +} diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/manager/EquilManager.java b/pump/equil/src/main/java/app/aaps/pump/equil/manager/EquilManager.java new file mode 100644 index 00000000000..a356ce8e3e6 --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/manager/EquilManager.java @@ -0,0 +1,1157 @@ +package app.aaps.pump.equil.manager; + +import android.os.SystemClock; +import android.text.TextUtils; + +import androidx.annotation.NonNull; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSerializer; + +import org.apache.commons.lang3.StringUtils; +import org.joda.time.DateTime; +import org.joda.time.DateTimeZone; +import org.joda.time.format.ISODateTimeFormat; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.List; +import java.util.Optional; +import java.util.function.Supplier; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import app.aaps.core.interfaces.logging.AAPSLogger; +import app.aaps.core.interfaces.logging.LTag; +import app.aaps.core.interfaces.notifications.Notification; +import app.aaps.core.interfaces.objects.Instantiator; +import app.aaps.core.interfaces.profile.Profile; +import app.aaps.core.interfaces.pump.DetailedBolusInfo; +import app.aaps.core.interfaces.pump.PumpEnactResult; +import app.aaps.core.interfaces.pump.PumpSync; +import app.aaps.core.interfaces.pump.defs.PumpType; +import app.aaps.core.interfaces.resources.ResourceHelper; +import app.aaps.core.interfaces.rx.bus.RxBus; +import app.aaps.core.interfaces.rx.events.Event; +import app.aaps.core.interfaces.rx.events.EventDismissNotification; +import app.aaps.core.interfaces.rx.events.EventOverviewBolusProgress; +import app.aaps.core.interfaces.sharedPreferences.SP; +import app.aaps.core.interfaces.utils.HardLimits; +import app.aaps.core.main.events.EventNewNotification; +import app.aaps.pump.equil.EquilConst; +import app.aaps.pump.equil.R; +import app.aaps.pump.equil.ble.EquilBLE; +import app.aaps.pump.equil.data.AlarmMode; +import app.aaps.pump.equil.data.BolusProfile; +import app.aaps.pump.equil.data.RunMode; +import app.aaps.pump.equil.database.BolusType; +import app.aaps.pump.equil.database.EquilBasalValuesRecord; +import app.aaps.pump.equil.database.EquilBolusRecord; +import app.aaps.pump.equil.database.EquilHistoryPump; +import app.aaps.pump.equil.database.EquilHistoryPumpDao; +import app.aaps.pump.equil.database.EquilHistoryRecord; +import app.aaps.pump.equil.database.EquilHistoryRecordDao; +import app.aaps.pump.equil.database.EquilTempBasalRecord; +import app.aaps.pump.equil.database.ResolvedResult; +import app.aaps.pump.equil.driver.definition.ActivationProgress; +import app.aaps.pump.equil.driver.definition.BasalSchedule; +import app.aaps.pump.equil.driver.definition.BluetoothConnectionState; +import app.aaps.pump.equil.events.EventEquilDataChanged; +import app.aaps.pump.equil.events.EventEquilInsulinChanged; +import app.aaps.pump.equil.events.EventEquilModeChanged; +import app.aaps.pump.equil.manager.command.BaseCmd; +import app.aaps.pump.equil.manager.command.CmdBasalGet; +import app.aaps.pump.equil.manager.command.CmdBasalSet; +import app.aaps.pump.equil.manager.command.CmdExtendedBolusSet; +import app.aaps.pump.equil.manager.command.CmdHistoryGet; +import app.aaps.pump.equil.manager.command.CmdLargeBasalSet; +import app.aaps.pump.equil.manager.command.CmdModelGet; +import app.aaps.pump.equil.manager.command.CmdTempBasalGet; +import app.aaps.pump.equil.manager.command.CmdTempBasalSet; +import app.aaps.pump.equil.manager.command.PumpEvent; +import dagger.android.HasAndroidInjector; + +@Singleton +public class EquilManager { + private final HasAndroidInjector injector; + private final AAPSLogger aapsLogger; + private final RxBus rxBus; + private final ResourceHelper rh; + private final SP sp; + private final PumpSync pumpSync; + private final Instantiator instantiator; + EquilBLE equilBLE; + EquilHistoryRecordDao equilHistoryRecordDao; + EquilHistoryPumpDao equilHistoryPumpDao; + + public AAPSLogger getAapsLogger() { + return aapsLogger; + } + + public SP getSp() { + return sp; + } + + + @Inject + public EquilManager( + HasAndroidInjector injector, + AAPSLogger aapsLogger, + RxBus rxBus, + SP sp, + ResourceHelper rh, + PumpSync pumpSync, + EquilBLE equilBLE, + EquilHistoryRecordDao equilHistoryRecordDao, + EquilHistoryPumpDao equilHistoryPumpDao, + Instantiator instantiator + ) { + this.injector = injector; + this.aapsLogger = aapsLogger; + this.rxBus = rxBus; + this.sp = sp; + this.rh = rh; + this.pumpSync = pumpSync; + this.equilBLE = equilBLE; + this.equilHistoryRecordDao = equilHistoryRecordDao; + this.equilHistoryPumpDao = equilHistoryPumpDao; + this.instantiator = instantiator; + + this.gsonInstance = createGson(); + loadPodState(); + initEquilError(); + equilBLE.init(this); + equilBLE.getEquilStatus(); + } + + List listEvent; + + private void initEquilError() { + listEvent = new ArrayList<>(); + listEvent.add(new PumpEvent(4, 2, 2, rh.gs(R.string.equil_history_item3))); + listEvent.add(new PumpEvent(4, 3, 0, rh.gs(R.string.equil_history_item4))); + listEvent.add(new PumpEvent(4, 3, 2, rh.gs(R.string.equil_history_item5))); + listEvent.add(new PumpEvent(4, 6, 1, rh.gs(R.string.equil_shutdown_be))); + listEvent.add(new PumpEvent(4, 6, 2, rh.gs(R.string.equil_shutdown))); + listEvent.add(new PumpEvent(4, 8, 0, rh.gs(R.string.equil_shutdown))); + listEvent.add(new PumpEvent(5, 1, 2, rh.gs(R.string.equil_history_item18))); + + } + + public String getEquilError(int port, int type, int level) { + PumpEvent pumpEvent = new PumpEvent(port, type, level, ""); + int index = listEvent.indexOf(pumpEvent); + if (index == -1) { + return ""; + } + return listEvent.get(index).getConent(); + } + + public void closeBleAuto() { + equilBLE.closeBleAuto(); + + } + + public PumpEnactResult closeBle() { + PumpEnactResult result = new PumpEnactResult(injector); + try { + equilBLE.disconnect(); + } catch (Exception ex) { + result.success(false).enacted(false).comment(translateException(ex)); + } + return result; + } + + public PumpEnactResult readStatus() { + PumpEnactResult result = new PumpEnactResult(injector); + try { + equilBLE.getEquilStatus(); + } catch (Exception ex) { + result.success(false).enacted(false).comment(translateException(ex)); + } + return result; + } + + public PumpEnactResult getTempBasalPump() { + PumpEnactResult result = new PumpEnactResult(injector); + try { + CmdTempBasalGet command = new CmdTempBasalGet(); + command.setEquilManager(this); + equilBLE.writeCmd(command); + synchronized (command) { + command.wait(command.getTimeOut()); + } + result.setSuccess(command.isCmdStatus()); + result.enacted(command.getTime() != 0); + SystemClock.sleep(EquilConst.EQUIL_BLE_NEXT_CMD); + } catch (Exception ex) { + ex.printStackTrace(); + result.success(false).enacted(false).comment(translateException(ex)); + } + return result; + } + + public PumpEnactResult setTempBasal(double insulin, int time, boolean cancel) { + PumpEnactResult result = new PumpEnactResult(injector); + try { + CmdTempBasalSet command = new CmdTempBasalSet(insulin, time); + command.setCancel(cancel); + EquilHistoryRecord equilHistoryRecord = addHistory(command); + command.setEquilManager(this); + equilBLE.writeCmd(command); + synchronized (command) { + command.wait(command.getTimeOut()); + } + if (command.isCmdStatus()) { + long currentTime = System.currentTimeMillis(); + if (cancel) { + pumpSync.syncStopTemporaryBasalWithPumpId( + currentTime, + currentTime, + PumpType.EQUIL, + getSerialNumber() + ); + setTempBasal(null); + } else { + EquilTempBasalRecord tempBasalRecord = + new EquilTempBasalRecord(time * 60 * 1000, + insulin, currentTime); + setTempBasal(tempBasalRecord); + pumpSync.syncTemporaryBasalWithPumpId( + currentTime, + insulin, + (long) time * 60 * 1000, + true, + PumpSync.TemporaryBasalType.NORMAL, + currentTime, + PumpType.EQUIL, + getSerialNumber() + ); + } + command.setResolvedResult(ResolvedResult.SUCCESS); + } + updateHistory(equilHistoryRecord, command.getResolvedResult()); + result.setSuccess(command.isCmdStatus()); + result.enacted(true); + } catch (Exception ex) { + ex.printStackTrace(); + result.success(false).enacted(false).comment(translateException(ex)); + } + return result; + } + + public PumpEnactResult setExtendedBolus(double insulin, int time, boolean cancel) { + PumpEnactResult result = new PumpEnactResult(injector); + try { + CmdExtendedBolusSet command = new CmdExtendedBolusSet(insulin, time, cancel); + EquilHistoryRecord equilHistoryRecord = addHistory(command); + command.setEquilManager(this); + equilBLE.writeCmd(command); + synchronized (command) { + command.wait(command.getTimeOut()); + } + + result.setSuccess(command.isCmdStatus()); + if (command.isCmdStatus()) { + command.setResolvedResult(ResolvedResult.SUCCESS); + long currentTimeMillis = System.currentTimeMillis(); + if (cancel) { + pumpSync.syncStopExtendedBolusWithPumpId( + currentTimeMillis, + currentTimeMillis, + PumpType.EQUIL, + getSerialNumber() + ); + } else { + pumpSync.syncExtendedBolusWithPumpId( + currentTimeMillis, + insulin, + (long) time * 60 * 1000, + true, + currentTimeMillis, + PumpType.EQUIL, + getSerialNumber() + ); + } + + result.enacted(true); + } else { + result.setSuccess(false); + } + updateHistory(equilHistoryRecord, command.getResolvedResult()); + } catch (Exception ex) { + result.success(false).enacted(false).comment(translateException(ex)); + } + return result; + } + + + public PumpEnactResult bolus(DetailedBolusInfo detailedBolusInfo, BolusProfile bolusProfile) { + EventOverviewBolusProgress progressUpdateEvent = EventOverviewBolusProgress.INSTANCE; + progressUpdateEvent.setT(new EventOverviewBolusProgress.Treatment(HardLimits.MAX_IOB_LGS, 0, + detailedBolusInfo.getBolusType() == + DetailedBolusInfo.BolusType.SMB, detailedBolusInfo.getId())); + PumpEnactResult result = new PumpEnactResult(injector); + try { + CmdLargeBasalSet command = new CmdLargeBasalSet(detailedBolusInfo.insulin); + EquilHistoryRecord equilHistoryRecord = addHistory(command); + command.setEquilManager(this); + equilBLE.writeCmd(command); + synchronized (command) { + command.wait(command.getTimeOut()); + } + bolusProfile.setStop(false); + int sleep = command.getStepTime() / 20 * 200; + sleep = 2000; + float percent1 = (float) (5f / detailedBolusInfo.insulin); + aapsLogger.debug(LTag.PUMPCOMM, "sleep===" + detailedBolusInfo.insulin + "===" + percent1); + float percent = 0; + if (command.isCmdStatus()) { + result.setSuccess(true); + result.enacted(true); + while (!bolusProfile.getStop() && percent < 100) { + progressUpdateEvent.setPercent((int) percent); + progressUpdateEvent.setStatus(this.rh.gs(R.string.equil_bolus_delivered, + percent / 100d * detailedBolusInfo.insulin, + detailedBolusInfo.insulin)); + rxBus.send(progressUpdateEvent); + SystemClock.sleep(sleep); + percent = percent + percent1; + aapsLogger.debug(LTag.PUMPCOMM, "isCmdStatus===" + percent + "====" + bolusProfile.getStop()); + } + result.setComment(rh.gs(app.aaps.core.ui.R.string.virtualpump_resultok)); + } else { + result.setSuccess(false); + result.enacted(false); + result.setComment(rh.gs(R.string.equil_command_connect_error)); + } + result.setBolusDelivered(percent / 100d * detailedBolusInfo.insulin); + if (result.getSuccess()) { + command.setResolvedResult(ResolvedResult.SUCCESS); + long currentTime = System.currentTimeMillis(); + pumpSync.syncBolusWithPumpId(currentTime, + result.getBolusDelivered(), + detailedBolusInfo.getBolusType(), + detailedBolusInfo.timestamp, + PumpType.EQUIL, + getSerialNumber()); + EquilBolusRecord equilBolusRecord = + new EquilBolusRecord(result.getBolusDelivered(), + BolusType.SMB, currentTime); + setBolusRecord(equilBolusRecord); + + } + updateHistory(equilHistoryRecord, command.getResolvedResult()); + } catch (Exception ex) { + result.success(false).enacted(false).comment(translateException(ex)); + } + return result; + } + + public PumpEnactResult stopBolus(BolusProfile bolusProfile) { + PumpEnactResult result = new PumpEnactResult(injector); + try { + BaseCmd command = new CmdLargeBasalSet(0); + EquilHistoryRecord equilHistoryRecord = addHistory(command); + command.setEquilManager(this); + equilBLE.writeCmd(command); + synchronized (command) { + command.wait(command.getTimeOut()); + } + bolusProfile.setStop(command.isCmdStatus()); + aapsLogger.debug(LTag.PUMPCOMM, "stopBolus==="); + result.setSuccess(command.isCmdStatus()); + if (command.isCmdStatus()) { + command.setResolvedResult(ResolvedResult.SUCCESS); + } + updateHistory(equilHistoryRecord, command.getResolvedResult()); + result.enacted(true); + } catch (Exception ex) { + result.success(false).enacted(false).comment(translateException(ex)); + } + return result; + } + + public int loadEquilHistory(int index) { + try { + aapsLogger.debug(LTag.PUMPCOMM, "loadHistory start: "); + CmdHistoryGet historyGet = new CmdHistoryGet(index); + historyGet.setEquilManager(this); + equilBLE.readHistory(historyGet); + synchronized (historyGet) { + historyGet.wait(historyGet.getTimeOut()); + } + aapsLogger.debug(LTag.PUMPCOMM, "loadHistory end: "); + return historyGet.getCurrentIndex(); + } catch (Exception ex) { + ex.printStackTrace(); + } + return -1; + } + + public int loadHistory(int index) { + try { + CmdHistoryGet historyGet = new CmdHistoryGet(index); + historyGet.setEquilManager(this); + equilBLE.writeCmd(historyGet); + synchronized (historyGet) { + historyGet.wait(historyGet.getTimeOut()); + } + aapsLogger.debug(LTag.PUMPCOMM, "loadHistory end: "); + return historyGet.getCurrentIndex(); + } catch (Exception ex) { + ex.printStackTrace(); + } + return -1; + } + + public PumpEnactResult getBasal(Profile profile) { + PumpEnactResult result = new PumpEnactResult(injector); + try { + CmdBasalGet cmdBasalGet = new CmdBasalGet(profile); + cmdBasalGet.setEquilManager(this); + equilBLE.writeCmd(cmdBasalGet); + synchronized (cmdBasalGet) { + cmdBasalGet.wait(cmdBasalGet.getTimeOut()); + } + result.setSuccess(cmdBasalGet.isCmdStatus()); + } catch (Exception ex) { + ex.printStackTrace(); + result.success(false).enacted(false).comment(translateException(ex)); + } + return result; + } + + public EquilHistoryRecord addHistory(BaseCmd command) { + EquilHistoryRecord equilHistoryRecord = new EquilHistoryRecord(System.currentTimeMillis(), getSerialNumber()); + if (command.getEventType() != null) { + equilHistoryRecord.setType(command.getEventType()); + } + if (command instanceof CmdBasalSet) { + Profile profile = ((CmdBasalSet) command).getProfile(); + equilHistoryRecord.setBasalValuesRecord(new EquilBasalValuesRecord(Arrays.asList(profile.getBasalValues()))); + } + if (command instanceof CmdTempBasalSet) { + CmdTempBasalSet cmd = ((CmdTempBasalSet) command); + boolean cancel = cmd.isCancel(); + if (!cancel) { + EquilTempBasalRecord equilTempBasalRecord = + new EquilTempBasalRecord(cmd.getDuration() * 60 * 1000, + cmd.getInsulin(), System.currentTimeMillis()); + equilHistoryRecord.setTempBasalRecord(equilTempBasalRecord); + } + } + if (command instanceof CmdExtendedBolusSet) { + CmdExtendedBolusSet cmd = ((CmdExtendedBolusSet) command); + boolean cancel = cmd.isCancel(); + if (!cancel) { + EquilTempBasalRecord equilTempBasalRecord = + new EquilTempBasalRecord(cmd.getDurationInMinutes() * 60 * 1000, + cmd.getInsulin(), System.currentTimeMillis()); + equilHistoryRecord.setTempBasalRecord(equilTempBasalRecord); + } + } + if (command instanceof CmdLargeBasalSet) { + CmdLargeBasalSet cmd = ((CmdLargeBasalSet) command); + double insulin = cmd.getInsulin(); + if (insulin != 0) { + EquilBolusRecord equilBolusRecord = + new EquilBolusRecord(insulin, + BolusType.SMB, System.currentTimeMillis()); + equilHistoryRecord.setBolusRecord(equilBolusRecord); + } + } + + if (equilHistoryRecord.getType() != null) { + long id = equilHistoryRecordDao.insert(equilHistoryRecord); + equilHistoryRecord.setId(id); + aapsLogger.debug(LTag.PUMPCOMM, "equilHistoryRecord is {}", id); + } + return equilHistoryRecord; + } + + public void updateHistory(EquilHistoryRecord equilHistoryRecord, ResolvedResult result) { + if (result != null && equilHistoryRecord != null) { + aapsLogger.debug(LTag.PUMPCOMM, "equilHistoryRecord2 is {} {}", + equilHistoryRecord.getId(), result); + equilHistoryRecord.setResolvedAt(System.currentTimeMillis()); + equilHistoryRecord.setResolvedStatus(result); + int status = equilHistoryRecordDao.update(equilHistoryRecord); + aapsLogger.debug(LTag.PUMPCOMM, "equilHistoryRecord3== is {} {} status {}", + equilHistoryRecord.getId(), equilHistoryRecord.getResolvedStatus(), status); + } + } + + public PumpEnactResult readEquilStatus() { + PumpEnactResult result = new PumpEnactResult(injector); + try { + BaseCmd command = new CmdModelGet(); + command.setEquilManager(this); + equilBLE.writeCmd(command); + synchronized (command) { + command.wait(command.getTimeOut()); + } + if (command.isCmdStatus()) { + command.setResolvedResult(ResolvedResult.SUCCESS); + SystemClock.sleep(EquilConst.EQUIL_BLE_NEXT_CMD); + return loadEquilHistory(); + } + result.setSuccess(command.isCmdStatus()); + result.enacted(command.isEnacted()); + } catch (Exception ex) { + ex.printStackTrace(); + result.success(false).enacted(false).comment(translateException(ex)); + } + return result; + } + + public PumpEnactResult loadEquilHistory() { + PumpEnactResult pumpEnactResult = new PumpEnactResult(injector); + int startIndex; + startIndex = getStartHistoryIndex(); + int index = getHistoryIndex(); + aapsLogger.debug(LTag.PUMPCOMM, "return ===" + index + "====" + startIndex); + if (index == -1) { + return pumpEnactResult.success(false); + } + int allCount = 1; + while (startIndex != index && allCount < 20) { + startIndex++; + if (startIndex > 2000) { + startIndex = 1; + } + SystemClock.sleep(EquilConst.EQUIL_BLE_NEXT_CMD); + int currentIndex = loadEquilHistory(startIndex); + aapsLogger.debug(LTag.PUMPCOMM, "while index===" + startIndex + "===" + index + "===" + currentIndex); + if (currentIndex > 1) { + setStartHistoryIndex(currentIndex); + allCount++; + } else { + break; + } + } + return pumpEnactResult.success(true); + } + + public PumpEnactResult executeCmd(BaseCmd command) { + PumpEnactResult result = new PumpEnactResult(injector); + try { + EquilHistoryRecord equilHistoryRecord = addHistory(command); + command.setEquilManager(this); + equilBLE.writeCmd(command); + synchronized (command) { + command.wait(command.getTimeOut()); + } + if (command.isCmdStatus()) { + command.setResolvedResult(ResolvedResult.SUCCESS); + } + updateHistory(equilHistoryRecord, command.getResolvedResult()); + aapsLogger.debug(LTag.PUMPCOMM, "executeCmd result {}", command.getResolvedResult()); + result.setSuccess(command.isCmdStatus()); + result.enacted(command.isEnacted()); + } catch (Exception ex) { + + ex.printStackTrace(); + result.success(false).enacted(false).comment(translateException(ex)); + } + return result; + } + + + public String translateException(Throwable ex) { + return ""; + } + + private void handleException(Exception ex) { + aapsLogger.error(LTag.PUMP, "Caught an unexpected non-OmnipodException from OmnipodManager", ex); + } + + public boolean isConnected() { + return equilBLE.isConnected(); + } + + public void showNotification(int id, String message, int urgency, Integer sound) { + Notification notification = new Notification( // + id, // + message, // + urgency); + if (sound != null) { + notification.setSoundId(sound); + } + sendEvent(new EventNewNotification(notification)); + } + + public void dismissNotification(int id) { + sendEvent(new EventDismissNotification(id)); + } + + private void sendEvent(Event event) { + rxBus.send(event); + } + + private final Gson gsonInstance; + private EquilState equilState; + + private static Gson createGson() { + GsonBuilder gsonBuilder = new GsonBuilder() + .registerTypeAdapter(DateTime.class, (JsonSerializer) (dateTime, typeOfSrc, context) -> + new JsonPrimitive(ISODateTimeFormat.dateTime().print(dateTime))) + .registerTypeAdapter(DateTime.class, (JsonDeserializer) (json, typeOfT, context) -> + ISODateTimeFormat.dateTime().parseDateTime(json.getAsString())) + .registerTypeAdapter(DateTimeZone.class, (JsonSerializer) (timeZone, typeOfSrc, context) -> + new JsonPrimitive(timeZone.getID())) + .registerTypeAdapter(DateTimeZone.class, (JsonDeserializer) (json, typeOfT, context) -> + DateTimeZone.forID(json.getAsString())); + + return gsonBuilder.create(); + } + + protected String readPodState() { + return sp.getString(EquilConst.Prefs.INSTANCE.getEQUIL_STATE(), ""); + } + + public final void loadPodState() { + equilState = null; + + String storedPodState = readPodState(); + + if (StringUtils.isEmpty(storedPodState)) { + equilState = new EquilState(); + aapsLogger.info(LTag.PUMP, "loadPodState: no Pod state was provided"); + } else { + aapsLogger.info(LTag.PUMP, "loadPodState: serialized Pod state was provided: " + storedPodState); + try { + equilState = gsonInstance.fromJson(storedPodState, EquilState.class); + } catch (Exception ex) { + equilState = new EquilState(); + aapsLogger.error(LTag.PUMP, "loadPodState: could not deserialize PodState: " + storedPodState, ex); + } + } + } + + public final boolean hasPodState() { + + return this.equilState != null; // 0x0=discarded + } + + private void setSafe(Runnable runnable) { + if (!hasPodState()) { + throw new IllegalStateException("Cannot mutate PodState: podState is null"); + } + runnable.run(); + } + + public void storePodState() { + String podState = gsonInstance.toJson(this.equilState); + aapsLogger.debug(LTag.PUMP, "storePodState: storing podState: {}", podState); + storePodState(podState); + } + + public void clearPodState() { + this.equilState = new EquilState(); + String podState = gsonInstance.toJson(equilState); + aapsLogger.debug(LTag.PUMP, "storePodState: storing podState: {}", podState); + storePodState(podState); + } + + private void setAndStore(Runnable runnable) { + setSafe(runnable); + storePodState(); + } + + private T getSafe(Supplier supplier) { + if (!hasPodState()) { + throw new IllegalStateException("Cannot read from PodState: podState is null"); + } + return supplier.get(); + } + + protected void storePodState(String podState) { + sp.putString(EquilConst.Prefs.INSTANCE.getEQUIL_STATE(), podState); + } + + public String getSerialNumber() { + return getSafe(() -> equilState.getSerialNumber()); + } + + public final void setSerialNumber(String serialNumber) { + setAndStore(() -> equilState.setSerialNumber(serialNumber)); + } + + public EquilBolusRecord getBolusRecord() { + return getSafe(() -> equilState.getBolusRecord()); + } + + public final void setBolusRecord(EquilBolusRecord bolusRecord) { + setAndStore(() -> equilState.setBolusRecord(bolusRecord)); + } + + + public EquilTempBasalRecord getTempBasal() { + return getSafe(() -> equilState.getTempBasal()); + } + + public final boolean hasTempBasal() { + return getTempBasal() != null; + } + + public final boolean isTempBasalRunning() { + return isTempBasalRunningAt(null); + } + + public final boolean isTempBasalRunningAt(DateTime time) { + if (time == null) { // now + if (!hasTempBasal()) { + return true; + } + time = DateTime.now(); + } + EquilTempBasalRecord equilTempBasalRecord = getTempBasal(); + if (hasTempBasal()) { + DateTime tempBasalStartTime = new DateTime(equilTempBasalRecord.getStartTime()); + DateTime tempBasalEndTime = tempBasalStartTime.plus(equilTempBasalRecord.getDuration()); + return (time.isAfter(tempBasalStartTime) || time.isEqual(tempBasalStartTime)) && time.isBefore(tempBasalEndTime); + } + return false; + } + + public final boolean isPumpRunning() { + return getRunMode() == RunMode.RUN; + } + + public final void setTempBasal(EquilTempBasalRecord tempBasal) { + setAndStore(() -> equilState.setTempBasal(tempBasal)); + } + + + public long getLastDataTime() { + return getSafe(() -> equilState.getLastDataTime()); + } + + public void setLastDataTime(long lastDataTime) { + setAndStore(() -> equilState.setLastDataTime(lastDataTime)); + } + + public long getDevicesTime() { + return getSafe(() -> equilState.getDevicesTime()); + } + + public void setDevicesTime(long devicesTime) { + setAndStore(() -> equilState.setDevicesTime(devicesTime)); + } + + public int getCurrentInsulin() { + return getSafe(() -> equilState.getCurrentInsulin()); + } + + public void setCurrentInsulin(int currentInsulin) { + setAndStore(() -> equilState.setCurrentInsulin(currentInsulin)); + + } + + public int getStartInsulin() { + return getSafe(() -> equilState.getStartInsulin()); +// return 200; + } + + public void setStartInsulin(int startInsulin) { + aapsLogger.debug(LTag.PUMPCOMM, "startInsulin {}", startInsulin); + setAndStore(() -> equilState.setStartInsulin(startInsulin)); + + } + + public int getBattery() { + return getSafe(() -> equilState.getBattery()); + + } + + public void setBattery(int battery) { + setAndStore(() -> equilState.setBattery(battery)); + + } + + public RunMode getRunMode() { + return getSafe(() -> equilState.getRunMode()); + } + + public void setRunMode(RunMode runMode) { + setAndStore(() -> equilState.setRunMode(runMode)); + } + + public String getFirmwareVersion() { + return getSafe(() -> equilState.getFirmwareVersion()); + + } + + public void setFirmwareVersion(String firmwareVersion) { + setAndStore(() -> equilState.setFirmwareVersion(firmwareVersion)); + } + + public float getRate() { + return getSafe(() -> equilState.getRate()); + } + + public void setRate(float rate) { + setAndStore(() -> equilState.setRate(rate)); + } + + public int getHistoryIndex() { + return getSafe(() -> equilState.getHistoryIndex()); + + } + + public void setHistoryIndex(int historyIndex) { + setAndStore(() -> equilState.setHistoryIndex(historyIndex)); + + } + + public String getAddress() { + return getSafe(() -> equilState.getAddress()); + } + + public void setAddress(String address) { + setAndStore(() -> equilState.setAddress(address)); + + } + + public final ActivationProgress getActivationProgress() { + if (hasPodState()) { + return Optional.ofNullable(equilState.getActivationProgress()).orElse(ActivationProgress.NONE); + } + return ActivationProgress.NONE; + } + + public final boolean isActivationCompleted() { + return getActivationProgress() == ActivationProgress.COMPLETED; + } + + public final boolean isActivationInitialized() { + return getActivationProgress() != ActivationProgress.NONE; + } + + public void setActivationProgress(ActivationProgress activationProgress) { + setAndStore(() -> equilState.setActivationProgress(activationProgress)); + } + + + public BluetoothConnectionState getBluetoothConnectionState() { + return getSafe(() -> equilState.getBluetoothConnectionState()); + } + + public void setBluetoothConnectionState(BluetoothConnectionState bluetoothConnectionState) { + setAndStore(() -> equilState.setBluetoothConnectionState(bluetoothConnectionState)); + } + + public int getStartHistoryIndex() { + return getSafe(() -> equilState.getStartHistoryIndex()); + + } + + public void setStartHistoryIndex(int startHistoryIndex) { + setAndStore(() -> equilState.setStartHistoryIndex(startHistoryIndex)); + + } + + public BasalSchedule getBasalSchedule() { + return getSafe(() -> equilState.getBasalSchedule()); + + } + + public void setBasalSchedule(BasalSchedule basalSchedule) { + setAndStore(() -> equilState.setBasalSchedule(basalSchedule)); + + } + + private static final class EquilState { + private ActivationProgress activationProgress; + private String serialNumber; + private String address; + private String firmwareVersion; + + private long lastDataTime; + private long devicesTime; + private int currentInsulin; + private int startInsulin; + private int battery; + private EquilTempBasalRecord tempBasal; + private EquilBolusRecord bolusRecord; + private RunMode runMode; + @NonNull private AlarmMode alarmMode = AlarmMode.TONE_AND_SHAKE; + private float rate; + private int historyIndex; + + BluetoothConnectionState bluetoothConnectionState = BluetoothConnectionState.DISCONNECTED; + private int startHistoryIndex; + private BasalSchedule basalSchedule; + + public BasalSchedule getBasalSchedule() { + return basalSchedule; + } + + public void setBasalSchedule(BasalSchedule basalSchedule) { + this.basalSchedule = basalSchedule; + } + + public int getStartHistoryIndex() { + return startHistoryIndex; + } + + public void setStartHistoryIndex(int startHistoryIndex) { + this.startHistoryIndex = startHistoryIndex; + } + + public BluetoothConnectionState getBluetoothConnectionState() { + return bluetoothConnectionState; + } + + public void setBluetoothConnectionState(BluetoothConnectionState bluetoothConnectionState) { + this.bluetoothConnectionState = bluetoothConnectionState; + } + + public ActivationProgress getActivationProgress() { + return activationProgress; + } + + public void setActivationProgress(ActivationProgress activationProgress) { + this.activationProgress = activationProgress; + } + + public int getHistoryIndex() { + return historyIndex; + } + + public void setHistoryIndex(int historyIndex) { + this.historyIndex = historyIndex; + } + + public float getRate() { + return rate; + } + + public void setRate(float rate) { + this.rate = rate; + } + + @NonNull public AlarmMode getAlarmMode() { + return alarmMode; + } + + public void setAlarmMode(AlarmMode alarmMode) { + this.alarmMode = alarmMode; + } + + public RunMode getRunMode() { + return runMode; + } + + public void setRunMode(RunMode runMode) { + this.runMode = runMode; + } + + public String getSerialNumber() { + return serialNumber; + } + + public void setSerialNumber(String serialNumber) { + this.serialNumber = serialNumber; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public long getLastDataTime() { + return lastDataTime; + } + + public void setLastDataTime(long lastDataTime) { + this.lastDataTime = lastDataTime; + } + + public long getDevicesTime() { + return devicesTime; + } + + public void setDevicesTime(long devicesTime) { + this.devicesTime = devicesTime; + } + + public int getCurrentInsulin() { + return currentInsulin; + } + + public void setCurrentInsulin(int currentInsulin) { + this.currentInsulin = currentInsulin; + } + + public int getStartInsulin() { + return startInsulin; + } + + public void setStartInsulin(int startInsulin) { + this.startInsulin = startInsulin; + } + + public String getFirmwareVersion() { + return firmwareVersion; + } + + public void setFirmwareVersion(String firmwareVersion) { + this.firmwareVersion = firmwareVersion; + } + + + public int getBattery() { + return battery; + } + + public void setBattery(int battery) { + this.battery = battery; + } + + public EquilTempBasalRecord getTempBasal() { + return tempBasal; + } + + public void setTempBasal(EquilTempBasalRecord tempBasal) { + this.tempBasal = tempBasal; + } + + public EquilBolusRecord getBolusRecord() { + return bolusRecord; + } + + public void setBolusRecord(EquilBolusRecord bolusRecord) { + this.bolusRecord = bolusRecord; + } + } + + public void setModel(int modeint) { + if (modeint == 0) { + setRunMode(RunMode.SUSPEND); + } else if (modeint == 1) { + setRunMode(RunMode.RUN); + } else if (modeint == 2) { + setRunMode(RunMode.STOP); + + } else { + setRunMode(RunMode.SUSPEND); + } + rxBus.send(new EventEquilModeChanged()); + } + + public void setInsulinChange(int status) { + if (status == 1) { + rxBus.send(new EventEquilInsulinChanged()); + } + } + + public void decodeHistory(byte[] data) { + int year = data[6] & 0xff; + year = year + 2000; + + int month = data[7] & 0xff; + int day = data[8] & 0xff; + int hour = data[9] & 0xff; + int min = data[10] & 0xff; + int second = data[11] & 0xff; + //a5e207590501 17070e100f161100000000007d0204080000 + //ae6ae9100501 17070e100f16 1100000000007d0204080000 + int battery = data[12] & 0xff; + int insulin = data[13] & 0xff; + int rate = Utils.bytesToInt(data[15], data[14]); + int largeRate = Utils.bytesToInt(data[17], data[16]); + int index = Utils.bytesToInt(data[19], data[18]); + + int port = data[20] & 0xff; + int type = data[21] & 0xff; + int level = data[22] & 0xff; + int parm = data[23] & 0xff; + Calendar calendar = Calendar.getInstance(); + calendar.set(Calendar.YEAR, year); + calendar.set(Calendar.MONTH, month - 1); + calendar.set(Calendar.DAY_OF_MONTH, day); + calendar.set(Calendar.HOUR_OF_DAY, hour); + calendar.set(Calendar.MINUTE, min); + calendar.set(Calendar.SECOND, second); + calendar.set(Calendar.MILLISECOND, 0); + EquilHistoryPump equilHistoryPump = new EquilHistoryPump(); + equilHistoryPump.setBattery(battery); + equilHistoryPump.setInsulin(insulin); + equilHistoryPump.setRate(rate); + equilHistoryPump.setLargeRate(largeRate); + equilHistoryPump.setTimestamp(System.currentTimeMillis()); + equilHistoryPump.setEventTimestamp((calendar.getTimeInMillis() + index)); + equilHistoryPump.setPort(port); + equilHistoryPump.setType(type); + equilHistoryPump.setLevel(level); + equilHistoryPump.setParm(parm); + equilHistoryPump.setEventIndex(index); + equilHistoryPump.setSerialNumber(getSerialNumber()); + long id = equilHistoryPumpDao.insert(equilHistoryPump); + aapsLogger.debug(LTag.PUMPCOMM, "decodeHistory insert id {}", id); + rxBus.send(new EventEquilDataChanged()); + } + + public void decodeData(byte[] data) { + int year = data[11] & 0xFF; + year = year + 2000; + int month = data[12] & 0xff; + int day = data[13] & 0xff; + int hour = data[14] & 0xff; + int min = data[15] & 0xff; + int second = data[16] & 0xff; + int battery = data[17] & 0xff; + int insulin = data[18] & 0xff; + int rate1 = Utils.bytesToInt(data[20], data[19]); + float rate = Utils.internalDecodeSpeedToUH(rate1); + float largeRate = Utils.bytesToInt(data[22], data[21]); + int historyIndex = Utils.bytesToInt(data[24], data[23]); + int currentIndex = getHistoryIndex(); + int port = data[25] & 0xff; + int level = data[26] & 0xff; + int parm = data[27] & 0xff; + String errorTips = getEquilError(port, level, parm); + if (!TextUtils.isEmpty(errorTips) && currentIndex != historyIndex) { + showNotification(Notification.FAILED_UPDATE_PROFILE, + errorTips, + Notification.NORMAL, app.aaps.core.ui.R.raw.alarm); + long time = System.currentTimeMillis(); + EquilHistoryRecord equilHistoryRecord = new EquilHistoryRecord( + EquilHistoryRecord.EventType.EQUIL_ALARM, + time, + getSerialNumber() + ); + equilHistoryRecord.setResolvedAt(System.currentTimeMillis()); + equilHistoryRecord.setResolvedStatus(ResolvedResult.SUCCESS); + equilHistoryRecord.setNote(errorTips); + equilHistoryRecordDao.insert(equilHistoryRecord); + } + aapsLogger.debug(LTag.PUMPCOMM, "decodeData historyIndex {} errorTips {} port:{} level:{} " + + "parm:{}", + historyIndex, + errorTips, port, level, parm); + setHistoryIndex(historyIndex); + Calendar calendar = Calendar.getInstance(); + calendar.set(Calendar.YEAR, year); + calendar.set(Calendar.MONTH, month - 1); + calendar.set(Calendar.DAY_OF_MONTH, day); + calendar.set(Calendar.HOUR_OF_DAY, hour); + calendar.set(Calendar.MINUTE, min); + calendar.set(Calendar.SECOND, second); + setLastDataTime(System.currentTimeMillis()); + setCurrentInsulin(insulin); + setBattery(battery); + setRate(rate); + rxBus.send(new EventEquilDataChanged()); + } +} diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/manager/EquilResponse.java b/pump/equil/src/main/java/app/aaps/pump/equil/manager/EquilResponse.java new file mode 100644 index 00000000000..0c108c72cf3 --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/manager/EquilResponse.java @@ -0,0 +1,60 @@ +package app.aaps.pump.equil.manager; + +import java.nio.ByteBuffer; +import java.util.LinkedList; + +/** + * + */ + +public class EquilResponse { + + private final LinkedList send; + private String error_message; + private long delay = 20; + private final long cmdCreateTime; + + public EquilResponse(long cmdCreateTime) { + this.cmdCreateTime = cmdCreateTime; + send = new LinkedList<>(); + } + + public long getCmdCreateTime() { + return cmdCreateTime; + } + + + public boolean hasError() { + return error_message != null; + } + + public void add(ByteBuffer buffer) { + send.add(buffer); + } + + public boolean shouldDelay() { + return delay > 0; + } + + public LinkedList getSend() { + return send; + } + + public String getError_message() { + return error_message; + } + + public void setError_message(String error_message) { + this.error_message = error_message; + } + + public long getDelay() { + return delay; + } + + public void setDelay(long delay) { + this.delay = delay; + } +} + + diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/manager/Utils.java b/pump/equil/src/main/java/app/aaps/pump/equil/manager/Utils.java new file mode 100644 index 00000000000..788bd881843 --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/manager/Utils.java @@ -0,0 +1,162 @@ +package app.aaps.pump.equil.manager; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.security.SecureRandom; +import java.util.List; + +public class Utils { + public static byte[] generateRandomPassword(int length) { + SecureRandom secureRandom = new SecureRandom(); + byte[] password = new byte[length]; + secureRandom.nextBytes(password); + return password; + } + + public static int bytesToInt(byte highByte, byte lowByte) { + int highValue = (highByte & 0xFF) << 8; + int lowValue = lowByte & 0xFF; + int value = highValue | lowValue; + if (value >= 0x8000) { + return value - 0x8000; + } + return value; + } + + public static float internalDecodeSpeedToUH(int i) { + return new BigDecimal(i).multiply(new BigDecimal("0.00625")).floatValue(); + } + + public static BigDecimal internalDecodeSpeedToUH2(int i) { + return new BigDecimal(i).multiply(new BigDecimal("0.00625")); + } + + public static float decodeSpeedToUH(int i) { + return new BigDecimal(i).multiply(new BigDecimal("0.00625")).floatValue(); + } + + public static double decodeSpeedToUS(int i) { + return internalDecodeSpeedToUH2(i).divide(new BigDecimal("3600"), 10, RoundingMode.DOWN).doubleValue(); + } + + public static int decodeSpeedToUH(double i) { + BigDecimal a = new BigDecimal(String.valueOf(i)); + BigDecimal b = new BigDecimal("0.00625"); + BigDecimal c = a.divide(b); + return c.intValue(); + } + + public static double decodeSpeedToUHT(double i) { + BigDecimal a = new BigDecimal(String.valueOf(i)); + BigDecimal b = new BigDecimal("0.00625"); + return a.divide(b).doubleValue(); + } + + public static byte[] basalToByteArray(double v) { + int value = decodeSpeedToUH(v); + byte[] result = new byte[2]; + result[0] = (byte) ((value >> 8) & 0xFF); // 高位 + result[1] = (byte) (value & 0xFF); // 低位 + return result; + } + + public static byte[] basalToByteArray2(double v) { + int value = decodeSpeedToUH(v); + byte[] result = new byte[2]; + result[1] = (byte) ((value >> 8) & 0xFF); // 高位 + result[0] = (byte) (value & 0xFF); // 低位 + return result; + } + + + private static byte charToByte(char c) { + return (byte) "0123456789ABCDEF".indexOf(c); + } + + + public static byte[] hexStringToBytes(String hexString) { + if (hexString == null || hexString.equals("")) { + return null; + } + hexString = hexString.toUpperCase(); + int length = hexString.length() / 2; + char[] hexChars = hexString.toCharArray(); + byte[] d = new byte[length]; + for (int i = 0; i < length; i++) { + int pos = i * 2; + d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1])); + } + return d; + } + + public static byte[] concat(byte[]... arrays) { + int length = 0; + for (byte[] array : arrays) { + length += array.length; + } + byte[] result = new byte[length]; + int pos = 0; + for (byte[] array : arrays) { + System.arraycopy(array, 0, result, pos, array.length); + pos += array.length; + } + return result; + } + + public static byte[] intToBytes(int value) { + byte[] src = new byte[4]; + src[3] = (byte) ((value >> 24) & 0xFF); + src[2] = (byte) ((value >> 16) & 0xFF); + src[1] = (byte) ((value >> 8) & 0xFF); + src[0] = (byte) (value & 0xFF); + return src; + } + public static int bytes2Int(byte[] bytes) { + int int1 = bytes[0] & 0xff; + int int2 = (bytes[1] & 0xff) << 8; + int int3 = (bytes[2] & 0xff) << 16; + int int4 = (bytes[3] & 0xff) << 24; + return int1 | int2 | int3 | int4; + } + public static byte[] intToTwoBytes(int value) { + byte[] bytes = new byte[2]; + bytes[1] = (byte) ((value >> 8) & 0xFF); + bytes[0] = (byte) (value & 0xFF); + return bytes; + + } + + public static byte[] convertByteArray(List byteList) { + byte[] byteArray = new byte[byteList.size()]; + for (int i = 0; i < byteList.size(); i++) { + byteArray[i] = byteList.get(i); + } + return byteArray; + } + + private final static char[] hexArray = "0123456789ABCDEF".toCharArray(); + + public static String bytesToHex(List bytes) { + if (bytes == null) return ""; + char[] hexChars = new char[bytes.size() * 2]; + for (int j = 0; j < bytes.size(); j++) { + int v = bytes.get(j) & 0xFF; + hexChars[j * 2] = hexArray[v >>> 4]; + hexChars[j * 2 + 1] = hexArray[v & 0x0F]; + } + return new String(hexChars); + } + + public static String bytesToHex(byte[] bytes) { + if (bytes == null) return ""; + final char[] hexChars = new char[bytes.length * 2]; + for (int j = 0; j < bytes.length; j++) { + final int v = bytes[j] & 0xFF; + hexChars[j * 2] = hexArray[v >>> 4]; + hexChars[j * 2 + 1] = hexArray[v & 0x0F]; + } + return new String(hexChars); + } + + +} diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/BaseCmd.java b/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/BaseCmd.java new file mode 100644 index 00000000000..833c6b2cf49 --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/BaseCmd.java @@ -0,0 +1,312 @@ +package app.aaps.pump.equil.manager.command; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import app.aaps.core.interfaces.logging.AAPSLogger; +import app.aaps.core.interfaces.logging.LTag; +import app.aaps.core.interfaces.queue.CustomCommand; +import app.aaps.core.interfaces.sharedPreferences.SP; +import app.aaps.pump.equil.EquilConst; +import app.aaps.pump.equil.database.EquilHistoryRecord; +import app.aaps.pump.equil.database.ResolvedResult; +import app.aaps.pump.equil.manager.Crc; +import app.aaps.pump.equil.manager.EquilCmdModel; +import app.aaps.pump.equil.manager.EquilManager; +import app.aaps.pump.equil.manager.EquilResponse; +import app.aaps.pump.equil.manager.Utils; + + +public abstract class BaseCmd implements CustomCommand { + public ResolvedResult resolvedResult = ResolvedResult.NONE; + public static final String defaultPort = "0F0F"; + public static int reqIndex; + public static int pumpReqIndex = 10; + public static int rspIndex = -1; + AAPSLogger aapsLogger; + SP sp; + EquilManager equilManager; + private int timeOut = 22000; + private int connectTimeOut = 15000; + + public String port = "0404"; + public boolean config; + public boolean isEnd; + boolean cmdStatus; + private boolean enacted = true; + public EquilResponse response; + public String runPwd; + public String runCode; + final long createTime; + + public BaseCmd(long createTime) { + this.createTime = createTime; + } + + public abstract EquilResponse getEquilResponse(); + + public abstract EquilResponse getNextEquilResponse(); + + @Nullable public abstract EquilResponse decodeEquilPacket(byte[] data); + + public abstract EquilResponse decode() throws Exception; + + public abstract EquilResponse decodeConfirm() throws Exception; + + public abstract EquilHistoryRecord.EventType getEventType(); + + + public ResolvedResult getResolvedResult() { + return resolvedResult; + } + + public void setResolvedResult(ResolvedResult resolvedResult) { + this.resolvedResult = resolvedResult; + } + + public boolean isEnacted() { + return enacted; + } + + public void setEnacted(boolean enacted) { + this.enacted = enacted; + } + + public int getConnectTimeOut() { + return connectTimeOut; + } + + public void setConnectTimeOut(int connectTimeOut) { + this.connectTimeOut = connectTimeOut; + } + + public int getTimeOut() { + return timeOut; + } + + public void setTimeOut(int timeOut) { + this.timeOut = timeOut; + } + + public boolean isCmdStatus() { + return cmdStatus; + } + + public void setCmdStatus(boolean cmdStatus) { + this.cmdStatus = cmdStatus; + } + + @NonNull @Override public String getStatusDescription() { + return this.getClass().getSimpleName(); + } + + public void setEquilManager(EquilManager equilManager) { + this.equilManager = equilManager; + this.aapsLogger = equilManager.getAapsLogger(); + this.sp = equilManager.getSp(); + } + + + public boolean checkData(byte[] data) { + if (response.getSend().size() > 0) { + byte[] preData = response.getSend().get(response.getSend().size() - 1).array(); + int index = data[3] & 0xff; + int preIndex = preData[3] & 0xff; + if (index == preIndex) { + aapsLogger.debug(LTag.PUMPCOMM, "checkData error "); + return false; + } + } + int crc = data[5] & 0xff; + int crc1 = Crc.CRC8_MAXIM(Arrays.copyOfRange(data, 0, 5)); + if (crc != crc1) { + aapsLogger.debug(LTag.PUMPCOMM, "checkData crc error"); + return false; + } + return true; + } + + + public String getRunCode() { + return runCode; + } + + public void setRunCode(String runCode) { + this.runCode = runCode; + } + + public String getRunPwd() { + return runPwd; + } + + public void setRunPwd(String runPwd) { + this.runPwd = runPwd; + } + + public String getEquilDevices() { + return sp.getString(EquilConst.Prefs.INSTANCE.getEQUIL_DEVICES(), ""); + } + + public String getEquilPassWord() { + return sp.getString(EquilConst.Prefs.INSTANCE.getEQUIL_PASSWORD(), ""); + } + + public static int up1(double value) { + BigDecimal bg = new BigDecimal(value); + return bg.setScale(0, RoundingMode.UP).intValue(); + } + + public boolean isPairStep() { + return false; + } + + public EquilResponse responseCmd(EquilCmdModel equilCmdModel, String port) { + StringBuilder allData = new StringBuilder(); + allData.append(port); + allData.append(equilCmdModel.getTag()); + allData.append(equilCmdModel.getIv()); + allData.append(equilCmdModel.getCiphertext()); + byte[] allByte = Utils.hexStringToBytes(allData.toString()); + byte[] crc1 = Crc.getCRC(allByte); + allByte = Utils.hexStringToBytes(allData.toString()); + int byteIndex = 0; + int lastLen = 0; + int index; + if ((allByte.length - 8) % 10 == 0) { + index = 1; + } else { + index = 2; + } + EquilResponse equilResponse = new EquilResponse(createTime); + int maxLen = up1((allByte.length - 8) / 10) + index; + for (int i = 0; i < maxLen; i++) { + ByteBuffer buffer = ByteBuffer.allocate(16); + if (i > 0 && lastLen < 10) { + buffer = ByteBuffer.allocate(6 + lastLen); + + } + buffer.put((byte) 0x00); + buffer.put((byte) 0x00); + if (i == maxLen - 1) { + buffer.put((byte) (6 + lastLen)); + buffer.put((byte) ((10 * i))); + buffer.put((byte) toNewEndConf((byte) reqIndex)); + } else { + buffer.put((byte) 0x10); + buffer.put((byte) (10 * i)); + buffer.put((byte) toNewStart((byte) reqIndex)); + } + byte[] crcArray = new byte[5]; + System.arraycopy(buffer.array(), 0, crcArray, 0, 5); + buffer.put((byte) Crc.CRC8_MAXIM(crcArray)); + if (i == 0) { + buffer.put((byte) allByte[byteIndex]); + byteIndex++; + buffer.put((byte) allByte[byteIndex]); + byteIndex++; + buffer.put((byte) allByte[byteIndex]); + byteIndex++; + buffer.put((byte) allByte[byteIndex]); + byteIndex++; + buffer.put(crc1[1]); + buffer.put(crc1[0]); + buffer.put((byte) allByte[byteIndex]); + byteIndex++; + buffer.put((byte) allByte[byteIndex]); + byteIndex++; + buffer.put((byte) allByte[byteIndex]); + byteIndex++; + buffer.put((byte) allByte[byteIndex]); + byteIndex++; + } else { + if (lastLen < 10) { + for (int j = 0; j < lastLen; j++) { + buffer.put((byte) allByte[byteIndex]); + byteIndex++; + } + } else { + for (int j = 0; j < 10; j++) { + buffer.put((byte) allByte[byteIndex]); + byteIndex++; + } + } + } + lastLen = allByte.length - byteIndex; + equilResponse.add(buffer); + } + reqIndex++; + return equilResponse; + } + + + public EquilCmdModel decodeModel() { + EquilCmdModel equilCmdModel = new EquilCmdModel(); + List list = new ArrayList<>(); + int index = 0; + for (ByteBuffer b : response.getSend()) { + if (index == 0) { + byte[] bs = b.array(); + for (int i = bs.length - 4; i < bs.length; i++) { + list.add((Byte) bs[i]); + } + byte[] codeByte = new byte[]{bs[10], bs[11]}; + equilCmdModel.setCode(Utils.bytesToHex(codeByte)); + } else { + byte[] bs = b.array(); + for (int i = 6; i < bs.length; i++) { + list.add((Byte) bs[i]); + } + } + index++; + } + List list1 = list.subList(0, 16); + List list2 = list.subList(16, 12 + 16); + List list3 = list.subList(12 + 16, list.size()); + equilCmdModel.setIv(Utils.bytesToHex(list2).toLowerCase()); + equilCmdModel.setTag(Utils.bytesToHex(list1).toLowerCase()); + equilCmdModel.setCiphertext(Utils.bytesToHex(list3).toLowerCase()); + return equilCmdModel; + } + + public byte toNewStart(byte number) { + number &= ~(1 << 7); // 清除指定位(设置为0) + return number; + } + + public byte toNewEndConf(byte number) { +// number &= ~(1 << 6); // 清除指定位(设置为0) + number |= (1 << 7); // 设置指定位为1 + return number; + } + + public boolean isEnd(byte b) { + int bit = getBit(b, 7); + return bit == 1; + } + + public int getIndex(byte b) { + int result = (b & 63); // 提取前6位并右移2位 + return result; + } + + public int getBit(byte b, int i) { + int bit = (int) ((b >> i) & 0x1); + return bit; + } + + public String convertString(String input) { + StringBuilder sb = new StringBuilder(); + for (char ch : input.toCharArray()) { + sb.append("0").append(ch); + } + return sb.toString(); + } + +} diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/BaseSetting.java b/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/BaseSetting.java new file mode 100644 index 00000000000..c66e46f002f --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/BaseSetting.java @@ -0,0 +1,144 @@ +package app.aaps.pump.equil.manager.command; + +import java.nio.ByteBuffer; + +import app.aaps.core.interfaces.logging.LTag; +import app.aaps.pump.equil.manager.AESUtil; +import app.aaps.pump.equil.manager.EquilCmdModel; +import app.aaps.pump.equil.manager.EquilResponse; +import app.aaps.pump.equil.manager.Utils; + + +public abstract class BaseSetting extends BaseCmd { + + public BaseSetting(long createTime) { + super(createTime); + } + + public byte[] getReqData() { + byte[] indexByte = Utils.intToBytes(pumpReqIndex); + byte[] tzm = Utils.hexStringToBytes(getEquilDevices()); + byte[] data = Utils.concat(indexByte, tzm); + pumpReqIndex++; + return data; + } + + + @Override + public EquilResponse getEquilResponse() { + config = false; + isEnd = false; + response = new EquilResponse(createTime); + byte[] pwd = Utils.hexStringToBytes(getEquilPassWord()); + byte[] data = getReqData(); + EquilCmdModel equilCmdModel; + try { + equilCmdModel = AESUtil.aesEncrypt(pwd, data); + return responseCmd(equilCmdModel, defaultPort + "0000"); + } catch (Exception e) { + synchronized (this) { + setCmdStatus(false); + } + } + return null; + } + + + public EquilResponse decodeEquilPacket(byte[] data) { + if (!checkData(data)) { + return null; + } + byte code = data[4]; + int intValue = getIndex(code); + if (config) { + if (rspIndex == intValue) { + return null; + } + boolean flag = isEnd(code); + ByteBuffer buffer = ByteBuffer.wrap(data); + response.add(buffer); + if (!flag) { + return null; + } + try { + EquilResponse response1 = decodeConfirm(); + isEnd = true; + response = new EquilResponse(createTime); + rspIndex = intValue; + return response1; + } catch (Exception e) { + response = new EquilResponse(createTime); + aapsLogger.debug(LTag.PUMPCOMM, "decodeEquilPacket error =====" + e.getMessage()); + } + return null; + } + boolean flag = isEnd(code); + ByteBuffer buffer = ByteBuffer.wrap(data); + response.add(buffer); + if (!flag) { + return null; + } + try { + EquilResponse list = decode(); + response = new EquilResponse(createTime); + config = true; + rspIndex = intValue; + return list; + } catch (Exception e) { + response = new EquilResponse(createTime); + aapsLogger.debug(LTag.PUMPCOMM, "decodeEquilPacket error=====" + e.getMessage()); + } + return null; + + } + + @Override + public EquilResponse decode() throws Exception { + EquilCmdModel reqModel = decodeModel(); + byte[] pwd = Utils.hexStringToBytes(getEquilPassWord()); + String content = AESUtil.decrypt(reqModel, pwd); + String pwd2 = content.substring(8); + runPwd = pwd2; + byte[] data = getFirstData(); + EquilCmdModel equilCmdModel = AESUtil.aesEncrypt(Utils.hexStringToBytes(runPwd), data); + runCode = reqModel.getCode(); + return responseCmd(equilCmdModel, port + runCode); + } + + public EquilResponse getNextEquilResponse() { + aapsLogger.debug(LTag.PUMPCOMM, "getNextEquilResponse=== start11 "); + config = true; + isEnd = false; + response = new EquilResponse(createTime); + byte[] data = getFirstData(); + EquilCmdModel equilCmdModel; + try { + aapsLogger.debug(LTag.PUMPCOMM, "getNextEquilResponse=== start "); + equilCmdModel = AESUtil.aesEncrypt(Utils.hexStringToBytes(runPwd), data); + return responseCmd(equilCmdModel, port + runCode); + } catch (Exception e) { + aapsLogger.debug(LTag.PUMPCOMM, "getNextEquilResponse===" + e.getMessage()); + synchronized (this) { + setCmdStatus(false); + } + } + return null; + } + + + public abstract byte[] getFirstData(); + + public abstract byte[] getNextData(); + + public abstract void decodeConfirmData(byte[] data); + + public EquilResponse decodeConfirm() throws Exception { + EquilCmdModel equilCmdModel = decodeModel(); + runCode = equilCmdModel.getCode(); + String content = AESUtil.decrypt(equilCmdModel, Utils.hexStringToBytes(runPwd)); + decodeConfirmData(Utils.hexStringToBytes(content)); + byte[] data = getNextData(); + EquilCmdModel equilCmdModel2 = AESUtil.aesEncrypt(Utils.hexStringToBytes(runPwd), data); + return responseCmd(equilCmdModel2, port + runCode); + } +} diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdAlarmSet.java b/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdAlarmSet.java new file mode 100644 index 00000000000..c553e3575cc --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdAlarmSet.java @@ -0,0 +1,64 @@ +package app.aaps.pump.equil.manager.command; + + +import app.aaps.pump.equil.data.AlarmMode; +import app.aaps.pump.equil.database.EquilHistoryRecord; +import app.aaps.pump.equil.manager.Utils; + +public class CmdAlarmSet extends BaseSetting { + + public CmdAlarmSet(int mode) { + super(System.currentTimeMillis()); + this.mode = mode; + } + + @Override + public byte[] getFirstData() { + byte[] indexByte = Utils.intToBytes(pumpReqIndex); + byte[] data2 = new byte[]{0x01, 0x0b}; + byte[] data3 = Utils.intToBytes(mode); + byte[] data = Utils.concat(indexByte, data2, data3); + pumpReqIndex++; + return data; + } + + public byte[] getNextData() { + byte[] indexByte = Utils.intToBytes(pumpReqIndex); + byte[] data2 = new byte[]{0x00, 0x0b, 0x01}; + byte[] data = Utils.concat(indexByte, data2); + pumpReqIndex++; + return data; + } + + @Override + public void decodeConfirmData(byte[] data) { + synchronized (this) { + setCmdStatus(true); + notifyAll(); + } + } + + + int mode; + + public int getMode() { + return mode; + } + + public void setMode(int mode) { + this.mode = mode; + } + + @Override public EquilHistoryRecord.EventType getEventType() { + if (mode == AlarmMode.MUTE.getCommand()) { + return EquilHistoryRecord.EventType.SET_ALARM_MUTE; + } else if (mode == AlarmMode.TONE.getCommand()) { + return EquilHistoryRecord.EventType.SET_ALARM_TONE; + } else if (mode == AlarmMode.TONE_AND_SHAKE.getCommand()) { + return EquilHistoryRecord.EventType.SET_ALARM_TONE_AND_SHAK; + } else if (mode == AlarmMode.SHAKE.getCommand()) { + return EquilHistoryRecord.EventType.SET_ALARM_SHAKE; + } + return null; + } +} diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdBasalGet.java b/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdBasalGet.java new file mode 100644 index 00000000000..3befd2094a2 --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdBasalGet.java @@ -0,0 +1,64 @@ +package app.aaps.pump.equil.manager.command; + + +import java.util.Arrays; + +import app.aaps.core.interfaces.logging.LTag; +import app.aaps.core.interfaces.profile.Profile; +import app.aaps.pump.equil.database.EquilHistoryRecord; +import app.aaps.pump.equil.manager.Utils; + + +public class CmdBasalGet extends BaseSetting { + + Profile profile; + + public CmdBasalGet(Profile profile) { + super(System.currentTimeMillis()); + this.profile = profile; + } + + @Override + public byte[] getFirstData() { + byte[] indexByte = Utils.intToBytes(pumpReqIndex); + byte[] data2 = new byte[]{0x02, 0x02}; + byte[] data = Utils.concat(indexByte, data2); + pumpReqIndex++; + return data; + } + + public byte[] getNextData() { + byte[] indexByte = Utils.intToBytes(pumpReqIndex); + byte[] data2 = new byte[]{0x00, 0x02, 0x01}; + byte[] data = Utils.concat(indexByte, data2); + pumpReqIndex++; + return data; + } + + public void decodeConfirmData(byte[] data) { + aapsLogger.debug(LTag.PUMPCOMM, "CmdBasalGet==" + Utils.bytesToHex(data)); + StringBuilder currentBasal = new StringBuilder(); + if (profile != null) { + for (int i = 0; i < 24; i++) { + double value = profile.getBasalTimeFromMidnight(i * 60 * 60); + value = value / 2f; + byte[] bs = Utils.basalToByteArray2(value); + currentBasal.append(Utils.bytesToHex(bs)); + currentBasal.append(Utils.bytesToHex(bs)); + } + } + byte[] rspByte = Arrays.copyOfRange(data, 6, data.length); + String rspBasal = Utils.bytesToHex(rspByte); + aapsLogger.debug(LTag.PUMPCOMM, + "CmdBasalGet==" + currentBasal + "====\n==" + rspBasal); + synchronized (this) { + setCmdStatus(true); + setEnacted(currentBasal.toString().equals(rspBasal)); + notify(); + } + } + + @Override public EquilHistoryRecord.EventType getEventType() { + return null; + } +} diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdBasalSet.java b/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdBasalSet.java new file mode 100644 index 00000000000..fc6d2b3464a --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdBasalSet.java @@ -0,0 +1,88 @@ +package app.aaps.pump.equil.manager.command; + + +import java.util.ArrayList; +import java.util.List; + +import app.aaps.core.interfaces.logging.LTag; +import app.aaps.core.interfaces.profile.Profile; +import app.aaps.pump.equil.EquilConst; +import app.aaps.pump.equil.database.EquilHistoryRecord; +import app.aaps.pump.equil.driver.definition.BasalSchedule; +import app.aaps.pump.equil.driver.definition.BasalScheduleEntry; +import app.aaps.pump.equil.manager.Utils; + + +public class CmdBasalSet extends BaseSetting { + Profile profile; + + public Profile getProfile() { + return profile; + } + + public void setProfile(Profile profile) { + this.profile = profile; + } + + BasalSchedule basalSchedule; + + public CmdBasalSet(BasalSchedule basalSchedule, Profile profile) { + super(System.currentTimeMillis()); + this.profile = profile; + this.basalSchedule = basalSchedule; + + } + + @Override + public byte[] getFirstData() { + byte[] indexByte = Utils.intToBytes(pumpReqIndex); + byte[] data2 = new byte[]{0x01, 0x02}; + List list = new ArrayList<>(); + int i = 0; + for (BasalScheduleEntry basalScheduleEntry : basalSchedule.getEntries()) { + double rate = basalScheduleEntry.getRate(); + double value = rate / 2f; + byte[] bs = Utils.basalToByteArray(value); + aapsLogger.debug(LTag.PUMPCOMM, + i + "==CmdBasalSet==" + value + "====" + rate + "===" + Utils.decodeSpeedToUH(value) + "===" + + Utils.decodeSpeedToUHT(value)); + list.add(bs[1]); + list.add(bs[0]); + list.add(bs[1]); + list.add(bs[0]); + i++; + } + String hex = Utils.bytesToHex(list); + byte[] data = Utils.concat(indexByte, data2, Utils.hexStringToBytes(hex)); + pumpReqIndex++; + return data; + } + + public static byte[] intToByteArray(double v) { + int value = (int) (v / 0.025f * 4); + byte[] result = new byte[2]; + result[0] = (byte) ((value >> 8) & 0xFF); // 高位 + result[1] = (byte) (value & 0xFF); // 低位 + return result; + } + + public byte[] getNextData() { + byte[] indexByte = Utils.intToBytes(pumpReqIndex); + byte[] data2 = new byte[]{0x00, 0x02, 0x01}; + byte[] data = Utils.concat(indexByte, data2); + pumpReqIndex++; + return data; + } + + public void decodeConfirmData(byte[] data) { + synchronized (this) { + sp.putBoolean(EquilConst.Prefs.INSTANCE.getEQUIL_BASAL_SET(), true); + setCmdStatus(true); + notifyAll(); + } + } + + @Override public EquilHistoryRecord.EventType getEventType() { + return EquilHistoryRecord.EventType.SET_BASAL_PROFILE; + } +} diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdDevicesGet.java b/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdDevicesGet.java new file mode 100644 index 00000000000..f44cf23f245 --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdDevicesGet.java @@ -0,0 +1,49 @@ +package app.aaps.pump.equil.manager.command; + + +import app.aaps.core.interfaces.logging.LTag; +import app.aaps.pump.equil.database.EquilHistoryRecord; +import app.aaps.pump.equil.manager.Utils; + + +public class CmdDevicesGet extends BaseSetting { + public CmdDevicesGet() { + super(System.currentTimeMillis()); + port = "0000"; + } + + @Override + public byte[] getFirstData() { + byte[] indexByte = Utils.intToBytes(pumpReqIndex); + byte[] data2 = new byte[]{0x02, 0x00}; + byte[] data = Utils.concat(indexByte, data2); + pumpReqIndex++; + return data; + } + + public byte[] getNextData() { + byte[] indexByte = Utils.intToBytes(pumpReqIndex); + byte[] data2 = new byte[]{0x00, 0x00, 0x01}; + byte[] data = Utils.concat(indexByte, data2); + pumpReqIndex++; + return data; + } + + @Override + public void decodeConfirmData(byte[] data) { + int value = Utils.bytesToInt(data[7], data[6]); + + String firmwareVersion = data[18] + "." + data[19]; + aapsLogger.debug(LTag.PUMPCOMM, "CmdGetDevices====" + + Utils.bytesToHex(data) + "=====" + value + "===" + firmwareVersion); + equilManager.setFirmwareVersion(firmwareVersion); + synchronized (this) { + setCmdStatus(true); + notify(); + } + } + + @Override public EquilHistoryRecord.EventType getEventType() { + return EquilHistoryRecord.EventType.READ_DEVICES; + } +} diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdDevicesOldGet.java b/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdDevicesOldGet.java new file mode 100644 index 00000000000..c6747a9ee35 --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdDevicesOldGet.java @@ -0,0 +1,186 @@ +package app.aaps.pump.equil.manager.command; + + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; + +import app.aaps.core.interfaces.logging.LTag; +import app.aaps.pump.equil.EquilConst; +import app.aaps.pump.equil.database.EquilHistoryRecord; +import app.aaps.pump.equil.manager.EquilCmdModel; +import app.aaps.pump.equil.manager.EquilResponse; +import app.aaps.pump.equil.manager.Utils; + + +public class CmdDevicesOldGet extends BaseSetting { + public String address; + private float firmwareVersion; + + public CmdDevicesOldGet(String address) { + super(System.currentTimeMillis()); + port = "0E0E"; + this.address = address; + } + + public String getAddress() { + return address; + } + + public EquilResponse getEquilResponse() { + config = false; + isEnd = false; + response = new EquilResponse(createTime); + EquilResponse temp = new EquilResponse(createTime); + ByteBuffer buffer = ByteBuffer.allocate(14); + buffer.put((byte) 0x00); + buffer.put((byte) 0x00); + buffer.put((byte) 0x0E); + buffer.put((byte) 0x00); + buffer.put((byte) 0x80); + buffer.put((byte) 0x78); + buffer.put((byte) 0x00); + buffer.put((byte) 0x00); + buffer.put((byte) 0x00); + buffer.put((byte) 0x00); + buffer.put((byte) 0x01); + buffer.put((byte) 0x7B); + buffer.put((byte) 0x02); + buffer.put((byte) 0x00); + temp.add(buffer); + return temp; + } + + @Override + public byte[] getFirstData() { + byte[] indexByte = Utils.intToBytes(pumpReqIndex); + byte[] data2 = new byte[]{0x02, 0x00}; + byte[] data = Utils.concat(indexByte, data2); + pumpReqIndex++; + return data; + } + + public byte[] getNextData() { + byte[] indexByte = Utils.intToBytes(pumpReqIndex); + byte[] data2 = new byte[]{0x00, 0x00, 0x01}; + byte[] data = Utils.concat(indexByte, data2); + pumpReqIndex++; + return data; + } + + public EquilResponse decodeEquilPacket(byte[] data) { + if (!checkData(data)) { + return null; + } + byte code = data[4]; + int intValue = getIndex(code); + if (config) { + if (rspIndex == intValue) { + return null; + } + boolean flag = isEnd(code); + ByteBuffer buffer = ByteBuffer.wrap(data); + response.add(buffer); + if (!flag) { + return null; + } + try { + EquilResponse list = decodeConfirm(); + isEnd = true; + response = new EquilResponse(createTime); + rspIndex = intValue; + aapsLogger.debug(LTag.PUMPCOMM, "intValue=====" + intValue + "====" + rspIndex); + return list; + } catch (Exception e) { + response = new EquilResponse(createTime); + aapsLogger.debug(LTag.PUMPCOMM, "decodeConfirm error =====" + e.getMessage()); + + } + + return null; + } + boolean flag = isEnd(code); + ByteBuffer buffer = ByteBuffer.wrap(data); + response.add(buffer); + if (!flag) { + return null; + } + try { + EquilResponse list = decode(); + response = new EquilResponse(createTime); + config = true; + rspIndex = intValue; + aapsLogger.debug(LTag.PUMPCOMM, "intValue=====" + intValue + "====" + rspIndex); + return list; + } catch (Exception e) { + e.printStackTrace(); + response = new EquilResponse(createTime); + aapsLogger.debug(LTag.PUMPCOMM, "decode error=====" + e.getMessage()); + } + return null; + + } + + public EquilResponse decode() { + EquilCmdModel reqModel = decodeModel(); + byte[] data = Utils.hexStringToBytes(reqModel.getCiphertext()); + String fv = data[12] + "." + data[13]; + firmwareVersion = Float.parseFloat(fv); + aapsLogger.debug(LTag.PUMPCOMM, "CmdDevicesOldGet====" + + Utils.bytesToHex(data) + "========" + firmwareVersion + "===" + (firmwareVersion < EquilConst.EQUIL_SUPPORT_LEVEL)); + reqModel.setCiphertext(Utils.bytesToHex(getNextData())); + synchronized (this) { + setCmdStatus(true); + notify(); + } + return responseCmd(reqModel, "0000" + reqModel.getCode()); + } + + public EquilCmdModel decodeModel() { + EquilCmdModel equilCmdModel = new EquilCmdModel(); + List list = new ArrayList<>(); + int index = 0; + for (ByteBuffer b : response.getSend()) { + if (index == 0) { + byte[] bs = b.array(); + byte[] codeByte = new byte[]{bs[10], bs[11]}; + list.add((Byte) bs[bs.length - 2]); + list.add((Byte) bs[bs.length - 1]); + equilCmdModel.setCode(Utils.bytesToHex(codeByte)); + } else { + byte[] bs = b.array(); + for (int i = 6; i < bs.length; i++) { + list.add((Byte) bs[i]); + } + } + index++; + } + List list3 = list.subList(0, list.size()); + equilCmdModel.setCiphertext(Utils.bytesToHex(list3).toLowerCase()); + equilCmdModel.setTag(""); + equilCmdModel.setIv(""); + return equilCmdModel; + } + + @Override + public void decodeConfirmData(byte[] data) { + int value = Utils.bytesToInt(data[7], data[6]); + String fv = data[18] + "." + data[19]; + firmwareVersion = Float.parseFloat(fv); + aapsLogger.debug(LTag.PUMPCOMM, "CmdDevicesOldGet====" + + Utils.bytesToHex(data) + "=====" + value + "===" + firmwareVersion + "====" + + (firmwareVersion < EquilConst.EQUIL_SUPPORT_LEVEL)); + synchronized (this) { + setCmdStatus(true); + notify(); + } + } + + public boolean isSupport() { + return !(firmwareVersion < EquilConst.EQUIL_SUPPORT_LEVEL); + } + + @Override public EquilHistoryRecord.EventType getEventType() { + return null; + } +} diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdExtendedBolusSet.java b/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdExtendedBolusSet.java new file mode 100644 index 00000000000..8431eb957ca --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdExtendedBolusSet.java @@ -0,0 +1,87 @@ +package app.aaps.pump.equil.manager.command; + + +import app.aaps.pump.equil.database.EquilHistoryRecord; +import app.aaps.pump.equil.manager.Utils; +import app.aaps.pump.equil.manager.Utils; + +public class CmdExtendedBolusSet extends BaseSetting { + double insulin; + int step; + int pumpTime; + int durationInMinutes; + boolean cancel; + + public boolean isCancel() { + return cancel; + } + + public void setCancel(boolean cancel) { + this.cancel = cancel; + } + + public double getInsulin() { + return insulin; + } + + public void setInsulin(double insulin) { + this.insulin = insulin; + } + + public int getDurationInMinutes() { + return durationInMinutes; + } + + public void setDurationInMinutes(int durationInMinutes) { + this.durationInMinutes = durationInMinutes; + } + + public CmdExtendedBolusSet(double insulin, int durationInMinutes, boolean cancel) { + super(System.currentTimeMillis()); + this.insulin = insulin; + this.durationInMinutes = durationInMinutes; + this.cancel = cancel; + if (insulin != 0) { + step = (int) (insulin / 0.05d * 8); + this.pumpTime = durationInMinutes * 60; + } + } + + @Override + public byte[] getFirstData() { + byte[] indexByte = Utils.intToBytes(pumpReqIndex); + byte[] data2 = new byte[]{0x01, 0x03}; + byte[] data3 = Utils.intToBytes(step); + byte[] data4 = Utils.intToBytes(pumpTime); + byte[] data5 = Utils.intToBytes(0); + byte[] data = Utils.concat(indexByte, data2, data5, data5, data3, data4); + pumpReqIndex++; + return data; + } + + public byte[] getNextData() { + byte[] indexByte = Utils.intToBytes(pumpReqIndex); + byte[] data2 = new byte[]{0x00, 0x03, 0x01}; + byte[] data3 = Utils.intToBytes(0); + byte[] data = Utils.concat(indexByte, data2, data3); + pumpReqIndex++; + return data; + } + + public void decodeConfirmData(byte[] data) { + int index = data[4]; + int status = data[6] & 0xff; + synchronized (this) { + setCmdStatus(true); + notify(); + } + } + + @Override public EquilHistoryRecord.EventType getEventType() { + if (cancel) { + return EquilHistoryRecord.EventType.CANCEL_EXTENDED_BOLUS; + } else { + return EquilHistoryRecord.EventType.SET_EXTENDED_BOLUS; + } + } +} diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdHistoryGet.java b/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdHistoryGet.java new file mode 100644 index 00000000000..71281d3ddee --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdHistoryGet.java @@ -0,0 +1,125 @@ +package app.aaps.pump.equil.manager.command; + +import androidx.annotation.NonNull; + +import app.aaps.core.interfaces.logging.LTag; +import app.aaps.pump.equil.database.EquilHistoryRecord; +import app.aaps.pump.equil.manager.Utils; + + +public class CmdHistoryGet extends BaseSetting { + + private int battery; + private int medicine; + private int rate; + private int largeRate; + private int year; + private int month; + private int day; + private int hour; + private int min; + private int second; + private int index; + // private int port; + private int type; + private int level; + private int parm; + private int currentIndex = 0; + private int resultIndex; + + public CmdHistoryGet() { + super(System.currentTimeMillis()); + this.port = "0505"; + } + + public CmdHistoryGet(int currentIndex) { + this(); + this.currentIndex = currentIndex; + } + + @Override + public byte[] getFirstData() { + byte[] indexByte = Utils.intToBytes(pumpReqIndex); + byte[] data2 = new byte[]{0x02, 0x01}; + byte[] data3 = Utils.intToBytes(currentIndex); + byte[] data = Utils.concat(indexByte, data2, data3); + pumpReqIndex++; + aapsLogger.debug(LTag.PUMPCOMM, "getReqData2===" + Utils.bytesToHex(data)); + return data; + } + + public byte[] getNextData() { + byte[] indexByte = Utils.intToBytes(pumpReqIndex); + byte[] data2 = new byte[]{0x00, 0x01, 0x01}; + aapsLogger.debug(LTag.PUMPCOMM, "currentIndex===" + currentIndex); + byte[] data = Utils.concat(indexByte, data2); + pumpReqIndex++; + return data; + } + + public void decodeConfirmData(byte[] data) { +// 67631679050017070e101319 + int index1 = data[4]; + year = data[6] & 0xff; + month = data[7] & 0xff; + day = data[8] & 0xff; + hour = data[9] & 0xff; + min = data[10] & 0xff; + second = data[11] & 0xff; + //a5e207590501 17070e100f161100000000007d0204080000 + //ae6ae9100501 17070e100f16 1100000000007d0204080000 + battery = data[12] & 0xff; + medicine = data[13] & 0xff; + rate = Utils.bytesToInt(data[15], data[14]); + largeRate = Utils.bytesToInt(data[17], data[16]); + index = Utils.bytesToInt(data[19], data[18]); +// port = data[20] & 0xff; + type = data[21] & 0xff; + level = data[22] & 0xff; + parm = data[23] & 0xff; + if (currentIndex != 0) { + equilManager.decodeHistory(data); + } + resultIndex = index; + aapsLogger.debug(LTag.PUMPCOMM, "history index==" + index + "===" + Utils.bytesToHex(data) + + "===" + rate + "====" + largeRate + "===" + Utils.bytesToHex(new byte[]{data[16], + data[17]})); + synchronized (this) { + setCmdStatus(true); + notify(); + } + } + + @NonNull @Override + public String toString() { + return "CmdHistoryGet{" + + "battery=" + battery + + ", medicine=" + medicine + + ", rate=" + rate + + ", largeRate=" + largeRate + + ", year=" + year + + ", month=" + month + + ", day=" + day + + ", hour=" + hour + + ", min=" + min + + ", second=" + second + + ", index=" + index + + ", port=" + port + + ", type=" + type + + ", level=" + level + + ", parm=" + parm + + '}'; + } + + public int getCurrentIndex() { + return resultIndex; + } + + public void setCurrentIndex(int currentIndex) { + this.currentIndex = currentIndex; + } + + @Override public EquilHistoryRecord.EventType getEventType() { + return null; + } +} diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdInsulinChange.java b/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdInsulinChange.java new file mode 100644 index 00000000000..f6584d97d4e --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdInsulinChange.java @@ -0,0 +1,48 @@ +package app.aaps.pump.equil.manager.command; + + +import app.aaps.core.interfaces.logging.LTag; +import app.aaps.pump.equil.database.EquilHistoryRecord; +import app.aaps.pump.equil.manager.Utils; + + +public class CmdInsulinChange extends BaseSetting { + + public CmdInsulinChange() { + super(System.currentTimeMillis()); + } + + @Override + public byte[] getFirstData() { + byte[] indexByte = Utils.intToBytes(pumpReqIndex); + byte[] data2 = new byte[]{0x01, 0x06}; + byte[] data3 = Utils.intToBytes(32000); + byte[] data = Utils.concat(indexByte, data2, data3); + pumpReqIndex++; + return data; + } + + public byte[] getNextData() { + byte[] indexByte = Utils.intToBytes(pumpReqIndex); + byte[] data2 = new byte[]{0x00, 0x06, 0x01}; + byte[] data3 = Utils.intToBytes(0); + byte[] data = Utils.concat(indexByte, data2, data3); + pumpReqIndex++; + return data; + } + + public void decodeConfirmData(byte[] data) { +// byte[] byteData = Crc.hexStringToBytes(data); + int status = data[6] & 0xff; + synchronized (this) { + setCmdStatus(true); + notify(); + } + equilManager.setInsulinChange(status); + aapsLogger.debug(LTag.PUMPCOMM, "status====" + status + "====" + Utils.bytesToHex(data)); + } + + @Override public EquilHistoryRecord.EventType getEventType() { + return EquilHistoryRecord.EventType.CHANGE_INSULIN; + } +} diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdInsulinGet.java b/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdInsulinGet.java new file mode 100644 index 00000000000..cac34293582 --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdInsulinGet.java @@ -0,0 +1,47 @@ +package app.aaps.pump.equil.manager.command; + + +import app.aaps.pump.equil.database.EquilHistoryRecord; +import app.aaps.pump.equil.manager.Utils; + +public class CmdInsulinGet extends BaseSetting { + + public CmdInsulinGet() { + super(System.currentTimeMillis()); + this.port = "0505"; + + } + + @Override + public byte[] getFirstData() { + byte[] indexByte = Utils.intToBytes(pumpReqIndex); + byte[] data2 = new byte[]{0x02, 0x07}; + byte[] data = Utils.concat(indexByte, data2); + pumpReqIndex++; + return data; + } + + public byte[] getNextData() { + byte[] indexByte = Utils.intToBytes(pumpReqIndex); + byte[] data2 = new byte[]{0x00, 0x07, 0x01}; + byte[] data3 = Utils.intToBytes(0); + byte[] data = Utils.concat(indexByte, data2, data3); + pumpReqIndex++; + return data; + } + + public void decodeConfirmData(byte[] data) { + int insulin = data[6] & 0xff; + equilManager.setStartInsulin(insulin); + equilManager.setCurrentInsulin(insulin); + synchronized (this) { + setCmdStatus(true); + notify(); + } + + } + + @Override public EquilHistoryRecord.EventType getEventType() { + return null; + } +} diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdLargeBasalSet.java b/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdLargeBasalSet.java new file mode 100644 index 00000000000..3e75b219038 --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdLargeBasalSet.java @@ -0,0 +1,73 @@ +package app.aaps.pump.equil.manager.command; + + +import app.aaps.core.interfaces.logging.LTag; +import app.aaps.pump.equil.database.EquilHistoryRecord; +import app.aaps.pump.equil.manager.Utils; + + +public class CmdLargeBasalSet extends BaseSetting { + double insulin; + int step; + int stepTime; + + public int getStepTime() { + return stepTime; + } + + public double getInsulin() { + return insulin; + } + + public void setInsulin(double insulin) { + this.insulin = insulin; + } + + public CmdLargeBasalSet(double insulin) { + super(System.currentTimeMillis()); + this.insulin = insulin; + if (insulin != 0) { + step = (int) (insulin / 0.05d * 8); + stepTime = (int) (insulin / 0.05d * 2); + } + } + + @Override + public byte[] getFirstData() { + aapsLogger.debug(LTag.PUMPCOMM, "step===" + step + "=====" + stepTime); + byte[] indexByte = Utils.intToBytes(pumpReqIndex); + byte[] data2 = new byte[]{0x01, 0x03}; + byte[] data3 = Utils.intToBytes(step); + byte[] data4 = Utils.intToBytes(stepTime); + byte[] data5 = Utils.intToBytes(0); + byte[] data = Utils.concat(indexByte, data2, data3, data4, data5, data5); + pumpReqIndex++; + return data; + } + + public byte[] getNextData() { + byte[] indexByte = Utils.intToBytes(pumpReqIndex); + byte[] data2 = new byte[]{0x00, 0x03, 0x01}; + byte[] data3 = Utils.intToBytes(0); + byte[] data = Utils.concat(indexByte, data2, data3); + pumpReqIndex++; + return data; + } + + public void decodeConfirmData(byte[] data) { + int index = data[4]; + int status = data[6] & 0xff; + synchronized (this) { + setCmdStatus(true); + notify(); + } + } + + @Override public EquilHistoryRecord.EventType getEventType() { + if (insulin == 0) { + return EquilHistoryRecord.EventType.CANCEL_BOLUS; + } else { + return EquilHistoryRecord.EventType.SET_BOLUS; + } + } +} diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdModelGet.java b/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdModelGet.java new file mode 100644 index 00000000000..bbb1380282d --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdModelGet.java @@ -0,0 +1,47 @@ +package app.aaps.pump.equil.manager.command; + + +import app.aaps.core.interfaces.logging.LTag; +import app.aaps.pump.equil.database.EquilHistoryRecord; +import app.aaps.pump.equil.manager.Utils; + + +public class CmdModelGet extends BaseSetting { + public CmdModelGet() { + super(System.currentTimeMillis()); + } + + @Override + public byte[] getFirstData() { + byte[] indexByte = Utils.intToBytes(pumpReqIndex); + byte[] data2 = new byte[]{0x02, 0x00}; + byte[] data = Utils.concat(indexByte, data2); + pumpReqIndex++; + return data; + } + + public byte[] getNextData() { + byte[] indexByte = Utils.intToBytes(pumpReqIndex); + byte[] data2 = new byte[]{0x00, 0x02, 0x01}; + byte[] data = Utils.concat(indexByte, data2); + pumpReqIndex++; + return data; + } + + @Override + public void decodeConfirmData(byte[] data) { + + int mode = data[6] & 0xff; + aapsLogger.debug(LTag.PUMPCOMM, "CmdGetModel====" + mode); + equilManager.setModel(mode); + cmdStatus = true; + synchronized (this) { + notify(); + } + + } + + @Override public EquilHistoryRecord.EventType getEventType() { + return null; + } +} diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdModelSet.java b/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdModelSet.java new file mode 100644 index 00000000000..003ff08f88d --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdModelSet.java @@ -0,0 +1,66 @@ +package app.aaps.pump.equil.manager.command; + + +import app.aaps.pump.equil.data.RunMode; +import app.aaps.pump.equil.database.EquilHistoryRecord; +import app.aaps.pump.equil.manager.Utils; + +public class CmdModelSet extends BaseSetting { + public RunMode getMode() { + if (mode == 0) { + return RunMode.SUSPEND; + } else if (mode == 1) { + return RunMode.RUN; + } else if (mode == 2) { + return RunMode.RUN; + } else { + return RunMode.SUSPEND; + } + } + + public void setMode(int mode) { + this.mode = mode; + } + + int mode; + + public CmdModelSet(int mode) { + super(System.currentTimeMillis()); + this.mode = mode; + } + + @Override + public byte[] getFirstData() { + byte[] indexByte = Utils.intToBytes(pumpReqIndex); + byte[] data2 = new byte[]{0x01, 0x00}; + byte[] data3 = Utils.intToBytes(mode); + byte[] data = Utils.concat(indexByte, data2, data3); + pumpReqIndex++; + return data; + } + + public byte[] getNextData() { + byte[] indexByte = Utils.intToBytes(pumpReqIndex); + byte[] data2 = new byte[]{0x00, 0x00, 0x01}; + byte[] data = Utils.concat(indexByte, data2); + pumpReqIndex++; + return data; + } + + public void decodeConfirmData(byte[] data) { + synchronized (this) { + setCmdStatus(true); + notify(); + } + } + + @Override public EquilHistoryRecord.EventType getEventType() { + RunMode runMode = getMode(); + if (runMode == RunMode.RUN) { + return EquilHistoryRecord.EventType.RESUME_DELIVERY; + } else if (runMode == RunMode.SUSPEND) { + return EquilHistoryRecord.EventType.SUSPEND_DELIVERY; + } + return null; + } +} diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdPair.java b/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdPair.java new file mode 100644 index 00000000000..07f94ff4bfe --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdPair.java @@ -0,0 +1,165 @@ +package app.aaps.pump.equil.manager.command; + + +import android.util.Log; + +import java.nio.ByteBuffer; +import java.security.MessageDigest; + +import app.aaps.core.interfaces.logging.LTag; +import app.aaps.pump.equil.EquilConst; +import app.aaps.pump.equil.database.EquilHistoryRecord; +import app.aaps.pump.equil.manager.AESUtil; +import app.aaps.pump.equil.manager.EquilCmdModel; +import app.aaps.pump.equil.manager.EquilResponse; +import app.aaps.pump.equil.manager.Utils; + + +public class CmdPair extends BaseCmd { + public static final String ERROR_PWD = + "0000000000000000000000000000000000000000000000000000000000000000"; + String sn; + public String address; + private final String password; + + public CmdPair(String name, String address, String password) { + super(System.currentTimeMillis()); + port = "0E0E"; + this.address = address; + this.password = password; + sn = name.replace("Equil - ", "").trim(); + sn = convertString(sn); + Log.e(LTag.PUMPCOMM.toString(), "sn===" + sn); + } + + public String getAddress() { + return address; + } + + byte[] randomPassword; + + @Override + public EquilResponse getEquilResponse() { + response = new EquilResponse(createTime); + try { + MessageDigest messageDigest = MessageDigest.getInstance("SHA-256"); + messageDigest.update(Utils.hexStringToBytes(sn)); + byte[] pwd = messageDigest.digest(); + + //B0EB6308060F79D685D6269DC048E32E4C103CD2B8EEA2DE4637EB8A5D6BCD08 + byte[] equilPassword = AESUtil.getEquilPassWord(password); + + + randomPassword = Utils.generateRandomPassword(32); + byte[] data = Utils.concat(equilPassword, randomPassword); + aapsLogger.debug(LTag.PUMPCOMM, "pwd==" + Utils.bytesToHex(pwd)); + aapsLogger.debug(LTag.PUMPCOMM, "data==" + Utils.bytesToHex(data)); + EquilCmdModel equilCmdModel = AESUtil.aesEncrypt(pwd, data); + return responseCmd(equilCmdModel, "0D0D0000"); + } catch (Exception e) { + e.printStackTrace(); + } + + + return null; + } + + @Override public EquilResponse getNextEquilResponse() { + return getEquilResponse(); + } + + + public EquilResponse decodeEquilPacket(byte[] data) { + if (!checkData(data)) { + return null; + } + byte code = data[4]; + int intValue = getIndex(code); + if (config) { + if (rspIndex == intValue) { + return null; + } + boolean flag = isEnd(code); + ByteBuffer buffer = ByteBuffer.wrap(data); + response.add(buffer); + if (!flag) { + return null; + } + try { + EquilResponse list = decodeConfirm(); + isEnd = true; + response = new EquilResponse(createTime); + rspIndex = intValue; + aapsLogger.debug(LTag.PUMPCOMM, "intValue=====" + intValue + "====" + rspIndex); + return list; + } catch (Exception e) { + response = new EquilResponse(createTime); + aapsLogger.debug(LTag.PUMPCOMM, "decodeConfirm error =====" + e.getMessage()); + + } + + return null; + } + boolean flag = isEnd(code); + ByteBuffer buffer = ByteBuffer.wrap(data); + response.add(buffer); + if (!flag) { + return null; + } + try { + EquilResponse list = decode(); + response = new EquilResponse(createTime); + config = true; + rspIndex = intValue; + aapsLogger.debug(LTag.PUMPCOMM, "intValue=====" + intValue + "====" + rspIndex); + return list; + } catch (Exception e) { + response = new EquilResponse(createTime); + aapsLogger.debug(LTag.PUMPCOMM, "decode error=====" + e.getMessage()); + } + return null; + + } + + @Override + public EquilResponse decode() throws Exception { + EquilCmdModel equilCmdModel = decodeModel(); + byte[] keyBytes = randomPassword; + String content = AESUtil.decrypt(equilCmdModel, keyBytes); + String pwd1 = content.substring(0, 64); + String pwd2 = content.substring(64); + aapsLogger.debug(LTag.PUMPCOMM, "decrypted====" + pwd1); + aapsLogger.debug(LTag.PUMPCOMM, "decrypted====" + pwd2); + if (ERROR_PWD.equals(pwd1) && ERROR_PWD.equals(pwd2)) { + synchronized (this) { + setCmdStatus(true); + setEnacted(false); + notifyAll(); + } + return null; + } + + sp.putString(EquilConst.Prefs.INSTANCE.getEQUIL_PASSWORD(), pwd2); + sp.putString(EquilConst.Prefs.INSTANCE.getEQUIL_DEVICES(), pwd1); + runPwd = pwd2; + byte[] data1 = Utils.hexStringToBytes(pwd1); + byte[] data = Utils.concat(data1, keyBytes); + EquilCmdModel equilCmdModel2 = AESUtil.aesEncrypt(Utils.hexStringToBytes(runPwd), data); + runCode = equilCmdModel.getCode(); + return responseCmd(equilCmdModel2, port + runCode); + } + + + @Override + public EquilResponse decodeConfirm() { + synchronized (this) { + setCmdStatus(true); + notifyAll(); + } + return null; + } + + @Override public EquilHistoryRecord.EventType getEventType() { + return EquilHistoryRecord.EventType.INITIALIZE_EQUIL; + } +} diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdResistanceGet.java b/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdResistanceGet.java new file mode 100644 index 00000000000..cbc7a5a2b79 --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdResistanceGet.java @@ -0,0 +1,61 @@ +package app.aaps.pump.equil.manager.command; + + +import app.aaps.pump.equil.database.EquilHistoryRecord; +import app.aaps.pump.equil.manager.AESUtil; +import app.aaps.pump.equil.manager.EquilCmdModel; +import app.aaps.pump.equil.manager.EquilResponse; +import app.aaps.pump.equil.manager.Utils; + +public class CmdResistanceGet extends BaseSetting { + public CmdResistanceGet() { + super(System.currentTimeMillis()); + port = "1515"; + } + + @Override + public byte[] getFirstData() { + byte[] indexByte = Utils.intToBytes(pumpReqIndex); + byte[] data2 = new byte[]{0x02, 0x02}; + byte[] data = Utils.concat(indexByte, data2); + pumpReqIndex++; + return data; + } + + public byte[] getNextData() { + byte[] indexByte = Utils.intToBytes(pumpReqIndex); + byte[] data2 = new byte[]{0x00, 0x02, 0x01}; + byte[] data = Utils.concat(indexByte, data2); + pumpReqIndex++; + return data; + } + + @Override + public void decodeConfirmData(byte[] data) { + int value = Utils.bytesToInt(data[7], data[6]); + cmdStatus = true; + if (value < 500) { + setEnacted(false); + } else { + setEnacted(true); + } + synchronized (this) { + notify(); + } + } + + public EquilResponse decodeConfirm() throws Exception { + EquilCmdModel equilCmdModel = decodeModel(); + runCode = equilCmdModel.getCode(); + String content = AESUtil.decrypt(equilCmdModel, Utils.hexStringToBytes(runPwd)); + decodeConfirmData(Utils.hexStringToBytes(content)); + byte[] data = getNextData(); + EquilCmdModel equilCmdModel2 = AESUtil.aesEncrypt(Utils.hexStringToBytes(runPwd), data); +// return responseCmd(equilCmdModel2, port + runCode,true); + return null; + } + + @Override public EquilHistoryRecord.EventType getEventType() { + return null; + } +} diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdSettingGet.java b/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdSettingGet.java new file mode 100644 index 00000000000..0bb46467a6e --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdSettingGet.java @@ -0,0 +1,137 @@ +package app.aaps.pump.equil.manager.command; + + +import app.aaps.core.interfaces.logging.LTag; +import app.aaps.pump.equil.database.EquilHistoryRecord; +import app.aaps.pump.equil.manager.Utils; + + +public class CmdSettingGet extends BaseSetting { + private long useTime; + private long closeTime; + private float lowAlarm; + private float largefastAlarm; + private float stopAlarm; + private float infusionUnit; + private float basalAlarm; + private float largeAlarm; + + public CmdSettingGet() { + super(System.currentTimeMillis()); + } + + @Override + public byte[] getFirstData() { + byte[] indexByte = Utils.intToBytes(pumpReqIndex); + byte[] data2 = new byte[]{0x02, 0x05}; + byte[] data = Utils.concat(indexByte, data2); + pumpReqIndex++; + return data; + } + + public byte[] getNextData() { + byte[] indexByte = Utils.intToBytes(pumpReqIndex); + byte[] data2 = new byte[]{0x00, 0x05, 0x01}; + byte[] data = Utils.concat(indexByte, data2); + pumpReqIndex++; + return data; + } + + public void decodeConfirmData(byte[] data) { + int index = data[4]; +// int i1 = ((data[15] & 0x0f) << 8) | data[14] & 0xff; +// int i2 = ((data[17] & 0x0f) << 8) | data[16] & 0xff; +// int i3 = ((data[19] & 0x0f) << 8) | data[18] & 0xff; +// int i4 = ((data[21] & 0x0f) << 8) | data[20] & 0xff; +// int i5 = ((data[23] & 0x0f) << 8) | data[22] & 0xff; +// int i6 = ((data[25] & 0x0f) << 8) | data[24] & 0xff; + + + int i1 = Utils.bytesToInt(data[15], data[14]); + int i2 = Utils.bytesToInt(data[17], data[16]); + int i3 = Utils.bytesToInt(data[19], data[18]); + int i4 = Utils.bytesToInt(data[21], data[20]); + int i5 = Utils.bytesToInt(data[23], data[22]); + int i6 = Utils.bytesToInt(data[25], data[24]); + lowAlarm = Utils.internalDecodeSpeedToUH(i1); + largefastAlarm = Utils.internalDecodeSpeedToUH(i2); + stopAlarm = Utils.internalDecodeSpeedToUH(i3); + infusionUnit = Utils.internalDecodeSpeedToUH(i4); + basalAlarm = Utils.internalDecodeSpeedToUH(i5); + largeAlarm = Utils.internalDecodeSpeedToUH(i6); + aapsLogger.debug(LTag.PUMPCOMM, + "CmdSettingGet===" + Utils.bytesToHex(data) + "====" + lowAlarm + "=======" + i1); + synchronized (this) { + setCmdStatus(true); + notify(); + } + } + + public long getUseTime() { + return useTime; + } + + public void setUseTime(long useTime) { + this.useTime = useTime; + } + + public long getCloseTime() { + return closeTime; + } + + public void setCloseTime(long closeTime) { + this.closeTime = closeTime; + } + + public float getLowAlarm() { + return lowAlarm; + } + + public void setLowAlarm(float lowAlarm) { + this.lowAlarm = lowAlarm; + } + + public float getLargefastAlarm() { + return largefastAlarm; + } + + public void setLargefastAlarm(float largefastAlarm) { + this.largefastAlarm = largefastAlarm; + } + + public float getStopAlarm() { + return stopAlarm; + } + + public void setStopAlarm(float stopAlarm) { + this.stopAlarm = stopAlarm; + } + + public float getInfusionUnit() { + return infusionUnit; + } + + public void setInfusionUnit(float infusionUnit) { + this.infusionUnit = infusionUnit; + } + + public float getBasalAlarm() { + return basalAlarm; + } + + public void setBasalAlarm(float basalAlarm) { + this.basalAlarm = basalAlarm; + } + + public float getLargeAlarm() { + return largeAlarm; + } + + public void setLargeAlarm(float largeAlarm) { + this.largeAlarm = largeAlarm; + } + + @Override public EquilHistoryRecord.EventType getEventType() { + return null; + } +} diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdSettingSet.java b/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdSettingSet.java new file mode 100644 index 00000000000..67e5345ea17 --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdSettingSet.java @@ -0,0 +1,82 @@ +package app.aaps.pump.equil.manager.command; + + +import app.aaps.core.interfaces.logging.LTag; +import app.aaps.pump.equil.EquilConst; +import app.aaps.pump.equil.database.EquilHistoryRecord; +import app.aaps.pump.equil.manager.Utils; + + +public class CmdSettingSet extends BaseSetting { + double lowAlarm; + + int bolusThresholdStep = EquilConst.EQUIL_BLOUS_THRESHOLD_STEP; + + public CmdSettingSet() { + super(System.currentTimeMillis()); + } + + public CmdSettingSet(double maxBolus) { + super(System.currentTimeMillis()); + bolusThresholdStep = Utils.decodeSpeedToUH(maxBolus); + } + + @Override + public byte[] getFirstData() { + byte[] indexByte = Utils.intToBytes(pumpReqIndex); + byte[] equilCmd = new byte[]{0x01, 0x05}; + byte[] useTime = Utils.intToBytes(0); + byte[] autoCloseTime = Utils.intToBytes(0); + byte[] lowAlarmByte = Utils.intToTwoBytes(bolusThresholdStep); + byte[] fastBolus = Utils.intToTwoBytes(0); + byte[] occlusion = Utils.intToTwoBytes(2800); + byte[] insulinUnit = Utils.intToTwoBytes(8); + byte[] basalThreshold = Utils.intToTwoBytes(240); + byte[] bolusThreshold = Utils.intToTwoBytes(1600); + byte[] data = Utils.concat(indexByte, equilCmd, useTime, autoCloseTime, + lowAlarmByte, fastBolus, + occlusion, insulinUnit, basalThreshold, bolusThreshold); + pumpReqIndex++; + aapsLogger.debug(LTag.PUMPCOMM, + "CmdSettingSet data===" + Utils.bytesToHex(data) + "====" + lowAlarm + "===" + Utils.decodeSpeedToUH(lowAlarm)); + return data; + } + + public byte[] getNextData() { + byte[] indexByte = Utils.intToBytes(pumpReqIndex); + byte[] data2 = new byte[]{0x00, 0x05, 0x01}; + byte[] data = Utils.concat(indexByte, data2); + pumpReqIndex++; + return data; + } + + public void decodeConfirmData(byte[] data) { + int index = data[4]; +// int i1 = ((data[15] & 0x0f) << 8) | data[14] & 0xff; +// int i2 = ((data[17] & 0x0f) << 8) | data[16] & 0xff; +// int i3 = ((data[19] & 0x0f) << 8) | data[18] & 0xff; +// int i4 = ((data[21] & 0x0f) << 8) | data[20] & 0xff; +// int i5 = ((data[23] & 0x0f) << 8) | data[22] & 0xff; +// int i6 = ((data[25] & 0x0f) << 8) | data[24] & 0xff; +// lowAlarm = Utils.internalDecodeSpeedToUH(i1); +// largefastAlarm = Utils.internalDecodeSpeedToUH(i2); +// stopAlarm = Utils.internalDecodeSpeedToUH(i3); +// infusionUnit = Utils.internalDecodeSpeedToUH(i4); +// basalAlarm = Utils.internalDecodeSpeedToUH(i5); +// largeAlarm = Utils.internalDecodeSpeedToUH(i6); +// aapsLogger.debug(LTag.PUMPCOMM, +// "CmdSettingSet===" + Crc.bytesToHex(data) + "====" + lowAlarm); + synchronized (this) { + setCmdStatus(true); + notify(); + } + } + + public boolean isPairStep() { + return true; + } + + @Override public EquilHistoryRecord.EventType getEventType() { + return null; + } +} diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdStatusGet.java b/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdStatusGet.java new file mode 100644 index 00000000000..436f85e8d71 --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdStatusGet.java @@ -0,0 +1,13 @@ +package app.aaps.pump.equil.manager.command; + + +import androidx.annotation.NonNull; + +import app.aaps.core.interfaces.queue.CustomCommand; + + +public class CmdStatusGet implements CustomCommand { + @NonNull @Override public String getStatusDescription() { + return "CmdStatusGet"; + } +} diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdStepSet.java b/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdStepSet.java new file mode 100644 index 00000000000..288e5ce0375 --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdStepSet.java @@ -0,0 +1,62 @@ +package app.aaps.pump.equil.manager.command; + + +import app.aaps.pump.equil.database.EquilHistoryRecord; +import app.aaps.pump.equil.manager.AESUtil; +import app.aaps.pump.equil.manager.EquilCmdModel; +import app.aaps.pump.equil.manager.EquilResponse; +import app.aaps.pump.equil.manager.Utils; + +public class CmdStepSet extends BaseSetting { + boolean sendConfig; + int step; + + public CmdStepSet(boolean sendConfig, int step) { + super(System.currentTimeMillis()); + this.sendConfig = sendConfig; + this.step = step; + } + + + public byte[] getFirstData() { + byte[] indexByte = Utils.intToBytes(pumpReqIndex); + byte[] data2 = new byte[]{0x01, 0x07}; + byte[] data3 = Utils.intToBytes(step); + byte[] data = Utils.concat(indexByte, data2, data3); + pumpReqIndex++; + return data; + } + + public byte[] getNextData() { + byte[] indexByte = Utils.intToBytes(pumpReqIndex); + byte[] data2 = new byte[]{0x00, 0x07, 0x01}; + byte[] data3 = Utils.intToBytes(0); + byte[] data = Utils.concat(indexByte, data2, data3); + pumpReqIndex++; + return data; + } + + public void decodeConfirmData(byte[] data) { +// byte[] byteData = Crc.hexStringToBytes(data); +// int status = byteData[6] & 0xff; + synchronized (this) { + setCmdStatus(true); + notify(); + } + } + public EquilResponse decodeConfirm() throws Exception { + EquilCmdModel equilCmdModel = decodeModel(); + runCode = equilCmdModel.getCode(); + String content = AESUtil.decrypt(equilCmdModel, Utils.hexStringToBytes(runPwd)); + decodeConfirmData(Utils.hexStringToBytes(content)); + byte[] data = getNextData(); + EquilCmdModel equilCmdModel2 = AESUtil.aesEncrypt(Utils.hexStringToBytes(runPwd), data); + if (sendConfig) { + return responseCmd(equilCmdModel2, port + runCode); + } + return null; + } + @Override public EquilHistoryRecord.EventType getEventType() { + return null; + } +} diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdTempBasalGet.java b/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdTempBasalGet.java new file mode 100644 index 00000000000..ce3aea17fa8 --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdTempBasalGet.java @@ -0,0 +1,66 @@ +package app.aaps.pump.equil.manager.command; + + +import app.aaps.core.interfaces.logging.LTag; +import app.aaps.pump.equil.database.EquilHistoryRecord; +import app.aaps.pump.equil.manager.Utils; + + +public class CmdTempBasalGet extends BaseSetting { + private int time; + private int step; + + public int getTime() { + return time; + } + + public void setTime(int time) { + this.time = time; + } + + public int getStep() { + return step; + } + + public void setStep(int step) { + this.step = step; + } + + public CmdTempBasalGet() { + super(System.currentTimeMillis()); + } + + @Override + public byte[] getFirstData() { + byte[] indexByte = Utils.intToBytes(pumpReqIndex); + byte[] data2 = new byte[]{0x02, 0x04}; + byte[] data = Utils.concat(indexByte, data2); + pumpReqIndex++; + return data; + } + + public byte[] getNextData() { + byte[] indexByte = Utils.intToBytes(pumpReqIndex); + byte[] data2 = new byte[]{0x00, 0x04, 0x02}; + byte[] data3 = Utils.intToBytes(0); + byte[] data = Utils.concat(indexByte, data2, data3); + pumpReqIndex++; + return data; + } + + public void decodeConfirmData(byte[] data) { + int index = data[4]; + step = Utils.bytes2Int(new byte[]{data[6], data[7], data[8], data[9]}); + time = Utils.bytes2Int(new byte[]{data[10], data[11], data[12], data[13]}); + aapsLogger.debug(LTag.PUMPCOMM, "CmdTempBasalGet===" + step + "====" + time); +// Utils.by + synchronized (this) { + setCmdStatus(true); + notify(); + } + } + + @Override public EquilHistoryRecord.EventType getEventType() { + return null; + } +} diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdTempBasalSet.java b/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdTempBasalSet.java new file mode 100644 index 00000000000..204318ea3de --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdTempBasalSet.java @@ -0,0 +1,89 @@ +package app.aaps.pump.equil.manager.command; + + +import app.aaps.core.interfaces.logging.LTag; +import app.aaps.pump.equil.database.EquilHistoryRecord; +import app.aaps.pump.equil.manager.Utils; + + +public class CmdTempBasalSet extends BaseSetting { + double insulin; + int duration; + int step; + int pumpTime; + boolean cancel; + + public double getInsulin() { + return insulin; + } + + public void setInsulin(double insulin) { + this.insulin = insulin; + } + + public int getDuration() { + return duration; + } + + public void setDuration(int duration) { + this.duration = duration; + } + + public boolean isCancel() { + return cancel; + } + + public void setCancel(boolean cancel) { + this.cancel = cancel; + } + + public CmdTempBasalSet(double insulin, int duration) { + super(System.currentTimeMillis()); + this.insulin = insulin; + this.duration = duration; + if (insulin != 0) { + step = (int) (insulin / 0.05d * 8) / 2; + } else { + step = 0; + } + this.pumpTime = duration * 60; + } + + @Override + public byte[] getFirstData() { + aapsLogger.debug(LTag.PUMPCOMM, "step===" + step + "=====" + pumpTime); + byte[] indexByte = Utils.intToBytes(pumpReqIndex); + byte[] data2 = new byte[]{0x01, 0x04}; + byte[] data3 = Utils.intToBytes(step); + byte[] data4 = Utils.intToBytes(pumpTime); + byte[] data = Utils.concat(indexByte, data2, data3, data4); + pumpReqIndex++; + return data; + } + + public byte[] getNextData() { + byte[] indexByte = Utils.intToBytes(pumpReqIndex); + byte[] data2 = new byte[]{0x00, 0x04, 0x01}; + byte[] data3 = Utils.intToBytes(0); + byte[] data = Utils.concat(indexByte, data2, data3); + pumpReqIndex++; + return data; + } + + public void decodeConfirmData(byte[] data) { + int index = data[4]; + int status = data[6] & 0xff; + synchronized (this) { + setCmdStatus(true); + notify(); + } + } + + @Override public EquilHistoryRecord.EventType getEventType() { + if (cancel) { + return EquilHistoryRecord.EventType.CANCEL_TEMPORARY_BASAL; + } else { + return EquilHistoryRecord.EventType.SET_TEMPORARY_BASAL; + } + } +} diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdTimeGet.java b/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdTimeGet.java new file mode 100644 index 00000000000..d78ebb977b2 --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdTimeGet.java @@ -0,0 +1,46 @@ +package app.aaps.pump.equil.manager.command; + + +import app.aaps.pump.equil.database.EquilHistoryRecord; +import app.aaps.pump.equil.manager.Utils; +import app.aaps.pump.equil.manager.Utils; + +public class CmdTimeGet extends BaseSetting { + public CmdTimeGet() { + super(System.currentTimeMillis()); + port = "0505"; + } + + @Override + public byte[] getFirstData() { + byte[] indexByte = Utils.intToBytes(pumpReqIndex); + byte[] data2 = new byte[]{0x02, 0x00}; + byte[] data = Utils.concat(indexByte, data2); + pumpReqIndex++; + return data; + } + + public byte[] getNextData() { + byte[] indexByte = Utils.intToBytes(pumpReqIndex); + byte[] data2 = new byte[]{0x00, 0x00, 0x01}; + byte[] data = Utils.concat(indexByte, data2); + pumpReqIndex++; + return data; + } + + @Override + public void decodeConfirmData(byte[] data) { +// 67631679050017070e101319 + int index = data[4]; + int year = data[6] & 0xff; + int month = data[7] & 0xff; + int day = data[8] & 0xff; + int hour = data[9] & 0xff; + int min = data[10] & 0xff; + int s = data[11] & 0xff; + } + + @Override public EquilHistoryRecord.EventType getEventType() { + return null; + } +} diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdTimeSet.java b/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdTimeSet.java new file mode 100644 index 00000000000..adb3f3edf5d --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdTimeSet.java @@ -0,0 +1,58 @@ +package app.aaps.pump.equil.manager.command; + + +import app.aaps.pump.equil.database.EquilHistoryRecord; +import app.aaps.pump.equil.manager.Utils; + +import java.util.Calendar; + +public class CmdTimeSet extends BaseSetting { + public CmdTimeSet() { + super(System.currentTimeMillis()); + port = "0505"; + } + + @Override + public byte[] getFirstData() { + byte[] indexByte = Utils.intToBytes(pumpReqIndex); + byte[] data2 = new byte[]{0x01, 0x00}; + byte[] data3 = new byte[6]; + Calendar calendar = Calendar.getInstance(); + int year = calendar.get(Calendar.YEAR) - 2000; + int month = calendar.get(Calendar.MONTH) + 1; + int day = calendar.get(Calendar.DAY_OF_MONTH); + int hour = calendar.get(Calendar.HOUR_OF_DAY); + int minute = calendar.get(Calendar.MINUTE); + int second = calendar.get(Calendar.SECOND); + + data3[0] = (byte) year; + data3[1] = (byte) month; + data3[2] = (byte) day; + data3[3] = (byte) hour; + data3[4] = (byte) minute; + data3[5] = (byte) second; + byte[] data = Utils.concat(indexByte, data2, data3); +// ZLog.e2("setTime==" + year + "==" + month + "===" + day + "===" + hour + "===" + minute + "===" + second); + pumpReqIndex++; + return data; + } + + public byte[] getNextData() { + byte[] indexByte = Utils.intToBytes(pumpReqIndex); + byte[] data2 = new byte[]{0x00, 0x00, 0x01}; + byte[] data = Utils.concat(indexByte, data2); + pumpReqIndex++; + return data; + } + + public void decodeConfirmData(byte[] data) { + synchronized (this) { + setCmdStatus(true); + notify(); + } + } + + @Override public EquilHistoryRecord.EventType getEventType() { + return EquilHistoryRecord.EventType.SET_TIME; + } +} diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdTmepBasalGet.java b/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdTmepBasalGet.java new file mode 100644 index 00000000000..f12dcb1fae6 --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdTmepBasalGet.java @@ -0,0 +1,38 @@ +package app.aaps.pump.equil.manager.command; + + +import app.aaps.pump.equil.database.EquilHistoryRecord; +import app.aaps.pump.equil.manager.Utils; + +public class CmdTmepBasalGet extends BaseSetting { + public CmdTmepBasalGet() { + super(System.currentTimeMillis()); + } + + @Override + public byte[] getFirstData() { + byte[] indexByte = Utils.intToBytes(pumpReqIndex); + byte[] data2 = new byte[]{0x02, 0x02}; + byte[] data3 = Utils.intToBytes(120); + byte[] data4 = Utils.intToBytes(120); + byte[] data = Utils.concat(indexByte, data2, data3, data4); + pumpReqIndex++; + return data; + } + + public byte[] getNextData() { + byte[] indexByte = Utils.intToBytes(pumpReqIndex); + byte[] data2 = new byte[]{0x00, 0x02, 0x01}; + byte[] data = Utils.concat(indexByte, data2); + pumpReqIndex++; + return data; + } + + public void decodeConfirmData(byte[] data) { + + } + + @Override public EquilHistoryRecord.EventType getEventType() { + return null; + } +} diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdUnPair.java b/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdUnPair.java new file mode 100644 index 00000000000..6a27855d795 --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/CmdUnPair.java @@ -0,0 +1,145 @@ +package app.aaps.pump.equil.manager.command; + + +import java.nio.ByteBuffer; +import java.security.MessageDigest; + +import app.aaps.core.interfaces.logging.LTag; +import app.aaps.pump.equil.database.EquilHistoryRecord; +import app.aaps.pump.equil.manager.AESUtil; +import app.aaps.pump.equil.manager.EquilCmdModel; +import app.aaps.pump.equil.manager.EquilResponse; +import app.aaps.pump.equil.manager.Utils; + + +public class CmdUnPair extends BaseCmd { + String sn; + String password; + + public CmdUnPair(String name, String password) { + super(System.currentTimeMillis()); + port = "0E0E"; + this.password = password; + sn = name.replace("Equil - ", "").trim(); + sn = convertString(sn); + } + + byte[] randomPassword; + + public EquilResponse clear1() { + try { + MessageDigest messageDigest = MessageDigest.getInstance("SHA-256"); + messageDigest.update(Utils.hexStringToBytes(sn)); + byte[] pwd = messageDigest.digest(); + + byte[] equilPassword = AESUtil.getEquilPassWord(password); + randomPassword = Utils.generateRandomPassword(32); + byte[] data = Utils.concat(equilPassword, randomPassword); + EquilCmdModel equilCmdModel = AESUtil.aesEncrypt(pwd, data); + return responseCmd(equilCmdModel, "0D0D0000"); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + + } + + @Override + public EquilResponse getEquilResponse() { + response = new EquilResponse(createTime); + return clear1(); + } + + @Override public EquilResponse getNextEquilResponse() { + return getEquilResponse(); + } + + public EquilResponse decodeEquilPacket(byte[] data) { + if (!checkData(data)) { + return null; + } + byte code = data[4]; + int intValue = getIndex(code); + if (config) { + if (rspIndex == intValue) { + return null; + } + boolean flag = isEnd(code); + ByteBuffer buffer = ByteBuffer.wrap(data); + response.add(buffer); + if (!flag) { + return null; + } + try { + EquilResponse list = decodeConfirm(); + isEnd = true; + response = new EquilResponse(createTime); + rspIndex = intValue; + return list; + } catch (Exception e) { + response = new EquilResponse(createTime); + aapsLogger.debug(LTag.PUMPCOMM, "decodeEquilPacket error =====" + e.getMessage()); + + } + + return null; + } + boolean flag = isEnd(code); + ByteBuffer buffer = ByteBuffer.wrap(data); + response.add(buffer); + if (!flag) { + return null; + } + try { + EquilResponse list = decode(); + response = new EquilResponse(createTime); + config = true; + rspIndex = intValue; + return list; + } catch (Exception e) { + response = new EquilResponse(createTime); + aapsLogger.debug(LTag.PUMPCOMM, "decodeEquilPacket error=====" + e.getMessage()); + } + return null; + + } + + @Override + public EquilResponse decode() throws Exception { + EquilCmdModel equilCmdModel = decodeModel(); + byte[] keyBytes = randomPassword; + + + byte[] data2 = new byte[]{ + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00}; + String content = AESUtil.decrypt(equilCmdModel, keyBytes); + String pwd1 = content.substring(0, 64); + String pwd2 = content.substring(64); + runPwd = pwd2; + byte[] data1 = Utils.hexStringToBytes(pwd1); + byte[] data = Utils.concat(data1, data2); + + EquilCmdModel equilCmdModel2 = AESUtil.aesEncrypt(Utils.hexStringToBytes(runPwd), data); + runCode = equilCmdModel.getCode(); + return responseCmd(equilCmdModel2, port + runCode); + } + + + @Override + public EquilResponse decodeConfirm() throws Exception { + EquilCmdModel equilCmdModel = decodeModel(); + String content = AESUtil.decrypt(equilCmdModel, Utils.hexStringToBytes(runPwd)); + synchronized (this) { + setCmdStatus(true); + notify(); + } + return null; + } + + @Override public EquilHistoryRecord.EventType getEventType() { + return EquilHistoryRecord.EventType.UNPAIR_EQUIL; + } +} diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/PumpEvent.java b/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/PumpEvent.java new file mode 100644 index 00000000000..3b91b81c5e8 --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/PumpEvent.java @@ -0,0 +1,105 @@ +package app.aaps.pump.equil.manager.command; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import app.aaps.core.interfaces.resources.ResourceHelper; +import app.aaps.pump.equil.R; + + +public class PumpEvent { + private int port; + private int type; + private int level;// + private String conent; + static List lists = new ArrayList<>(); + + public static void init(ResourceHelper rh) { + lists = new ArrayList<>(); + lists.add(new PumpEvent(4, 0, 0, "--")); + lists.add(new PumpEvent(4, 1, 1, rh.gs(R.string.equil_history_item1))); + lists.add(new PumpEvent(4, 1, 2, rh.gs(R.string.equil_history_item2))); + lists.add(new PumpEvent(4, 2, 2, rh.gs(R.string.equil_history_item3))); + lists.add(new PumpEvent(4, 3, 0, rh.gs(R.string.equil_history_item4))); + lists.add(new PumpEvent(4, 3, 2, rh.gs(R.string.equil_history_item5))); + lists.add(new PumpEvent(4, 5, 0, rh.gs(R.string.equil_history_item6))); + lists.add(new PumpEvent(4, 5, 1, rh.gs(R.string.equil_history_item7))); + lists.add(new PumpEvent(4, 6, 1, rh.gs(R.string.equil_history_item8))); + lists.add(new PumpEvent(4, 6, 2, rh.gs(R.string.equil_history_item9))); + lists.add(new PumpEvent(4, 7, 0, rh.gs(R.string.equil_history_item10))); + lists.add(new PumpEvent(4, 8, 0, rh.gs(R.string.equil_history_item11))); + lists.add(new PumpEvent(4, 9, 0, rh.gs(R.string.equil_history_item12))); + lists.add(new PumpEvent(4, 10, 0, rh.gs(R.string.equil_history_item13))); + lists.add(new PumpEvent(4, 11, 0, rh.gs(R.string.equil_history_item14))); + lists.add(new PumpEvent(5, 0, 1, rh.gs(R.string.equil_history_item15))); + lists.add(new PumpEvent(5, 0, 2, rh.gs(R.string.equil_history_item16))); + lists.add(new PumpEvent(5, 1, 0, rh.gs(R.string.equil_history_item17))); + lists.add(new PumpEvent(5, 1, 2, rh.gs(R.string.equil_history_item18))); + } + + public PumpEvent(int port, int type, int level, String conent) { + this.port = port; + this.type = type; + this.level = level; + this.conent = conent; + } + + + public static String getTips(int port, int type, int level) { + PumpEvent pumpEvent = new PumpEvent(port, type, level, ""); + int index = lists.indexOf(pumpEvent); + if (index == -1) { + return ""; + } + return lists.get(index).getConent(); + } + + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PumpEvent pumpEvent = (PumpEvent) o; + return port == pumpEvent.port && type == pumpEvent.type && level == pumpEvent.level; + } + + @Override + public int hashCode() { + return Objects.hash(port, type, level); + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public int getType() { + return type; + } + + public void setType(int type) { + this.type = type; + } + + public int getLevel() { + return level; + } + + public void setLevel(int level) { + this.level = level; + } + + public String getConent() { + return conent; + } + + public void setConent(String conent) { + this.conent = conent; + } + + +} diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/SettingCmd.java b/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/SettingCmd.java new file mode 100644 index 00000000000..4e36030487e --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/manager/command/SettingCmd.java @@ -0,0 +1,54 @@ +package app.aaps.pump.equil.manager.command; + + +import app.aaps.pump.equil.database.EquilHistoryRecord; +import app.aaps.pump.equil.manager.Utils; + +public class SettingCmd extends BaseSetting { + public SettingCmd() { + super(System.currentTimeMillis()); + } + + + public byte[] getData() { + byte[] indexByte = Utils.intToBytes(pumpReqIndex); + byte[] tzm = Utils.hexStringToBytes(getEquilDevices()); + byte[] data = Utils.concat(indexByte, tzm); + pumpReqIndex++; + return data; + } + + + public byte[] getData2() { + byte[] indexByte = Utils.intToBytes(pumpReqIndex); + byte[] data2 = new byte[]{0x01, 0x07}; + byte[] data3 = Utils.intToBytes(120); + byte[] data = Utils.concat(indexByte, data2, data3); + pumpReqIndex++; + return data; + } + + @Override public byte[] getFirstData() { + return new byte[0]; + } + + @Override public byte[] getNextData() { + return new byte[0]; + } + + @Override public void decodeConfirmData(byte[] data) { + + } + + public byte[] getData3() { + byte[] indexByte = Utils.intToBytes(pumpReqIndex); + byte[] data2 = new byte[]{0x00, 0x07, 0x01}; + byte[] data = Utils.concat(indexByte, data2); + pumpReqIndex++; + return data; + } + + @Override public EquilHistoryRecord.EventType getEventType() { + return null; + } +} diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/ui/EquilHistoryRecordActivity.kt b/pump/equil/src/main/java/app/aaps/pump/equil/ui/EquilHistoryRecordActivity.kt new file mode 100644 index 00000000000..521606e023f --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/ui/EquilHistoryRecordActivity.kt @@ -0,0 +1,482 @@ +package app.aaps.pump.equil.ui + +import android.annotation.SuppressLint +import android.content.Context +import android.os.Bundle +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.AdapterView +import android.widget.ArrayAdapter +import android.widget.TextView +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import app.aaps.core.interfaces.extensions.toVisibility +import app.aaps.core.interfaces.logging.AAPSLogger +import app.aaps.core.interfaces.logging.LTag +import app.aaps.core.interfaces.plugin.ActivePlugin +import app.aaps.core.interfaces.pump.BlePreCheck +import app.aaps.core.interfaces.pump.defs.PumpType +import app.aaps.core.interfaces.queue.CommandQueue +import app.aaps.core.interfaces.resources.ResourceHelper +import app.aaps.core.interfaces.rx.AapsSchedulers +import app.aaps.core.interfaces.rx.bus.RxBus +import app.aaps.core.interfaces.sharedPreferences.SP +import app.aaps.core.interfaces.utils.DateUtil +import app.aaps.core.interfaces.utils.fabric.FabricPrivacy +import app.aaps.core.ui.activities.TranslatedDaggerAppCompatActivity +import app.aaps.pump.equil.EquilPumpPlugin +import app.aaps.pump.equil.R +import app.aaps.pump.equil.database.EquilHistoryPump +import app.aaps.pump.equil.database.EquilHistoryPumpDao +import app.aaps.pump.equil.database.EquilHistoryRecord +import app.aaps.pump.equil.database.EquilHistoryRecordDao +import app.aaps.pump.equil.database.ResolvedResult +import app.aaps.pump.equil.databinding.EquilHistoryRecordActivityBinding +import app.aaps.pump.equil.driver.definition.EquilHistoryEntryGroup +import app.aaps.pump.equil.events.EventEquilDataChanged +import app.aaps.pump.equil.manager.Utils +import app.aaps.pump.equil.manager.command.PumpEvent +import com.google.android.material.tabs.TabLayout +import info.nightscout.pump.common.utils.ProfileUtil +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.kotlin.plusAssign +import java.text.SimpleDateFormat +import java.util.Calendar +import java.util.Locale +import javax.inject.Inject +import kotlin.math.abs + +// IMPORTANT: This activity needs to be called from RileyLinkSelectPreference (see pref_medtronic.xml as example) +class EquilHistoryRecordActivity : TranslatedDaggerAppCompatActivity() { + + @Inject lateinit var sp: SP + @Inject lateinit var blePreCheck: BlePreCheck + @Inject lateinit var activePlugin: ActivePlugin + @Inject lateinit var context: Context + @Inject lateinit var rh: ResourceHelper + @Inject lateinit var aapsLogger: AAPSLogger + @Inject lateinit var equilHistoryRecordDao: EquilHistoryRecordDao + @Inject lateinit var equilHistoryPumpDao: EquilHistoryPumpDao + @Inject lateinit var aapsSchedulers: AapsSchedulers + @Inject lateinit var commandQueue: CommandQueue + @Inject lateinit var fabricPrivacy: FabricPrivacy + @Inject lateinit var equilPumpPlugin: EquilPumpPlugin + @Inject lateinit var rxBus: RxBus + @Inject lateinit var dateUtil: DateUtil + + private lateinit var binding: EquilHistoryRecordActivityBinding + private lateinit var llm: LinearLayoutManager + private lateinit var recyclerViewAdapter: RecyclerViewAdapter + + private val disposable = CompositeDisposable() + private var filteredHistoryList: MutableList = java.util.ArrayList() + private val fullHistoryList: MutableList = java.util.ArrayList() + private val calendar: Calendar = Calendar.getInstance() + + //private var typeListFull: List? = null + private var manualChange = false + + public override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = EquilHistoryRecordActivityBinding.inflate(layoutInflater) + setContentView(binding.root) + title = rh.gs(R.string.equil_title_history_events) + supportActionBar?.setDisplayHomeAsUpEnabled(true) + supportActionBar?.setDisplayShowHomeEnabled(true) + loadData() + + recyclerViewAdapter = RecyclerViewAdapter(filteredHistoryList, rh) + llm = LinearLayoutManager(this) + binding.recyclerview.run { + setHasFixedSize(true) + layoutManager = llm + adapter = recyclerViewAdapter + } + binding.recyclerviewEquil.run { + setHasFixedSize(true) + layoutManager = LinearLayoutManager(this@EquilHistoryRecordActivity) + } + disposable += rxBus + .toObservable(EventEquilDataChanged::class.java) + .observeOn(aapsSchedulers.main) + .subscribe({ + loadData() + loadDataEquil() + }, fabricPrivacy::logException) + //typeListFull = getTypeList(EquilHistoryEntry.getTranslatedList(rh)) + + val spinnerAdapter = ArrayAdapter(this, app.aaps.core.ui.R.layout.spinner_centered, EquilHistoryEntryGroup.getTranslatedList(rh)) + binding.equilHistorytype.run { + adapter = spinnerAdapter + onItemSelectedListener = object : AdapterView.OnItemSelectedListener { + override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { + if (manualChange) return + val selected = selectedItem as EquilHistoryEntryGroup + filterHistory(selected) + } + + override fun onNothingSelected(parent: AdapterView<*>?) { + if (manualChange) return + filterHistory(EquilHistoryEntryGroup.All) + } + } + } + binding.tabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener { + override fun onTabSelected(tab: TabLayout.Tab) { + processVisibility(tab.position) + } + + override fun onTabUnselected(tab: TabLayout.Tab) {} + override fun onTabReselected(tab: TabLayout.Tab) {} + }) + loadDataEquil() + } + + private fun processVisibility(position: Int) { + binding.recyclerview.visibility = (position == 0).toVisibility() + binding.recyclerviewEquil.visibility = (position == 1).toVisibility() + + } + + @SuppressLint("NotifyDataSetChanged") + private fun filterHistory(group: EquilHistoryEntryGroup) { + filteredHistoryList.clear() + aapsLogger.debug(LTag.PUMPCOMM, "Items on full list: {}", fullHistoryList.size) + if (group === EquilHistoryEntryGroup.All) { + aapsLogger.debug(LTag.PUMPCOMM, "all===") + filteredHistoryList.addAll(fullHistoryList) + } else { + filteredHistoryList.addAll(fullHistoryList.filter { it.type?.let { it1 -> groupForCommandType(it1) } == group }) + } + + recyclerViewAdapter.let { + it.historyList = filteredHistoryList + it.notifyDataSetChanged() + } + aapsLogger.debug(LTag.PUMPCOMM, "Items on filtered list: {}", filteredHistoryList.size) + } + + private fun groupForCommandType(type: EquilHistoryRecord.EventType): EquilHistoryEntryGroup { + return when (type) { + EquilHistoryRecord.EventType.INITIALIZE_EQUIL -> + EquilHistoryEntryGroup.Pair + + EquilHistoryRecord.EventType.INSERT_CANNULA -> + EquilHistoryEntryGroup.Pair + + EquilHistoryRecord.EventType.UNPAIR_EQUIL -> + EquilHistoryEntryGroup.Pair + // EquilHistoryRecord.EventType.DEACTIVATE_POD -> + // PumpHistoryEntryGroup.Prime + // + // EquilHistoryRecord.EventType.DISCARD_POD -> + // PumpHistoryEntryGroup.Prime + + EquilHistoryRecord.EventType.CANCEL_TEMPORARY_BASAL -> + EquilHistoryEntryGroup.Basal + + EquilHistoryRecord.EventType.CANCEL_EXTENDED_BOLUS -> + EquilHistoryEntryGroup.Basal + + EquilHistoryRecord.EventType.SET_EXTENDED_BOLUS -> + EquilHistoryEntryGroup.Basal + + EquilHistoryRecord.EventType.SET_BASAL_PROFILE -> + EquilHistoryEntryGroup.Basal + + EquilHistoryRecord.EventType.SET_TEMPORARY_BASAL -> + EquilHistoryEntryGroup.Basal + + EquilHistoryRecord.EventType.RESUME_DELIVERY -> + EquilHistoryEntryGroup.Basal + + EquilHistoryRecord.EventType.SUSPEND_DELIVERY -> + EquilHistoryEntryGroup.Basal + + EquilHistoryRecord.EventType.SET_BOLUS -> + EquilHistoryEntryGroup.Bolus + + EquilHistoryRecord.EventType.CANCEL_BOLUS -> + EquilHistoryEntryGroup.Bolus + + EquilHistoryRecord.EventType.SET_TIME -> + EquilHistoryEntryGroup.Configuration + + EquilHistoryRecord.EventType.SET_ALARM_MUTE -> + EquilHistoryEntryGroup.Configuration + + EquilHistoryRecord.EventType.SET_ALARM_SHAKE -> + EquilHistoryEntryGroup.Configuration + + EquilHistoryRecord.EventType.SET_ALARM_TONE -> + EquilHistoryEntryGroup.Configuration + + EquilHistoryRecord.EventType.SET_ALARM_TONE_AND_SHAK -> + EquilHistoryEntryGroup.Configuration + + // EquilHistoryRecord.EventType.READ_POD_PULSE_LOG -> + // PumpHistoryEntryGroup.Unknown + else -> { + EquilHistoryEntryGroup.All + + } + } + } + + private fun loadData() { + calendar.apply { + set(Calendar.HOUR_OF_DAY, 0) + set(Calendar.MINUTE, 0) + set(Calendar.SECOND, 0) + set(Calendar.MILLISECOND, 0) + add(Calendar.DAY_OF_MONTH, -5) + } + val startTime = calendar.timeInMillis + + aapsLogger.debug(LTag.PUMPCOMM, "loadData===" + dateUtil.dateAndTimeAndSecondsString(startTime) + "====") + disposable += equilHistoryRecordDao + .allSince(startTime, dateUtil.now()) + .subscribeOn(aapsSchedulers.io) + .observeOn(aapsSchedulers.main) + .subscribe({ historyList -> + aapsLogger.debug(LTag.PUMPCOMM, "historyList===" + historyList.size) + fullHistoryList.clear() + fullHistoryList.addAll(historyList) + }, fabricPrivacy::logException) + } + + private fun loadDataEquil() { + calendar.apply { + set(Calendar.HOUR_OF_DAY, 0) + set(Calendar.MINUTE, 0) + set(Calendar.SECOND, 0) + set(Calendar.MILLISECOND, 0) + add(Calendar.DAY_OF_MONTH, -5) + } + val startTime = calendar.timeInMillis + val endTime = dateUtil.now() + aapsLogger.debug(LTag.PUMPCOMM, "loadData===" + dateUtil.dateAndTimeAndSecondsString(startTime) + "====" + dateUtil.dateAndTimeAndSecondsString(endTime)) + disposable += equilHistoryPumpDao + .allFromByType(startTime, endTime, equilPumpPlugin.serialNumber()) + .subscribeOn(aapsSchedulers.io) + .observeOn(aapsSchedulers.main) + .subscribe({ historyList -> + aapsLogger.debug(LTag.PUMPCOMM, "loadDataEquil===" + historyList.size) + binding.recyclerviewEquil.swapAdapter(RecyclerViewAdapterEquil(toModels(historyList), rh), false) + }, fabricPrivacy::logException) + } + + class RecyclerViewAdapter internal constructor( + var historyList: List, + private val rh: ResourceHelper + ) : RecyclerView.Adapter() { + + override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): HistoryViewHolder { + val v = LayoutInflater.from(viewGroup.context).inflate( + R.layout.equil_item_record, // + viewGroup, false + ) + return HistoryViewHolder(v) + } + + override fun onBindViewHolder(holder: HistoryViewHolder, position: Int) { + val item = historyList[position] + holder.timeView.text = android.text.format.DateFormat.format( + "yyyy-MM-dd HH:mm:ss", + item.timestamp + ).toString() + // holder.timeView.text = item.type.name + // holder.typeView.text = item.serialNumber + holder.typeView.text = item.tempBasalRecord?.rate.toString() + holder.typeView.text = rh.gs(item.type!!.resourceId) + + if (!item.isSuccess()) { + holder.valueView.text = rh.gs(translatedFailure(item)) + return + } + holder.valueView.text = when (item.type) { + EquilHistoryRecord.EventType.SET_TEMPORARY_BASAL -> { + val tbr = item.tempBasalRecord + val duration = (tbr?.duration?.div(60 * 1000) ?: 0) + + tbr.let { + rh.gs(R.string.equil_common_history_tbr_value, it?.rate, duration) + } + } + + EquilHistoryRecord.EventType.SET_EXTENDED_BOLUS -> { + val tbr = item.tempBasalRecord + val duration = (tbr?.duration?.div(60 * 1000) ?: 0) + val rate = (tbr!!.rate * (60 / duration)) + tbr.let { + rh.gs(R.string.equil_common_history_tbr_value, rate, duration) + } + } + + EquilHistoryRecord.EventType.SET_BOLUS -> item.bolusRecord.let { rh.gs(R.string.equil_common_history_bolus_value, it?.amount) } + + EquilHistoryRecord.EventType.INSERT_CANNULA -> rh.gs(R.string.history_manual_confirm) + + EquilHistoryRecord.EventType.EQUIL_ALARM -> item.note + + EquilHistoryRecord.EventType.SET_BASAL_PROFILE -> ProfileUtil.getBasalProfilesDisplayable(item.basalValuesRecord!!.segments.toTypedArray(), PumpType.EQUIL) + + else -> rh.gs(R.string.equil_success) + } + } + + private fun translatedFailure(historyEntry: EquilHistoryRecord): Int = + when (historyEntry.resolvedStatus) { + ResolvedResult.NOT_FOUNT -> R.string.equil_command_not_found + ResolvedResult.CONNECT_ERROR -> R.string.equil_command_connect_error + ResolvedResult.FAILURE -> R.string.equil_command_connect_no_response + ResolvedResult.SUCCESS -> R.string.equil_success + ResolvedResult.NONE -> R.string.equil_none + else -> R.string.equil_command__unknown + } + + override fun getItemCount(): Int = historyList.size + + class HistoryViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + + var timeView: TextView + var typeView: TextView + var valueView: TextView + + // var valueView: TextView + init { + timeView = itemView.findViewById(R.id.equil_history_time) + typeView = itemView.findViewById(R.id.equil_history_source) + valueView = itemView.findViewById(R.id.equil_history_description) + } + } + + } + + private fun toModels(list: List): List { + val arrayList = ArrayList() + var record: EquilHistoryPump? = null + var record2: EquilHistoryPump? = null + val dateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US) + val list2 = list.sortedWith(compareBy(EquilHistoryPump::eventTimestamp, EquilHistoryPump::eventIndex)) + val iterator = list2.listIterator() + var pre: EquilHistoryPump? = null + + while (iterator.hasNext()) { + val next = iterator.next() + + // Process basal speed + if (record2 == null || record2.rate != next.rate) { + val format = dateFormat.format(next.eventTimestamp) + val valueOf = Utils.decodeSpeedToUH(next.rate).toString() + if (pre?.type == 10) { + // arrayList.add(ItemModel(format, valueOf, ItemModel.TYPE_BASAL, next.eventTimestamp)) + arrayList.add(ItemModel(format, valueOf, ItemModel.TYPE_BASAL_TEMP, next.eventTimestamp)) + } else { + arrayList.add(ItemModel(format, valueOf, ItemModel.TYPE_BASAL, next.eventTimestamp)) + + } + record2 = next + } + // Process bolus speed + if (record != null && next.largeRate != record.largeRate) { + val time = next.eventTimestamp + val time2 = record.eventTimestamp + + val format2 = dateFormat.format(time2) + val format3 = "%.3f".format( + (Math.abs(time - time2) / 1000.0) + * Utils.decodeSpeedToUS(record.largeRate) + ) + val t = (abs(time - time2) / 1000.0) + aapsLogger.debug(LTag.PUMPCOMM, "time===$t===$format3") + arrayList.add(ItemModel(format2, format3, ItemModel.TYPE_BOLUS, time2)) + record = null + } + pre = next + if (next.largeRate > 0) { + record = next + } + // Process event + val string = PumpEvent.getTips(next.port, next.type, next.level) + + if ("--" != string) { + val format4 = dateFormat.format(next.eventTimestamp) + arrayList.add(ItemModel(format4, string, ItemModel.TYPE_TEXT, next.eventTimestamp)) + } + } + + // Process remaining bolus speed + // record?.let { + // val decodeSpeedToUH = Utils.decodeSpeedToUH(it.largeRate) + // val format5 = format.format(it.eventTimestamp) + // arrayList.add(ItemModel(format5, "正在开始 $decodeSpeedToUH U/H", 4, it.eventTimestamp)) + // } + + return arrayList.reversed() + } + + data class ItemModel(val time: String, val text: String, val type: Int, var eventTime: Long) { + companion object { + + const val TYPE_BOLUS = 1 + const val TYPE_BASAL = 2 + const val TYPE_TEXT = 3 + const val TYPE_BOLUS_ING = 4 + const val TYPE_BASAL_TEMP = 5 + + } + } + + class RecyclerViewAdapterEquil internal constructor(private val historyList: List, private val rh: ResourceHelper) : + RecyclerView.Adapter() { + + override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): EquilHistoryViewHolder { + val v = LayoutInflater.from(viewGroup.context).inflate(R.layout.equil_item_record, viewGroup, false) + return EquilHistoryViewHolder(v) + } + + override fun onBindViewHolder(holder: EquilHistoryViewHolder, position: Int) { + val item = historyList[position] + Log.e(LTag.PUMPCOMM.tag, "onBindViewHolder $position") + holder.timeView.text = item.time + holder.typeView.text = item.text + val type = item.type + val textColor = when (type) { + ItemModel.TYPE_BOLUS -> rh.gc(R.color.equil_bolus) + ItemModel.TYPE_BASAL -> rh.gc(R.color.equil_basal) + ItemModel.TYPE_BASAL_TEMP -> rh.gc(R.color.equil_basal) + ItemModel.TYPE_BOLUS_ING -> rh.gc(R.color.equil_bolus_ing) + else -> rh.gc(R.color.equil_normal) + } + val text = when (type) { + ItemModel.TYPE_BOLUS -> rh.gs(R.string.equil_record_bolus, item.text) + ItemModel.TYPE_BASAL -> rh.gs(R.string.equil_record_basal, item.text) + ItemModel.TYPE_BASAL_TEMP -> rh.gs(R.string.equil_record_basal_temp, item.text) + else -> item.text + } + holder.timeView.text = item.time + holder.typeView.text = text + holder.typeView.setTextColor(textColor) + } + + override fun getItemCount(): Int = historyList.size + + class EquilHistoryViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + + var timeView: TextView + var typeView: TextView + private var descriptionView: TextView + + init { + timeView = itemView.findViewById(R.id.equil_history_time) + typeView = itemView.findViewById(R.id.equil_history_source) + descriptionView = itemView.findViewById(R.id.equil_history_description) + descriptionView.visibility = View.GONE + + } + } + } +} diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/ui/EquilUnPairActivity.kt b/pump/equil/src/main/java/app/aaps/pump/equil/ui/EquilUnPairActivity.kt new file mode 100644 index 00000000000..8892be41b4f --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/ui/EquilUnPairActivity.kt @@ -0,0 +1,122 @@ +package app.aaps.pump.equil.ui + +import android.content.Context +import android.content.DialogInterface +import android.os.Bundle +import android.os.SystemClock +import android.view.View +import androidx.appcompat.app.AlertDialog +import androidx.fragment.app.DialogFragment +import app.aaps.core.interfaces.logging.AAPSLogger +import app.aaps.core.interfaces.plugin.ActivePlugin +import app.aaps.core.interfaces.profile.ProfileFunction +import app.aaps.core.interfaces.pump.BlePreCheck +import app.aaps.core.interfaces.queue.Callback +import app.aaps.core.interfaces.queue.CommandQueue +import app.aaps.core.interfaces.resources.ResourceHelper +import app.aaps.core.interfaces.rx.bus.RxBus +import app.aaps.core.interfaces.sharedPreferences.SP +import app.aaps.core.ui.activities.TranslatedDaggerAppCompatActivity +import app.aaps.pump.equil.EquilPumpPlugin +import app.aaps.pump.equil.R +import app.aaps.pump.equil.databinding.EquilUnpairActivityBinding +import app.aaps.pump.equil.events.EventEquilUnPairChanged +import app.aaps.pump.equil.manager.EquilManager +import app.aaps.pump.equil.manager.command.CmdUnPair +import app.aaps.pump.equil.ui.dlg.LoadingDlg +import javax.inject.Inject + +// IMPORTANT: This activity needs to be called from RileyLinkSelectPreference (see pref_medtronic.xml as example) +class EquilUnPairActivity : TranslatedDaggerAppCompatActivity() { + + @Inject lateinit var sp: SP + @Inject lateinit var blePreCheck: BlePreCheck + @Inject lateinit var activePlugin: ActivePlugin + @Inject lateinit var context: Context + @Inject lateinit var rh: ResourceHelper + @Inject lateinit var aapsLogger: AAPSLogger + @Inject lateinit var commandQueue: CommandQueue + @Inject lateinit var equilPumpPlugin: EquilPumpPlugin + @Inject lateinit var rxBus: RxBus + @Inject lateinit var equilManager: EquilManager + + private lateinit var binding: EquilUnpairActivityBinding + @Inject lateinit var profileFunction: ProfileFunction + var errorCount = 0 + public override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + binding = EquilUnpairActivityBinding.inflate(layoutInflater) + setContentView(binding.root) + title = rh.gs(R.string.equil_change) + supportActionBar?.setDisplayHomeAsUpEnabled(true) + supportActionBar?.setDisplayShowHomeEnabled(true) + val name = equilManager.serialNumber + binding.tvHint2.text = String.format(rh.gs(R.string.equil_unpair), name) + binding.btnNext.setOnClickListener { + showUnPairConfig() + } + binding.btnDelete.visibility = View.GONE + binding.btnDelete.setOnClickListener { + rxBus.send(EventEquilUnPairChanged()) + equilPumpPlugin.clearData() + SystemClock.sleep(50) + finish() + } + } + + private fun showUnPairConfig() { + val name = equilManager.serialNumber + val content = String.format(rh.gs(R.string.equil_unpair_alert), name) + val alertDialog = AlertDialog.Builder(this) + .setTitle(rh.gs(R.string.equil_title_tips)) + .setMessage(content) + .setPositiveButton(rh.gs(app.aaps.core.ui.R.string.ok)) { _: DialogInterface, _: Int -> + unpair(name) + } + .setNegativeButton(rh.gs(app.aaps.core.ui.R.string.cancel)) { dialog: DialogInterface, _: Int -> + dialog.dismiss() + } + .create() + alertDialog.show() + + } + + private fun unpair(name: String) { + showLoading() + commandQueue.customCommand(CmdUnPair(name, sp.getString(rh.gs(R.string.key_equil_pair_password), "")), object : Callback() { + override fun run() { + dismissLoading() + if (result.success) { + equilManager.serialNumber = "" + equilManager.address = "" + equilPumpPlugin.showToast(rh.gs(R.string.equil_unbind)) + rxBus.send(EventEquilUnPairChanged()) + equilPumpPlugin.clearData() + SystemClock.sleep(50) + finish() + } else { + errorCount += 1 + dismissLoading() + equilPumpPlugin.showToast(rh.gs(R.string.equil_error)) + if (errorCount > 5) { + runOnUiThread { + binding.btnDelete.visibility = View.VISIBLE + } + } + } + } + }) + } + + private fun showLoading() { + LoadingDlg().show(supportFragmentManager, "loading") + } + + private fun dismissLoading() { + val fragment = supportFragmentManager.findFragmentByTag("loading") + if (fragment is DialogFragment) { + fragment.dismiss() + } + } +} diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/ui/EquilUnPairDetachActivity.kt b/pump/equil/src/main/java/app/aaps/pump/equil/ui/EquilUnPairDetachActivity.kt new file mode 100644 index 00000000000..d171b02e33d --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/ui/EquilUnPairDetachActivity.kt @@ -0,0 +1,124 @@ +package app.aaps.pump.equil.ui + +import android.content.Context +import android.content.DialogInterface +import android.content.Intent +import android.os.Bundle +import android.os.SystemClock +import androidx.appcompat.app.AlertDialog +import androidx.fragment.app.DialogFragment +import app.aaps.core.interfaces.logging.AAPSLogger +import app.aaps.core.interfaces.plugin.ActivePlugin +import app.aaps.core.interfaces.profile.ProfileFunction +import app.aaps.core.interfaces.pump.BlePreCheck +import app.aaps.core.interfaces.queue.Callback +import app.aaps.core.interfaces.queue.CommandQueue +import app.aaps.core.interfaces.resources.ResourceHelper +import app.aaps.core.interfaces.rx.AapsSchedulers +import app.aaps.core.interfaces.rx.bus.RxBus +import app.aaps.core.interfaces.sharedPreferences.SP +import app.aaps.core.interfaces.utils.fabric.FabricPrivacy +import app.aaps.core.ui.activities.TranslatedDaggerAppCompatActivity +import app.aaps.pump.equil.EquilPumpPlugin +import app.aaps.pump.equil.R +import app.aaps.pump.equil.data.RunMode +import app.aaps.pump.equil.databinding.EquilUnpairDetachActivityBinding +import app.aaps.pump.equil.driver.definition.ActivationProgress +import app.aaps.pump.equil.events.EventEquilUnPairChanged +import app.aaps.pump.equil.manager.EquilManager +import app.aaps.pump.equil.manager.command.CmdInsulinChange +import app.aaps.pump.equil.ui.dlg.LoadingDlg +import com.bumptech.glide.Glide +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.kotlin.plusAssign +import javax.inject.Inject + +// IMPORTANT: This activity needs to be called from RileyLinkSelectPreference (see pref_medtronic.xml as example) +class EquilUnPairDetachActivity : TranslatedDaggerAppCompatActivity() { + + @Inject lateinit var sp: SP + @Inject lateinit var blePreCheck: BlePreCheck + @Inject lateinit var activePlugin: ActivePlugin + @Inject lateinit var context: Context + @Inject lateinit var rh: ResourceHelper + @Inject lateinit var aapsLogger: AAPSLogger + @Inject lateinit var commandQueue: CommandQueue + @Inject lateinit var equilPumpPlugin: EquilPumpPlugin + @Inject lateinit var rxBus: RxBus + @Inject lateinit var aapsSchedulers: AapsSchedulers + @Inject lateinit var fabricPrivacy: FabricPrivacy + @Inject lateinit var equilManager: EquilManager + + private val disposable = CompositeDisposable() + + private lateinit var binding: EquilUnpairDetachActivityBinding + @Inject lateinit var profileFunction: ProfileFunction + public override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + binding = EquilUnpairDetachActivityBinding.inflate(layoutInflater) + setContentView(binding.root) + title = rh.gs(R.string.equil_change) + supportActionBar?.setDisplayHomeAsUpEnabled(true) + supportActionBar?.setDisplayShowHomeEnabled(true) + + disposable += rxBus + .toObservable(EventEquilUnPairChanged::class.java) + .observeOn(aapsSchedulers.main) + .subscribe({ finish() }, fabricPrivacy::logException) + Glide.with(this) + .asGif() + .load(R.drawable.equil_animation_wizard_detach) + .into(binding.imv) + binding.btnNext.setOnClickListener { + // startActivity(Intent(context, EquilPairInsertActivity::class.java)) + showUnPairConfig() + } + } + + private fun showUnPairConfig() { + + val alertDialog = AlertDialog.Builder(this) + .setTitle(rh.gs(R.string.equil_title_tips)) + .setMessage(rh.gs(R.string.equil_hint_dressing)) + .setPositiveButton(rh.gs(app.aaps.core.ui.R.string.ok)) { _: DialogInterface, _: Int -> + changeInsulin() + } + .setNegativeButton(rh.gs(app.aaps.core.ui.R.string.cancel)) { dialog: DialogInterface, _: Int -> + dialog.dismiss() + } + .create() + alertDialog.show() + + } + + private fun changeInsulin() { + showLoading() + commandQueue.customCommand(CmdInsulinChange(), object : Callback() { + override fun run() { + if (result.success) { + equilManager.runMode = RunMode.STOP + equilPumpPlugin.resetData() + equilManager.activationProgress = ActivationProgress.CANNULA_CHANGE + dismissLoading() + startActivity(Intent(context, EquilUnPairActivity::class.java)) + } else { + equilPumpPlugin.showToast(rh.gs(R.string.equil_error)) + dismissLoading() + + } + } + }) + } + + private fun showLoading() { + LoadingDlg().show(supportFragmentManager, "loading") + } + + private fun dismissLoading() { + val fragment = supportFragmentManager.findFragmentByTag("loading") + if (fragment is DialogFragment) { + fragment.dismiss() + } + } +} diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/ui/dlg/EquilAutoDressingDlg.kt b/pump/equil/src/main/java/app/aaps/pump/equil/ui/dlg/EquilAutoDressingDlg.kt new file mode 100644 index 00000000000..2572d0d988c --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/ui/dlg/EquilAutoDressingDlg.kt @@ -0,0 +1,78 @@ +package app.aaps.pump.equil.ui.dlg + +import android.content.Context +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.FragmentManager +import app.aaps.core.interfaces.logging.AAPSLogger +import app.aaps.core.interfaces.protection.ProtectionCheck +import app.aaps.core.interfaces.rx.bus.RxBus +import app.aaps.pump.equil.databinding.EquilAutoDressingDialogBinding +import dagger.android.support.DaggerDialogFragment +import javax.inject.Inject + +class EquilAutoDressingDlg : DaggerDialogFragment() { + + @Inject lateinit var aapsLogger: AAPSLogger + @Inject lateinit var ctx: Context + @Inject lateinit var protectionCheck: ProtectionCheck + @Inject lateinit var rxBus: RxBus + + private var _binding: EquilAutoDressingDialogBinding? = null + + val binding get() = _binding!! + + override fun onStart() { + super.onStart() + dialog?.window?.setLayout( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ) + } + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + isCancelable = true + dialog?.setCanceledOnTouchOutside(false) + + _binding = EquilAutoDressingDialogBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + binding.btnCancel.setOnClickListener { + binding.btnCancel.let { onDialogResultListener?.invoke() } + + dismiss() + } + } + + @Synchronized + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } + + override fun show(manager: FragmentManager, tag: String?) { + try { + manager.beginTransaction().let { + it.add(this, tag) + it.commitAllowingStateLoss() + } + } catch (e: IllegalStateException) { + aapsLogger.debug(e.localizedMessage ?: e.toString()) + } + } + + fun setDialogResultListener(listener: () -> Unit) { + onDialogResultListener = listener + + } + + private var onDialogResultListener: (() -> Unit)? = null + +} diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/ui/dlg/EquilChangeInsulinDlg.kt b/pump/equil/src/main/java/app/aaps/pump/equil/ui/dlg/EquilChangeInsulinDlg.kt new file mode 100644 index 00000000000..28356ff1005 --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/ui/dlg/EquilChangeInsulinDlg.kt @@ -0,0 +1,74 @@ +package app.aaps.pump.equil.ui.dlg + +import android.content.Context +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.FragmentManager +import app.aaps.core.interfaces.logging.AAPSLogger +import app.aaps.core.interfaces.protection.ProtectionCheck +import app.aaps.core.interfaces.rx.bus.RxBus +import app.aaps.pump.equil.R +import app.aaps.pump.equil.databinding.EquilDialogAlertDressingBinding +import com.bumptech.glide.Glide +import dagger.android.support.DaggerDialogFragment +import javax.inject.Inject + +class EquilChangeInsulinDlg : DaggerDialogFragment() { + + @Inject lateinit var aapsLogger: AAPSLogger + @Inject lateinit var ctx: Context + @Inject lateinit var protectionCheck: ProtectionCheck + @Inject lateinit var rxBus: RxBus + + private var _binding: EquilDialogAlertDressingBinding? = null + + val binding get() = _binding!! + override fun onStart() { + super.onStart() + dialog?.window?.setLayout( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ) + } + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + isCancelable = true + dialog?.setCanceledOnTouchOutside(false) + + _binding = EquilDialogAlertDressingBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + binding.btnCancel.setOnClickListener { dismiss() } + Glide.with(view) + .asGif() + .load(R.drawable.equil_animation_wizard_detach) + .into(binding.imv) + binding.btnOk.setOnClickListener { + dismiss() + } + } + + @Synchronized + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } + + override fun show(manager: FragmentManager, tag: String?) { + try { + manager.beginTransaction().let { + it.add(this, tag) + it.commitAllowingStateLoss() + } + } catch (e: IllegalStateException) { + aapsLogger.debug(e.localizedMessage ?: e.toString()) + } + } +} diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/ui/dlg/EquilPairConfigDlg.kt b/pump/equil/src/main/java/app/aaps/pump/equil/ui/dlg/EquilPairConfigDlg.kt new file mode 100644 index 00000000000..bc2f35e9c78 --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/ui/dlg/EquilPairConfigDlg.kt @@ -0,0 +1,71 @@ +package app.aaps.pump.equil.ui.dlg + +import android.content.Context +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.FragmentManager +import app.aaps.core.interfaces.logging.AAPSLogger +import app.aaps.core.interfaces.protection.ProtectionCheck +import app.aaps.core.interfaces.rx.bus.RxBus +import app.aaps.pump.equil.databinding.EquilPairConfigDialogBinding +import dagger.android.support.DaggerDialogFragment +import javax.inject.Inject + +class EquilPairConfigDlg : DaggerDialogFragment() { + + @Inject lateinit var aapsLogger: AAPSLogger + @Inject lateinit var ctx: Context + @Inject lateinit var protectionCheck: ProtectionCheck + @Inject lateinit var rxBus: RxBus + + private var _binding: EquilPairConfigDialogBinding? = null + + val binding get() = _binding!! + + override fun onStart() { + super.onStart() + dialog?.window?.setLayout( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ) + } + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + isCancelable = true + dialog?.setCanceledOnTouchOutside(false) + + _binding = EquilPairConfigDialogBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + binding.btnSkip.setOnClickListener { + dismiss() + } + binding.btnNeed.setOnClickListener { + dismiss() + } + } + + @Synchronized + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } + + override fun show(manager: FragmentManager, tag: String?) { + try { + manager.beginTransaction().let { + it.add(this, tag) + it.commitAllowingStateLoss() + } + } catch (e: IllegalStateException) { + aapsLogger.debug(e.localizedMessage ?: e.toString()) + } + } +} diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/ui/dlg/EquilUnPairDlg.kt b/pump/equil/src/main/java/app/aaps/pump/equil/ui/dlg/EquilUnPairDlg.kt new file mode 100644 index 00000000000..5aa31241d87 --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/ui/dlg/EquilUnPairDlg.kt @@ -0,0 +1,69 @@ +package app.aaps.pump.equil.ui.dlg + +import android.content.Context +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.FragmentManager +import app.aaps.core.interfaces.logging.AAPSLogger +import app.aaps.core.interfaces.protection.ProtectionCheck +import app.aaps.core.interfaces.rx.bus.RxBus +import app.aaps.pump.equil.databinding.EquilDialogAlertDressingBinding +import dagger.android.support.DaggerDialogFragment +import javax.inject.Inject + +class EquilUnPairDlg : DaggerDialogFragment() { + + @Inject lateinit var aapsLogger: AAPSLogger + @Inject lateinit var ctx: Context + @Inject lateinit var protectionCheck: ProtectionCheck + @Inject lateinit var rxBus: RxBus + + private var _binding: EquilDialogAlertDressingBinding? = null + + val binding get() = _binding!! + + override fun onStart() { + super.onStart() + dialog?.window?.setLayout( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ) + } + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + isCancelable = true + dialog?.setCanceledOnTouchOutside(false) + + _binding = EquilDialogAlertDressingBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + binding.btnCancel.setOnClickListener { dismiss() } + binding.btnOk.setOnClickListener { + dismiss() + } + } + + @Synchronized + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } + + override fun show(manager: FragmentManager, tag: String?) { + try { + manager.beginTransaction().let { + it.add(this, tag) + it.commitAllowingStateLoss() + } + } catch (e: IllegalStateException) { + aapsLogger.debug(e.localizedMessage ?: e.toString()) + } + } +} diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/ui/dlg/LoadingDlg.kt b/pump/equil/src/main/java/app/aaps/pump/equil/ui/dlg/LoadingDlg.kt new file mode 100644 index 00000000000..b61eaaa2026 --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/ui/dlg/LoadingDlg.kt @@ -0,0 +1,66 @@ +package app.aaps.pump.equil.ui.dlg + +import android.content.Context +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.FragmentManager +import app.aaps.core.interfaces.logging.AAPSLogger +import app.aaps.core.interfaces.protection.ProtectionCheck +import app.aaps.core.interfaces.rx.bus.RxBus +import app.aaps.pump.equil.databinding.LoadingDialogBinding +import dagger.android.support.DaggerDialogFragment +import javax.inject.Inject + +class LoadingDlg : DaggerDialogFragment() { + + @Inject lateinit var aapsLogger: AAPSLogger + @Inject lateinit var ctx: Context + @Inject lateinit var protectionCheck: ProtectionCheck + @Inject lateinit var rxBus: RxBus + + private var _binding: LoadingDialogBinding? = null + + val binding get() = _binding!! + + override fun onStart() { + super.onStart() + dialog?.window?.setLayout( + ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ) + } + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + isCancelable = true + dialog?.setCanceledOnTouchOutside(false) + + _binding = LoadingDialogBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + } + + @Synchronized + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } + + override fun show(manager: FragmentManager, tag: String?) { + try { + manager.beginTransaction().let { + it.add(this, tag) + it.commitAllowingStateLoss() + } + } catch (e: IllegalStateException) { + aapsLogger.debug(e.localizedMessage ?: e.toString()) + } + } + +} diff --git a/pump/equil/src/main/java/app/aaps/pump/equil/ui/pair/EquilChangeInsulinFragment.kt b/pump/equil/src/main/java/app/aaps/pump/equil/ui/pair/EquilChangeInsulinFragment.kt new file mode 100644 index 00000000000..55721bd89e8 --- /dev/null +++ b/pump/equil/src/main/java/app/aaps/pump/equil/ui/pair/EquilChangeInsulinFragment.kt @@ -0,0 +1,60 @@ +package app.aaps.pump.equil.ui.pair + +import android.os.Bundle +import android.view.View +import android.widget.Button +import androidx.navigation.fragment.findNavController +import app.aaps.core.interfaces.extensions.runOnUiThread +import app.aaps.core.interfaces.queue.Callback +import app.aaps.pump.equil.R +import app.aaps.pump.equil.data.RunMode +import app.aaps.pump.equil.manager.command.CmdInsulinChange +import com.bumptech.glide.Glide + +// IMPORTANT: This activity needs to be called from RileyLinkSelectPreference (see pref_medtronic.xml as example) +class EquilChangeInsulinFragment : EquilPairFragmentBase() { + + override fun getLayoutId(): Int { + return R.layout.equil_pair_change_insulin_fragment + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + Glide.with(view) + .asGif() + .load(R.drawable.equil_animation_wizard_detach) + .into(view.findViewById(R.id.imv)) + + view.findViewById