diff --git a/.travis.yml b/.travis.yml index 5c6309f817..165a1eff9a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,24 +1,25 @@ language: android jdk: oraclejdk8 +dist: trusty env: matrix: - - ANDROID_TARGET=android-23 ANDROID_ABI=x86 org.gradle.jvmargs=-XX:-OmitStackTraceInFastThrow + - ANDROID_TARGET=android-28 ANDROID_ABI=x86 org.gradle.jvmargs=-XX:-OmitStackTraceInFastThrow android: components: - platform-tools - tools - - build-tools-27.0.2 - - android-23 + - build-tools-28.0.3 + - android-28 - extra-google-m2repository - extra-android-m2repository - extra-google-google_play_services before_install: -- yes | sdkmanager "platforms;android-27" +#- yes | sdkmanager "platforms;android-28" script: # Unit Test - - ./gradlew -Pcoverage testFullDebugUnitTest jacocoTestFullDebugUnitTestReport + - ./gradlew -Pcoverage -PfirebaseDisable testFullDebugUnitTest jacocoTestFullDebugUnitTestReport after_success: - bash <(curl -s https://codecov.io/bash) @@ -31,4 +32,4 @@ cache: directories: - $HOME/.gradle/caches/ - $HOME/.gradle/wrapper/ - - $HOME/.android/build-cache \ No newline at end of file + - $HOME/.android/build-cache diff --git a/README.md b/README.md index d1e1e8a47c..32aef48cd9 100644 --- a/README.md +++ b/README.md @@ -11,4 +11,4 @@ dev: [![codecov](https://codecov.io/gh/MilosKozak/AndroidAPS/branch/dev/graph/badge.svg)](https://codecov.io/gh/MilosKozak/AndroidAPS) -[![Donate via PayPal](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=Y4LHGJJESAVB8) +[![Donate via PayPal](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=Y4LHGJJESAVB8) \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 8c153a9e81..86709bfdd2 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -6,23 +6,28 @@ buildscript { dependencies { classpath 'io.fabric.tools:gradle:1.+' - classpath 'com.dicedmelon.gradle:jacoco-android:0.1.3' + classpath 'com.dicedmelon.gradle:jacoco-android:0.1.4' + classpath 'de.undercouch:gradle-download-task:3.4.3' } } -apply plugin: "com.android.application" -apply plugin: 'kotlin-android-extensions' +apply plugin: 'com.android.application' apply plugin: 'kotlin-android' +apply plugin: 'kotlin-android-extensions' apply plugin: 'com.google.gms.google-services' -apply plugin: "io.fabric" -apply plugin: "jacoco-android" -apply plugin: 'com.jakewharton.butterknife' +apply plugin: 'io.fabric' +apply plugin: 'jacoco-android' +apply plugin: 'de.undercouch.download' + + +jacoco { + toolVersion = "0.8.3" +} ext { - supportLibraryVersion = "27.1.1" + supportLibraryVersion = "28.0.0" ormLiteVersion = "4.46" powermockVersion = "1.7.3" dexmakerVersion = "1.2" - butterknifeVersion = "8.8.1" } @@ -33,7 +38,7 @@ repositories { } def generateGitBuild = { -> - StringBuilder stringBuilder = new StringBuilder(); + StringBuilder stringBuilder = new StringBuilder() try { def stdout = new ByteArrayOutputStream() exec { @@ -49,7 +54,7 @@ def generateGitBuild = { -> } def generateGitRemote = { -> - StringBuilder stringBuilder = new StringBuilder(); + StringBuilder stringBuilder = new StringBuilder() try { def stdout = new ByteArrayOutputStream() exec { @@ -65,7 +70,7 @@ def generateGitRemote = { -> } def generateDate = { -> - StringBuilder stringBuilder = new StringBuilder(); + StringBuilder stringBuilder = new StringBuilder() stringBuilder.append((new Date()).format('yyyy.MM.dd-HH:mm')) return stringBuilder.toString() } @@ -75,7 +80,7 @@ def isMaster = { -> } def allCommited = { -> - StringBuilder stringBuilder = new StringBuilder(); + StringBuilder stringBuilder = new StringBuilder() try { def stdout = new ByteArrayOutputStream() exec { @@ -85,7 +90,7 @@ def allCommited = { -> String commitObject = stdout.toString().trim() stringBuilder.append(commitObject) } catch (ignored) { - return false; // NoGitSystemAvailable + return false // NoGitSystemAvailable } return stringBuilder.toString().isEmpty() @@ -97,19 +102,19 @@ tasks.matching { it instanceof Test }.all { } android { - compileSdkVersion 27 + compileSdkVersion 28 defaultConfig { - minSdkVersion 21 - targetSdkVersion 25 + minSdkVersion 23 + targetSdkVersion 28 multiDexEnabled true versionCode 1500 - version "2.3" + version "2.5.0" buildConfigField "String", "VERSION", '"' + version + '"' buildConfigField "String", "BUILDVERSION", '"' + generateGitBuild() + '-' + generateDate() + '"' buildConfigField "String", "REMOTE", '"' + generateGitRemote() + '"' buildConfigField "String", "HEAD", '"' + generateGitBuild() + '"' - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" // if you change minSdkVersion to less than 11, you need to change executeTask for wear ndk { @@ -138,6 +143,9 @@ android { debug { testCoverageEnabled(project.hasProperty('coverage')) } + firebaseDisable { + System.setProperty("disableFirebase", "true") + } } productFlavors { flavorDimensions "standard" @@ -188,8 +196,15 @@ android { } testOptions { - unitTests.returnDefaultValues = true - unitTests.includeAndroidResources = true + unitTests { + returnDefaultValues = true + includeAndroidResources = true + + all { + maxParallelForks = 10 + forkEvery = 20 + } + } } useLibrary "org.apache.http.legacy" @@ -201,34 +216,32 @@ allprojects { flatDir { dirs 'libs' } + maven { url 'https://jitpack.io' } } } -configurations { - libs -} - dependencies { wearApp project(':wear') implementation fileTree(include: ['*.jar'], dir: 'libs') - implementation 'com.google.android.gms:play-services-wearable:16.0.1' - implementation 'com.google.firebase:firebase-core:16.0.8' + implementation 'com.google.android.gms:play-services-wearable:17.0.0' + implementation 'com.google.firebase:firebase-core:17.2.0' implementation("com.crashlytics.sdk.android:crashlytics:2.9.9@aar") { transitive = true; } - libs "MilosKozak:danars-support-lib:master@zip" - implementation "com.android.support:appcompat-v7:${supportLibraryVersion}" - implementation "com.android.support:support-v13:${supportLibraryVersion}" - implementation "com.android.support:support-v4:${supportLibraryVersion}" - implementation "com.android.support:cardview-v7:${supportLibraryVersion}" - implementation "com.android.support:recyclerview-v7:${supportLibraryVersion}" - implementation "com.android.support:gridlayout-v7:${supportLibraryVersion}" - implementation "com.android.support:design:${supportLibraryVersion}" - implementation "com.android.support:percent:${supportLibraryVersion}" + implementation 'androidx.appcompat:appcompat:1.1.0' + implementation 'androidx.legacy:legacy-support-v13:1.0.0' + implementation 'androidx.legacy:legacy-support-v4:1.0.0' + implementation 'androidx.cardview:cardview:1.0.0' + implementation 'androidx.recyclerview:recyclerview:1.0.0' + implementation 'androidx.gridlayout:gridlayout:1.0.0' + implementation 'com.google.android.material:material:1.0.0' + implementation 'androidx.percentlayout:percentlayout:1.0.0' implementation "com.wdullaer:materialdatetimepicker:2.3.0" - implementation "com.squareup:otto:1.3.7" + + implementation "io.reactivex.rxjava2:rxandroid:2.1.1" + implementation "com.j256.ormlite:ormlite-core:${ormLiteVersion}" implementation "com.j256.ormlite:ormlite-android:${ormLiteVersion}" implementation("com.github.tony19:logback-android-classic:1.1.1-6") { @@ -239,7 +252,7 @@ dependencies { // Graphview cannot be upgraded implementation "com.jjoe64:graphview:4.0.1" implementation "com.joanzapata.iconify:android-iconify-fontawesome:2.1.1" - implementation 'com.android.support.constraint:constraint-layout:1.1.3' + implementation 'androidx.constraintlayout:constraintlayout:1.1.3' implementation(name: "android-edittext-validator-v1.3.4-mod", ext: "aar") implementation 'com.madgag.spongycastle:core:1.58.0.0' @@ -250,7 +263,7 @@ dependencies { // excluding org.json which is provided by Android exclude group: "org.json", module: "json" } - implementation "com.google.code.gson:gson:2.8.2" + implementation "com.google.code.gson:gson:2.8.5" implementation "com.google.guava:guava:24.1-jre" implementation "net.danlew:android.joda:2.9.9.1" @@ -258,8 +271,7 @@ dependencies { implementation 'org.mozilla:rhino:1.7.7.2' - implementation "com.jakewharton:butterknife:${butterknifeVersion}" - annotationProcessor "com.jakewharton:butterknife-compiler:${butterknifeVersion}" + implementation 'com.github.DavidProdinger:weekdays-selector:1.1.0' testImplementation "junit:junit:4.12" testImplementation "org.json:json:20140107" @@ -269,26 +281,47 @@ dependencies { testImplementation "org.powermock:powermock-module-junit4-rule:${powermockVersion}" testImplementation "org.powermock:powermock-module-junit4:${powermockVersion}" testImplementation "joda-time:joda-time:2.9.9" - testImplementation "com.google.truth:truth:0.39" - testImplementation 'org.robolectric:robolectric:3.8' + testImplementation("com.google.truth:truth:0.39") { + exclude group: "com.google.guava", module: "guava" + } testImplementation "org.skyscreamer:jsonassert:1.5.0" + testImplementation "org.hamcrest:hamcrest-all:1.3" +/* + testImplementation("uk.org.lidalia:slf4j-test:1.2.0") { + exclude group: "com.google.guava", module: "guava" + } +*/ androidTestImplementation "org.mockito:mockito-core:2.8.47" androidTestImplementation "com.google.dexmaker:dexmaker:${dexmakerVersion}" androidTestImplementation "com.google.dexmaker:dexmaker-mockito:${dexmakerVersion}" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" + + + // new for tidepool + implementation 'com.squareup.okhttp3:okhttp:3.10.0' + implementation 'com.squareup.okhttp3:logging-interceptor:3.10.0' + implementation "com.squareup.retrofit2:retrofit:2.4.0" + implementation "com.squareup.retrofit2:adapter-rxjava2:2.4.0" + implementation "com.squareup.retrofit2:converter-gson:2.4.0" + } -task unzip(type: Copy) { - def zipPath = configurations.libs.find { it.name.startsWith("danars") } - def zipFile = file(zipPath) - def outputDir = file("${buildDir}/unpacked/dist") - from zipTree(zipFile) +task downloadZipFile(type: Download) { + src 'https://github.com/MilosKozak/danars-support-lib/archive/master.zip' + dest new File(buildDir, 'danars.zip') +} + +task downloadAndUnzipFile(dependsOn: downloadZipFile, type: Copy) { + from zipTree(downloadZipFile.dest) + def outputDir = file("${buildDir}/unpacked/dist") into outputDir } -task copyLibs(dependsOn: unzip, type: Copy) { + +task copyLibs(dependsOn: downloadAndUnzipFile, type: Copy) { def src = file("${buildDir}/unpacked/dist/danars-support-lib-master") def target = file("src/main/jniLibs/") diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 3eaa81ba45..aeeb53475d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -17,10 +17,12 @@ + + - + @@ -34,13 +36,15 @@ android:label="@string/app_name" android:roundIcon="${appIconRound}" android:supportsRtl="true" - android:theme="@style/AppTheme.NoActionBar"> + android:theme="@style/AppTheme.Launcher" + android:fullBackupContent="true"> + @@ -85,10 +89,8 @@ - - - - + + @@ -121,15 +123,7 @@ - - - - - - @@ -147,7 +141,7 @@ @@ -160,6 +154,9 @@ + + + + + + + + + + + + + + + + diff --git a/app/src/main/assets/OpenAPSAMA/loggerhelper.js b/app/src/main/assets/OpenAPSAMA/loggerhelper.js index e790f465c8..81c19b5a38 100644 --- a/app/src/main/assets/OpenAPSAMA/loggerhelper.js +++ b/app/src/main/assets/OpenAPSAMA/loggerhelper.js @@ -1,12 +1,33 @@ var console = { }; console.error = function error(){ + var s = ''; for (var i = 0, len = arguments.length; i < len; i++) { - console2.log(arguments[i]); + if (i > 0) s = s + ' '; + if (typeof arguments[i] === 'undefined') { + s = s + 'undefined'; + } else if (typeof arguments[i] === 'object') { + s = s + JSON.stringify(arguments[i]); + } else { + s = s + arguments[i].toString(); + } } + s = s + "\n"; + console2.log(s); }; console.log = function log(){ + var s = ''; for (var i = 0, len = arguments.length; i < len; i++) { - console2.log(arguments[i]); + if (i > 0) s = s + ' '; + if (typeof arguments[i] === 'undefined') { + s = s + 'undefined'; + } else if (typeof arguments[i] === 'object') { + s = s + JSON.stringify(arguments[i]); + } else { + s = s + arguments[i].toString(); + } + //console2.log(arguments[i]); } + s = s + "\n"; + console2.log(s); }; diff --git a/app/src/main/assets/revoked_certs.txt b/app/src/main/assets/revoked_certs.txt new file mode 100644 index 0000000000..59a55d9e42 --- /dev/null +++ b/app/src/main/assets/revoked_certs.txt @@ -0,0 +1,2 @@ +#Demo certificate +51:6D:12:67:4C:27:F4:9B:9F:E5:42:9B:01:B3:98:E4:66:2B:85:B7:A8:DD:70:32:B7:6A:D7:97:9A:0D:97:10 \ No newline at end of file diff --git a/app/src/main/java/com/squareup/otto/LoggingBus.java b/app/src/main/java/com/squareup/otto/LoggingBus.java deleted file mode 100644 index d9758a9a24..0000000000 --- a/app/src/main/java/com/squareup/otto/LoggingBus.java +++ /dev/null @@ -1,96 +0,0 @@ -package com.squareup.otto; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.util.ConcurrentModificationException; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -import info.nightscout.androidaps.events.Event; -import info.nightscout.androidaps.logging.L; - -/** - * Logs events has they're being posted to and dispatched from the event bus. - *

- * A summary of event-receiver calls that occurred so far is logged - * after 10s (after startup) and then again every 60s. - */ -public class LoggingBus extends Bus { - private static Logger log = LoggerFactory.getLogger(L.EVENTS); - - private static long everyMinute = System.currentTimeMillis() + 10 * 1000; - private Map> event2Receiver = new HashMap<>(); - - public LoggingBus(ThreadEnforcer enforcer) { - super(enforcer); - } - - @Override - public void post(Object event) { - if (event instanceof DeadEvent) { - log.debug("Event has no receiver: " + ((DeadEvent) event).event + ", source: " + ((DeadEvent) event).source); - return; - } - - if (!(event instanceof Event)) { - log.error("Posted event not an event class: " + event.getClass()); - } - - log.debug("<<< " + event); - try { - StackTraceElement caller = new Throwable().getStackTrace()[1]; - String className = caller.getClassName(); - className = className.substring(className.lastIndexOf(".") + 1); - log.debug(" source: " + className + "." + caller.getMethodName() + ":" + caller.getLineNumber()); - } catch (RuntimeException e) { - log.debug(" source: "); - } - - try { - super.post(event); - } catch (IllegalStateException ignored) { - } - } - - @Override - protected void dispatch(Object event, EventHandler wrapper) { - try { - log.debug(">>> " + event); - Field methodField = wrapper.getClass().getDeclaredField("method"); - methodField.setAccessible(true); - Method targetMethod = (Method) methodField.get(wrapper); - String className = targetMethod.getDeclaringClass().getSimpleName(); - String methodName = targetMethod.getName(); - String receiverMethod = className + "." + methodName; - log.debug(" receiver: " + receiverMethod); - - String key = event.getClass().getSimpleName(); - if (!event2Receiver.containsKey(key)) event2Receiver.put(key, new HashSet()); - event2Receiver.get(key).add(receiverMethod); - } catch (ReflectiveOperationException e) { - log.debug(" receiver: "); - } - - try { - if (everyMinute < System.currentTimeMillis()) { - log.debug("***************** Event -> receiver pairings seen so far ****************"); - for (Map.Entry> stringSetEntry : event2Receiver.entrySet()) { - log.debug(" " + stringSetEntry.getKey()); - for (String s : stringSetEntry.getValue()) { - log.debug(" -> " + s); - } - } - log.debug("*************************************************************************"); - everyMinute = System.currentTimeMillis() + 60 * 1000; - } - } catch (ConcurrentModificationException ignored) { - } - - super.dispatch(event, wrapper); - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/Config.java b/app/src/main/java/info/nightscout/androidaps/Config.java index ae0e14c9b6..bb967cdeb6 100644 --- a/app/src/main/java/info/nightscout/androidaps/Config.java +++ b/app/src/main/java/info/nightscout/androidaps/Config.java @@ -12,13 +12,4 @@ public class Config { public static final boolean PUMPCONTROL = BuildConfig.FLAVOR.equals("pumpcontrol"); public static final boolean PUMPDRIVERS = BuildConfig.FLAVOR.equals("full") || BuildConfig.FLAVOR.equals("pumpcontrol"); - - public static final boolean ACTION = !NSCLIENT; - public static final boolean MDI = !NSCLIENT; - public static final boolean OTHERPROFILES = !NSCLIENT; - public static final boolean SAFETY = !NSCLIENT; - - public static final boolean SMSCOMMUNICATORENABLED = !NSCLIENT; - - } diff --git a/app/src/main/java/info/nightscout/androidaps/Constants.java b/app/src/main/java/info/nightscout/androidaps/Constants.java index 2ecabfe965..21a89d8920 100644 --- a/app/src/main/java/info/nightscout/androidaps/Constants.java +++ b/app/src/main/java/info/nightscout/androidaps/Constants.java @@ -31,11 +31,13 @@ public class Constants { public static final long remoteBolusMinDistance = 15 * 60 * 1000L; // Circadian Percentage Profile - public static final int CPP_MIN_PERCENTAGE = 50; + public static final int CPP_MIN_PERCENTAGE = 30; public static final int CPP_MAX_PERCENTAGE = 200; public static final int CPP_MIN_TIMESHIFT = -6; public static final int CPP_MAX_TIMESHIFT = 23; + public static final double MAX_PROFILE_SWITCH_DURATION = 7 * 24 * 60; // [min] ~ 7 days + //DanaR public static final double dailyLimitWarning = 0.95d; @@ -50,6 +52,11 @@ public class Constants { public static final double defaultHypoTTmgdl = 120d; public static final double defaultHypoTTmmol = 6.5d; + public static final double MIN_TT_MGDL = 72d; + public static final double MAX_TT_MGDL = 180d; + public static final double MIN_TT_MMOL = 4d; + public static final double MAX_TT_MMOL = 10d; + //NSClientInternal public static final int MAX_LOG_LINES = 100; diff --git a/app/src/main/java/info/nightscout/androidaps/MainActivity.java b/app/src/main/java/info/nightscout/androidaps/MainActivity.java index 6779b0a467..64c2656086 100644 --- a/app/src/main/java/info/nightscout/androidaps/MainActivity.java +++ b/app/src/main/java/info/nightscout/androidaps/MainActivity.java @@ -6,17 +6,6 @@ import android.content.pm.PackageManager; import android.graphics.Rect; import android.os.Bundle; import android.os.PersistableBundle; -import android.os.PowerManager; -import android.support.annotation.Nullable; -import android.support.design.widget.NavigationView; -import android.support.design.widget.TabLayout; -import android.support.v4.app.ActivityCompat; -import android.support.v4.view.ViewPager; -import android.support.v4.widget.DrawerLayout; -import android.support.v7.app.ActionBarDrawerToggle; -import android.support.v7.app.AlertDialog; -import android.support.v7.app.AppCompatActivity; -import android.support.v7.widget.Toolbar; import android.text.SpannableString; import android.text.method.LinkMovementMethod; import android.text.util.Linkify; @@ -31,29 +20,40 @@ import android.widget.EditText; import android.widget.LinearLayout; import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.ActionBarDrawerToggle; +import androidx.appcompat.app.AlertDialog; +import androidx.appcompat.widget.Toolbar; +import androidx.core.app.ActivityCompat; +import androidx.drawerlayout.widget.DrawerLayout; +import androidx.viewpager.widget.ViewPager; + +import com.google.android.material.navigation.NavigationView; +import com.google.android.material.tabs.TabLayout; import com.joanzapata.iconify.Iconify; import com.joanzapata.iconify.fonts.FontAwesomeModule; -import com.squareup.otto.Subscribe; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import info.nightscout.androidaps.activities.AgreementActivity; import info.nightscout.androidaps.activities.HistoryBrowseActivity; +import info.nightscout.androidaps.activities.NoSplashAppCompatActivity; import info.nightscout.androidaps.activities.PreferencesActivity; import info.nightscout.androidaps.activities.SingleFragmentActivity; import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.events.EventAppExit; -import info.nightscout.androidaps.events.EventFeatureRunning; import info.nightscout.androidaps.events.EventPreferenceChange; -import info.nightscout.androidaps.events.EventRefreshGui; +import info.nightscout.androidaps.events.EventRebuildTabs; import info.nightscout.androidaps.interfaces.PluginBase; import info.nightscout.androidaps.interfaces.PluginType; import info.nightscout.androidaps.logging.L; import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; +import info.nightscout.androidaps.plugins.constraints.versionChecker.VersionCheckerUtilsKt; import info.nightscout.androidaps.plugins.general.nsclient.data.NSSettingsStatus; -import info.nightscout.androidaps.plugins.general.versionChecker.VersionCheckerUtilsKt; import info.nightscout.androidaps.setupwizard.SetupWizardActivity; import info.nightscout.androidaps.tabs.TabPageAdapter; import info.nightscout.androidaps.utils.AndroidPermission; @@ -62,25 +62,23 @@ import info.nightscout.androidaps.utils.LocaleHelper; import info.nightscout.androidaps.utils.OKDialog; import info.nightscout.androidaps.utils.PasswordProtection; import info.nightscout.androidaps.utils.SP; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.CompositeDisposable; -public class MainActivity extends AppCompatActivity { +public class MainActivity extends NoSplashAppCompatActivity { private static Logger log = LoggerFactory.getLogger(L.CORE); - - protected PowerManager.WakeLock mWakeLock; + private CompositeDisposable disposable = new CompositeDisposable(); private ActionBarDrawerToggle actionBarDrawerToggle; private MenuItem pluginPreferencesMenuItem; @Override - protected void onCreate(Bundle savedInstanceState) { + public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - if (L.isEnabled(L.CORE)) - log.debug("onCreate"); - Iconify.with(new FontAwesomeModule()); - LocaleHelper.onCreate(this, "en"); + LocaleHelper.INSTANCE.update(getApplicationContext()); setContentView(R.layout.activity_main); setSupportActionBar(findViewById(R.id.toolbar)); @@ -94,14 +92,10 @@ public class MainActivity extends AppCompatActivity { actionBarDrawerToggle.syncState(); // initialize screen wake lock - onEventPreferenceChange(new EventPreferenceChange(R.string.key_keep_screen_on)); + processPreferenceChange(new EventPreferenceChange(R.string.key_keep_screen_on)); doMigrations(); - registerBus(); - setupTabs(); - setupViews(false); - final ViewPager viewPager = findViewById(R.id.pager); viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override @@ -123,6 +117,43 @@ public class MainActivity extends AppCompatActivity { VersionCheckerUtilsKt.triggerCheckVersion(); FabricPrivacy.setUserStats(); + + setupTabs(); + setupViews(); + + disposable.add(RxBus.INSTANCE + .toObservable(EventRebuildTabs.class) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(event -> { + LocaleHelper.INSTANCE.update(getApplicationContext()); + if (event.getRecreate()) { + recreate(); + } else { + setupTabs(); + setupViews(); + } + setWakeLock(); + }, FabricPrivacy::logException) + ); + disposable.add(RxBus.INSTANCE + .toObservable(EventPreferenceChange.class) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(this::processPreferenceChange, FabricPrivacy::logException) + ); + + if (!SP.getBoolean(R.string.key_setupwizard_processed, false)) { + Intent intent = new Intent(this, SetupWizardActivity.class); + startActivity(intent); + } else { + checkEula(); + } + + AndroidPermission.notifyForStoragePermission(this); + AndroidPermission.notifyForBatteryOptimizationPermission(this); + if (Config.PUMPDRIVERS) { + AndroidPermission.notifyForLocationPermissions(this); + AndroidPermission.notifyForSMSPermissions(this); + } } private void checkPluginPreferences(ViewPager viewPager) { @@ -138,86 +169,29 @@ public class MainActivity extends AppCompatActivity { actionBarDrawerToggle.syncState(); } - @Override - protected void onResume() { - super.onResume(); - - if (L.isEnabled(L.CORE)) - log.debug("onResume"); - - if (!SP.getBoolean(R.string.key_setupwizard_processed, false)) { - Intent intent = new Intent(this, SetupWizardActivity.class); - startActivity(intent); - } else { - checkEula(); - } - - AndroidPermission.notifyForStoragePermission(this); - AndroidPermission.notifyForBatteryOptimizationPermission(this); - if (Config.PUMPDRIVERS) { - AndroidPermission.notifyForLocationPermissions(this); - AndroidPermission.notifyForSMSPermissions(this); - } - - MainApp.bus().post(new EventFeatureRunning(EventFeatureRunning.Feature.MAIN)); - } - @Override public void onDestroy() { - if (L.isEnabled(L.CORE)) - log.debug("onDestroy"); - if (mWakeLock != null) - if (mWakeLock.isHeld()) - mWakeLock.release(); super.onDestroy(); + disposable.clear(); } - @Subscribe - public void onEventPreferenceChange(final EventPreferenceChange ev) { - if (ev.isChanged(R.string.key_keep_screen_on)) { - boolean keepScreenOn = SP.getBoolean(R.string.key_keep_screen_on, false); - final PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); - if (keepScreenOn) { - mWakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "AndroidAPS:MainActivity_onEventPreferenceChange"); - if (!mWakeLock.isHeld()) - mWakeLock.acquire(); - } else { - if (mWakeLock != null && mWakeLock.isHeld()) - mWakeLock.release(); - } - } + private void setWakeLock() { + boolean keepScreenOn = SP.getBoolean(R.string.key_keep_screen_on, false); + if (keepScreenOn) + getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + else + getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); } - @Subscribe - public void onStatusEvent(final EventRefreshGui ev) { - String lang = SP.getString(R.string.key_language, "en"); - LocaleHelper.setLocale(getApplicationContext(), lang); - runOnUiThread(() -> { - if (ev.recreate) { - recreate(); - } else { - try { // activity may be destroyed - setupTabs(); - setupViews(true); - } catch (IllegalStateException e) { - log.error("Unhandled exception", e); - } - } - - boolean keepScreenOn = Config.NSCLIENT && SP.getBoolean(R.string.key_keep_screen_on, false); - if (keepScreenOn) - getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); - else - getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); - }); + public void processPreferenceChange(final EventPreferenceChange ev) { + if (ev.isChanged(R.string.key_keep_screen_on)) + setWakeLock(); } - private void setupViews(boolean switchToLast) { + private void setupViews() { TabPageAdapter pageAdapter = new TabPageAdapter(getSupportFragmentManager(), this); NavigationView navigationView = findViewById(R.id.navigation_view); - navigationView.setNavigationItemSelectedListener(menuItem -> { - return true; - }); + navigationView.setNavigationItemSelectedListener(menuItem -> true); Menu menu = navigationView.getMenu(); menu.clear(); for (PluginBase p : MainApp.getPluginsList()) { @@ -236,8 +210,8 @@ public class MainActivity extends AppCompatActivity { } ViewPager mPager = findViewById(R.id.pager); mPager.setAdapter(pageAdapter); - if (switchToLast) - mPager.setCurrentItem(pageAdapter.getCount() - 1, false); + //if (switchToLast) + // mPager.setCurrentItem(pageAdapter.getCount() - 1, false); checkPluginPreferences(mPager); } @@ -263,15 +237,6 @@ public class MainActivity extends AppCompatActivity { } } - private void registerBus() { - try { - MainApp.bus().unregister(this); - } catch (RuntimeException x) { - // Ignore - } - MainApp.bus().register(this); - } - private void checkEula() { //SP.removeBoolean(R.string.key_i_understand); boolean IUnderstand = SP.getBoolean(R.string.key_i_understand, false); @@ -288,10 +253,10 @@ public class MainActivity extends AppCompatActivity { // guarantee that the unreachable threshold is at least 30 and of type String // Added in 1.57 at 21.01.2018 - Integer unreachable_threshold = SP.getInt(R.string.key_pump_unreachable_threshold, 30); + int unreachable_threshold = SP.getInt(R.string.key_pump_unreachable_threshold, 30); SP.remove(R.string.key_pump_unreachable_threshold); if (unreachable_threshold < 30) unreachable_threshold = 30; - SP.putString(R.string.key_pump_unreachable_threshold, unreachable_threshold.toString()); + SP.putString(R.string.key_pump_unreachable_threshold, Integer.toString(unreachable_threshold)); } @@ -307,19 +272,16 @@ public class MainActivity extends AppCompatActivity { String message = "Target range is changed in current version.\n\nIt's not taken from preferences but from profile.\n\n!!! REVIEW YOUR SETTINGS !!!"; message += "\n\nOld settings: " + oldRange; message += "\nProfile settings: " + newRange; - OKDialog.show(this, "Target range change", message, new Runnable() { - @Override - public void run() { - SP.remove("openapsma_min_bg"); - SP.remove("openapsma_max_bg"); - SP.remove("openapsma_target_bg"); - } + OKDialog.show(this, "Target range change", message, () -> { + SP.remove("openapsma_min_bg"); + SP.remove("openapsma_max_bg"); + SP.remove("openapsma_target_bg"); }); } } @Override - public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (permissions.length != 0) { if (ActivityCompat.checkSelfPermission(this, permissions[0]) == PackageManager.PERMISSION_GRANTED) { @@ -334,7 +296,7 @@ public class MainActivity extends AppCompatActivity { case AndroidPermission.CASE_LOCATION: case AndroidPermission.CASE_SMS: case AndroidPermission.CASE_BATTERY: - case AndroidPermission.CASE_PHONESTATE: + case AndroidPermission.CASE_PHONE_STATE: break; } } @@ -404,7 +366,7 @@ public class MainActivity extends AppCompatActivity { case R.id.nav_exit: log.debug("Exiting"); MainApp.instance().stopKeepAliveService(); - MainApp.bus().post(new EventAppExit()); + RxBus.INSTANCE.send(new EventAppExit()); MainApp.closeDbHelper(); finish(); System.runFinalization(); diff --git a/app/src/main/java/info/nightscout/androidaps/MainApp.java b/app/src/main/java/info/nightscout/androidaps/MainApp.java index 3259483033..379afabddf 100644 --- a/app/src/main/java/info/nightscout/androidaps/MainApp.java +++ b/app/src/main/java/info/nightscout/androidaps/MainApp.java @@ -1,19 +1,17 @@ package info.nightscout.androidaps; import android.app.Application; +import android.content.BroadcastReceiver; import android.content.IntentFilter; import android.content.res.Resources; import android.os.SystemClock; -import android.support.annotation.Nullable; -import android.support.annotation.PluralsRes; -import android.support.v4.content.LocalBroadcastManager; + +import androidx.annotation.PluralsRes; +import androidx.localbroadcastmanager.content.LocalBroadcastManager; import com.crashlytics.android.Crashlytics; import com.google.firebase.analytics.FirebaseAnalytics; import com.j256.ormlite.android.apptools.OpenHelperManager; -import com.squareup.otto.Bus; -import com.squareup.otto.LoggingBus; -import com.squareup.otto.ThreadEnforcer; import net.danlew.android.joda.JodaTimeAndroid; @@ -37,8 +35,11 @@ import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.constraints.dstHelper.DstHelperPlugin; import info.nightscout.androidaps.plugins.constraints.objectives.ObjectivesPlugin; import info.nightscout.androidaps.plugins.constraints.safety.SafetyPlugin; +import info.nightscout.androidaps.plugins.constraints.signatureVerifier.SignatureVerifierPlugin; import info.nightscout.androidaps.plugins.constraints.storage.StorageConstraintPlugin; -import info.nightscout.androidaps.plugins.general.actions.ActionsFragment; +import info.nightscout.androidaps.plugins.constraints.versionChecker.VersionCheckerPlugin; +import info.nightscout.androidaps.plugins.general.actions.ActionsPlugin; +import info.nightscout.androidaps.plugins.general.automation.AutomationPlugin; import info.nightscout.androidaps.plugins.general.careportal.CareportalPlugin; import info.nightscout.androidaps.plugins.general.food.FoodPlugin; import info.nightscout.androidaps.plugins.general.maintenance.LoggerUtils; @@ -50,7 +51,6 @@ import info.nightscout.androidaps.plugins.general.nsclient.receivers.DBAccessRec import info.nightscout.androidaps.plugins.general.overview.OverviewPlugin; import info.nightscout.androidaps.plugins.general.persistentNotification.PersistentNotificationPlugin; import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorPlugin; -import info.nightscout.androidaps.plugins.general.versionChecker.VersionCheckerPlugin; import info.nightscout.androidaps.plugins.general.wear.WearPlugin; import info.nightscout.androidaps.plugins.general.xdripStatusline.StatuslinePlugin; import info.nightscout.androidaps.plugins.insulin.InsulinOrefFreePeakPlugin; @@ -67,13 +67,13 @@ import info.nightscout.androidaps.plugins.pump.danaRS.DanaRSPlugin; import info.nightscout.androidaps.plugins.pump.danaRv2.DanaRv2Plugin; import info.nightscout.androidaps.plugins.pump.insight.LocalInsightPlugin; import info.nightscout.androidaps.plugins.pump.mdi.MDIPlugin; +import info.nightscout.androidaps.plugins.pump.medtronic.MedtronicPumpPlugin; import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin; import info.nightscout.androidaps.plugins.sensitivity.SensitivityAAPSPlugin; import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref0Plugin; import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref1Plugin; import info.nightscout.androidaps.plugins.sensitivity.SensitivityWeightedAveragePlugin; -import info.nightscout.androidaps.plugins.source.SourceDexcomG5Plugin; -import info.nightscout.androidaps.plugins.source.SourceDexcomG6Plugin; +import info.nightscout.androidaps.plugins.source.SourceDexcomPlugin; import info.nightscout.androidaps.plugins.source.SourceEversensePlugin; import info.nightscout.androidaps.plugins.source.SourceGlimpPlugin; import info.nightscout.androidaps.plugins.source.SourceMM640gPlugin; @@ -85,18 +85,19 @@ import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; import info.nightscout.androidaps.receivers.DataReceiver; import info.nightscout.androidaps.receivers.KeepAliveReceiver; import info.nightscout.androidaps.receivers.NSAlarmReceiver; +import info.nightscout.androidaps.receivers.TimeDateOrTZChangeReceiver; import info.nightscout.androidaps.services.Intents; import info.nightscout.androidaps.utils.FabricPrivacy; +import info.nightscout.androidaps.utils.LocaleHelper; import io.fabric.sdk.android.Fabric; -import static info.nightscout.androidaps.plugins.general.versionChecker.VersionCheckerUtilsKt.triggerCheckVersion; +import static info.nightscout.androidaps.plugins.constraints.versionChecker.VersionCheckerUtilsKt.triggerCheckVersion; public class MainApp extends Application { private static Logger log = LoggerFactory.getLogger(L.CORE); private static KeepAliveReceiver keepAliveReceiver; - private static Bus sBus; private static MainApp sInstance; public static Resources sResources; @@ -112,6 +113,8 @@ public class MainApp extends Application { private static AckAlarmReceiver ackAlarmReciever = new AckAlarmReceiver(); private static DBAccessReceiver dbAccessReciever = new DBAccessReceiver(); private LocalBroadcastManager lbm; + BroadcastReceiver btReceiver; + TimeDateOrTZChangeReceiver timeDateOrTZChangeReceiver; public static boolean devBranch; public static boolean engineeringMode; @@ -122,9 +125,12 @@ public class MainApp extends Application { log.debug("onCreate"); sInstance = this; sResources = getResources(); + LocaleHelper.INSTANCE.update(this); sConstraintsChecker = new ConstraintChecker(); sDatabaseHelper = OpenHelperManager.getHelper(sInstance, DatabaseHelper.class); + Thread.setDefaultUncaughtExceptionHandler((thread, ex) -> log.error("Uncaught exception crashing app", ex)); + try { if (FabricPrivacy.fabricEnabled()) { Fabric.with(this, new Crashlytics()); @@ -134,6 +140,7 @@ public class MainApp extends Application { } mFirebaseAnalytics = FirebaseAnalytics.getInstance(this); + mFirebaseAnalytics.setAnalyticsCollectionEnabled(!Boolean.getBoolean("disableFirebase")); JodaTimeAndroid.init(this); @@ -145,21 +152,20 @@ public class MainApp extends Application { File engineeringModeSemaphore = new File(extFilesDir, "engineering_mode"); engineeringMode = engineeringModeSemaphore.exists() && engineeringModeSemaphore.isFile(); - devBranch = BuildConfig.VERSION.contains("dev"); - - sBus = L.isEnabled(L.EVENTS) && devBranch ? new LoggingBus(ThreadEnforcer.ANY) : new Bus(ThreadEnforcer.ANY); + devBranch = BuildConfig.VERSION.contains("-") || BuildConfig.VERSION.matches(".*[a-zA-Z]+.*"); registerLocalBroadcastReceiver(); //trigger here to see the new version on app start after an update triggerCheckVersion(); + //setBTReceiver(); if (pluginsList == null) { pluginsList = new ArrayList<>(); // Register all tabs in app here - pluginsList.add(OverviewPlugin.getPlugin()); + pluginsList.add(OverviewPlugin.INSTANCE); pluginsList.add(IobCobCalculatorPlugin.getPlugin()); - if (Config.ACTION) pluginsList.add(ActionsFragment.getPlugin()); + if (!Config.NSCLIENT) pluginsList.add(ActionsPlugin.INSTANCE); pluginsList.add(InsulinOrefRapidActingPlugin.getPlugin()); pluginsList.add(InsulinOrefUltraRapidActingPlugin.getPlugin()); pluginsList.add(InsulinOrefFreePeakPlugin.getPlugin()); @@ -172,39 +178,42 @@ public class MainApp extends Application { if (Config.PUMPDRIVERS) pluginsList.add(DanaRv2Plugin.getPlugin()); if (Config.PUMPDRIVERS) pluginsList.add(DanaRSPlugin.getPlugin()); if (Config.PUMPDRIVERS) pluginsList.add(LocalInsightPlugin.getPlugin()); - pluginsList.add(CareportalPlugin.getPlugin()); if (Config.PUMPDRIVERS) pluginsList.add(ComboPlugin.getPlugin()); - if (Config.MDI) pluginsList.add(MDIPlugin.getPlugin()); + if (Config.PUMPDRIVERS) pluginsList.add(MedtronicPumpPlugin.getPlugin()); + if (!Config.NSCLIENT) pluginsList.add(MDIPlugin.getPlugin()); pluginsList.add(VirtualPumpPlugin.getPlugin()); + pluginsList.add(CareportalPlugin.getPlugin()); if (Config.APS) pluginsList.add(LoopPlugin.getPlugin()); if (Config.APS) pluginsList.add(OpenAPSMAPlugin.getPlugin()); if (Config.APS) pluginsList.add(OpenAPSAMAPlugin.getPlugin()); if (Config.APS) pluginsList.add(OpenAPSSMBPlugin.getPlugin()); pluginsList.add(NSProfilePlugin.getPlugin()); - if (Config.OTHERPROFILES) pluginsList.add(SimpleProfilePlugin.getPlugin()); - if (Config.OTHERPROFILES) pluginsList.add(LocalProfilePlugin.getPlugin()); + if (!Config.NSCLIENT) pluginsList.add(SimpleProfilePlugin.getPlugin()); + if (!Config.NSCLIENT) pluginsList.add(LocalProfilePlugin.getPlugin()); pluginsList.add(TreatmentsPlugin.getPlugin()); - if (Config.SAFETY) pluginsList.add(SafetyPlugin.getPlugin()); - if (Config.SAFETY) pluginsList.add(VersionCheckerPlugin.INSTANCE); - if (Config.SAFETY) pluginsList.add(StorageConstraintPlugin.getPlugin()); - if (Config.APS) pluginsList.add(ObjectivesPlugin.getPlugin()); + if (!Config.NSCLIENT) pluginsList.add(SafetyPlugin.getPlugin()); + if (!Config.NSCLIENT) pluginsList.add(VersionCheckerPlugin.INSTANCE); + if (Config.APS) pluginsList.add(StorageConstraintPlugin.getPlugin()); + if (Config.APS) pluginsList.add(SignatureVerifierPlugin.getPlugin()); + if (Config.APS) pluginsList.add(ObjectivesPlugin.INSTANCE); pluginsList.add(SourceXdripPlugin.getPlugin()); pluginsList.add(SourceNSClientPlugin.getPlugin()); pluginsList.add(SourceMM640gPlugin.getPlugin()); pluginsList.add(SourceGlimpPlugin.getPlugin()); - pluginsList.add(SourceDexcomG5Plugin.getPlugin()); - pluginsList.add(SourceDexcomG6Plugin.getPlugin()); + pluginsList.add(SourceDexcomPlugin.INSTANCE); pluginsList.add(SourcePoctechPlugin.getPlugin()); pluginsList.add(SourceTomatoPlugin.getPlugin()); pluginsList.add(SourceEversensePlugin.getPlugin()); - if (Config.SMSCOMMUNICATORENABLED) pluginsList.add(SmsCommunicatorPlugin.getPlugin()); + if (!Config.NSCLIENT) pluginsList.add(SmsCommunicatorPlugin.getPlugin()); pluginsList.add(FoodPlugin.getPlugin()); pluginsList.add(WearPlugin.initPlugin(this)); pluginsList.add(StatuslinePlugin.initPlugin(this)); pluginsList.add(PersistentNotificationPlugin.getPlugin()); pluginsList.add(NSClientPlugin.getPlugin()); +// if (engineeringMode) pluginsList.add(TidepoolPlugin.INSTANCE); pluginsList.add(MaintenancePlugin.initPlugin(this)); + pluginsList.add(AutomationPlugin.INSTANCE); pluginsList.add(ConfigBuilderPlugin.getPlugin()); @@ -252,6 +261,10 @@ public class MainApp extends Application { //register dbaccess lbm.registerReceiver(dbAccessReciever, new IntentFilter(Intents.ACTION_DATABASE)); + + this.timeDateOrTZChangeReceiver = new TimeDateOrTZChangeReceiver(); + this.timeDateOrTZChangeReceiver.registerBroadcasts(this); + } private void startKeepAliveService() { @@ -266,26 +279,6 @@ public class MainApp extends Application { KeepAliveReceiver.cancelAlarm(this); } - public static void subscribe(Object subscriber) { - try { - bus().register(subscriber); - } catch (IllegalArgumentException e) { - // already registered - } - } - - public static void unsubscribe(Object subscriber) { - try { - bus().unregister(subscriber); - } catch (IllegalArgumentException e) { - // already unregistered - } - } - - public static Bus bus() { - return sBus; - } - public static String gs(int id) { return sResources.getString(id); } @@ -387,19 +380,6 @@ public class MainApp extends Application { return newList; } - @Nullable - public static T getSpecificPlugin(Class pluginClass) { - if (pluginsList != null) { - for (PluginBase p : pluginsList) { - if (pluginClass.isAssignableFrom(p.getClass())) - return (T) p; - } - } else { - log.error("pluginsList=null"); - } - return null; - } - public static boolean isEngineeringModeOrRelease() { if (!Config.APS) return true; @@ -437,5 +417,19 @@ public class MainApp extends Application { sDatabaseHelper.close(); sDatabaseHelper = null; } + + if (btReceiver != null) { + unregisterReceiver(btReceiver); + } + + if (timeDateOrTZChangeReceiver != null) { + unregisterReceiver(timeDateOrTZChangeReceiver); + } + + } + + public static int dpToPx(int dp) { + float scale = sResources.getDisplayMetrics().density; + return (int) (dp * scale + 0.5f); } } diff --git a/app/src/main/java/info/nightscout/androidaps/activities/AgreementActivity.java b/app/src/main/java/info/nightscout/androidaps/activities/AgreementActivity.java index 32352e1371..d3792bbad5 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/AgreementActivity.java +++ b/app/src/main/java/info/nightscout/androidaps/activities/AgreementActivity.java @@ -11,13 +11,13 @@ import info.nightscout.androidaps.MainActivity; import info.nightscout.androidaps.R; import info.nightscout.androidaps.utils.SP; -public class AgreementActivity extends Activity { +public class AgreementActivity extends NoSplashActivity { boolean IUnderstand; CheckBox agreeCheckBox; Button saveButton; @Override - protected void onCreate(Bundle savedInstanceState) { + public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_agreement); IUnderstand = SP.getBoolean(R.string.key_i_understand, false); diff --git a/app/src/main/java/info/nightscout/androidaps/activities/HistoryBrowseActivity.java b/app/src/main/java/info/nightscout/androidaps/activities/HistoryBrowseActivity.java index c007d1fced..79f3fbf4d4 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/HistoryBrowseActivity.java +++ b/app/src/main/java/info/nightscout/androidaps/activities/HistoryBrowseActivity.java @@ -2,9 +2,6 @@ package info.nightscout.androidaps.activities; import android.os.Bundle; import android.os.SystemClock; -import android.support.v4.content.res.ResourcesCompat; -import android.support.v7.app.AppCompatActivity; -import android.support.v7.widget.PopupMenu; import android.text.SpannableString; import android.text.style.ForegroundColorSpan; import android.view.Menu; @@ -15,8 +12,10 @@ import android.widget.ImageButton; import android.widget.SeekBar; import android.widget.TextView; +import androidx.appcompat.widget.PopupMenu; +import androidx.core.content.res.ResourcesCompat; + import com.jjoe64.graphview.GraphView; -import com.squareup.otto.Subscribe; import com.wdullaer.materialdatetimepicker.date.DatePickerDialog; import org.slf4j.Logger; @@ -25,49 +24,43 @@ import org.slf4j.LoggerFactory; import java.util.Calendar; import java.util.Date; -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.OnClick; -import butterknife.OnLongClick; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.events.EventCustomCalculationFinished; import info.nightscout.androidaps.interfaces.PumpInterface; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin; -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventAutosensCalculationFinished; -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventIobCalculationProgress; import info.nightscout.androidaps.plugins.general.overview.OverviewFragment; import info.nightscout.androidaps.plugins.general.overview.OverviewPlugin; import info.nightscout.androidaps.plugins.general.overview.graphData.GraphData; +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin; +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventAutosensCalculationFinished; +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventIobCalculationProgress; import info.nightscout.androidaps.utils.DateUtil; +import info.nightscout.androidaps.utils.FabricPrivacy; +import info.nightscout.androidaps.utils.SP; import info.nightscout.androidaps.utils.T; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.CompositeDisposable; -public class HistoryBrowseActivity extends AppCompatActivity { +public class HistoryBrowseActivity extends NoSplashActivity { private static Logger log = LoggerFactory.getLogger(HistoryBrowseActivity.class); - + private CompositeDisposable disposable = new CompositeDisposable(); ImageButton chartButton; boolean showBasal = true; - boolean showIob, showCob, showDev, showRat, showDevslope; + boolean showIob, showCob, showDev, showRat, showActPrim, showActSec, showDevslope; - @BindView(R.id.historybrowse_date) Button buttonDate; - @BindView(R.id.historybrowse_zoom) Button buttonZoom; - @BindView(R.id.historyybrowse_bggraph) GraphView bgGraph; - @BindView(R.id.historybrowse_iobgraph) GraphView iobGraph; - @BindView(R.id.historybrowse_seekBar) SeekBar seekBar; - @BindView(R.id.historybrowse_noprofile) TextView noProfile; - @BindView(R.id.overview_iobcalculationprogess) TextView iobCalculationProgressView; private int rangeToDisplay = 24; // for graph @@ -82,11 +75,83 @@ public class HistoryBrowseActivity extends AppCompatActivity { } @Override - protected void onCreate(Bundle savedInstanceState) { + public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_historybrowse); - ButterKnife.bind(this); + buttonDate = findViewById(R.id.historybrowse_date); + buttonZoom = findViewById(R.id.historybrowse_zoom); + bgGraph = findViewById(R.id.historyybrowse_bggraph); + iobGraph = findViewById(R.id.historybrowse_iobgraph); + seekBar = findViewById(R.id.historybrowse_seekBar); + noProfile = findViewById(R.id.historybrowse_noprofile); + iobCalculationProgressView = findViewById(R.id.overview_iobcalculationprogess); + + findViewById(R.id.historybrowse_left).setOnClickListener(v -> { + start -= T.hours(rangeToDisplay).msecs(); + updateGUI("onClickLeft"); + runCalculation("onClickLeft"); + }); + + findViewById(R.id.historybrowse_right).setOnClickListener(v -> { + start += T.hours(rangeToDisplay).msecs(); + updateGUI("onClickRight"); + runCalculation("onClickRight"); + }); + + findViewById(R.id.historybrowse_end).setOnClickListener(v -> { + Calendar calendar = Calendar.getInstance(); + calendar.setTimeInMillis(System.currentTimeMillis()); + calendar.set(Calendar.MILLISECOND, 0); + calendar.set(Calendar.SECOND, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.HOUR_OF_DAY, 0); + start = calendar.getTimeInMillis(); + updateGUI("onClickEnd"); + runCalculation("onClickEnd"); + }); + + findViewById(R.id.historybrowse_zoom).setOnClickListener(v -> { + rangeToDisplay += 6; + rangeToDisplay = rangeToDisplay > 24 ? 6 : rangeToDisplay; + updateGUI("rangeChange"); + }); + + findViewById(R.id.historybrowse_zoom).setOnLongClickListener(v -> { + Calendar calendar = Calendar.getInstance(); + calendar.setTimeInMillis(start); + calendar.set(Calendar.MILLISECOND, 0); + calendar.set(Calendar.SECOND, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.HOUR_OF_DAY, 0); + start = calendar.getTimeInMillis(); + updateGUI("resetToMidnight"); + runCalculation("onLongClickZoom"); + return true; + }); + + findViewById(R.id.historybrowse_date).setOnClickListener(v -> { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(new Date(start)); + DatePickerDialog dpd = DatePickerDialog.newInstance( + (view, year, monthOfYear, dayOfMonth) -> { + Date date = new Date(0); + date.setYear(year - 1900); + date.setMonth(monthOfYear); + date.setDate(dayOfMonth); + date.setHours(0); + start = date.getTime(); + updateGUI("onClickDate"); + runCalculation("onClickDate"); + }, + calendar.get(Calendar.YEAR), + calendar.get(Calendar.MONTH), + calendar.get(Calendar.DAY_OF_MONTH) + ); + dpd.setThemeDark(true); + dpd.dismissOnPause(true); + dpd.show(getFragmentManager(), "Datepickerdialog"); + }); bgGraph.getGridLabelRenderer().setGridColor(MainApp.gc(R.color.graphgrid)); bgGraph.getGridLabelRenderer().reloadStyles(); @@ -103,14 +168,33 @@ public class HistoryBrowseActivity extends AppCompatActivity { @Override public void onPause() { super.onPause(); - MainApp.bus().unregister(this); + disposable.clear(); iobCobCalculatorPlugin.stopCalculation("onPause"); } @Override public void onResume() { super.onResume(); - MainApp.bus().register(this); + disposable.add(RxBus.INSTANCE + .toObservable(EventAutosensCalculationFinished.class) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(event -> { + if (event.getCause() == eventCustomCalculationFinished) { + log.debug("EventAutosensCalculationFinished"); + synchronized (HistoryBrowseActivity.this) { + updateGUI("EventAutosensCalculationFinished"); + } + } + }, FabricPrivacy::logException) + ); + disposable.add(RxBus.INSTANCE + .toObservable(EventIobCalculationProgress.class) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(event -> { + if (iobCalculationProgressView != null) + iobCalculationProgressView.setText(event.getProgress()); + }, FabricPrivacy::logException) + ); // set start of current day Calendar calendar = Calendar.getInstance(); calendar.setTimeInMillis(System.currentTimeMillis()); @@ -124,78 +208,6 @@ public class HistoryBrowseActivity extends AppCompatActivity { updateGUI("onResume"); } - @OnClick(R.id.historybrowse_left) - void onClickLeft() { - start -= T.hours(rangeToDisplay).msecs(); - updateGUI("onClickLeft"); - runCalculation("onClickLeft"); - } - - @OnClick(R.id.historybrowse_right) - void onClickRight() { - start += T.hours(rangeToDisplay).msecs(); - updateGUI("onClickRight"); - runCalculation("onClickRight"); - } - - @OnClick(R.id.historybrowse_end) - void onClickEnd() { - Calendar calendar = Calendar.getInstance(); - calendar.setTimeInMillis(System.currentTimeMillis()); - calendar.set(Calendar.MILLISECOND, 0); - calendar.set(Calendar.SECOND, 0); - calendar.set(Calendar.MINUTE, 0); - calendar.set(Calendar.HOUR_OF_DAY, 0); - start = calendar.getTimeInMillis(); - updateGUI("onClickEnd"); - runCalculation("onClickEnd"); - } - - @OnClick(R.id.historybrowse_zoom) - void onClickZoom() { - rangeToDisplay += 6; - rangeToDisplay = rangeToDisplay > 24 ? 6 : rangeToDisplay; - updateGUI("rangeChange"); - } - - @OnLongClick(R.id.historybrowse_zoom) - boolean onLongClickZoom() { - Calendar calendar = Calendar.getInstance(); - calendar.setTimeInMillis(start); - calendar.set(Calendar.MILLISECOND, 0); - calendar.set(Calendar.SECOND, 0); - calendar.set(Calendar.MINUTE, 0); - calendar.set(Calendar.HOUR_OF_DAY, 0); - start = calendar.getTimeInMillis(); - updateGUI("resetToMidnight"); - runCalculation("onLongClickZoom"); - return true; - } - - @OnClick(R.id.historybrowse_date) - void onClickDate() { - Calendar calendar = Calendar.getInstance(); - calendar.setTime(new Date(start)); - DatePickerDialog dpd = DatePickerDialog.newInstance( - (view, year, monthOfYear, dayOfMonth) -> { - Date date = new Date(0); - date.setYear(year - 1900); - date.setMonth(monthOfYear); - date.setDate(dayOfMonth); - date.setHours(0); - start = date.getTime(); - updateGUI("onClickDate"); - runCalculation("onClickDate"); - }, - calendar.get(Calendar.YEAR), - calendar.get(Calendar.MONTH), - calendar.get(Calendar.DAY_OF_MONTH) - ); - dpd.setThemeDark(true); - dpd.dismissOnPause(true); - dpd.show(getFragmentManager(), "Datepickerdialog"); - } - private void runCalculation(String from) { long end = start + T.hours(rangeToDisplay).msecs(); iobCobCalculatorPlugin.stopCalculation(from); @@ -203,26 +215,6 @@ public class HistoryBrowseActivity extends AppCompatActivity { iobCobCalculatorPlugin.runCalculation(from, end, true, false, eventCustomCalculationFinished); } - @Subscribe - public void onStatusEvent(final EventAutosensCalculationFinished e) { - if (e.cause == eventCustomCalculationFinished) { - log.debug("EventAutosensCalculationFinished"); - runOnUiThread(() -> { - synchronized (HistoryBrowseActivity.this) { - updateGUI("EventAutosensCalculationFinished"); - } - }); - } - } - - @Subscribe - public void onStatusEvent(final EventIobCalculationProgress e) { - runOnUiThread(() -> { - if (iobCalculationProgressView != null) - iobCalculationProgressView.setText(e.progress); - }); - } - void updateGUI(String from) { log.debug("updateGUI from: " + from); @@ -240,14 +232,23 @@ public class HistoryBrowseActivity extends AppCompatActivity { } final String units = profile.getUnits(); - final double lowLine = OverviewPlugin.getPlugin().determineLowLine(units); - final double highLine = OverviewPlugin.getPlugin().determineHighLine(units); + final double lowLine = OverviewPlugin.INSTANCE.determineLowLine(units); + final double highLine = OverviewPlugin.INSTANCE.determineHighLine(units); buttonDate.setText(DateUtil.dateAndTimeString(start)); buttonZoom.setText(String.valueOf(rangeToDisplay)); final boolean showPrediction = false; + showBasal = SP.getBoolean("hist_showbasals", true); + showIob = SP.getBoolean("hist_showiob", true); + showCob = SP.getBoolean("hist_showcob", true); + showDev = SP.getBoolean("hist_showdeviations", false); + showRat = SP.getBoolean("hist_showratios", false); + showActPrim = SP.getBoolean("hist_showactivityprimary", false); + showActSec = SP.getBoolean("hist_showactivitysecondary", false); + showDevslope = SP.getBoolean("hist_showdevslope", false); + int hoursToFetch; final long toTime; final long fromTime; @@ -285,6 +286,10 @@ public class HistoryBrowseActivity extends AppCompatActivity { // set manual x bounds to have nice steps graphData.formatAxis(fromTime, toTime); + if (showActPrim) { + graphData.addActivity(fromTime, toTime, false, 1d); + } + // Treatments graphData.addTreatments(fromTime, toTime); @@ -305,6 +310,7 @@ public class HistoryBrowseActivity extends AppCompatActivity { boolean useCobForScale = false; boolean useDevForScale = false; boolean useRatioForScale = false; + boolean useIAForScale = false; boolean useDSForScale = false; if (showIob) { @@ -315,18 +321,22 @@ public class HistoryBrowseActivity extends AppCompatActivity { useDevForScale = true; } else if (showRat) { useRatioForScale = true; + } else if (showActSec) { + useIAForScale = true; } else if (showDevslope) { useDSForScale = true; } if (showIob) - secondGraphData.addIob(fromTime, toTime, useIobForScale, 1d); + secondGraphData.addIob(fromTime, toTime, useIobForScale, 1d, showPrediction); if (showCob) secondGraphData.addCob(fromTime, toTime, useCobForScale, useCobForScale ? 1d : 0.5d); if (showDev) secondGraphData.addDeviations(fromTime, toTime, useDevForScale, 1d); if (showRat) secondGraphData.addRatio(fromTime, toTime, useRatioForScale, 1d); + if (showActSec) + secondGraphData.addActivity(fromTime, toTime, useIAForScale, useIAForScale ? 2d : 1d); if (showDevslope) secondGraphData.addDeviationSlope(fromTime, toTime, useDSForScale, 1d); @@ -337,14 +347,14 @@ public class HistoryBrowseActivity extends AppCompatActivity { // do GUI update runOnUiThread(() -> { - if (showIob || showCob || showDev || showRat || showDevslope) { + if (showIob || showCob || showDev || showRat || showActSec || showDevslope) { iobGraph.setVisibility(View.VISIBLE); } else { iobGraph.setVisibility(View.GONE); } // finally enforce drawing of graphs graphData.performUpdate(); - if (showIob || showCob || showDev || showRat || showDevslope) + if (showIob || showCob || showDev || showRat || showActSec || showDevslope) secondGraphData.performUpdate(); }); }).start(); @@ -353,22 +363,37 @@ public class HistoryBrowseActivity extends AppCompatActivity { private void setupChartMenu() { chartButton = (ImageButton) findViewById(R.id.overview_chartMenuButton); chartButton.setOnClickListener(v -> { - MenuItem item; + MenuItem item, dividerItem; CharSequence title; + int titleMaxChars = 0; SpannableString s; PopupMenu popup = new PopupMenu(v.getContext(), v); item = popup.getMenu().add(Menu.NONE, OverviewFragment.CHARTTYPE.BAS.ordinal(), Menu.NONE, MainApp.gs(R.string.overview_show_basals)); title = item.getTitle(); + if (titleMaxChars < title.length()) titleMaxChars = title.length(); s = new SpannableString(title); s.setSpan(new ForegroundColorSpan(ResourcesCompat.getColor(getResources(), R.color.basal, null)), 0, s.length(), 0); item.setTitle(s); item.setCheckable(true); item.setChecked(showBasal); + item = popup.getMenu().add(Menu.NONE, OverviewFragment.CHARTTYPE.ACTPRIM.ordinal(), Menu.NONE, MainApp.gs(R.string.overview_show_activity)); + title = item.getTitle(); + if (titleMaxChars < title.length()) titleMaxChars = title.length(); + s = new SpannableString(title); + s.setSpan(new ForegroundColorSpan(ResourcesCompat.getColor(getResources(), R.color.activity, null)), 0, s.length(), 0); + item.setTitle(s); + item.setCheckable(true); + item.setChecked(showActPrim); + + dividerItem = popup.getMenu().add(""); + dividerItem.setEnabled(false); + item = popup.getMenu().add(Menu.NONE, OverviewFragment.CHARTTYPE.IOB.ordinal(), Menu.NONE, MainApp.gs(R.string.overview_show_iob)); title = item.getTitle(); + if (titleMaxChars < title.length()) titleMaxChars = title.length(); s = new SpannableString(title); s.setSpan(new ForegroundColorSpan(ResourcesCompat.getColor(getResources(), R.color.iob, null)), 0, s.length(), 0); item.setTitle(s); @@ -377,6 +402,7 @@ public class HistoryBrowseActivity extends AppCompatActivity { item = popup.getMenu().add(Menu.NONE, OverviewFragment.CHARTTYPE.COB.ordinal(), Menu.NONE, MainApp.gs(R.string.overview_show_cob)); title = item.getTitle(); + if (titleMaxChars < title.length()) titleMaxChars = title.length(); s = new SpannableString(title); s.setSpan(new ForegroundColorSpan(ResourcesCompat.getColor(getResources(), R.color.cob, null)), 0, s.length(), 0); item.setTitle(s); @@ -385,6 +411,7 @@ public class HistoryBrowseActivity extends AppCompatActivity { item = popup.getMenu().add(Menu.NONE, OverviewFragment.CHARTTYPE.DEV.ordinal(), Menu.NONE, MainApp.gs(R.string.overview_show_deviations)); title = item.getTitle(); + if (titleMaxChars < title.length()) titleMaxChars = title.length(); s = new SpannableString(title); s.setSpan(new ForegroundColorSpan(ResourcesCompat.getColor(getResources(), R.color.deviations, null)), 0, s.length(), 0); item.setTitle(s); @@ -393,15 +420,27 @@ public class HistoryBrowseActivity extends AppCompatActivity { item = popup.getMenu().add(Menu.NONE, OverviewFragment.CHARTTYPE.SEN.ordinal(), Menu.NONE, MainApp.gs(R.string.overview_show_sensitivity)); title = item.getTitle(); + if (titleMaxChars < title.length()) titleMaxChars = title.length(); s = new SpannableString(title); s.setSpan(new ForegroundColorSpan(ResourcesCompat.getColor(getResources(), R.color.ratio, null)), 0, s.length(), 0); item.setTitle(s); item.setCheckable(true); item.setChecked(showRat); + item = popup.getMenu().add(Menu.NONE, OverviewFragment.CHARTTYPE.ACTSEC.ordinal(), Menu.NONE, MainApp.gs(R.string.overview_show_activity)); + title = item.getTitle(); + if (titleMaxChars < title.length()) titleMaxChars = title.length(); + s = new SpannableString(title); + s.setSpan(new ForegroundColorSpan(ResourcesCompat.getColor(getResources(), R.color.activity, null)), 0, s.length(), 0); + item.setTitle(s); + item.setCheckable(true); + item.setChecked(showActSec); + + if (MainApp.devBranch) { item = popup.getMenu().add(Menu.NONE, OverviewFragment.CHARTTYPE.DEVSLOPE.ordinal(), Menu.NONE, "Deviation slope"); title = item.getTitle(); + if (titleMaxChars < title.length()) titleMaxChars = title.length(); s = new SpannableString(title); s.setSpan(new ForegroundColorSpan(ResourcesCompat.getColor(getResources(), R.color.devslopepos, null)), 0, s.length(), 0); item.setTitle(s); @@ -409,19 +448,27 @@ public class HistoryBrowseActivity extends AppCompatActivity { item.setChecked(showDevslope); } + // Fairly good guestimate for required divider text size... + title = new String(new char[titleMaxChars + 10]).replace("\0", "_"); + dividerItem.setTitle(title); + popup.setOnMenuItemClickListener(item1 -> { if (item1.getItemId() == OverviewFragment.CHARTTYPE.BAS.ordinal()) { - showBasal = !item1.isChecked(); + SP.putBoolean("hist_showbasals", !item1.isChecked()); } else if (item1.getItemId() == OverviewFragment.CHARTTYPE.IOB.ordinal()) { - showIob = !item1.isChecked(); + SP.putBoolean("hist_showiob", !item1.isChecked()); } else if (item1.getItemId() == OverviewFragment.CHARTTYPE.COB.ordinal()) { - showCob = !item1.isChecked(); + SP.putBoolean("hist_showcob", !item1.isChecked()); } else if (item1.getItemId() == OverviewFragment.CHARTTYPE.DEV.ordinal()) { - showDev = !item1.isChecked(); + SP.putBoolean("hist_showdeviations", !item1.isChecked()); } else if (item1.getItemId() == OverviewFragment.CHARTTYPE.SEN.ordinal()) { - showRat = !item1.isChecked(); + SP.putBoolean("hist_showratios", !item1.isChecked()); + } else if (item1.getItemId() == OverviewFragment.CHARTTYPE.ACTPRIM.ordinal()) { + SP.putBoolean("hist_showactivityprimary", !item1.isChecked()); + } else if (item1.getItemId() == OverviewFragment.CHARTTYPE.ACTSEC.ordinal()) { + SP.putBoolean("hist_showactivitysecondary", !item1.isChecked()); } else if (item1.getItemId() == OverviewFragment.CHARTTYPE.DEVSLOPE.ordinal()) { - showDevslope = !item1.isChecked(); + SP.putBoolean("hist_showdevslope", !item1.isChecked()); } updateGUI("onGraphCheckboxesCheckedChanged"); return true; diff --git a/app/src/main/java/info/nightscout/androidaps/activities/NoSplashActivity.kt b/app/src/main/java/info/nightscout/androidaps/activities/NoSplashActivity.kt new file mode 100644 index 0000000000..0b7af2bd1d --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/activities/NoSplashActivity.kt @@ -0,0 +1,13 @@ +package info.nightscout.androidaps.activities + +import android.app.Activity +import android.os.Bundle + +import info.nightscout.androidaps.R + +open class NoSplashActivity : Activity() { + public override fun onCreate(savedInstanceState: Bundle?) { + setTheme(R.style.AppTheme_NoActionBar) + super.onCreate(savedInstanceState) + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/activities/NoSplashAppCompatActivity.kt b/app/src/main/java/info/nightscout/androidaps/activities/NoSplashAppCompatActivity.kt new file mode 100644 index 0000000000..e4c8027cd5 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/activities/NoSplashAppCompatActivity.kt @@ -0,0 +1,12 @@ +package info.nightscout.androidaps.activities + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import info.nightscout.androidaps.R + +open class NoSplashAppCompatActivity : AppCompatActivity() { + public override fun onCreate(savedInstanceState: Bundle?) { + setTheme(R.style.AppTheme_NoActionBar) + super.onCreate(savedInstanceState) + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/activities/PreferencesActivity.java b/app/src/main/java/info/nightscout/androidaps/activities/PreferencesActivity.java index aea7bcfca6..367429dbbf 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/PreferencesActivity.java +++ b/app/src/main/java/info/nightscout/androidaps/activities/PreferencesActivity.java @@ -15,12 +15,15 @@ import android.text.TextUtils; import info.nightscout.androidaps.Config; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.events.EventPreferenceChange; -import info.nightscout.androidaps.events.EventRefreshGui; +import info.nightscout.androidaps.events.EventRebuildTabs; import info.nightscout.androidaps.interfaces.PluginBase; import info.nightscout.androidaps.interfaces.PluginType; import info.nightscout.androidaps.plugins.general.careportal.CareportalPlugin; import info.nightscout.androidaps.plugins.constraints.safety.SafetyPlugin; +import info.nightscout.androidaps.plugins.general.tidepool.TidepoolPlugin; +import info.nightscout.androidaps.plugins.general.tidepool.comm.TidepoolUploader; import info.nightscout.androidaps.plugins.insulin.InsulinOrefFreePeakPlugin; import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin; import info.nightscout.androidaps.plugins.general.nsclient.NSClientPlugin; @@ -33,24 +36,27 @@ import info.nightscout.androidaps.plugins.pump.danaRKorean.DanaRKoreanPlugin; import info.nightscout.androidaps.plugins.pump.danaRS.DanaRSPlugin; import info.nightscout.androidaps.plugins.pump.danaRv2.DanaRv2Plugin; import info.nightscout.androidaps.plugins.pump.insight.LocalInsightPlugin; +import info.nightscout.androidaps.plugins.pump.medtronic.MedtronicPumpPlugin; import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin; import info.nightscout.androidaps.plugins.sensitivity.SensitivityAAPSPlugin; import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref0Plugin; import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref1Plugin; import info.nightscout.androidaps.plugins.sensitivity.SensitivityWeightedAveragePlugin; import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorPlugin; -import info.nightscout.androidaps.plugins.source.SourceDexcomG5Plugin; import info.nightscout.androidaps.plugins.general.wear.WearPlugin; import info.nightscout.androidaps.plugins.general.xdripStatusline.StatuslinePlugin; +import info.nightscout.androidaps.plugins.source.SourceDexcomPlugin; import info.nightscout.androidaps.utils.LocaleHelper; import info.nightscout.androidaps.utils.OKDialog; import info.nightscout.androidaps.utils.SP; +import info.nightscout.androidaps.plugins.general.automation.AutomationPlugin; public class PreferencesActivity extends PreferenceActivity implements SharedPreferences.OnSharedPreferenceChangeListener { MyPreferenceFragment myPreferenceFragment; @Override protected void onCreate(Bundle savedInstanceState) { + setTheme(R.style.AppTheme_NoActionBar); super.onCreate(savedInstanceState); myPreferenceFragment = new MyPreferenceFragment(); Bundle args = new Bundle(); @@ -62,16 +68,14 @@ public class PreferencesActivity extends PreferenceActivity implements SharedPre @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { - MainApp.bus().post(new EventPreferenceChange(key)); + RxBus.INSTANCE.send(new EventPreferenceChange(key)); if (key.equals("language")) { - String lang = sharedPreferences.getString("language", "en"); - LocaleHelper.setLocale(getApplicationContext(), lang); - MainApp.bus().post(new EventRefreshGui(true)); + RxBus.INSTANCE.send(new EventRebuildTabs(true)); //recreate() does not update language so better close settings finish(); } if (key.equals("short_tabtitles")) { - MainApp.bus().post(new EventRefreshGui()); + RxBus.INSTANCE.send(new EventRebuildTabs()); } if (key.equals(MainApp.gs(R.string.key_openapsama_useautosens)) && SP.getBoolean(R.string.key_openapsama_useautosens, false)) { OKDialog.show(this, MainApp.gs(R.string.configbuilder_sensitivity), MainApp.gs(R.string.sensitivity_warning), null); @@ -93,7 +97,7 @@ public class PreferencesActivity extends PreferenceActivity implements SharedPre } else if (editTextPref.getText() != null) { ((EditTextPreference) pref).setDialogMessage(editTextPref.getDialogMessage()); pref.setSummary(editTextPref.getText()); - } else if (pref.getKey().contains("smscommunicator_allowednumbers") && TextUtils.isEmpty(editTextPref.getText().trim())) { + } else if (pref.getKey().contains("smscommunicator_allowednumbers") && (editTextPref.getText() == null || TextUtils.isEmpty(editTextPref.getText().trim()))) { pref.setSummary(MainApp.gs(R.string.smscommunicator_allowednumbers_summary)); } } @@ -144,7 +148,7 @@ public class PreferencesActivity extends PreferenceActivity implements SharedPre addPreferencesFromResource(R.xml.pref_overview); - addPreferencesFromResourceIfEnabled(SourceDexcomG5Plugin.getPlugin(), PluginType.BGSOURCE); + addPreferencesFromResourceIfEnabled(SourceDexcomPlugin.INSTANCE, PluginType.BGSOURCE); addPreferencesFromResourceIfEnabled(CareportalPlugin.getPlugin(), PluginType.GENERAL); addPreferencesFromResourceIfEnabled(SafetyPlugin.getPlugin(), PluginType.CONSTRAINTS); if (Config.APS) { @@ -166,6 +170,7 @@ public class PreferencesActivity extends PreferenceActivity implements SharedPre addPreferencesFromResourceIfEnabled(DanaRSPlugin.getPlugin(), PluginType.PUMP); addPreferencesFromResourceIfEnabled(LocalInsightPlugin.getPlugin(), PluginType.PUMP); addPreferencesFromResourceIfEnabled(ComboPlugin.getPlugin(), PluginType.PUMP); + addPreferencesFromResourceIfEnabled(MedtronicPumpPlugin.getPlugin(), PluginType.PUMP); if (DanaRPlugin.getPlugin().isEnabled(PluginType.PROFILE) || DanaRKoreanPlugin.getPlugin().isEnabled(PluginType.PROFILE) @@ -182,7 +187,9 @@ public class PreferencesActivity extends PreferenceActivity implements SharedPre addPreferencesFromResourceIfEnabled(InsulinOrefFreePeakPlugin.getPlugin(), PluginType.INSULIN); addPreferencesFromResourceIfEnabled(NSClientPlugin.getPlugin(), PluginType.GENERAL); + addPreferencesFromResourceIfEnabled(TidepoolPlugin.INSTANCE, PluginType.GENERAL); addPreferencesFromResourceIfEnabled(SmsCommunicatorPlugin.getPlugin(), PluginType.GENERAL); + addPreferencesFromResourceIfEnabled(AutomationPlugin.INSTANCE, PluginType.GENERAL); addPreferencesFromResource(R.xml.pref_others); addPreferencesFromResource(R.xml.pref_datachoices); @@ -192,17 +199,25 @@ public class PreferencesActivity extends PreferenceActivity implements SharedPre } if (Config.NSCLIENT) { - PreferenceScreen scrnAdvancedSettings = (PreferenceScreen)findPreference(getString(R.string.key_advancedsettings)); + PreferenceScreen scrnAdvancedSettings = (PreferenceScreen) findPreference(getString(R.string.key_advancedsettings)); if (scrnAdvancedSettings != null) { scrnAdvancedSettings.removePreference(getPreference(getString(R.string.key_statuslights_res_warning))); scrnAdvancedSettings.removePreference(getPreference(getString(R.string.key_statuslights_res_critical))); scrnAdvancedSettings.removePreference(getPreference(getString(R.string.key_statuslights_bat_warning))); scrnAdvancedSettings.removePreference(getPreference(getString(R.string.key_statuslights_bat_critical))); scrnAdvancedSettings.removePreference(getPreference(getString(R.string.key_show_statuslights))); + scrnAdvancedSettings.removePreference(getPreference(getString(R.string.key_show_statuslights_extended))); } } initSummary(getPreferenceScreen()); + + final Preference tidepoolTestLogin = findPreference(MainApp.gs(R.string.key_tidepool_test_login)); + if (tidepoolTestLogin != null) + tidepoolTestLogin.setOnPreferenceClickListener(preference -> { + TidepoolUploader.INSTANCE.testLogin(getActivity()); + return false; + }); } @Override diff --git a/app/src/main/java/info/nightscout/androidaps/activities/RequestDexcomPermissionActivity.kt b/app/src/main/java/info/nightscout/androidaps/activities/RequestDexcomPermissionActivity.kt new file mode 100644 index 0000000000..96a399a299 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/activities/RequestDexcomPermissionActivity.kt @@ -0,0 +1,19 @@ +package info.nightscout.androidaps.activities + +import android.os.Bundle +import info.nightscout.androidaps.plugins.source.SourceDexcomPlugin + +class RequestDexcomPermissionActivity : NoSplashAppCompatActivity() { + + private val requestCode = "AndroidAPS <3".map { it.toInt() }.sum() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + requestPermissions(arrayOf(SourceDexcomPlugin.PERMISSION), requestCode) + } + + override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { + finish() + } + +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/activities/SingleFragmentActivity.java b/app/src/main/java/info/nightscout/androidaps/activities/SingleFragmentActivity.java index 2a381cd0a8..688ba82c6a 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/SingleFragmentActivity.java +++ b/app/src/main/java/info/nightscout/androidaps/activities/SingleFragmentActivity.java @@ -2,12 +2,13 @@ package info.nightscout.androidaps.activities; import android.content.Intent; import android.os.Bundle; -import android.support.annotation.Nullable; -import android.support.v4.app.Fragment; -import android.support.v7.app.AppCompatActivity; import android.view.Menu; import android.view.MenuItem; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; +import androidx.fragment.app.Fragment; + import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.interfaces.PluginBase; @@ -18,7 +19,7 @@ public class SingleFragmentActivity extends AppCompatActivity { private PluginBase plugin; @Override - protected void onCreate(@Nullable Bundle savedInstanceState) { + public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_single_fragment); diff --git a/app/src/main/java/info/nightscout/androidaps/activities/TDDStatsActivity.java b/app/src/main/java/info/nightscout/androidaps/activities/TDDStatsActivity.java index e42d31d7c0..24cdceada6 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/TDDStatsActivity.java +++ b/app/src/main/java/info/nightscout/androidaps/activities/TDDStatsActivity.java @@ -1,10 +1,8 @@ package info.nightscout.androidaps.activities; -import android.app.Activity; import android.graphics.Color; import android.graphics.Rect; import android.os.Bundle; -import android.support.v7.widget.LinearLayoutManager; import android.text.TextUtils; import android.view.KeyEvent; import android.view.MotionEvent; @@ -18,7 +16,7 @@ import android.widget.TableLayout; import android.widget.TableRow; import android.widget.TextView; -import com.squareup.otto.Subscribe; +import androidx.recyclerview.widget.LinearLayoutManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -39,6 +37,7 @@ import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.db.TDD; import info.nightscout.androidaps.events.EventPumpStatusChanged; import info.nightscout.androidaps.interfaces.PumpInterface; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; import info.nightscout.androidaps.plugins.pump.danaR.DanaRPlugin; @@ -49,11 +48,15 @@ import info.nightscout.androidaps.plugins.pump.danaRv2.DanaRv2Plugin; import info.nightscout.androidaps.plugins.pump.insight.LocalInsightPlugin; import info.nightscout.androidaps.queue.Callback; import info.nightscout.androidaps.utils.DecimalFormatter; +import info.nightscout.androidaps.utils.FabricPrivacy; import info.nightscout.androidaps.utils.SP; import info.nightscout.androidaps.utils.SafeParse; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.CompositeDisposable; -public class TDDStatsActivity extends Activity { +public class TDDStatsActivity extends NoSplashActivity { private static Logger log = LoggerFactory.getLogger(TDDStatsActivity.class); + private CompositeDisposable disposable = new CompositeDisposable(); TextView statusView, statsMessage, totalBaseBasal2; EditText totalBaseBasal; @@ -74,13 +77,25 @@ public class TDDStatsActivity extends Activity { @Override protected void onResume() { super.onResume(); - MainApp.bus().register(this); + disposable.add(RxBus.INSTANCE + .toObservable(EventPumpStatusChanged.class) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(event -> statusView.setText(event.getStatus()), FabricPrivacy::logException) + ); + disposable.add(RxBus.INSTANCE + .toObservable(EventDanaRSyncStatus.class) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(event -> { + log.debug("EventDanaRSyncStatus: " + event.getMessage()); + statusView.setText(event.getMessage()); + }, FabricPrivacy::logException) + ); } @Override protected void onPause() { super.onPause(); - MainApp.bus().unregister(this); + disposable.clear(); } @Override @@ -99,7 +114,7 @@ public class TDDStatsActivity extends Activity { } @Override - protected void onCreate(Bundle savedInstanceState) { + public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.danar_statsactivity); getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN); @@ -239,7 +254,7 @@ public class TDDStatsActivity extends Activity { statsMessage.setText(MainApp.gs(R.string.danar_stats_warning_Message)); } }); - ConfigBuilderPlugin.getPlugin().getCommandQueue().loadTDDs( new Callback() { + ConfigBuilderPlugin.getPlugin().getCommandQueue().loadTDDs(new Callback() { @Override public void run() { loadDataFromDB(); @@ -399,7 +414,7 @@ public class TDDStatsActivity extends Activity { //cumulative TDDs for (TDD record : historyList) { - if(!historyList.isEmpty() && df.format(new Date(record.date)).equals(df.format(new Date()))) { + if (!historyList.isEmpty() && df.format(new Date(record.date)).equals(df.format(new Date()))) { //Today should not be included continue; } @@ -448,7 +463,7 @@ public class TDDStatsActivity extends Activity { tl.setBackgroundColor(Color.TRANSPARENT); } - if(!historyList.isEmpty() && df.format(new Date(historyList.get(0).date)).equals(df.format(new Date()))) { + if (!historyList.isEmpty() && df.format(new Date(historyList.get(0).date)).equals(df.format(new Date()))) { //Today should not be included historyList.remove(0); } @@ -519,42 +534,17 @@ public class TDDStatsActivity extends Activity { } } - @Subscribe - public void onStatusEvent(final EventDanaRSyncStatus s) { - log.debug("EventDanaRSyncStatus: " + s.message); - runOnUiThread( - new Runnable() { - @Override - public void run() { - statusView.setText(s.message); - } - }); - } - - @Subscribe - public void onStatusEvent(final EventPumpStatusChanged c) { - runOnUiThread( - new Runnable() { - @Override - public void run() { - statusView.setText(c.textStatus()); - } - } - ); - } - - public static boolean isOldData(List historyList) { Object activePump = ConfigBuilderPlugin.getPlugin().getActivePump(); - PumpInterface dana = MainApp.getSpecificPlugin(DanaRPlugin.class); - PumpInterface danaRS = MainApp.getSpecificPlugin(DanaRSPlugin.class); - PumpInterface danaV2 = MainApp.getSpecificPlugin(DanaRv2Plugin.class); - PumpInterface danaKorean = MainApp.getSpecificPlugin(DanaRKoreanPlugin.class); - PumpInterface insight = MainApp.getSpecificPlugin(LocalInsightPlugin.class); + PumpInterface dana = DanaRPlugin.getPlugin(); + PumpInterface danaRS = DanaRSPlugin.getPlugin(); + PumpInterface danaV2 = DanaRv2Plugin.getPlugin(); + PumpInterface danaKorean = DanaRKoreanPlugin.getPlugin(); + PumpInterface insight = LocalInsightPlugin.getPlugin(); boolean startsYesterday = activePump == dana || activePump == danaRS || activePump == danaV2 || activePump == danaKorean || activePump == insight; DateFormat df = new SimpleDateFormat("dd.MM."); - return (historyList.size() < 3 || !(df.format(new Date(historyList.get(0).date)).equals(df.format(new Date(System.currentTimeMillis() - (startsYesterday?1000 * 60 * 60 * 24:0)))))); + return (historyList.size() < 3 || !(df.format(new Date(historyList.get(0).date)).equals(df.format(new Date(System.currentTimeMillis() - (startsYesterday ? 1000 * 60 * 60 * 24 : 0)))))); } } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/data/ConstraintChecker.java b/app/src/main/java/info/nightscout/androidaps/data/ConstraintChecker.java index b1267e81bc..9d7a49493b 100644 --- a/app/src/main/java/info/nightscout/androidaps/data/ConstraintChecker.java +++ b/app/src/main/java/info/nightscout/androidaps/data/ConstraintChecker.java @@ -1,11 +1,9 @@ package info.nightscout.androidaps.data; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import java.util.ArrayList; -import javax.annotation.Nonnull; - import info.nightscout.androidaps.Constants; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.interfaces.Constraint; diff --git a/app/src/main/java/info/nightscout/androidaps/data/Intervals.java b/app/src/main/java/info/nightscout/androidaps/data/Intervals.java index 66d567d9bd..efb3b76aea 100644 --- a/app/src/main/java/info/nightscout/androidaps/data/Intervals.java +++ b/app/src/main/java/info/nightscout/androidaps/data/Intervals.java @@ -1,7 +1,7 @@ package info.nightscout.androidaps.data; -import android.support.annotation.Nullable; -import android.support.v4.util.LongSparseArray; +import androidx.annotation.Nullable; +import androidx.collection.LongSparseArray; import java.util.ArrayList; import java.util.List; @@ -22,7 +22,7 @@ public abstract class Intervals { rawData = new LongSparseArray(); } - public synchronized Intervals reset() { + public synchronized Intervals reset() { rawData = new LongSparseArray(); return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/data/IobTotal.java b/app/src/main/java/info/nightscout/androidaps/data/IobTotal.java index 73f75b9c7d..79885e297f 100644 --- a/app/src/main/java/info/nightscout/androidaps/data/IobTotal.java +++ b/app/src/main/java/info/nightscout/androidaps/data/IobTotal.java @@ -9,10 +9,12 @@ import org.slf4j.LoggerFactory; import java.util.Date; +import info.nightscout.androidaps.plugins.general.overview.graphExtensions.DataPointWithLabelInterface; +import info.nightscout.androidaps.plugins.general.overview.graphExtensions.PointsWithLabelGraphSeries; import info.nightscout.androidaps.utils.DateUtil; import info.nightscout.androidaps.utils.Round; -public class IobTotal { +public class IobTotal implements DataPointWithLabelInterface { private static Logger log = LoggerFactory.getLogger(IobTotal.class); public double iob; @@ -133,4 +135,52 @@ public class IobTotal { return json; } + // DataPoint interface + + int color; + + @Override + public double getX() { + return time; + } + + @Override + public double getY() { + return iob; + } + + @Override + public void setY(double y) { + + } + + @Override + public String getLabel() { + return null; + } + + @Override + public long getDuration() { + return 0; + } + + @Override + public PointsWithLabelGraphSeries.Shape getShape() { + return PointsWithLabelGraphSeries.Shape.IOBPREDICTION; + } + + @Override + public float getSize() { + return 0.5f; + } + + @Override + public int getColor() { + return color; + } + + public IobTotal setColor(int color) { + this.color = color; + return this; + } } diff --git a/app/src/main/java/info/nightscout/androidaps/data/NonOverlappingIntervals.java b/app/src/main/java/info/nightscout/androidaps/data/NonOverlappingIntervals.java index 8e0c286e7e..c426aede40 100644 --- a/app/src/main/java/info/nightscout/androidaps/data/NonOverlappingIntervals.java +++ b/app/src/main/java/info/nightscout/androidaps/data/NonOverlappingIntervals.java @@ -1,10 +1,8 @@ package info.nightscout.androidaps.data; -import android.support.annotation.Nullable; -import android.support.v4.util.LongSparseArray; +import androidx.annotation.Nullable; -import info.nightscout.androidaps.db.TemporaryBasal; import info.nightscout.androidaps.interfaces.Interval; /** @@ -21,7 +19,7 @@ public class NonOverlappingIntervals extends Intervals { rawData = other.rawData.clone(); } - protected synchronized void merge() { + public synchronized void merge() { for (int index = 0; index < rawData.size() - 1; index++) { Interval i = rawData.valueAt(index); long startOfNewer = rawData.valueAt(index + 1).start(); diff --git a/app/src/main/java/info/nightscout/androidaps/data/OverlappingIntervals.java b/app/src/main/java/info/nightscout/androidaps/data/OverlappingIntervals.java index 76c2ff3615..5515ea15e1 100644 --- a/app/src/main/java/info/nightscout/androidaps/data/OverlappingIntervals.java +++ b/app/src/main/java/info/nightscout/androidaps/data/OverlappingIntervals.java @@ -1,7 +1,7 @@ package info.nightscout.androidaps.data; -import android.support.annotation.Nullable; +import androidx.annotation.Nullable; import info.nightscout.androidaps.interfaces.Interval; diff --git a/app/src/main/java/info/nightscout/androidaps/data/Profile.java b/app/src/main/java/info/nightscout/androidaps/data/Profile.java index 23a7e62426..6f18ba7af5 100644 --- a/app/src/main/java/info/nightscout/androidaps/data/Profile.java +++ b/app/src/main/java/info/nightscout/androidaps/data/Profile.java @@ -1,6 +1,6 @@ package info.nightscout.androidaps.data; -import android.support.v4.util.LongSparseArray; +import androidx.collection.LongSparseArray; import org.json.JSONArray; import org.json.JSONException; @@ -11,11 +11,13 @@ import org.slf4j.LoggerFactory; import java.text.DecimalFormat; import java.util.TimeZone; +import info.nightscout.androidaps.Config; import info.nightscout.androidaps.Constants; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.interfaces.PumpDescription; import info.nightscout.androidaps.interfaces.PumpInterface; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification; import info.nightscout.androidaps.plugins.general.overview.notifications.Notification; @@ -165,17 +167,18 @@ public class Profile { final JSONObject o = array.getJSONObject(index); long tas = 0; try { - tas = getShitfTimeSecs((int) o.getLong("timeAsSeconds")); - } catch (JSONException e) { String time = o.getString("time"); tas = getShitfTimeSecs(DateUtil.toSeconds(time)); + } catch (JSONException e) { //log.debug(">>>>>>>>>>>> Used recalculated timeAsSecons: " + time + " " + tas); + tas = getShitfTimeSecs((int) o.getLong("timeAsSeconds")); } double value = o.getDouble("value") * multiplier; sparse.put(tas, value); - } catch (JSONException e) { + } catch (Exception e) { log.error("Unhandled exception", e); log.error(json.toString()); + FabricPrivacy.logException(e); } } @@ -226,8 +229,10 @@ public class Profile { for (int index = 0; index < basal_v.size(); index++) { long secondsFromMidnight = basal_v.keyAt(index); if (notify && secondsFromMidnight % 3600 != 0) { - Notification notification = new Notification(Notification.BASAL_PROFILE_NOT_ALIGNED_TO_HOURS, String.format(MainApp.gs(R.string.basalprofilenotaligned), from), Notification.NORMAL); - MainApp.bus().post(new EventNewNotification(notification)); + if (Config.APS) { + Notification notification = new Notification(Notification.BASAL_PROFILE_NOT_ALIGNED_TO_HOURS, String.format(MainApp.gs(R.string.basalprofilenotaligned), from), Notification.NORMAL); + RxBus.INSTANCE.send(new EventNewNotification(notification)); + } } } } @@ -258,11 +263,11 @@ public class Profile { } protected void sendBelowMinimumNotification(String from) { - MainApp.bus().post(new EventNewNotification(new Notification(Notification.MINIMAL_BASAL_VALUE_REPLACED, String.format(MainApp.gs(R.string.minimalbasalvaluereplaced), from), Notification.NORMAL))); + RxBus.INSTANCE.send(new EventNewNotification(new Notification(Notification.MINIMAL_BASAL_VALUE_REPLACED, String.format(MainApp.gs(R.string.minimalbasalvaluereplaced), from), Notification.NORMAL))); } protected void sendAboveMaximumNotification(String from) { - MainApp.bus().post(new EventNewNotification(new Notification(Notification.MAXIMUM_BASAL_VALUE_REPLACED, String.format(MainApp.gs(R.string.maximumbasalvaluereplaced), from), Notification.NORMAL))); + RxBus.INSTANCE.send(new EventNewNotification(new Notification(Notification.MAXIMUM_BASAL_VALUE_REPLACED, String.format(MainApp.gs(R.string.maximumbasalvaluereplaced), from), Notification.NORMAL))); } private void validate(LongSparseArray array) { @@ -400,6 +405,19 @@ public class Profile { return getValuesList(isf_v, null, new DecimalFormat("0.0"), getUnits() + MainApp.gs(R.string.profile_per_unit)); } + public ProfileValue[] getIsfs() { + if (isf_v == null) + isf_v = convertToSparseArray(ic); + ProfileValue[] ret = new ProfileValue[isf_v.size()]; + + for (Integer index = 0; index < isf_v.size(); index++) { + Integer tas = (int) isf_v.keyAt(index); + double value = isf_v.valueAt(index); + ret[index] = new ProfileValue(tas, value); + } + return ret; + } + public double getIc() { return getIcTimeFromMidnight(secondsFromMidnight()); } @@ -420,6 +438,19 @@ public class Profile { return getValuesList(ic_v, null, new DecimalFormat("0.0"), MainApp.gs(R.string.profile_carbs_per_unit)); } + public ProfileValue[] getIcs() { + if (ic_v == null) + ic_v = convertToSparseArray(ic); + ProfileValue[] ret = new ProfileValue[ic_v.size()]; + + for (Integer index = 0; index < ic_v.size(); index++) { + Integer tas = (int) ic_v.keyAt(index); + double value = ic_v.valueAt(index); + ret[index] = new ProfileValue(tas, value); + } + return ret; + } + public double getBasal() { return getBasalTimeFromMidnight(secondsFromMidnight()); } @@ -438,11 +469,11 @@ public class Profile { public String getBasalList() { if (basal_v == null) basal_v = convertToSparseArray(basal); - return getValuesList(basal_v, null, new DecimalFormat("0.00"), MainApp.gs(R.string.profile_ins_units_per_hout)); + return getValuesList(basal_v, null, new DecimalFormat("0.00"), MainApp.gs(R.string.profile_ins_units_per_hour)); } - public class BasalValue { - public BasalValue(int timeAsSeconds, double value) { + public class ProfileValue { + public ProfileValue(int timeAsSeconds, double value) { this.timeAsSeconds = timeAsSeconds; this.value = value; } @@ -451,15 +482,15 @@ public class Profile { public double value; } - public synchronized BasalValue[] getBasalValues() { + public synchronized ProfileValue[] getBasalValues() { if (basal_v == null) basal_v = convertToSparseArray(basal); - BasalValue[] ret = new BasalValue[basal_v.size()]; + ProfileValue[] ret = new ProfileValue[basal_v.size()]; for (Integer index = 0; index < basal_v.size(); index++) { Integer tas = (int) basal_v.keyAt(index); double value = basal_v.valueAt(index); - ret[index] = new BasalValue(tas, value); + ret[index] = new ProfileValue(tas, value); } return ret; } @@ -500,6 +531,49 @@ public class Profile { return getValueToTime(targetHigh_v, timeAsSeconds); } + public class TargetValue { + public TargetValue(int timeAsSeconds, double low, double high) { + this.timeAsSeconds = timeAsSeconds; + this.low = low; + this.high = high; + } + + public int timeAsSeconds; + public double low; + public double high; + } + + public TargetValue[] getTargets() { + if (targetLow_v == null) + targetLow_v = convertToSparseArray(targetLow); + if (targetHigh_v == null) + targetHigh_v = convertToSparseArray(targetHigh); + TargetValue[] ret = new TargetValue[targetLow_v.size()]; + + for (Integer index = 0; index < targetLow_v.size(); index++) { + Integer tas = (int) targetLow_v.keyAt(index); + double low = targetLow_v.valueAt(index); + double high = targetHigh_v.valueAt(index); + ret[index] = new TargetValue(tas, low, high); + } + return ret; + } + + public ProfileValue[] getSingleTargets() { + if (targetLow_v == null) + targetLow_v = convertToSparseArray(targetLow); + if (targetHigh_v == null) + targetHigh_v = convertToSparseArray(targetHigh); + ProfileValue[] ret = new ProfileValue[targetLow_v.size()]; + + for (Integer index = 0; index < targetLow_v.size(); index++) { + Integer tas = (int) targetLow_v.keyAt(index); + double target = (targetLow_v.valueAt(index) + targetHigh_v.valueAt(index)) / 2; + ret[index] = new ProfileValue(tas, target); + } + return ret; + } + public String getTargetList() { if (targetLow_v == null) targetLow_v = convertToSparseArray(targetLow); diff --git a/app/src/main/java/info/nightscout/androidaps/data/ProfileIntervals.java b/app/src/main/java/info/nightscout/androidaps/data/ProfileIntervals.java index 8938b53936..37be1dc6e9 100644 --- a/app/src/main/java/info/nightscout/androidaps/data/ProfileIntervals.java +++ b/app/src/main/java/info/nightscout/androidaps/data/ProfileIntervals.java @@ -1,7 +1,7 @@ package info.nightscout.androidaps.data; -import android.support.annotation.Nullable; -import android.support.v4.util.LongSparseArray; +import androidx.annotation.Nullable; +import androidx.collection.LongSparseArray; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -31,7 +31,7 @@ public class ProfileIntervals { rawData = other.rawData.clone(); } - public synchronized ProfileIntervals reset() { + public synchronized ProfileIntervals reset() { rawData = new LongSparseArray<>(); return this; } diff --git a/app/src/main/java/info/nightscout/androidaps/data/ProfileStore.java b/app/src/main/java/info/nightscout/androidaps/data/ProfileStore.java index f97d8b5e02..a0b54ec0bb 100644 --- a/app/src/main/java/info/nightscout/androidaps/data/ProfileStore.java +++ b/app/src/main/java/info/nightscout/androidaps/data/ProfileStore.java @@ -1,7 +1,7 @@ package info.nightscout.androidaps.data; -import android.support.annotation.Nullable; -import android.support.v4.util.ArrayMap; +import androidx.annotation.Nullable; +import androidx.collection.ArrayMap; import org.json.JSONException; import org.json.JSONObject; diff --git a/app/src/main/java/info/nightscout/androidaps/data/PumpEnactResult.java b/app/src/main/java/info/nightscout/androidaps/data/PumpEnactResult.java index d38b355634..461d8faa7b 100644 --- a/app/src/main/java/info/nightscout/androidaps/data/PumpEnactResult.java +++ b/app/src/main/java/info/nightscout/androidaps/data/PumpEnactResult.java @@ -45,6 +45,11 @@ public class PumpEnactResult { return this; } + public PumpEnactResult comment(int comment) { + this.comment = MainApp.gs(comment); + return this; + } + public PumpEnactResult duration(int duration) { this.duration = duration; return this; diff --git a/app/src/main/java/info/nightscout/androidaps/data/QuickWizard.java b/app/src/main/java/info/nightscout/androidaps/data/QuickWizard.java deleted file mode 100644 index 80a6e1b457..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/data/QuickWizard.java +++ /dev/null @@ -1,86 +0,0 @@ -package info.nightscout.androidaps.data; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import info.nightscout.androidaps.utils.SP; - -/** - * Created by mike on 12.10.2016. - */ - -public class QuickWizard { - private static Logger log = LoggerFactory.getLogger(QuickWizard.class); - - private JSONArray storage = new JSONArray(); - - public void setData(JSONArray newData) { - storage = newData; - } - - public void save() { - SP.putString("QuickWizard", storage.toString()); - } - - public int size() { - return storage.length(); - } - - public QuickWizardEntry get(int position) { - try { - return new QuickWizardEntry((JSONObject) storage.get(position), position); - } catch (JSONException e) { - log.error("Unhandled exception", e); - } - return null; - } - - public Boolean isActive() { - for (int i = 0; i < storage.length(); i++) { - try { - if (new QuickWizardEntry((JSONObject) storage.get(i), i).isActive()) return true; - } catch (JSONException e) { - log.error("Unhandled exception", e); - } - } - return false; - } - - public QuickWizardEntry getActive() { - for (int i = 0; i < storage.length(); i++) { - QuickWizardEntry entry; - try { - entry = new QuickWizardEntry((JSONObject) storage.get(i), i); - } catch (JSONException e) { - continue; - } - if (entry.isActive()) return entry; - } - return null; - } - - public QuickWizardEntry newEmptyItem() { - return new QuickWizardEntry(); - } - - public void addOrUpdate(QuickWizardEntry newItem) { - if (newItem.position == -1) - storage.put(newItem.storage); - else { - try { - storage.put(newItem.position, newItem.storage); - } catch (JSONException e) { - log.error("Unhandled exception", e); - } - } - save(); - } - - public void remove(int position) { - storage.remove(position); - save(); - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/data/QuickWizard.kt b/app/src/main/java/info/nightscout/androidaps/data/QuickWizard.kt new file mode 100644 index 0000000000..f8bca8c450 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/data/QuickWizard.kt @@ -0,0 +1,52 @@ +package info.nightscout.androidaps.data + +import info.nightscout.androidaps.utils.SP +import org.json.JSONArray +import org.json.JSONObject + +object QuickWizard { + private var storage = JSONArray() + + init { + setData(JSONArray(SP.getString("QuickWizard", "[]"))) + } + + fun getActive(): QuickWizardEntry? { + for (i in 0 until storage.length()) { + val entry = QuickWizardEntry(storage.get(i) as JSONObject, i) + if (entry.isActive) return entry + } + return null + } + + fun setData(newData: JSONArray) { + storage = newData + } + + fun save() { + SP.putString("QuickWizard", storage.toString()) + } + + fun size(): Int = storage.length() + + operator fun get(position: Int): QuickWizardEntry = + QuickWizardEntry(storage.get(position) as JSONObject, position) + + + fun newEmptyItem(): QuickWizardEntry { + return QuickWizardEntry() + } + + fun addOrUpdate(newItem: QuickWizardEntry) { + if (newItem.position == -1) + storage.put(newItem.storage) + else + storage.put(newItem.position, newItem.storage) + save() + } + + fun remove(position: Int) { + storage.remove(position) + save() + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/data/QuickWizardEntry.java b/app/src/main/java/info/nightscout/androidaps/data/QuickWizardEntry.java index 0bfcbb1002..062f6ae391 100644 --- a/app/src/main/java/info/nightscout/androidaps/data/QuickWizardEntry.java +++ b/app/src/main/java/info/nightscout/androidaps/data/QuickWizardEntry.java @@ -50,7 +50,7 @@ public class QuickWizardEntry { useTemptarget: 0 } */ - public QuickWizardEntry() { + QuickWizardEntry() { String emptyData = "{\"buttonText\":\"\",\"carbs\":0,\"validFrom\":0,\"validTo\":86340}"; try { storage = new JSONObject(emptyData); @@ -60,18 +60,17 @@ public class QuickWizardEntry { position = -1; } - public QuickWizardEntry(JSONObject entry, int position) { + QuickWizardEntry(JSONObject entry, int position) { storage = entry; this.position = position; } - public Boolean isActive() { + Boolean isActive() { return Profile.secondsFromMidnight() >= validFrom() && Profile.secondsFromMidnight() <= validTo(); } - public BolusWizard doCalc(Profile profile, TempTarget tempTarget, BgReading lastBG, boolean _synchronized) { - BolusWizard wizard = new BolusWizard(); - + public BolusWizard doCalc(Profile profile, String profileName, BgReading lastBG, boolean _synchronized) { + final TempTarget tempTarget = TreatmentsPlugin.getPlugin().getTempTargetFromHistory(); //BG double bg = 0; if (lastBG != null && useBG() == YES) { @@ -86,11 +85,6 @@ public class QuickWizardEntry { cob = cobInfo.displayCob; } - // Temp target - if (useTempTarget() == NO) { - tempTarget = null; - } - // Bolus IOB boolean bolusIOB = false; if (useBolusIOB() == YES) { @@ -130,8 +124,7 @@ public class QuickWizardEntry { trend = true; } - wizard.doCalc(profile, tempTarget, carbs(), cob, bg, 0d, bolusIOB, basalIOB, superBolus, trend); - return wizard; + return new BolusWizard(profile, profileName, tempTarget, carbs(), cob, bg, 0d, 100, true, useCOB() == YES, bolusIOB, basalIOB, superBolus, useTempTarget() == YES, trend, "QuickWizard"); } public String buttonText() { diff --git a/app/src/main/java/info/nightscout/androidaps/db/BgReading.java b/app/src/main/java/info/nightscout/androidaps/db/BgReading.java index 6fafdc4b6d..8b206e2c78 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/BgReading.java +++ b/app/src/main/java/info/nightscout/androidaps/db/BgReading.java @@ -216,8 +216,8 @@ public class BgReading implements DataPointWithLabelInterface { @Override public int getColor() { String units = ProfileFunctions.getInstance().getProfileUnits(); - Double lowLine = OverviewPlugin.getPlugin().determineLowLine(units); - Double highLine = OverviewPlugin.getPlugin().determineHighLine(units); + Double lowLine = OverviewPlugin.INSTANCE.determineLowLine(units); + Double highLine = OverviewPlugin.INSTANCE.determineHighLine(units); int color = MainApp.gc(R.color.inrange); if (isPrediction()) return getPredectionColor(); diff --git a/app/src/main/java/info/nightscout/androidaps/db/CareportalEvent.java b/app/src/main/java/info/nightscout/androidaps/db/CareportalEvent.java index f0e70c0a25..40fadff684 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/CareportalEvent.java +++ b/app/src/main/java/info/nightscout/androidaps/db/CareportalEvent.java @@ -93,12 +93,22 @@ public class CareportalEvent implements DataPointWithLabelInterface, Interval { return (System.currentTimeMillis() - date) / (60 * 60 * 1000.0); } - public String age() { + public String age(boolean useShortText) { Map diff = computeDiff(date, System.currentTimeMillis()); - if (OverviewFragment.shorttextmode) - return diff.get(TimeUnit.DAYS) + "d" + diff.get(TimeUnit.HOURS) + "h"; - else - return diff.get(TimeUnit.DAYS) + " " + MainApp.gs(R.string.days) + " " + diff.get(TimeUnit.HOURS) + " " + MainApp.gs(R.string.hours); + + String days = " " + MainApp.gs(R.string.days) + " "; + String hours = " " + MainApp.gs(R.string.hours) + " "; + + if (useShortText) { + days = "d"; + hours = "h"; + } + + return diff.get(TimeUnit.DAYS) + days + diff.get(TimeUnit.HOURS) + hours; + } + + public String age() { + return age(OverviewFragment.shorttextmode); } public boolean isOlderThan(double hours) { diff --git a/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java b/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java index 1f071a2f18..d8926fc4c1 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java +++ b/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java @@ -3,7 +3,7 @@ package info.nightscout.androidaps.db; import android.content.Context; import android.database.DatabaseUtils; import android.database.sqlite.SQLiteDatabase; -import android.support.annotation.Nullable; +import androidx.annotation.Nullable; import com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper; import com.j256.ormlite.dao.CloseableIterator; @@ -21,6 +21,8 @@ import org.slf4j.LoggerFactory; import java.sql.SQLException; import java.util.ArrayList; +import java.util.Calendar; +import java.util.GregorianCalendar; import java.util.List; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; @@ -29,6 +31,7 @@ import java.util.concurrent.TimeUnit; import info.nightscout.androidaps.Constants; import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.data.OverlappingIntervals; import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.data.ProfileStore; @@ -240,7 +243,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { new java.util.TimerTask() { @Override public void run() { - MainApp.bus().post(new EventRefreshOverview("resetDatabases")); + RxBus.INSTANCE.send(new EventRefreshOverview("resetDatabases")); } }, 3000 @@ -409,7 +412,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { public void run() { if (L.isEnabled(L.DATABASE)) log.debug("Firing EventNewBg"); - MainApp.bus().post(new EventNewBG(bgReading)); + RxBus.INSTANCE.send(new EventNewBG(bgReading)); scheduledBgPost = null; } } @@ -434,7 +437,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { return null; for (int i = 0; i < bgList.size(); i++) - if (bgList.get(i).value > 39) + if (bgList.get(i).value >= 39) return bgList.get(i); return null; } @@ -534,6 +537,24 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { return tddList; } + public List getTDDsForLastXDays(int days) { + List tddList; + GregorianCalendar gc = new GregorianCalendar(); + gc.add(Calendar.DAY_OF_YEAR, (-1) * days); + + try { + QueryBuilder queryBuilder = getDaoTDD().queryBuilder(); + queryBuilder.orderBy("date", false); + Where where = queryBuilder.where(); + where.ge("date", gc.getTimeInMillis()); + PreparedQuery preparedQuery = queryBuilder.prepare(); + tddList = getDaoTDD().query(preparedQuery); + } catch (SQLException e) { + log.error("Unhandled exception", e); + tddList = new ArrayList<>(); + } + return tddList; + } // ------------- DbRequests handling ------------------- @@ -596,7 +617,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { } } - // -------------------- TREATMENT HANDLING ------------------- + // -------------------- TEMPTARGET HANDLING ------------------- public static void updateEarliestDataChange(long newDate) { if (earliestDataChange == null) { @@ -627,6 +648,23 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { return new ArrayList(); } + public List getTemptargetsDataFromTime(long from, long to, boolean ascending) { + try { + Dao daoTempTargets = getDaoTempTargets(); + List tempTargets; + QueryBuilder queryBuilder = daoTempTargets.queryBuilder(); + queryBuilder.orderBy("date", ascending); + Where where = queryBuilder.where(); + where.between("date", from, to); + PreparedQuery preparedQuery = queryBuilder.prepare(); + tempTargets = daoTempTargets.query(preparedQuery); + return tempTargets; + } catch (SQLException e) { + log.error("Unhandled exception", e); + } + return new ArrayList(); + } + public boolean createOrUpdate(TempTarget tempTarget) { try { TempTarget old; @@ -699,7 +737,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { public void run() { if (L.isEnabled(L.DATABASE)) log.debug("Firing EventTempTargetChange"); - MainApp.bus().post(new EventTempTargetChange()); + RxBus.INSTANCE.send(new EventTempTargetChange()); scheduledTemTargetPost = null; } } @@ -852,6 +890,31 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { log.debug("TEMPBASAL: Already exists from: " + Source.getString(tempBasal.source) + " " + tempBasal.toString()); return false; } + + // search by date (in case its standard record that has become pump record) + QueryBuilder queryBuilder2 = getDaoTemporaryBasal().queryBuilder(); + Where where2 = queryBuilder2.where(); + where2.eq("date", tempBasal.date); + PreparedQuery preparedQuery2 = queryBuilder2.prepare(); + List trList2 = getDaoTemporaryBasal().query(preparedQuery2); + + if (trList2.size() > 0) { + old = trList2.get(0); + + old.copyFromPump(tempBasal); + old.source = Source.PUMP; + + if (L.isEnabled(L.DATABASE)) + log.debug("TEMPBASAL: Updated record with Pump Data : " + Source.getString(tempBasal.source) + " " + tempBasal.toString()); + + getDaoTemporaryBasal().update(old); + + updateEarliestDataChange(tempBasal.date); + scheduleTemporaryBasalChange(); + + return false; + } + getDaoTemporaryBasal().create(tempBasal); if (L.isEnabled(L.DATABASE)) log.debug("TEMPBASAL: New record from: " + Source.getString(tempBasal.source) + " " + tempBasal.toString()); @@ -950,15 +1013,31 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { return new ArrayList(); } + public List getTemporaryBasalsDataFromTime(long from, long to, boolean ascending) { + try { + List tempbasals; + QueryBuilder queryBuilder = getDaoTemporaryBasal().queryBuilder(); + queryBuilder.orderBy("date", ascending); + Where where = queryBuilder.where(); + where.between("date", from, to); + PreparedQuery preparedQuery = queryBuilder.prepare(); + tempbasals = getDaoTemporaryBasal().query(preparedQuery); + return tempbasals; + } catch (SQLException e) { + log.error("Unhandled exception", e); + } + return new ArrayList(); + } + private static void scheduleTemporaryBasalChange() { class PostRunnable implements Runnable { public void run() { if (L.isEnabled(L.DATABASE)) log.debug("Firing EventTempBasalChange"); - MainApp.bus().post(new EventReloadTempBasalData()); - MainApp.bus().post(new EventTempBasalChange()); + RxBus.INSTANCE.send(new EventReloadTempBasalData()); + RxBus.INSTANCE.send(new EventTempBasalChange()); if (earliestDataChange != null) - MainApp.bus().post(new EventNewHistoryData(earliestDataChange)); + RxBus.INSTANCE.send(new EventNewHistoryData(earliestDataChange)); earliestDataChange = null; scheduledTemBasalsPost = null; } @@ -1076,6 +1155,29 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { return null; } + + public TemporaryBasal findTempBasalByPumpId(Long pumpId) { + try { + QueryBuilder queryBuilder = null; + queryBuilder = getDaoTemporaryBasal().queryBuilder(); + queryBuilder.orderBy("date", false); + Where where = queryBuilder.where(); + where.eq("pumpId", pumpId); + PreparedQuery preparedQuery = queryBuilder.prepare(); + List list = getDaoTemporaryBasal().query(preparedQuery); + + if (list.size() > 0) + return list.get(0); + else + return null; + + } catch (SQLException e) { + log.error("Unhandled exception", e); + } + return null; + } + + // ------------ ExtendedBolus handling --------------- public boolean createOrUpdate(ExtendedBolus extendedBolus) { @@ -1268,9 +1370,9 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { public void run() { if (L.isEnabled(L.DATABASE)) log.debug("Firing EventExtendedBolusChange"); - MainApp.bus().post(new EventReloadTreatmentData(new EventExtendedBolusChange())); + RxBus.INSTANCE.send(new EventReloadTreatmentData(new EventExtendedBolusChange())); if (earliestDataChange != null) - MainApp.bus().post(new EventNewHistoryData(earliestDataChange)); + RxBus.INSTANCE.send(new EventNewHistoryData(earliestDataChange)); earliestDataChange = null; scheduledExtendedBolusPost = null; } @@ -1354,6 +1456,23 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { return new ArrayList<>(); } + public List getCareportalEvents(long start, long end, boolean ascending) { + try { + List careportalEvents; + QueryBuilder queryBuilder = getDaoCareportalEvents().queryBuilder(); + queryBuilder.orderBy("date", ascending); + Where where = queryBuilder.where(); + where.between("date", start, end); + PreparedQuery preparedQuery = queryBuilder.prepare(); + careportalEvents = getDaoCareportalEvents().query(preparedQuery); + preprocessOpenAPSOfflineEvents(careportalEvents); + return careportalEvents; + } catch (SQLException e) { + log.error("Unhandled exception", e); + } + return new ArrayList<>(); + } + public void preprocessOpenAPSOfflineEvents(List list) { OverlappingIntervals offlineEvents = new OverlappingIntervals(); for (int i = 0; i < list.size(); i++) { @@ -1457,7 +1576,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { public void run() { if (L.isEnabled(L.DATABASE)) log.debug("Firing scheduleCareportalEventChange"); - MainApp.bus().post(new EventCareportalEventChange()); + RxBus.INSTANCE.send(new EventCareportalEventChange()); scheduledCareportalEventPost = null; } } @@ -1507,6 +1626,24 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { return new ArrayList<>(); } + public List getProfileSwitchEventsFromTime(long from, long to, boolean ascending) { + try { + Dao daoProfileSwitch = getDaoProfileSwitch(); + List profileSwitches; + QueryBuilder queryBuilder = daoProfileSwitch.queryBuilder(); + queryBuilder.orderBy("date", ascending); + queryBuilder.limit(100L); + Where where = queryBuilder.where(); + where.between("date", from, to); + PreparedQuery preparedQuery = queryBuilder.prepare(); + profileSwitches = daoProfileSwitch.query(preparedQuery); + return profileSwitches; + } catch (SQLException e) { + log.error("Unhandled exception", e); + } + return new ArrayList<>(); + } + public boolean createOrUpdate(ProfileSwitch profileSwitch) { try { ProfileSwitch old; @@ -1582,8 +1719,8 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { public void run() { if (L.isEnabled(L.DATABASE)) log.debug("Firing EventProfileNeedsUpdate"); - MainApp.bus().post(new EventReloadProfileSwitchData()); - MainApp.bus().post(new EventProfileNeedsUpdate()); + RxBus.INSTANCE.send(new EventReloadProfileSwitchData()); + RxBus.INSTANCE.send(new EventProfileNeedsUpdate()); scheduledProfileSwitchEventPost = null; } } diff --git a/app/src/main/java/info/nightscout/androidaps/db/DbObjectBase.java b/app/src/main/java/info/nightscout/androidaps/db/DbObjectBase.java new file mode 100644 index 0000000000..a0c7f4bd7b --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/db/DbObjectBase.java @@ -0,0 +1,9 @@ +package info.nightscout.androidaps.db; + +public interface DbObjectBase { + + long getDate(); + + long getPumpId(); + +} diff --git a/app/src/main/java/info/nightscout/androidaps/db/ExtendedBolus.java b/app/src/main/java/info/nightscout/androidaps/db/ExtendedBolus.java index 2f11b752c8..25d2c308c1 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/ExtendedBolus.java +++ b/app/src/main/java/info/nightscout/androidaps/db/ExtendedBolus.java @@ -18,12 +18,14 @@ import java.util.Objects; import info.nightscout.androidaps.Constants; import info.nightscout.androidaps.data.Iob; import info.nightscout.androidaps.data.IobTotal; +import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.interfaces.InsulinInterface; import info.nightscout.androidaps.interfaces.Interval; import info.nightscout.androidaps.logging.L; import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.general.overview.graphExtensions.DataPointWithLabelInterface; import info.nightscout.androidaps.plugins.general.overview.graphExtensions.PointsWithLabelGraphSeries; +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult; import info.nightscout.androidaps.plugins.treatments.Treatment; import info.nightscout.androidaps.utils.DateUtil; import info.nightscout.androidaps.utils.DecimalFormatter; @@ -219,7 +221,7 @@ public class ExtendedBolus implements Interval, DataPointWithLabelInterface { IobTotal result = new IobTotal(time); InsulinInterface insulinInterface = ConfigBuilderPlugin.getPlugin().getActiveInsulin(); - int realDuration = getDurationToTime(time); + double realDuration = getDurationToTime(time); if (realDuration > 0) { double dia_ago = time - dia * 60 * 60 * 1000; @@ -247,6 +249,56 @@ public class ExtendedBolus implements Interval, DataPointWithLabelInterface { return result; } + public IobTotal iobCalc(long time, Profile profile, AutosensResult lastAutosensResult, boolean exercise_mode, int half_basal_exercise_target, boolean isTempTarget) { + IobTotal result = new IobTotal(time); + InsulinInterface insulinInterface = ConfigBuilderPlugin.getPlugin().getActiveInsulin(); + + double realDuration = getDurationToTime(time); + double netBasalAmount = 0d; + + double sensitivityRatio = lastAutosensResult.ratio; + double normalTarget = 100; + + if (exercise_mode && isTempTarget && profile.getTarget() >= normalTarget + 5) { + // w/ target 100, temp target 110 = .89, 120 = 0.8, 140 = 0.67, 160 = .57, and 200 = .44 + // e.g.: Sensitivity ratio set to 0.8 based on temp target of 120; Adjusting basal from 1.65 to 1.35; ISF from 58.9 to 73.6 + double c = half_basal_exercise_target - normalTarget; + sensitivityRatio = c / (c + profile.getTarget() - normalTarget); + } + + if (realDuration > 0) { + double netBasalRate; + double dia_ago = time - dia * 60 * 60 * 1000; + int aboutFiveMinIntervals = (int) Math.ceil(realDuration / 5d); + double spacing = realDuration / aboutFiveMinIntervals; + + for (long j = 0L; j < aboutFiveMinIntervals; j++) { + // find middle of the interval + long calcdate = (long) (date + j * spacing * 60 * 1000 + 0.5d * spacing * 60 * 1000); + + double basalRate = profile.getBasal(calcdate); + double basalRateCorrection = basalRate * (sensitivityRatio - 1); + + + netBasalRate = absoluteRate() - basalRateCorrection; + + if (calcdate > dia_ago && calcdate <= time) { + double tempBolusSize = netBasalRate * spacing / 60d; + + Treatment tempBolusPart = new Treatment(); + tempBolusPart.insulin = tempBolusSize; + tempBolusPart.date = calcdate; + + Iob aIOB = insulinInterface.iobCalcForTreatment(tempBolusPart, time, dia); + result.iob += aIOB.iobContrib; + result.activity += aIOB.activityContrib; + result.extendedBolusInsulin += tempBolusPart.insulin; + } + } + } + return result; + } + public int getRealDuration() { return getDurationToTime(System.currentTimeMillis()); } diff --git a/app/src/main/java/info/nightscout/androidaps/db/ProfileSwitch.java b/app/src/main/java/info/nightscout/androidaps/db/ProfileSwitch.java index 0e589f0595..62b963e9ef 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/ProfileSwitch.java +++ b/app/src/main/java/info/nightscout/androidaps/db/ProfileSwitch.java @@ -1,7 +1,9 @@ package info.nightscout.androidaps.db; import android.graphics.Color; -import android.support.annotation.Nullable; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.j256.ormlite.field.DatabaseField; import com.j256.ormlite.table.DatabaseTable; @@ -18,11 +20,13 @@ import info.nightscout.androidaps.R; import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.interfaces.Interval; import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification; import info.nightscout.androidaps.plugins.general.overview.graphExtensions.DataPointWithLabelInterface; import info.nightscout.androidaps.plugins.general.overview.graphExtensions.PointsWithLabelGraphSeries; import info.nightscout.androidaps.plugins.general.overview.notifications.Notification; import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin; +import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; import info.nightscout.androidaps.utils.DateUtil; import info.nightscout.androidaps.utils.DecimalFormatter; import info.nightscout.androidaps.utils.T; @@ -78,12 +82,12 @@ public class ProfileSwitch implements Interval, DataPointWithLabelInterface { return this; } - public ProfileSwitch source(int source) { + public ProfileSwitch source(int source) { this.source = source; return this; } - public ProfileSwitch duration(int duration) { + public ProfileSwitch duration(int duration) { this.durationInMinutes = duration; return this; } @@ -107,7 +111,7 @@ public class ProfileSwitch implements Interval, DataPointWithLabelInterface { */ public String getCustomizedName() { String name = profileName; - if(LocalProfilePlugin.LOCAL_PROFILE.equals(name)){ + if (LocalProfilePlugin.LOCAL_PROFILE.equals(name)) { name = DecimalFormatter.to2Decimal(getProfileObject().percentageBasalSum()) + "U "; } if (isCPP) { @@ -156,7 +160,7 @@ public class ProfileSwitch implements Interval, DataPointWithLabelInterface { // -------- Interval interface --------- - Long cuttedEnd = null; + private Long cuttedEnd = null; public long durationInMsec() { return durationInMinutes * 60 * 1000L; @@ -212,16 +216,17 @@ public class ProfileSwitch implements Interval, DataPointWithLabelInterface { @Override public boolean isValid() { - boolean isValid = getProfileObject() != null && getProfileObject().isValid(DateUtil.dateAndTimeString(date)); - if (!isValid) + ProfileSwitch active = TreatmentsPlugin.getPlugin().getProfileSwitchFromHistory(DateUtil.now()); + long activeProfileSwitchDate = active != null ? active.date : -1L; + if (!isValid && date == activeProfileSwitchDate) createNotificationInvalidProfile(DateUtil.dateAndTimeString(date)); return isValid; } - public void createNotificationInvalidProfile(String detail) { + private void createNotificationInvalidProfile(String detail) { Notification notification = new Notification(Notification.ZERO_VALUE_IN_PROFILE, String.format(MainApp.gs(R.string.zerovalueinprofile), detail), Notification.LOW, 5); - MainApp.bus().post(new EventNewNotification(notification)); + RxBus.INSTANCE.send(new EventNewNotification(notification)); } public static boolean isEvent5minBack(List list, long time, boolean zeroDurationOnly) { @@ -290,6 +295,7 @@ public class ProfileSwitch implements Interval, DataPointWithLabelInterface { return Color.CYAN; } + @NonNull public String toString() { return "ProfileSwitch{" + "date=" + date + diff --git a/app/src/main/java/info/nightscout/androidaps/db/TDD.java b/app/src/main/java/info/nightscout/androidaps/db/TDD.java index 9ca849b7b6..93a228316c 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/TDD.java +++ b/app/src/main/java/info/nightscout/androidaps/db/TDD.java @@ -6,9 +6,8 @@ import com.j256.ormlite.table.DatabaseTable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.Objects; - import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.pump.common.utils.DateTimeUtil; /** * Created by mike on 20.09.2017. @@ -45,4 +44,16 @@ public class TDD { this.basal = basal; this.total = total; } + + + @Override + public String toString() { + return "TDD [" + + "date=" + date + + "date(str)=" + DateTimeUtil.toStringFromTimeInMillis(date) + + ", bolus=" + bolus + + ", basal=" + basal + + ", total=" + total + + ']'; + } } diff --git a/app/src/main/java/info/nightscout/androidaps/db/TempTarget.java b/app/src/main/java/info/nightscout/androidaps/db/TempTarget.java index a496af802b..ef60d02911 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/TempTarget.java +++ b/app/src/main/java/info/nightscout/androidaps/db/TempTarget.java @@ -9,6 +9,9 @@ import org.slf4j.LoggerFactory; import java.util.Objects; import info.nightscout.androidaps.Constants; +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.interfaces.Interval; import info.nightscout.androidaps.logging.L; import info.nightscout.androidaps.utils.DateUtil; @@ -191,4 +194,11 @@ public class TempTarget implements Interval { '}'; } + public String friendlyDescription(String units) { + return Profile.toTargetRangeString(low, high, Constants.MGDL, units) + + units + + "@" + MainApp.gs(R.string.mins, durationInMinutes) + + (reason != null && !reason.equals("") ? "(" + reason + ")" : ""); + } + } diff --git a/app/src/main/java/info/nightscout/androidaps/db/TemporaryBasal.java b/app/src/main/java/info/nightscout/androidaps/db/TemporaryBasal.java index 336332790a..f2a10cf4a4 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/TemporaryBasal.java +++ b/app/src/main/java/info/nightscout/androidaps/db/TemporaryBasal.java @@ -17,6 +17,7 @@ import info.nightscout.androidaps.interfaces.Interval; import info.nightscout.androidaps.logging.L; import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult; import info.nightscout.androidaps.plugins.treatments.Treatment; import info.nightscout.androidaps.utils.DateUtil; import info.nightscout.androidaps.utils.DecimalFormatter; @@ -27,7 +28,7 @@ import info.nightscout.androidaps.utils.SP; */ @DatabaseTable(tableName = DatabaseHelper.DATABASE_TEMPORARYBASALS) -public class TemporaryBasal implements Interval { +public class TemporaryBasal implements Interval, DbObjectBase { private static Logger log = LoggerFactory.getLogger(L.DATABASE); @DatabaseField(id = true) @@ -156,6 +157,14 @@ public class TemporaryBasal implements Interval { netExtendedRate = t.netExtendedRate; } + public void copyFromPump(TemporaryBasal t) { + durationInMinutes = t.durationInMinutes; + isAbsolute = t.isAbsolute; + percentRate = t.percentRate; + absoluteRate = t.absoluteRate; + pumpId = t.pumpId; + } + // -------- Interval interface --------- Long cuttedEnd = null; @@ -233,7 +242,7 @@ public class TemporaryBasal implements Interval { double netBasalAmount = 0d; if (realDuration > 0) { - double netBasalRate = 0d; + double netBasalRate; double dia = profile.getDia(); double dia_ago = time - dia * 60 * 60 * 1000; int aboutFiveMinIntervals = (int) Math.ceil(realDuration / 5d); @@ -243,10 +252,8 @@ public class TemporaryBasal implements Interval { // find middle of the interval long calcdate = (long) (date + j * tempBolusSpacing * 60 * 1000 + 0.5d * tempBolusSpacing * 60 * 1000); - Double basalRate = profile.getBasal(calcdate); + double basalRate = profile.getBasal(calcdate); - if (basalRate == null) - continue; if (isAbsolute) { netBasalRate = absoluteRate - basalRate; } else { @@ -276,6 +283,73 @@ public class TemporaryBasal implements Interval { return result; } + public IobTotal iobCalc(long time, Profile profile, AutosensResult lastAutosensResult, boolean exercise_mode, int half_basal_exercise_target, boolean isTempTarget) { + + if (isFakeExtended) { + log.error("iobCalc should only be called on Extended boluses separately"); + return new IobTotal(time); + } + + IobTotal result = new IobTotal(time); + InsulinInterface insulinInterface = ConfigBuilderPlugin.getPlugin().getActiveInsulin(); + + double realDuration = getDurationToTime(time); + double netBasalAmount = 0d; + + double sensitivityRatio = lastAutosensResult.ratio; + double normalTarget = 100; + + if (exercise_mode && isTempTarget && profile.getTarget() >= normalTarget + 5) { + // w/ target 100, temp target 110 = .89, 120 = 0.8, 140 = 0.67, 160 = .57, and 200 = .44 + // e.g.: Sensitivity ratio set to 0.8 based on temp target of 120; Adjusting basal from 1.65 to 1.35; ISF from 58.9 to 73.6 + double c = half_basal_exercise_target - normalTarget; + sensitivityRatio = c / (c + profile.getTarget() - normalTarget); + } + + if (realDuration > 0) { + double netBasalRate; + double dia = profile.getDia(); + double dia_ago = time - dia * 60 * 60 * 1000; + int aboutFiveMinIntervals = (int) Math.ceil(realDuration / 5d); + double tempBolusSpacing = realDuration / aboutFiveMinIntervals; + + for (long j = 0L; j < aboutFiveMinIntervals; j++) { + // find middle of the interval + long calcdate = (long) (date + j * tempBolusSpacing * 60 * 1000 + 0.5d * tempBolusSpacing * 60 * 1000); + + double basalRate = profile.getBasal(calcdate); + basalRate *= sensitivityRatio; + + if (isAbsolute) { + netBasalRate = absoluteRate - basalRate; + } else { + double abs = percentRate / 100d * profile.getBasal(calcdate); + netBasalRate = abs - basalRate; + } + + if (calcdate > dia_ago && calcdate <= time) { + double tempBolusSize = netBasalRate * tempBolusSpacing / 60d; + netBasalAmount += tempBolusSize; + + Treatment tempBolusPart = new Treatment(); + tempBolusPart.insulin = tempBolusSize; + tempBolusPart.date = calcdate; + + Iob aIOB = insulinInterface.iobCalcForTreatment(tempBolusPart, time, dia); + result.basaliob += aIOB.iobContrib; + result.activity += aIOB.activityContrib; + result.netbasalinsulin += tempBolusPart.insulin; + if (tempBolusPart.insulin > 0) { + result.hightempinsulin += tempBolusPart.insulin; + } + } + result.netRatio = netBasalRate; // ratio at the end of interval + } + } + result.netInsulin = netBasalAmount; + return result; + } + public int getRealDuration() { return getDurationToTime(System.currentTimeMillis()); } @@ -331,8 +405,10 @@ public class TemporaryBasal implements Interval { if (isFakeExtended) { Profile profile = ProfileFunctions.getInstance().getProfile(); + if (profile == null) + return "null"; Double currentBasalRate = profile.getBasal(); - double rate = (currentBasalRate == null) ? 0d : (currentBasalRate + netExtendedRate); + double rate = currentBasalRate + netExtendedRate; return getCalcuatedPercentageIfNeeded() + DecimalFormatter.to2Decimal(rate) + "U/h (" + DecimalFormatter.to2Decimal(netExtendedRate) + "E) @" + DateUtil.timeString(date) + " " + getRealDuration() + "/" + durationInMinutes + "'"; @@ -350,12 +426,14 @@ public class TemporaryBasal implements Interval { public String toStringShort() { if (isAbsolute || isFakeExtended) { - double rate = 0d; + double rate; if (isFakeExtended) { Profile profile = ProfileFunctions.getInstance().getProfile(); - Double currentBasalRate = profile.getBasal(); - rate = (currentBasalRate == null) ? 0d : (currentBasalRate + netExtendedRate); - } else if (isAbsolute) { + if (profile == null) + return "null"; + double currentBasalRate = profile.getBasal(); + rate = currentBasalRate + netExtendedRate; + } else { rate = absoluteRate; } @@ -375,24 +453,25 @@ public class TemporaryBasal implements Interval { } private String getCalcuatedPercentageIfNeeded() { + Profile profile = ProfileFunctions.getInstance().getProfile(); + + if (profile == null) + return "null"; + if (isAbsolute || isFakeExtended) { - double rate = 0d; + double rate; if (isFakeExtended) { - Profile profile = ProfileFunctions.getInstance().getProfile(); - Double currentBasalRate = profile.getBasal(); - rate = (currentBasalRate == null) ? 0d : (currentBasalRate + netExtendedRate); - } else if (isAbsolute) { + double currentBasalRate = profile.getBasal(); + rate = currentBasalRate + netExtendedRate; + } else { rate = absoluteRate; } if (SP.getBoolean(R.string.key_danar_visualizeextendedaspercentage, false) && SP.getBoolean(R.string.key_danar_useextended, false)) { - Profile profile = ProfileFunctions.getInstance().getProfile(); - if (profile != null) { - double basal = profile.getBasal(); - if (basal != 0) { - return Math.round(rate * 100d / basal) + "% "; - } + double basal = profile.getBasal(); + if (basal != 0) { + return Math.round(rate * 100d / basal) + "% "; } } } @@ -400,14 +479,18 @@ public class TemporaryBasal implements Interval { } public String toStringVeryShort() { + Profile profile = ProfileFunctions.getInstance().getProfile(); + + if (profile == null) + return "null"; + if (isAbsolute || isFakeExtended) { - double rate = 0d; + double rate; if (isFakeExtended) { - Profile profile = ProfileFunctions.getInstance().getProfile(); - Double currentBasalRate = profile.getBasal(); - rate = (currentBasalRate == null) ? 0d : (currentBasalRate + netExtendedRate); - } else if (isAbsolute) { + double currentBasalRate = profile.getBasal(); + rate = currentBasalRate + netExtendedRate; + } else { rate = absoluteRate; } return DecimalFormatter.to2Decimal(rate) + "U/h "; @@ -416,4 +499,13 @@ public class TemporaryBasal implements Interval { } } + @Override + public long getDate() { + return this.date; + } + + @Override + public long getPumpId() { + return this.pumpId; + } } diff --git a/app/src/main/java/info/nightscout/androidaps/events/Event.java b/app/src/main/java/info/nightscout/androidaps/events/Event.java deleted file mode 100644 index 864d55d6f7..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/events/Event.java +++ /dev/null @@ -1,16 +0,0 @@ -package info.nightscout.androidaps.events; - -import org.apache.commons.lang3.builder.ReflectionToStringBuilder; -import org.apache.commons.lang3.builder.ToStringStyle; - -/** Base class for all events posted on the event bus. */ -public abstract class Event { - static { - ReflectionToStringBuilder.setDefaultStyle(ToStringStyle.SHORT_PREFIX_STYLE); - } - - @Override - public String toString() { - return ReflectionToStringBuilder.toString(this); - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/events/Event.kt b/app/src/main/java/info/nightscout/androidaps/events/Event.kt new file mode 100644 index 0000000000..a44f65e836 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/events/Event.kt @@ -0,0 +1,18 @@ +package info.nightscout.androidaps.events + +import org.apache.commons.lang3.builder.ReflectionToStringBuilder +import org.apache.commons.lang3.builder.ToStringStyle + +/** Base class for all events posted on the event bus. */ +abstract class Event { + + override fun toString(): String { + return ReflectionToStringBuilder.toString(this) + } + + companion object { + init { + ReflectionToStringBuilder.setDefaultStyle(ToStringStyle.SHORT_PREFIX_STYLE) + } + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventAcceptOpenLoopChange.java b/app/src/main/java/info/nightscout/androidaps/events/EventAcceptOpenLoopChange.java deleted file mode 100644 index 2dfbf9ae35..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/events/EventAcceptOpenLoopChange.java +++ /dev/null @@ -1,5 +0,0 @@ -package info.nightscout.androidaps.events; - -/** Base class for events to update the UI, mostly a specific tab. */ -public class EventAcceptOpenLoopChange extends Event { -} diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventAcceptOpenLoopChange.kt b/app/src/main/java/info/nightscout/androidaps/events/EventAcceptOpenLoopChange.kt new file mode 100644 index 0000000000..552564edfc --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/events/EventAcceptOpenLoopChange.kt @@ -0,0 +1,3 @@ +package info.nightscout.androidaps.events + +class EventAcceptOpenLoopChange : Event() diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventAppExit.java b/app/src/main/java/info/nightscout/androidaps/events/EventAppExit.java deleted file mode 100644 index 9ce91a9a39..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/events/EventAppExit.java +++ /dev/null @@ -1,7 +0,0 @@ -package info.nightscout.androidaps.events; - -/** - * Created by mike on 07.07.2016. - */ -public class EventAppExit extends Event { -} diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventAppExit.kt b/app/src/main/java/info/nightscout/androidaps/events/EventAppExit.kt new file mode 100644 index 0000000000..640b586f5f --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/events/EventAppExit.kt @@ -0,0 +1,3 @@ +package info.nightscout.androidaps.events + +class EventAppExit : Event() \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventAppInitialized.java b/app/src/main/java/info/nightscout/androidaps/events/EventAppInitialized.java deleted file mode 100644 index 17262cfb85..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/events/EventAppInitialized.java +++ /dev/null @@ -1,8 +0,0 @@ -package info.nightscout.androidaps.events; - -/** - * Created by mike on 23.01.2018. - */ - -public class EventAppInitialized extends Event { -} diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventAppInitialized.kt b/app/src/main/java/info/nightscout/androidaps/events/EventAppInitialized.kt new file mode 100644 index 0000000000..293f9698f2 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/events/EventAppInitialized.kt @@ -0,0 +1,3 @@ +package info.nightscout.androidaps.events + +class EventAppInitialized : Event() \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventBolusRequested.java b/app/src/main/java/info/nightscout/androidaps/events/EventBolusRequested.java deleted file mode 100644 index cb727758bb..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/events/EventBolusRequested.java +++ /dev/null @@ -1,21 +0,0 @@ -package info.nightscout.androidaps.events; - -/** - * Created by adrian on 07/02/17. - */ - -public class EventBolusRequested extends Event { - private double amount; - - public EventBolusRequested (double amount){ - this.amount = amount; - } - - public double getAmount() { - return amount; - } - - public void setAmount(double amount) { - this.amount = amount; - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventBolusRequested.kt b/app/src/main/java/info/nightscout/androidaps/events/EventBolusRequested.kt new file mode 100644 index 0000000000..a528ef1656 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/events/EventBolusRequested.kt @@ -0,0 +1,3 @@ +package info.nightscout.androidaps.events + +class EventBolusRequested(var amount: Double) : Event() \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventCareportalEventChange.java b/app/src/main/java/info/nightscout/androidaps/events/EventCareportalEventChange.java deleted file mode 100644 index 9b47ed39cb..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/events/EventCareportalEventChange.java +++ /dev/null @@ -1,8 +0,0 @@ -package info.nightscout.androidaps.events; - -/** - * Created by mike on 25.05.2017. - */ - -public class EventCareportalEventChange extends Event { -} diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventCareportalEventChange.kt b/app/src/main/java/info/nightscout/androidaps/events/EventCareportalEventChange.kt new file mode 100644 index 0000000000..e7d52d86c0 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/events/EventCareportalEventChange.kt @@ -0,0 +1,3 @@ +package info.nightscout.androidaps.events + +class EventCareportalEventChange : Event() diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventChargingState.java b/app/src/main/java/info/nightscout/androidaps/events/EventChargingState.java deleted file mode 100644 index bcd9061133..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/events/EventChargingState.java +++ /dev/null @@ -1,13 +0,0 @@ -package info.nightscout.androidaps.events; - -public class EventChargingState { - - public boolean isCharging = false; - - public EventChargingState() {} - - public EventChargingState(boolean isCharging) { - this.isCharging = isCharging; - } - -} diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventChargingState.kt b/app/src/main/java/info/nightscout/androidaps/events/EventChargingState.kt new file mode 100644 index 0000000000..f9ff60a71d --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/events/EventChargingState.kt @@ -0,0 +1,3 @@ +package info.nightscout.androidaps.events + +class EventChargingState(val isCharging: Boolean) : Event() diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventConfigBuilderChange.java b/app/src/main/java/info/nightscout/androidaps/events/EventConfigBuilderChange.java deleted file mode 100644 index ad5f558fe8..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/events/EventConfigBuilderChange.java +++ /dev/null @@ -1,8 +0,0 @@ -package info.nightscout.androidaps.events; - -/** - * Created by mike on 17.02.2017. - */ - -public class EventConfigBuilderChange extends Event { -} diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventConfigBuilderChange.kt b/app/src/main/java/info/nightscout/androidaps/events/EventConfigBuilderChange.kt new file mode 100644 index 0000000000..b674374fad --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/events/EventConfigBuilderChange.kt @@ -0,0 +1,3 @@ +package info.nightscout.androidaps.events + +class EventConfigBuilderChange : Event() diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventCustomActionsChanged.kt b/app/src/main/java/info/nightscout/androidaps/events/EventCustomActionsChanged.kt new file mode 100644 index 0000000000..d75bf612ce --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/events/EventCustomActionsChanged.kt @@ -0,0 +1,3 @@ +package info.nightscout.androidaps.events + +class EventCustomActionsChanged : Event() diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventCustomCalculationFinished.java b/app/src/main/java/info/nightscout/androidaps/events/EventCustomCalculationFinished.java deleted file mode 100644 index e52761dc58..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/events/EventCustomCalculationFinished.java +++ /dev/null @@ -1,8 +0,0 @@ -package info.nightscout.androidaps.events; - -/** - * Created by mike on 13.02.2018. - */ - -public class EventCustomCalculationFinished extends Event { -} diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventCustomCalculationFinished.kt b/app/src/main/java/info/nightscout/androidaps/events/EventCustomCalculationFinished.kt new file mode 100644 index 0000000000..f6092b395d --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/events/EventCustomCalculationFinished.kt @@ -0,0 +1,3 @@ +package info.nightscout.androidaps.events + +class EventCustomCalculationFinished : Event() \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventExtendedBolusChange.java b/app/src/main/java/info/nightscout/androidaps/events/EventExtendedBolusChange.java deleted file mode 100644 index 8881b0ecc1..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/events/EventExtendedBolusChange.java +++ /dev/null @@ -1,8 +0,0 @@ -package info.nightscout.androidaps.events; - -/** - * Created by mike on 15.05.2017. - */ - -public class EventExtendedBolusChange extends Event { -} diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventExtendedBolusChange.kt b/app/src/main/java/info/nightscout/androidaps/events/EventExtendedBolusChange.kt new file mode 100644 index 0000000000..4ed0ca5ffe --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/events/EventExtendedBolusChange.kt @@ -0,0 +1,3 @@ +package info.nightscout.androidaps.events + +class EventExtendedBolusChange : EventLoop() diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventFeatureRunning.java b/app/src/main/java/info/nightscout/androidaps/events/EventFeatureRunning.java deleted file mode 100644 index 0d07cd6c61..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/events/EventFeatureRunning.java +++ /dev/null @@ -1,36 +0,0 @@ -package info.nightscout.androidaps.events; - -/** - * Created by jamorham on 07/02/2018. - * - * Event to indicate that an app feature is being used, for example bolus wizard being opened - * - * The purpose this has been created for is to enable opportunistic connection to the pump - * so that it is already connected before the user wishes to enact a pump function - * - */ - -public class EventFeatureRunning extends Event { - - private Feature feature = Feature.UNKNOWN; - - public EventFeatureRunning() { - } - - public EventFeatureRunning(Feature feature) { - this.feature = feature; - } - - public Feature getFeature() { - return feature; - } - - public enum Feature { - UNKNOWN, - MAIN, - WIZARD, - - JUST_ADD_MORE_HERE - } - -} diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventFoodDatabaseChanged.java b/app/src/main/java/info/nightscout/androidaps/events/EventFoodDatabaseChanged.java deleted file mode 100644 index 48e6be3a14..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/events/EventFoodDatabaseChanged.java +++ /dev/null @@ -1,8 +0,0 @@ -package info.nightscout.androidaps.events; - -/** - * Created by mike on 20.09.2017. - */ - -public class EventFoodDatabaseChanged extends Event { -} diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventFoodDatabaseChanged.kt b/app/src/main/java/info/nightscout/androidaps/events/EventFoodDatabaseChanged.kt new file mode 100644 index 0000000000..83402c3cb6 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/events/EventFoodDatabaseChanged.kt @@ -0,0 +1,3 @@ +package info.nightscout.androidaps.events + +class EventFoodDatabaseChanged : Event() \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventInitializationChanged.java b/app/src/main/java/info/nightscout/androidaps/events/EventInitializationChanged.java deleted file mode 100644 index f2bef1d3d0..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/events/EventInitializationChanged.java +++ /dev/null @@ -1,8 +0,0 @@ -package info.nightscout.androidaps.events; - -/** - * Created by mike on 13.12.2016. - */ - -public class EventInitializationChanged extends Event { -} diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventInitializationChanged.kt b/app/src/main/java/info/nightscout/androidaps/events/EventInitializationChanged.kt new file mode 100644 index 0000000000..33ab0062c5 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/events/EventInitializationChanged.kt @@ -0,0 +1,3 @@ +package info.nightscout.androidaps.events + +class EventInitializationChanged : Event() diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventLocationChange.kt b/app/src/main/java/info/nightscout/androidaps/events/EventLocationChange.kt new file mode 100644 index 0000000000..fee6c9f800 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/events/EventLocationChange.kt @@ -0,0 +1,5 @@ +package info.nightscout.androidaps.events + +import android.location.Location + +class EventLocationChange(var location: Location) : Event() diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventLoop.java b/app/src/main/java/info/nightscout/androidaps/events/EventLoop.java deleted file mode 100644 index d694d52537..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/events/EventLoop.java +++ /dev/null @@ -1,5 +0,0 @@ -package info.nightscout.androidaps.events; - -/** Supeclass for all events concerned with input or output into or from the LoopPlugin. */ -public abstract class EventLoop extends Event { -} diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventLoop.kt b/app/src/main/java/info/nightscout/androidaps/events/EventLoop.kt new file mode 100644 index 0000000000..dd28e2323b --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/events/EventLoop.kt @@ -0,0 +1,4 @@ +package info.nightscout.androidaps.events + +/** Supeclass for all events concerned with input or output into or from the LoopPlugin. */ +abstract class EventLoop : Event() diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventNetworkChange.java b/app/src/main/java/info/nightscout/androidaps/events/EventNetworkChange.java deleted file mode 100644 index 546d6f8624..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/events/EventNetworkChange.java +++ /dev/null @@ -1,17 +0,0 @@ -package info.nightscout.androidaps.events; - - -import info.nightscout.androidaps.utils.StringUtils; - -public class EventNetworkChange extends Event { - - public boolean mobileConnected = false; - public boolean wifiConnected = false; - - public String ssid = ""; - public boolean roaming = false; - - public String getSsid() { - return StringUtils.removeSurroundingQuotes(ssid); - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventNetworkChange.kt b/app/src/main/java/info/nightscout/androidaps/events/EventNetworkChange.kt new file mode 100644 index 0000000000..62c8bdd13e --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/events/EventNetworkChange.kt @@ -0,0 +1,16 @@ +package info.nightscout.androidaps.events + +import info.nightscout.androidaps.utils.StringUtils + +class EventNetworkChange : Event() { + + var mobileConnected = false + var wifiConnected = false + + var ssid = "" + var roaming = false + + fun connectedSsid(): String { + return StringUtils.removeSurroundingQuotes(ssid) + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventNewBG.java b/app/src/main/java/info/nightscout/androidaps/events/EventNewBG.java deleted file mode 100644 index dc4d434e0a..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/events/EventNewBG.java +++ /dev/null @@ -1,17 +0,0 @@ -package info.nightscout.androidaps.events; - -import android.support.annotation.Nullable; - -import info.nightscout.androidaps.db.BgReading; - -/** - * Created by mike on 05.06.2016. - */ -public class EventNewBG extends EventLoop { - @Nullable - public final BgReading bgReading; - - public EventNewBG(BgReading bgReading) { - this.bgReading = bgReading; - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventNewBG.kt b/app/src/main/java/info/nightscout/androidaps/events/EventNewBG.kt new file mode 100644 index 0000000000..08c05407c9 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/events/EventNewBG.kt @@ -0,0 +1,5 @@ +package info.nightscout.androidaps.events + +import info.nightscout.androidaps.db.BgReading + +class EventNewBG(val bgReading: BgReading?) : EventLoop() \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventNewBasalProfile.java b/app/src/main/java/info/nightscout/androidaps/events/EventNewBasalProfile.java deleted file mode 100644 index f26a310b6b..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/events/EventNewBasalProfile.java +++ /dev/null @@ -1,7 +0,0 @@ -package info.nightscout.androidaps.events; - -/** - * Created by mike on 04.06.2016. - */ -public class EventNewBasalProfile extends Event { -} diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventNewBasalProfile.kt b/app/src/main/java/info/nightscout/androidaps/events/EventNewBasalProfile.kt new file mode 100644 index 0000000000..2ffa5a9724 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/events/EventNewBasalProfile.kt @@ -0,0 +1,3 @@ +package info.nightscout.androidaps.events + +class EventNewBasalProfile : Event() \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventNsFood.java b/app/src/main/java/info/nightscout/androidaps/events/EventNsFood.java deleted file mode 100644 index 90b6f5681b..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/events/EventNsFood.java +++ /dev/null @@ -1,35 +0,0 @@ -package info.nightscout.androidaps.events; - -import android.os.Bundle; - -/** - * Event which is published with data fetched from NightScout specific for the - * Food-class. - * - * Payload is the from NS retrieved JSON-String which should be handled by all - * subscriber. - */ - -public class EventNsFood extends Event { - - public static final int ADD = 0; - public static final int UPDATE = 1; - public static final int REMOVE = 2; - - private final int mode; - - private final Bundle payload; - - public EventNsFood(int mode, Bundle payload) { - this.mode = mode; - this.payload = payload; - } - - public int getMode() { - return mode; - } - - public Bundle getPayload() { - return payload; - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventNsFood.kt b/app/src/main/java/info/nightscout/androidaps/events/EventNsFood.kt new file mode 100644 index 0000000000..2f34e76c85 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/events/EventNsFood.kt @@ -0,0 +1,19 @@ +package info.nightscout.androidaps.events + +import android.os.Bundle + +/** + * Event which is published with data fetched from NightScout specific for the + * Food-class. + * + * Payload is the from NS retrieved JSON-String which should be handled by all + * subscriber. + */ + +class EventNsFood(val mode: Int, val payload: Bundle) : Event() { + companion object { + val ADD = 0 + val UPDATE = 1 + val REMOVE = 2 + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventNsTreatment.java b/app/src/main/java/info/nightscout/androidaps/events/EventNsTreatment.java deleted file mode 100644 index 2c5ba6c9c0..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/events/EventNsTreatment.java +++ /dev/null @@ -1,36 +0,0 @@ -package info.nightscout.androidaps.events; - -import org.json.JSONObject; - - -/** - * Event which is published with data fetched from NightScout specific for the - * Treatment-class. - *

- * Payload is the from NS retrieved JSON-String which should be handled by all - * subscriber. - */ - -public class EventNsTreatment extends Event { - - public static final int ADD = 0; - public static final int UPDATE = 1; - public static final int REMOVE = 2; - - private final int mode; - - private final JSONObject payload; - - public EventNsTreatment(int mode, JSONObject payload) { - this.mode = mode; - this.payload = payload; - } - - public int getMode() { - return mode; - } - - public JSONObject getPayload() { - return payload; - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventNsTreatment.kt b/app/src/main/java/info/nightscout/androidaps/events/EventNsTreatment.kt new file mode 100644 index 0000000000..149894c221 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/events/EventNsTreatment.kt @@ -0,0 +1,21 @@ +package info.nightscout.androidaps.events + +import org.json.JSONObject + + +/** + * Event which is published with data fetched from NightScout specific for the + * Treatment-class. + * + * + * Payload is the from NS retrieved JSON-String which should be handled by all + * subscriber. + */ + +class EventNsTreatment(val mode: Int, val payload: JSONObject) : Event() { + companion object { + val ADD = 0 + val UPDATE = 1 + val REMOVE = 2 + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventPreferenceChange.java b/app/src/main/java/info/nightscout/androidaps/events/EventPreferenceChange.java deleted file mode 100644 index f23d4e802a..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/events/EventPreferenceChange.java +++ /dev/null @@ -1,25 +0,0 @@ -package info.nightscout.androidaps.events; - -import info.nightscout.androidaps.MainApp; - -/** - * Created by mike on 19.06.2016. - */ -public class EventPreferenceChange extends Event { - public String changedKey; - public EventPreferenceChange(String key) { - changedKey = key; - } - - public EventPreferenceChange(int resourceID) { - changedKey = MainApp.gs(resourceID); - } - - public boolean isChanged(int id) { - return changedKey.equals(MainApp.gs(id)); - } - - public boolean isChanged(String id) { - return changedKey.equals(id); - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventPreferenceChange.kt b/app/src/main/java/info/nightscout/androidaps/events/EventPreferenceChange.kt new file mode 100644 index 0000000000..d224d75df1 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/events/EventPreferenceChange.kt @@ -0,0 +1,19 @@ +package info.nightscout.androidaps.events + +import info.nightscout.androidaps.MainApp + +class EventPreferenceChange : Event { + private var changedKey: String? = null + + constructor(key: String) { + changedKey = key + } + + constructor(resourceID: Int) { + changedKey = MainApp.gs(resourceID) + } + + fun isChanged(id: Int): Boolean { + return changedKey == MainApp.gs(id) + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventProfileNeedsUpdate.java b/app/src/main/java/info/nightscout/androidaps/events/EventProfileNeedsUpdate.java deleted file mode 100644 index 9e3f3b08c7..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/events/EventProfileNeedsUpdate.java +++ /dev/null @@ -1,8 +0,0 @@ -package info.nightscout.androidaps.events; - -/** - * Created by mike on 02.06.2017. - */ - -public class EventProfileNeedsUpdate extends Event { -} diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventProfileNeedsUpdate.kt b/app/src/main/java/info/nightscout/androidaps/events/EventProfileNeedsUpdate.kt new file mode 100644 index 0000000000..2baf1db945 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/events/EventProfileNeedsUpdate.kt @@ -0,0 +1,3 @@ +package info.nightscout.androidaps.events + +class EventProfileNeedsUpdate : Event() \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventProfileStoreChanged.java b/app/src/main/java/info/nightscout/androidaps/events/EventProfileStoreChanged.java deleted file mode 100644 index 0b2d933c12..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/events/EventProfileStoreChanged.java +++ /dev/null @@ -1,4 +0,0 @@ -package info.nightscout.androidaps.events; - -public class EventProfileStoreChanged extends Event { -} diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventProfileStoreChanged.kt b/app/src/main/java/info/nightscout/androidaps/events/EventProfileStoreChanged.kt new file mode 100644 index 0000000000..0e839ca2d3 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/events/EventProfileStoreChanged.kt @@ -0,0 +1,3 @@ +package info.nightscout.androidaps.events + +class EventProfileStoreChanged : Event() diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventPumpStatusChanged.java b/app/src/main/java/info/nightscout/androidaps/events/EventPumpStatusChanged.java deleted file mode 100644 index 6729a4e703..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/events/EventPumpStatusChanged.java +++ /dev/null @@ -1,63 +0,0 @@ -package info.nightscout.androidaps.events; - -import info.nightscout.androidaps.MainApp; -import info.nightscout.androidaps.R; - -/** - * Created by mike on 19.02.2017. - */ - -public class EventPumpStatusChanged extends Event { - public static final int CONNECTING = 0; - public static final int CONNECTED = 1; - public static final int HANDSHAKING = 2; - public static final int PERFORMING = 3; - public static final int DISCONNECTING = 4; - public static final int DISCONNECTED = 5; - - public int sStatus = DISCONNECTED; - public int sSecondsElapsed = 0; - public String sPerfomingAction = ""; - - public static String error = ""; - - public EventPumpStatusChanged(int status) { - sStatus = status; - sSecondsElapsed = 0; - error = ""; - } - - public EventPumpStatusChanged(int status, int secondsElapsed) { - sStatus = status; - sSecondsElapsed = secondsElapsed; - error = ""; - } - - public EventPumpStatusChanged(int status, String error) { - sStatus = status; - sSecondsElapsed = 0; - this.error = error; - } - - public EventPumpStatusChanged(String action) { - sStatus = PERFORMING; - sSecondsElapsed = 0; - sPerfomingAction = action; - } - - public String textStatus() { - if (sStatus == CONNECTING) - return String.format(MainApp.gs(R.string.danar_history_connectingfor), sSecondsElapsed); - else if (sStatus == HANDSHAKING) - return MainApp.gs(R.string.handshaking); - else if (sStatus == CONNECTED) - return MainApp.gs(R.string.connected); - else if (sStatus == PERFORMING) - return sPerfomingAction; - else if (sStatus == DISCONNECTING) - return MainApp.gs(R.string.disconnecting); - else if (sStatus == DISCONNECTED) - return ""; - return ""; - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventPumpStatusChanged.kt b/app/src/main/java/info/nightscout/androidaps/events/EventPumpStatusChanged.kt new file mode 100644 index 0000000000..3d25bc1ca5 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/events/EventPumpStatusChanged.kt @@ -0,0 +1,62 @@ +package info.nightscout.androidaps.events + +import info.nightscout.androidaps.MainApp +import info.nightscout.androidaps.R + +class EventPumpStatusChanged : EventStatus { + + enum class Status { + CONNECTING, + CONNECTED, + HANDSHAKING, + PERFORMING, + DISCONNECTING, + DISCONNECTED + } + + var sStatus: Status = Status.DISCONNECTED + var sSecondsElapsed = 0 + var sPerfomingAction = "" + var error = "" + + constructor(status: Status) { + sStatus = status + sSecondsElapsed = 0 + error = "" + } + + constructor(status: Status, secondsElapsed: Int) { + sStatus = status + sSecondsElapsed = secondsElapsed + error = "" + } + + constructor(status: Status, error: String) { + sStatus = status + sSecondsElapsed = 0 + this.error = error + } + + constructor(action: String) { + sStatus = Status.PERFORMING + sSecondsElapsed = 0 + sPerfomingAction = action + } + + // status for startup wizard + override fun getStatus(): String { + if (sStatus == Status.CONNECTING) + return String.format(MainApp.gs(R.string.danar_history_connectingfor), sSecondsElapsed) + else if (sStatus == Status.HANDSHAKING) + return MainApp.gs(R.string.handshaking) + else if (sStatus == Status.CONNECTED) + return MainApp.gs(R.string.connected) + else if (sStatus == Status.PERFORMING) + return sPerfomingAction + else if (sStatus == Status.DISCONNECTING) + return MainApp.gs(R.string.disconnecting) + else if (sStatus == Status.DISCONNECTED) + return "" + return "" + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventRebuildTabs.kt b/app/src/main/java/info/nightscout/androidaps/events/EventRebuildTabs.kt new file mode 100644 index 0000000000..aa0db3467a --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/events/EventRebuildTabs.kt @@ -0,0 +1,3 @@ +package info.nightscout.androidaps.events + +class EventRebuildTabs @JvmOverloads constructor(var recreate: Boolean = false) : Event() diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventRefreshGui.java b/app/src/main/java/info/nightscout/androidaps/events/EventRefreshGui.java deleted file mode 100644 index 390ad8ea4f..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/events/EventRefreshGui.java +++ /dev/null @@ -1,14 +0,0 @@ -package info.nightscout.androidaps.events; - -/** - * Created by mike on 13.06.2016. - */ -public class EventRefreshGui extends Event { - public boolean recreate = false; - public EventRefreshGui(boolean recreate) { - this.recreate = recreate; - } - public EventRefreshGui(){ - this(false); - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventRefreshOverview.java b/app/src/main/java/info/nightscout/androidaps/events/EventRefreshOverview.java deleted file mode 100644 index 2ba78fa9ec..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/events/EventRefreshOverview.java +++ /dev/null @@ -1,13 +0,0 @@ -package info.nightscout.androidaps.events; - -/** - * Created by mike on 16.06.2017. - */ - -public class EventRefreshOverview extends Event { - public String from; - - public EventRefreshOverview(String from) { - this.from = from; - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventRefreshOverview.kt b/app/src/main/java/info/nightscout/androidaps/events/EventRefreshOverview.kt new file mode 100644 index 0000000000..533a25dd40 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/events/EventRefreshOverview.kt @@ -0,0 +1,3 @@ +package info.nightscout.androidaps.events + +class EventRefreshOverview(var from: String) : Event() diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventReloadProfileSwitchData.java b/app/src/main/java/info/nightscout/androidaps/events/EventReloadProfileSwitchData.java deleted file mode 100644 index 212e8856d9..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/events/EventReloadProfileSwitchData.java +++ /dev/null @@ -1,8 +0,0 @@ -package info.nightscout.androidaps.events; - -/** - * Created by mike on 12.06.2017. - */ - -public class EventReloadProfileSwitchData extends Event { -} diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventReloadProfileSwitchData.kt b/app/src/main/java/info/nightscout/androidaps/events/EventReloadProfileSwitchData.kt new file mode 100644 index 0000000000..6f6d848b5e --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/events/EventReloadProfileSwitchData.kt @@ -0,0 +1,3 @@ +package info.nightscout.androidaps.events + +class EventReloadProfileSwitchData : Event() diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventReloadTempBasalData.java b/app/src/main/java/info/nightscout/androidaps/events/EventReloadTempBasalData.java deleted file mode 100644 index 80125cbb4a..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/events/EventReloadTempBasalData.java +++ /dev/null @@ -1,8 +0,0 @@ -package info.nightscout.androidaps.events; - -/** - * Created by mike on 29.05.2017. - */ - -public class EventReloadTempBasalData extends Event { -} diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventReloadTempBasalData.kt b/app/src/main/java/info/nightscout/androidaps/events/EventReloadTempBasalData.kt new file mode 100644 index 0000000000..fa8f720896 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/events/EventReloadTempBasalData.kt @@ -0,0 +1,3 @@ +package info.nightscout.androidaps.events + +class EventReloadTempBasalData : Event() diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventReloadTreatmentData.java b/app/src/main/java/info/nightscout/androidaps/events/EventReloadTreatmentData.java deleted file mode 100644 index 0ba9b95ad7..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/events/EventReloadTreatmentData.java +++ /dev/null @@ -1,13 +0,0 @@ -package info.nightscout.androidaps.events; - -/** - * Created by mike on 29.05.2017. - */ - -public class EventReloadTreatmentData extends Event { - public Object next; - - public EventReloadTreatmentData(Object next) { - this.next = next; - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventReloadTreatmentData.kt b/app/src/main/java/info/nightscout/androidaps/events/EventReloadTreatmentData.kt new file mode 100644 index 0000000000..1f8b2938b9 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/events/EventReloadTreatmentData.kt @@ -0,0 +1,3 @@ +package info.nightscout.androidaps.events + +class EventReloadTreatmentData(var next: Event) : Event() diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventStatus.kt b/app/src/main/java/info/nightscout/androidaps/events/EventStatus.kt new file mode 100644 index 0000000000..193c3b1fdb --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/events/EventStatus.kt @@ -0,0 +1,6 @@ +package info.nightscout.androidaps.events + +// pass string to startup wizard +abstract class EventStatus :Event() { + abstract fun getStatus() : String +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventTempBasalChange.java b/app/src/main/java/info/nightscout/androidaps/events/EventTempBasalChange.java deleted file mode 100644 index 73660bb00e..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/events/EventTempBasalChange.java +++ /dev/null @@ -1,7 +0,0 @@ -package info.nightscout.androidaps.events; - -/** - * Created by mike on 05.06.2016. - */ -public class EventTempBasalChange extends EventLoop { -} diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventTempBasalChange.kt b/app/src/main/java/info/nightscout/androidaps/events/EventTempBasalChange.kt new file mode 100644 index 0000000000..3f3ecf732e --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/events/EventTempBasalChange.kt @@ -0,0 +1,3 @@ +package info.nightscout.androidaps.events + +class EventTempBasalChange : EventLoop() diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventTempTargetChange.java b/app/src/main/java/info/nightscout/androidaps/events/EventTempTargetChange.java deleted file mode 100644 index 4e3bf5c5f8..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/events/EventTempTargetChange.java +++ /dev/null @@ -1,8 +0,0 @@ -package info.nightscout.androidaps.events; - -/** - * Created by mike on 13.01.2017. - */ - -public class EventTempTargetChange extends Event { -} diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventTempTargetChange.kt b/app/src/main/java/info/nightscout/androidaps/events/EventTempTargetChange.kt new file mode 100644 index 0000000000..c108d6589c --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/events/EventTempTargetChange.kt @@ -0,0 +1,3 @@ +package info.nightscout.androidaps.events + +class EventTempTargetChange : Event() diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventTreatmentChange.java b/app/src/main/java/info/nightscout/androidaps/events/EventTreatmentChange.java deleted file mode 100644 index 990f28a388..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/events/EventTreatmentChange.java +++ /dev/null @@ -1,17 +0,0 @@ -package info.nightscout.androidaps.events; - -import android.support.annotation.Nullable; - -import info.nightscout.androidaps.plugins.treatments.Treatment; - -/** - * Created by mike on 04.06.2016. - */ -public class EventTreatmentChange extends EventLoop { - @Nullable - public final Treatment treatment; - - public EventTreatmentChange(Treatment treatment) { - this.treatment = treatment; - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventTreatmentChange.kt b/app/src/main/java/info/nightscout/androidaps/events/EventTreatmentChange.kt new file mode 100644 index 0000000000..9cbc9d1563 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/events/EventTreatmentChange.kt @@ -0,0 +1,5 @@ +package info.nightscout.androidaps.events + +import info.nightscout.androidaps.plugins.treatments.Treatment + +class EventTreatmentChange(val treatment: Treatment?) : EventLoop() diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventUpdateGui.java b/app/src/main/java/info/nightscout/androidaps/events/EventUpdateGui.java deleted file mode 100644 index 3471d2e851..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/events/EventUpdateGui.java +++ /dev/null @@ -1,5 +0,0 @@ -package info.nightscout.androidaps.events; - -/** Base class for events to update the UI, mostly a specific tab. */ -public abstract class EventUpdateGui extends Event { -} diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventUpdateGui.kt b/app/src/main/java/info/nightscout/androidaps/events/EventUpdateGui.kt new file mode 100644 index 0000000000..cc21e784b9 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/events/EventUpdateGui.kt @@ -0,0 +1,4 @@ +package info.nightscout.androidaps.events + +/** Base class for events to update the UI, mostly a specific tab. */ +abstract class EventUpdateGui : Event() diff --git a/app/src/main/java/info/nightscout/androidaps/interfaces/APSInterface.java b/app/src/main/java/info/nightscout/androidaps/interfaces/APSInterface.java index e2f3460701..7d32e37ff0 100644 --- a/app/src/main/java/info/nightscout/androidaps/interfaces/APSInterface.java +++ b/app/src/main/java/info/nightscout/androidaps/interfaces/APSInterface.java @@ -6,8 +6,9 @@ import info.nightscout.androidaps.plugins.aps.loop.APSResult; * Created by mike on 10.06.2016. */ public interface APSInterface { - public APSResult getLastAPSResult(); - public long getLastAPSRun(); + APSResult getLastAPSResult(); - public void invoke(String initiator, boolean tempBasalFallback); + long getLastAPSRun(); + + void invoke(String initiator, boolean tempBasalFallback); } diff --git a/app/src/main/java/info/nightscout/androidaps/interfaces/ConstraintsInterface.java b/app/src/main/java/info/nightscout/androidaps/interfaces/ConstraintsInterface.java index f1c79dd5cb..14b2d549af 100644 --- a/app/src/main/java/info/nightscout/androidaps/interfaces/ConstraintsInterface.java +++ b/app/src/main/java/info/nightscout/androidaps/interfaces/ConstraintsInterface.java @@ -61,6 +61,6 @@ public interface ConstraintsInterface { default Constraint applyMaxIOBConstraints(Constraint maxIob) { return maxIob; - }; + } } diff --git a/app/src/main/java/info/nightscout/androidaps/interfaces/PluginBase.java b/app/src/main/java/info/nightscout/androidaps/interfaces/PluginBase.java index 01be296539..f7c8e9ada4 100644 --- a/app/src/main/java/info/nightscout/androidaps/interfaces/PluginBase.java +++ b/app/src/main/java/info/nightscout/androidaps/interfaces/PluginBase.java @@ -1,16 +1,23 @@ package info.nightscout.androidaps.interfaces; import android.os.SystemClock; -import android.support.v4.app.FragmentActivity; + +import androidx.appcompat.app.AlertDialog; +import androidx.fragment.app.FragmentActivity; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.events.EventConfigBuilderChange; +import info.nightscout.androidaps.events.EventRebuildTabs; import info.nightscout.androidaps.logging.L; -import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderFragment; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; +import info.nightscout.androidaps.plugins.configBuilder.EventConfigBuilderUpdateGui; import info.nightscout.androidaps.queue.CommandQueue; +import info.nightscout.androidaps.utils.SP; /** * Created by mike on 09.06.2016. @@ -38,17 +45,46 @@ public abstract class PluginBase { // Default always calls invoke // Plugins that have special constraints if they get switched to may override this method - public void switchAllowed(ConfigBuilderFragment.PluginViewHolder.PluginSwitcher pluginSwitcher, FragmentActivity activity) { - pluginSwitcher.invoke(); + public void switchAllowed(boolean newState, FragmentActivity activity, PluginType type) { + performPluginSwitch(newState, type); } -// public PluginType getType() { -// return mainType; -// } + protected void confirmPumpPluginActivation(boolean newState, FragmentActivity activity, PluginType type) { + if (type == PluginType.PUMP) { + boolean allowHardwarePump = SP.getBoolean("allow_hardware_pump", false); + if (allowHardwarePump || activity == null) { + performPluginSwitch(newState, type); + } else { + AlertDialog.Builder builder = new AlertDialog.Builder(activity); + builder.setMessage(R.string.allow_hardware_pump_text) + .setPositiveButton(R.string.yes, (dialog, id) -> { + performPluginSwitch(newState, type); + SP.putBoolean("allow_hardware_pump", true); + if (L.isEnabled(L.PUMP)) + log.debug("First time HW pump allowed!"); + }) + .setNegativeButton(R.string.cancel, (dialog, id) -> { + RxBus.INSTANCE.send(new EventConfigBuilderUpdateGui()); + if (L.isEnabled(L.PUMP)) + log.debug("User does not allow switching to HW pump!"); + }); + builder.create().show(); + } + } else { + performPluginSwitch(newState, type); + } + } -// public String getFragmentClass() { -// return fragmentClass; -// } + private void performPluginSwitch(boolean enabled, PluginType type) { + setPluginEnabled(type, enabled); + setFragmentVisible(type, enabled); + ConfigBuilderPlugin.getPlugin().processOnEnabledCategoryChanged(this, getType()); + ConfigBuilderPlugin.getPlugin().storeSettings("CheckedCheckboxEnabled"); + RxBus.INSTANCE.send(new EventRebuildTabs()); + RxBus.INSTANCE.send(new EventConfigBuilderChange()); + RxBus.INSTANCE.send(new EventConfigBuilderUpdateGui()); + ConfigBuilderPlugin.getPlugin().logPluginStatus(); + } public String getName() { if (pluginDescription.pluginName == -1) @@ -80,10 +116,6 @@ public abstract class PluginBase { return pluginDescription.preferencesId; } - public int getAdvancedPreferencesId() { - return pluginDescription.advancedPreferencesId; - } - public boolean isEnabled(PluginType type) { if (pluginDescription.alwaysEnabled && type == pluginDescription.mainType) return true; @@ -143,7 +175,7 @@ public abstract class PluginBase { } public boolean isFragmentVisible() { - if (pluginDescription.alwayVisible) + if (pluginDescription.alwaysVisible) return true; if (pluginDescription.neverVisible) return false; diff --git a/app/src/main/java/info/nightscout/androidaps/interfaces/PluginDescription.java b/app/src/main/java/info/nightscout/androidaps/interfaces/PluginDescription.java index 1634fc672d..5882c5870c 100644 --- a/app/src/main/java/info/nightscout/androidaps/interfaces/PluginDescription.java +++ b/app/src/main/java/info/nightscout/androidaps/interfaces/PluginDescription.java @@ -3,7 +3,7 @@ package info.nightscout.androidaps.interfaces; public class PluginDescription { PluginType mainType = PluginType.GENERAL; String fragmentClass = null; - public boolean alwayVisible = false; + public boolean alwaysVisible = false; public boolean neverVisible = false; public boolean alwaysEnabled = false; boolean showInList = true; @@ -11,7 +11,6 @@ public class PluginDescription { int shortName = -1; int description = -1; int preferencesId = -1; - int advancedPreferencesId = -1; public boolean enableByDefault = false; public boolean visibleByDefault = false; @@ -30,8 +29,8 @@ public class PluginDescription { return this; } - public PluginDescription alwayVisible(boolean alwayVisible) { - this.alwayVisible = alwayVisible; + public PluginDescription alwaysVisible(boolean alwayVisible) { + this.alwaysVisible = alwayVisible; return this; } @@ -60,11 +59,6 @@ public class PluginDescription { return this; } - public PluginDescription advancedPreferencesId(int advancedPreferencesId) { - this.advancedPreferencesId = advancedPreferencesId; - return this; - } - public PluginDescription enableByDefault(boolean enableByDefault) { this.enableByDefault = enableByDefault; return this; diff --git a/app/src/main/java/info/nightscout/androidaps/interfaces/ProfileInterface.java b/app/src/main/java/info/nightscout/androidaps/interfaces/ProfileInterface.java index e3b368fe86..482278e437 100644 --- a/app/src/main/java/info/nightscout/androidaps/interfaces/ProfileInterface.java +++ b/app/src/main/java/info/nightscout/androidaps/interfaces/ProfileInterface.java @@ -1,6 +1,6 @@ package info.nightscout.androidaps.interfaces; -import android.support.annotation.Nullable; +import androidx.annotation.Nullable; import info.nightscout.androidaps.data.ProfileStore; diff --git a/app/src/main/java/info/nightscout/androidaps/interfaces/PumpInterface.java b/app/src/main/java/info/nightscout/androidaps/interfaces/PumpInterface.java index 3690312cf8..025f10076d 100644 --- a/app/src/main/java/info/nightscout/androidaps/interfaces/PumpInterface.java +++ b/app/src/main/java/info/nightscout/androidaps/interfaces/PumpInterface.java @@ -7,8 +7,10 @@ import java.util.List; import info.nightscout.androidaps.data.DetailedBolusInfo; import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.data.PumpEnactResult; +import info.nightscout.androidaps.plugins.common.ManufacturerType; import info.nightscout.androidaps.plugins.general.actions.defs.CustomAction; import info.nightscout.androidaps.plugins.general.actions.defs.CustomActionType; +import info.nightscout.androidaps.plugins.pump.common.defs.PumpType; /** * Created by mike on 04.06.2016. @@ -16,21 +18,30 @@ import info.nightscout.androidaps.plugins.general.actions.defs.CustomActionType; public interface PumpInterface { boolean isInitialized(); // true if pump status has been read and is ready to accept commands + boolean isSuspended(); // true if suspended (not delivering insulin) + boolean isBusy(); // if true pump is not ready to accept commands right now + boolean isConnected(); // true if BT connection is established + boolean isConnecting(); // true if BT connection is in progress + boolean isHandshakeInProgress(); // true if BT is connected but initial handshake is still in progress + void finishHandshaking(); // set initial handshake completed void connect(String reason); + void disconnect(String reason); + void stopConnecting(); void getPumpStatus(); // Upload to pump new basal profile PumpEnactResult setNewBasalProfile(Profile profile); + boolean isThisProfileSet(Profile profile); long lastDataTime(); @@ -42,18 +53,29 @@ public interface PumpInterface { int getBatteryLevel(); // in percent as integer PumpEnactResult deliverTreatment(DetailedBolusInfo detailedBolusInfo); + void stopBolusDelivering(); + PumpEnactResult setTempBasalAbsolute(Double absoluteRate, Integer durationInMinutes, Profile profile, boolean enforceNew); + PumpEnactResult setTempBasalPercent(Integer percent, Integer durationInMinutes, Profile profile, boolean enforceNew); + PumpEnactResult setExtendedBolus(Double insulin, Integer durationInMinutes); + //some pumps might set a very short temp close to 100% as cancelling a temp can be noisy //when the cancel request is requested by the user (forced), the pump should always do a real cancel PumpEnactResult cancelTempBasal(boolean enforceNew); + PumpEnactResult cancelExtendedBolus(); // Status to be passed to NS JSONObject getJSONStatus(Profile profile, String profileName); - String deviceID(); + + ManufacturerType manufacturer(); + + PumpType model(); + + String serialNumber(); // Pump capabilities PumpDescription getPumpDescription(); @@ -65,10 +87,16 @@ public interface PumpInterface { PumpEnactResult loadTDDs(); - public boolean canHandleDST(); + boolean canHandleDST(); List getCustomActions(); void executeCustomAction(CustomActionType customActionType); + /** + * This method will be called when time or Timezone changes, and pump driver can then do a specific action (for + * example update clock on pump). + */ + void timeDateOrTimeZoneChanged(); + } diff --git a/app/src/main/java/info/nightscout/androidaps/interfaces/TreatmentsInterface.java b/app/src/main/java/info/nightscout/androidaps/interfaces/TreatmentsInterface.java index 0ef71dac2a..43661b117a 100644 --- a/app/src/main/java/info/nightscout/androidaps/interfaces/TreatmentsInterface.java +++ b/app/src/main/java/info/nightscout/androidaps/interfaces/TreatmentsInterface.java @@ -5,6 +5,7 @@ import java.util.List; import info.nightscout.androidaps.data.DetailedBolusInfo; import info.nightscout.androidaps.data.IobTotal; import info.nightscout.androidaps.data.MealData; +import info.nightscout.androidaps.data.NonOverlappingIntervals; import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.db.ExtendedBolus; import info.nightscout.androidaps.db.ProfileSwitch; @@ -25,12 +26,13 @@ public interface TreatmentsInterface { IobTotal getLastCalculationTreatments(); IobTotal getCalculationToTimeTreatments(long time); IobTotal getLastCalculationTempBasals(); - IobTotal getCalculationToTimeTempBasals(long time, Profile profile); + IobTotal getCalculationToTimeTempBasals(long time); MealData getMealData(); List getTreatmentsFromHistory(); List getTreatments5MinBackFromHistory(long time); + List getTreatmentsFromHistoryAfterTimestamp(long timestamp); long getLastBolusTime(); // real basals (not faked by extended bolus) @@ -42,7 +44,7 @@ public interface TreatmentsInterface { // basal that can be faked by extended boluses boolean isTempBasalInProgress(); TemporaryBasal getTempBasalFromHistory(long time); - Intervals getTemporaryBasalsFromHistory(); + NonOverlappingIntervals getTemporaryBasalsFromHistory(); boolean isInHistoryExtendedBoluslInProgress(); ExtendedBolus getExtendedBolusFromHistory(long time); @@ -63,4 +65,4 @@ public interface TreatmentsInterface { long oldestDataAvailable(); -} +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/logging/L.java b/app/src/main/java/info/nightscout/androidaps/logging/L.java index f685bbd424..25b6750844 100644 --- a/app/src/main/java/info/nightscout/androidaps/logging/L.java +++ b/app/src/main/java/info/nightscout/androidaps/logging/L.java @@ -77,6 +77,7 @@ public class L { public static final String CORE = "CORE"; public static final String AUTOSENS = "AUTOSENS"; + public static final String AUTOMATION = "AUTOMATION"; public static final String EVENTS = "EVENTS"; public static final String GLUCOSE = "GLUCOSE"; public static final String BGSOURCE = "BGSOURCE"; @@ -87,6 +88,7 @@ public class L { public static final String DATAFOOD = "DATAFOOD"; public static final String DATATREATMENTS = "DATATREATMENTS"; public static final String NSCLIENT = "NSCLIENT"; + public static final String TIDEPOOL = "TIDEPOOL"; public static final String CONSTRAINTS = "CONSTRAINTS"; public static final String PUMP = "PUMP"; public static final String PUMPQUEUE = "PUMPQUEUE"; @@ -96,11 +98,13 @@ public class L { public static final String PROFILE = "PROFILE"; public static final String CONFIGBUILDER = "CONFIGBUILDER"; public static final String UI = "UI"; + public static final String LOCATION = "LOCATION"; public static final String SMS = "SMS"; private static void initialize() { logElements = new ArrayList<>(); logElements.add(new LogElement(APS, true)); + logElements.add(new LogElement(AUTOMATION, true)); logElements.add(new LogElement(AUTOSENS, false)); logElements.add(new LogElement(BGSOURCE, true)); logElements.add(new LogElement(GLUCOSE, false)); @@ -112,8 +116,10 @@ public class L { logElements.add(new LogElement(DATASERVICE, true)); logElements.add(new LogElement(DATATREATMENTS, true)); logElements.add(new LogElement(EVENTS, false, true)); + logElements.add(new LogElement(LOCATION, true)); logElements.add(new LogElement(NOTIFICATION, true)); logElements.add(new LogElement(NSCLIENT, true)); + logElements.add(new LogElement(TIDEPOOL, true)); logElements.add(new LogElement(OVERVIEW, true)); logElements.add(new LogElement(PROFILE, true)); logElements.add(new LogElement(PUMP, true)); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopFragment.java deleted file mode 100644 index 951631eb0c..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopFragment.java +++ /dev/null @@ -1,145 +0,0 @@ -package info.nightscout.androidaps.plugins.aps.loop; - - -import android.app.Activity; -import android.os.Bundle; -import android.text.Html; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.Button; -import android.widget.TextView; - -import com.squareup.otto.Subscribe; - -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.OnClick; -import info.nightscout.androidaps.MainApp; -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.interfaces.Constraint; -import info.nightscout.androidaps.plugins.aps.loop.events.EventLoopSetLastRunGui; -import info.nightscout.androidaps.plugins.aps.loop.events.EventLoopUpdateGui; -import info.nightscout.androidaps.plugins.common.SubscriberFragment; -import info.nightscout.androidaps.utils.FabricPrivacy; - -public class LoopFragment extends SubscriberFragment { - @BindView(R.id.loop_run) - Button runNowButton; - @BindView(R.id.loop_lastrun) - TextView lastRunView; - @BindView(R.id.loop_lastenact) - TextView lastEnactView; - @BindView(R.id.loop_source) - TextView sourceView; - @BindView(R.id.loop_request) - TextView requestView; - @BindView(R.id.loop_constraintsprocessed) - TextView constraintsProcessedView; - @BindView(R.id.loop_constraints) - TextView constraintsView; - @BindView(R.id.loop_tbrsetbypump) - TextView tbrSetByPumpView; - @BindView(R.id.loop_smbsetbypump) - TextView smbSetByPumpView; - - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - try { - View view = inflater.inflate(R.layout.loop_fragment, container, false); - unbinder = ButterKnife.bind(this, view); - return view; - } catch (Exception e) { - FabricPrivacy.logException(e); - } - return null; - } - - @OnClick(R.id.loop_run) - void onRunClick() { - lastRunView.setText(MainApp.gs(R.string.executing)); - new Thread(() -> LoopPlugin.getPlugin().invoke("Loop button", true)).start(); - } - - @Subscribe - public void onStatusEvent(final EventLoopUpdateGui ev) { - updateGUI(); - } - - @Subscribe - public void onStatusEvent(final EventLoopSetLastRunGui ev) { - clearGUI(); - final Activity activity = getActivity(); - if (activity != null) - activity.runOnUiThread(() -> { - synchronized (LoopFragment.this) { - if (lastRunView != null) lastRunView.setText(ev.text); - } - }); - } - - - @Override - protected void updateGUI() { - Activity activity = getActivity(); - if (activity != null) - activity.runOnUiThread(() -> { - synchronized (LoopFragment.this) { - if (!isBound()) return; - LoopPlugin.LastRun lastRun = LoopPlugin.lastRun; - if (lastRun != null) { - requestView.setText(lastRun.request != null ? lastRun.request.toSpanned() : ""); - constraintsProcessedView.setText(lastRun.constraintsProcessed != null ? lastRun.constraintsProcessed.toSpanned() : ""); - sourceView.setText(lastRun.source != null ? lastRun.source : ""); - lastRunView.setText(lastRun.lastAPSRun != null && lastRun.lastAPSRun.getTime() != 0 ? lastRun.lastAPSRun.toLocaleString() : ""); - lastEnactView.setText(lastRun.lastEnact != null && lastRun.lastEnact.getTime() != 0 ? lastRun.lastEnact.toLocaleString() : ""); - tbrSetByPumpView.setText(lastRun.tbrSetByPump != null ? Html.fromHtml(lastRun.tbrSetByPump.toHtml()) : ""); - smbSetByPumpView.setText(lastRun.smbSetByPump != null ? Html.fromHtml(lastRun.smbSetByPump.toHtml()) : ""); - - String constraints = ""; - if (lastRun.constraintsProcessed != null) { - Constraint allConstraints = new Constraint<>(0d); - if (lastRun.constraintsProcessed.rateConstraint != null) - allConstraints.copyReasons(lastRun.constraintsProcessed.rateConstraint); - if (lastRun.constraintsProcessed.smbConstraint != null) - allConstraints.copyReasons(lastRun.constraintsProcessed.smbConstraint); - constraints = allConstraints.getMostLimitedReasons(); - } - constraintsView.setText(constraints); - } - } - }); - } - - void clearGUI() { - Activity activity = getActivity(); - if (activity != null) - activity.runOnUiThread(() -> { - synchronized (LoopFragment.this) { - if (isBound()) { - requestView.setText(""); - constraintsProcessedView.setText(""); - sourceView.setText(""); - lastRunView.setText(""); - lastEnactView.setText(""); - tbrSetByPumpView.setText(""); - smbSetByPumpView.setText(""); - } - } - }); - } - - boolean isBound() { - return requestView != null - && constraintsProcessedView != null - && sourceView != null - && lastRunView != null - && lastEnactView != null - && tbrSetByPumpView != null - && smbSetByPumpView != null - && constraintsView != null - && runNowButton != null; - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopFragment.kt new file mode 100644 index 0000000000..b7549fcfed --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopFragment.kt @@ -0,0 +1,109 @@ +package info.nightscout.androidaps.plugins.aps.loop + + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import info.nightscout.androidaps.MainApp +import info.nightscout.androidaps.R +import info.nightscout.androidaps.interfaces.Constraint +import info.nightscout.androidaps.plugins.aps.loop.events.EventLoopSetLastRunGui +import info.nightscout.androidaps.plugins.aps.loop.events.EventLoopUpdateGui +import info.nightscout.androidaps.plugins.bus.RxBus +import info.nightscout.androidaps.utils.* +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.CompositeDisposable +import kotlinx.android.synthetic.main.loop_fragment.* + +class LoopFragment : Fragment() { + + private var disposable: CompositeDisposable = CompositeDisposable() + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.loop_fragment, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + loop_run.setOnClickListener { + loop_lastrun.text = MainApp.gs(R.string.executing) + Thread { LoopPlugin.getPlugin().invoke("Loop button", true) }.start() + } + } + + @Synchronized + override fun onResume() { + super.onResume() + disposable += RxBus + .toObservable(EventLoopUpdateGui::class.java) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ + updateGUI() + }, { + FabricPrivacy.logException(it) + }) + + disposable += RxBus + .toObservable(EventLoopSetLastRunGui::class.java) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ + clearGUI() + loop_lastrun.text = it.text + }, { + FabricPrivacy.logException(it) + }) + + updateGUI() + SP.putBoolean(R.string.key_objectiveuseloop, true) + } + + @Synchronized + override fun onPause() { + super.onPause() + disposable.clear() + } + + @Synchronized + fun updateGUI() { + if (loop_request == null) return + LoopPlugin.lastRun?.let { + loop_request.text = it.request?.toSpanned() ?: "" + loop_constraintsprocessed.text = it.constraintsProcessed?.toSpanned() ?: "" + loop_source.text = it.source ?: "" + loop_lastrun.text = it.lastAPSRun?.let { lastRun -> DateUtil.dateAndTimeString(lastRun.time) } + ?: "" + loop_lastenact.text = it.lastAPSRun?.let { lastEnact -> DateUtil.dateAndTimeString(lastEnact.time) } + ?: "" + loop_tbrsetbypump.text = it.tbrSetByPump?.let { tbrSetByPump -> HtmlHelper.fromHtml(tbrSetByPump.toHtml()) } + ?: "" + loop_smbsetbypump.text = it.smbSetByPump?.let { smbSetByPump -> HtmlHelper.fromHtml(smbSetByPump.toHtml()) } + ?: "" + + val constraints = + it.constraintsProcessed?.let { constraintsProcessed -> + val allConstraints = Constraint(0.0) + constraintsProcessed.rateConstraint?.let { rateConstraint -> allConstraints.copyReasons(rateConstraint) } + constraintsProcessed.smbConstraint?.let { smbConstraint -> allConstraints.copyReasons(smbConstraint) } + allConstraints.mostLimitedReasons + } ?: "" + loop_constraints.text = constraints + } + } + + @Synchronized + private fun clearGUI() { + if (loop_request == null) return + loop_request.text = "" + loop_constraints.text = "" + loop_constraintsprocessed.text = "" + loop_source.text = "" + loop_lastrun.text = "" + loop_lastenact.text = "" + loop_tbrsetbypump.text = "" + loop_smbsetbypump.text = "" + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.java index 92378a1d6b..f339bd3771 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.java @@ -10,10 +10,9 @@ import android.content.Context; import android.content.Intent; import android.os.Build; import android.os.SystemClock; -import android.support.annotation.NonNull; -import android.support.v4.app.NotificationCompat; -import com.squareup.otto.Subscribe; +import androidx.annotation.NonNull; +import androidx.core.app.NotificationCompat; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -47,10 +46,11 @@ import info.nightscout.androidaps.logging.L; import info.nightscout.androidaps.plugins.aps.loop.events.EventLoopSetLastRunGui; import info.nightscout.androidaps.plugins.aps.loop.events.EventLoopUpdateGui; import info.nightscout.androidaps.plugins.aps.loop.events.EventNewOpenLoopNotification; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; -import info.nightscout.androidaps.plugins.constraints.objectives.ObjectivesPlugin; import info.nightscout.androidaps.plugins.general.nsclient.NSUpload; +import info.nightscout.androidaps.plugins.general.overview.dialogs.ErrorHelperActivity; import info.nightscout.androidaps.plugins.general.wear.ActionStringHandler; import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventAutosensCalculationFinished; import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin; @@ -61,12 +61,15 @@ import info.nightscout.androidaps.utils.FabricPrivacy; import info.nightscout.androidaps.utils.SP; import info.nightscout.androidaps.utils.T; import info.nightscout.androidaps.utils.ToastUtils; +import io.reactivex.disposables.CompositeDisposable; +import io.reactivex.schedulers.Schedulers; /** * Created by mike on 05.08.2016. */ public class LoopPlugin extends PluginBase { private static Logger log = LoggerFactory.getLogger(L.APS); + private CompositeDisposable disposable = new CompositeDisposable(); private static final String CHANNEL_ID = "AndroidAPS-Openloop"; @@ -115,9 +118,39 @@ public class LoopPlugin extends PluginBase { @Override protected void onStart() { - MainApp.bus().register(this); createNotificationChannel(); super.onStart(); + disposable.add(RxBus.INSTANCE + .toObservable(EventTempTargetChange.class) + .observeOn(Schedulers.io()) + .subscribe(event -> { + invoke("EventTempTargetChange", true); + }, FabricPrivacy::logException) + ); + /** + * This method is triggered once autosens calculation has completed, so the LoopPlugin + * has current data to work with. However, autosens calculation can be triggered by multiple + * sources and currently only a new BG should trigger a loop run. Hence we return early if + * the event causing the calculation is not EventNewBg. + *

+ */ + disposable.add(RxBus.INSTANCE + .toObservable(EventAutosensCalculationFinished.class) + .observeOn(Schedulers.io()) + .subscribe(event -> { + // Autosens calculation not triggered by a new BG + if (!(event.getCause() instanceof EventNewBG)) return; + + BgReading bgReading = DatabaseHelper.actualBg(); + // BG outdated + if (bgReading == null) return; + // already looped with that value + if (bgReading.date <= lastBgTriggeredRun) return; + + lastBgTriggeredRun = bgReading.date; + invoke("AutosenseCalculation for " + bgReading, true); + }, FabricPrivacy::logException) + ); } private void createNotificationChannel() { @@ -134,8 +167,8 @@ public class LoopPlugin extends PluginBase { @Override protected void onStop() { + disposable.clear(); super.onStop(); - MainApp.bus().unregister(this); } @Override @@ -144,43 +177,10 @@ public class LoopPlugin extends PluginBase { return pump == null || pump.getPumpDescription().isTempBasalCapable; } - /** - * This method is triggered once autosens calculation has completed, so the LoopPlugin - * has current data to work with. However, autosens calculation can be triggered by multiple - * sources and currently only a new BG should trigger a loop run. Hence we return early if - * the event causing the calculation is not EventNewBg. - *

- */ - @Subscribe - public void onStatusEvent(final EventAutosensCalculationFinished ev) { - if (!(ev.cause instanceof EventNewBG)) { - // Autosens calculation not triggered by a new BG - return; - } - BgReading bgReading = DatabaseHelper.actualBg(); - if (bgReading == null) { - // BG outdated - return; - } - if (bgReading.date <= lastBgTriggeredRun) { - // already looped with that value - return; - } - - lastBgTriggeredRun = bgReading.date; - invoke("AutosenseCalculation for " + bgReading, true); - } - public long suspendedTo() { return loopSuspendedTill; } - @Subscribe - public void onStatusEvent(final EventTempTargetChange ev) { - new Thread(() -> invoke("EventTempTargetChange", true)).start(); - } - - public void suspendTo(long endTime) { loopSuspendedTill = endTime; isSuperBolus = false; @@ -278,7 +278,7 @@ public class LoopPlugin extends PluginBase { String message = MainApp.gs(R.string.loopdisabled) + "\n" + loopEnabled.getReasons(); if (L.isEnabled(L.APS)) log.debug(message); - MainApp.bus().post(new EventLoopSetLastRunGui(message)); + RxBus.INSTANCE.send(new EventLoopSetLastRunGui(message)); return; } final PumpInterface pump = ConfigBuilderPlugin.getPlugin().getActivePump(); @@ -289,10 +289,10 @@ public class LoopPlugin extends PluginBase { Profile profile = ProfileFunctions.getInstance().getProfile(); - if (!ProfileFunctions.getInstance().isProfileValid("Loop")) { + if (profile == null || !ProfileFunctions.getInstance().isProfileValid("Loop")) { if (L.isEnabled(L.APS)) log.debug(MainApp.gs(R.string.noprofileselected)); - MainApp.bus().post(new EventLoopSetLastRunGui(MainApp.gs(R.string.noprofileselected))); + RxBus.INSTANCE.send(new EventLoopSetLastRunGui(MainApp.gs(R.string.noprofileselected))); return; } @@ -307,7 +307,7 @@ public class LoopPlugin extends PluginBase { // Check if we have any result if (result == null) { - MainApp.bus().post(new EventLoopSetLastRunGui(MainApp.gs(R.string.noapsselected))); + RxBus.INSTANCE.send(new EventLoopSetLastRunGui(MainApp.gs(R.string.noapsselected))); return; } @@ -349,14 +349,14 @@ public class LoopPlugin extends PluginBase { if (isSuspended()) { if (L.isEnabled(L.APS)) log.debug(MainApp.gs(R.string.loopsuspended)); - MainApp.bus().post(new EventLoopSetLastRunGui(MainApp.gs(R.string.loopsuspended))); + RxBus.INSTANCE.send(new EventLoopSetLastRunGui(MainApp.gs(R.string.loopsuspended))); return; } if (pump.isSuspended()) { if (L.isEnabled(L.APS)) log.debug(MainApp.gs(R.string.pumpsuspended)); - MainApp.bus().post(new EventLoopSetLastRunGui(MainApp.gs(R.string.pumpsuspended))); + RxBus.INSTANCE.send(new EventLoopSetLastRunGui(MainApp.gs(R.string.pumpsuspended))); return; } @@ -372,7 +372,7 @@ public class LoopPlugin extends PluginBase { lastRun.tbrSetByPump = waiting; if (resultAfterConstraints.bolusRequested) lastRun.smbSetByPump = waiting; - MainApp.bus().post(new EventLoopUpdateGui()); + RxBus.INSTANCE.send(new EventLoopUpdateGui()); FabricPrivacy.getInstance().logCustom("APSRequest"); applyTBRRequest(resultAfterConstraints, profile, new Callback() { @Override @@ -393,11 +393,11 @@ public class LoopPlugin extends PluginBase { LoopPlugin.getPlugin().invoke("tempBasalFallback", allowNotification, true); }).start(); } - MainApp.bus().post(new EventLoopUpdateGui()); + RxBus.INSTANCE.send(new EventLoopUpdateGui()); } }); } - MainApp.bus().post(new EventLoopUpdateGui()); + RxBus.INSTANCE.send(new EventLoopUpdateGui()); } }); } else { @@ -414,7 +414,7 @@ public class LoopPlugin extends PluginBase { .setAutoCancel(true) .setPriority(Notification.PRIORITY_HIGH) .setCategory(Notification.CATEGORY_ALARM) - .setVisibility(Notification.VISIBILITY_PUBLIC); + .setVisibility(NotificationCompat.VISIBILITY_PUBLIC); if (SP.getBoolean("wearcontrol", false)) { builder.setLocalOnly(true); } @@ -438,7 +438,7 @@ public class LoopPlugin extends PluginBase { (NotificationManager) MainApp.instance().getSystemService(Context.NOTIFICATION_SERVICE); // mId allows you to update the notification later on. mNotificationManager.notify(Constants.notificationID, builder.build()); - MainApp.bus().post(new EventNewOpenLoopNotification()); + RxBus.INSTANCE.send(new EventNewOpenLoopNotification()); // Send to Wear ActionStringHandler.handleInitiate("changeRequest"); @@ -451,7 +451,7 @@ public class LoopPlugin extends PluginBase { } } - MainApp.bus().post(new EventLoopUpdateGui()); + RxBus.INSTANCE.send(new EventLoopUpdateGui()); } finally { if (L.isEnabled(L.APS)) log.debug("invoke end"); @@ -469,13 +469,9 @@ public class LoopPlugin extends PluginBase { lastRun.lastEnact = new Date(); lastRun.lastOpenModeAccept = new Date(); NSUpload.uploadDeviceStatus(); - ObjectivesPlugin objectivesPlugin = MainApp.getSpecificPlugin(ObjectivesPlugin.class); - if (objectivesPlugin != null) { - ObjectivesPlugin.getPlugin().manualEnacts++; - ObjectivesPlugin.getPlugin().saveProgress(); - } + SP.incInt(R.string.key_ObjectivesmanualEnacts); } - MainApp.bus().post(new EventAcceptOpenLoopChange()); + RxBus.INSTANCE.send(new EventAcceptOpenLoopChange()); } }); FabricPrivacy.getInstance().logCustom("AcceptTemp"); @@ -644,20 +640,48 @@ public class LoopPlugin extends PluginBase { TreatmentsInterface activeTreatments = TreatmentsPlugin.getPlugin(); LoopPlugin.getPlugin().disconnectTo(System.currentTimeMillis() + durationInMinutes * 60 * 1000L); - ConfigBuilderPlugin.getPlugin().getCommandQueue().tempBasalPercent(0, durationInMinutes, true, profile, new Callback() { - @Override - public void run() { - if (!result.success) { - ToastUtils.showToastInUiThread(MainApp.instance().getApplicationContext(), MainApp.gs(R.string.tempbasaldeliveryerror)); + + if (pump.getPumpDescription().tempBasalStyle == PumpDescription.ABSOLUTE) { + ConfigBuilderPlugin.getPlugin().getCommandQueue().tempBasalAbsolute(0, durationInMinutes, true, profile, new Callback() { + @Override + public void run() { + if (!result.success) { + Intent i = new Intent(MainApp.instance(), ErrorHelperActivity.class); + i.putExtra("soundid", R.raw.boluserror); + i.putExtra("status", result.comment); + i.putExtra("title", MainApp.gs(R.string.tempbasaldeliveryerror)); + i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + MainApp.instance().startActivity(i); + } } - } - }); + }); + } else { + ConfigBuilderPlugin.getPlugin().getCommandQueue().tempBasalPercent(0, durationInMinutes, true, profile, new Callback() { + @Override + public void run() { + if (!result.success) { + Intent i = new Intent(MainApp.instance(), ErrorHelperActivity.class); + i.putExtra("soundid", R.raw.boluserror); + i.putExtra("status", result.comment); + i.putExtra("title", MainApp.gs(R.string.tempbasaldeliveryerror)); + i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + MainApp.instance().startActivity(i); + } + } + }); + } + if (pump.getPumpDescription().isExtendedBolusCapable && activeTreatments.isInHistoryExtendedBoluslInProgress()) { ConfigBuilderPlugin.getPlugin().getCommandQueue().cancelExtended(new Callback() { @Override public void run() { if (!result.success) { - ToastUtils.showToastInUiThread(MainApp.instance().getApplicationContext(), MainApp.gs(R.string.extendedbolusdeliveryerror)); + Intent i = new Intent(MainApp.instance(), ErrorHelperActivity.class); + i.putExtra("soundid", R.raw.boluserror); + i.putExtra("status", result.comment); + i.putExtra("title", MainApp.gs(R.string.extendedbolusdeliveryerror)); + i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + MainApp.instance().startActivity(i); } } }); @@ -671,7 +695,12 @@ public class LoopPlugin extends PluginBase { @Override public void run() { if (!result.success) { - ToastUtils.showToastInUiThread(MainApp.instance().getApplicationContext(), MainApp.gs(R.string.tempbasaldeliveryerror)); + Intent i = new Intent(MainApp.instance(), ErrorHelperActivity.class); + i.putExtra("soundid", R.raw.boluserror); + i.putExtra("status", result.comment); + i.putExtra("title", MainApp.gs(R.string.tempbasaldeliveryerror)); + i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + MainApp.instance().startActivity(i); } } }); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/events/EventLoopSetLastRunGui.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/events/EventLoopSetLastRunGui.java deleted file mode 100644 index 0d2a45f528..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/events/EventLoopSetLastRunGui.java +++ /dev/null @@ -1,14 +0,0 @@ -package info.nightscout.androidaps.plugins.aps.loop.events; - -import info.nightscout.androidaps.events.EventUpdateGui; - -/** - * Created by mike on 05.08.2016. - */ -public class EventLoopSetLastRunGui extends EventUpdateGui { - public String text = null; - - public EventLoopSetLastRunGui(String text) { - this.text = text; - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/events/EventLoopSetLastRunGui.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/events/EventLoopSetLastRunGui.kt new file mode 100644 index 0000000000..19c7e92c5c --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/events/EventLoopSetLastRunGui.kt @@ -0,0 +1,8 @@ +package info.nightscout.androidaps.plugins.aps.loop.events + +import info.nightscout.androidaps.events.EventUpdateGui + +/** + * Created by mike on 05.08.2016. + */ +class EventLoopSetLastRunGui(val text: String) : EventUpdateGui() diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/events/EventLoopUpdateGui.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/events/EventLoopUpdateGui.java deleted file mode 100644 index f746dfb0a0..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/events/EventLoopUpdateGui.java +++ /dev/null @@ -1,9 +0,0 @@ -package info.nightscout.androidaps.plugins.aps.loop.events; - -import info.nightscout.androidaps.events.EventUpdateGui; - -/** - * Created by mike on 05.08.2016. - */ -public class EventLoopUpdateGui extends EventUpdateGui { -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/events/EventLoopUpdateGui.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/events/EventLoopUpdateGui.kt new file mode 100644 index 0000000000..89507d85f8 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/events/EventLoopUpdateGui.kt @@ -0,0 +1,8 @@ +package info.nightscout.androidaps.plugins.aps.loop.events + +import info.nightscout.androidaps.events.EventUpdateGui + +/** + * Created by mike on 05.08.2016. + */ +class EventLoopUpdateGui : EventUpdateGui() diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/events/EventNewOpenLoopNotification.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/events/EventNewOpenLoopNotification.java deleted file mode 100644 index 6a0a4dc0cf..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/events/EventNewOpenLoopNotification.java +++ /dev/null @@ -1,9 +0,0 @@ -package info.nightscout.androidaps.plugins.aps.loop.events; - -import info.nightscout.androidaps.events.Event; - -/** - * Created by mike on 07.08.2016. - */ -public class EventNewOpenLoopNotification extends Event { -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/events/EventNewOpenLoopNotification.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/events/EventNewOpenLoopNotification.kt new file mode 100644 index 0000000000..2933318dd6 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/events/EventNewOpenLoopNotification.kt @@ -0,0 +1,5 @@ +package info.nightscout.androidaps.plugins.aps.loop.events + +import info.nightscout.androidaps.events.Event + +class EventNewOpenLoopNotification : Event() \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/DetermineBasalAdapterAMAJS.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/DetermineBasalAdapterAMAJS.java index 5b5c8cb63d..703dc4255b 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/DetermineBasalAdapterAMAJS.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/DetermineBasalAdapterAMAJS.java @@ -16,6 +16,9 @@ import org.slf4j.LoggerFactory; import java.io.IOException; import java.lang.reflect.InvocationTargetException; +import java.nio.charset.StandardCharsets; + +import javax.annotation.Nullable; import info.nightscout.androidaps.Constants; import info.nightscout.androidaps.MainApp; @@ -59,6 +62,7 @@ public class DetermineBasalAdapterAMAJS { mScriptReader = scriptReader; } + @Nullable public DetermineBasalResultAMA invoke() { if (L.isEnabled(L.APS)) { @@ -277,7 +281,7 @@ public class DetermineBasalAdapterAMAJS { private String readFile(String filename) throws IOException { byte[] bytes = mScriptReader.readFile(filename); - String string = new String(bytes, "UTF-8"); + String string = new String(bytes, StandardCharsets.UTF_8); if (string.startsWith("#!/usr/bin/env node")) { string = string.substring(20); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAFragment.java deleted file mode 100644 index 0aadf52cbd..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAFragment.java +++ /dev/null @@ -1,134 +0,0 @@ -package info.nightscout.androidaps.plugins.aps.openAPSAMA; - -import android.app.Activity; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.Button; -import android.widget.TextView; - -import com.squareup.otto.Subscribe; - -import org.json.JSONArray; -import org.json.JSONException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import info.nightscout.androidaps.MainApp; -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.logging.L; -import info.nightscout.androidaps.plugins.aps.openAPSMA.events.EventOpenAPSUpdateGui; -import info.nightscout.androidaps.plugins.aps.openAPSMA.events.EventOpenAPSUpdateResultGui; -import info.nightscout.androidaps.plugins.common.SubscriberFragment; -import info.nightscout.androidaps.utils.DateUtil; -import info.nightscout.androidaps.utils.JSONFormatter; - -public class OpenAPSAMAFragment extends SubscriberFragment implements View.OnClickListener { - private static Logger log = LoggerFactory.getLogger(L.APS); - - Button run; - TextView lastRunView; - TextView glucoseStatusView; - TextView currentTempView; - TextView iobDataView; - TextView profileView; - TextView mealDataView; - TextView autosensDataView; - TextView resultView; - TextView scriptdebugView; - TextView requestView; - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.openapsama_fragment, container, false); - - run = (Button) view.findViewById(R.id.openapsma_run); - run.setOnClickListener(this); - lastRunView = (TextView) view.findViewById(R.id.openapsma_lastrun); - glucoseStatusView = (TextView) view.findViewById(R.id.openapsma_glucosestatus); - currentTempView = (TextView) view.findViewById(R.id.openapsma_currenttemp); - iobDataView = (TextView) view.findViewById(R.id.openapsma_iobdata); - profileView = (TextView) view.findViewById(R.id.openapsma_profile); - mealDataView = (TextView) view.findViewById(R.id.openapsma_mealdata); - autosensDataView = (TextView) view.findViewById(R.id.openapsma_autosensdata); - scriptdebugView = (TextView) view.findViewById(R.id.openapsma_scriptdebugdata); - resultView = (TextView) view.findViewById(R.id.openapsma_result); - requestView = (TextView) view.findViewById(R.id.openapsma_request); - - updateGUI(); - return view; - } - - @Override - public void onClick(View view) { - switch (view.getId()) { - case R.id.openapsma_run: - OpenAPSAMAPlugin.getPlugin().invoke("OpenAPSAMA button", false); - break; - } - - } - - @Subscribe - public void onStatusEvent(final EventOpenAPSUpdateGui ev) { - updateGUI(); - } - - @Subscribe - public void onStatusEvent(final EventOpenAPSUpdateResultGui ev) { - updateResultGUI(ev.text); - } - - @Override - protected void updateGUI() { - Activity activity = getActivity(); - if (activity != null) - activity.runOnUiThread(() -> { - DetermineBasalResultAMA lastAPSResult = OpenAPSAMAPlugin.getPlugin().lastAPSResult; - if (lastAPSResult != null) { - resultView.setText(JSONFormatter.format(lastAPSResult.json)); - requestView.setText(lastAPSResult.toSpanned()); - } - DetermineBasalAdapterAMAJS determineBasalAdapterAMAJS = OpenAPSAMAPlugin.getPlugin().lastDetermineBasalAdapterAMAJS; - if (determineBasalAdapterAMAJS != null) { - glucoseStatusView.setText(JSONFormatter.format(determineBasalAdapterAMAJS.getGlucoseStatusParam())); - currentTempView.setText(JSONFormatter.format(determineBasalAdapterAMAJS.getCurrentTempParam())); - try { - JSONArray iobArray = new JSONArray(determineBasalAdapterAMAJS.getIobDataParam()); - iobDataView.setText(String.format(MainApp.gs(R.string.array_of_elements), iobArray.length()) + "\n" + JSONFormatter.format(iobArray.getString(0))); - } catch (JSONException e) { - log.error("Unhandled exception", e); - iobDataView.setText("JSONException"); - } - profileView.setText(JSONFormatter.format(determineBasalAdapterAMAJS.getProfileParam())); - mealDataView.setText(JSONFormatter.format(determineBasalAdapterAMAJS.getMealDataParam())); - scriptdebugView.setText(determineBasalAdapterAMAJS.getScriptDebug()); - } - if (OpenAPSAMAPlugin.getPlugin().lastAPSRun != 0) { - lastRunView.setText(DateUtil.dateAndTimeFullString(OpenAPSAMAPlugin.getPlugin().lastAPSRun)); - } - if (OpenAPSAMAPlugin.getPlugin().lastAutosensResult != null) { - autosensDataView.setText(JSONFormatter.format(OpenAPSAMAPlugin.getPlugin().lastAutosensResult.json())); - } - }); - } - - void updateResultGUI(final String text) { - Activity activity = getActivity(); - if (activity != null) - activity.runOnUiThread(() -> { - resultView.setText(text); - glucoseStatusView.setText(""); - currentTempView.setText(""); - iobDataView.setText(""); - profileView.setText(""); - mealDataView.setText(""); - autosensDataView.setText(""); - scriptdebugView.setText(""); - requestView.setText(""); - lastRunView.setText(""); - }); - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAFragment.kt new file mode 100644 index 0000000000..94747a4597 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAFragment.kt @@ -0,0 +1,115 @@ +package info.nightscout.androidaps.plugins.aps.openAPSAMA + +import android.os.Bundle +import android.text.TextUtils +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import info.nightscout.androidaps.MainApp +import info.nightscout.androidaps.R +import info.nightscout.androidaps.logging.L +import info.nightscout.androidaps.plugins.aps.openAPSMA.events.EventOpenAPSUpdateGui +import info.nightscout.androidaps.plugins.aps.openAPSMA.events.EventOpenAPSUpdateResultGui +import info.nightscout.androidaps.plugins.bus.RxBus +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.FabricPrivacy +import info.nightscout.androidaps.utils.JSONFormatter +import info.nightscout.androidaps.utils.plusAssign +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.CompositeDisposable +import kotlinx.android.synthetic.main.openapsama_fragment.* +import org.json.JSONArray +import org.json.JSONException +import org.slf4j.LoggerFactory + +class OpenAPSAMAFragment : Fragment() { + private val log = LoggerFactory.getLogger(L.APS) + private var disposable: CompositeDisposable = CompositeDisposable() + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.openapsama_fragment, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + openapsma_run.setOnClickListener { + OpenAPSAMAPlugin.getPlugin().invoke("OpenAPSAMA button", false) + } + } + + @Synchronized + override fun onResume() { + super.onResume() + + disposable += RxBus + .toObservable(EventOpenAPSUpdateGui::class.java) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ + updateGUI() + }, { + FabricPrivacy.logException(it) + }) + disposable += RxBus + .toObservable(EventOpenAPSUpdateResultGui::class.java) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ + updateResultGUI(it.text) + }, { + FabricPrivacy.logException(it) + }) + + updateGUI() + } + + @Synchronized + override fun onPause() { + super.onPause() + disposable.clear() + } + + @Synchronized + private fun updateGUI() { + if (openapsma_result == null) return + OpenAPSAMAPlugin.getPlugin().lastAPSResult?.let { lastAPSResult -> + openapsma_result.text = JSONFormatter.format(lastAPSResult.json) + openapsma_request.text = lastAPSResult.toSpanned() + } + OpenAPSAMAPlugin.getPlugin().lastDetermineBasalAdapterAMAJS?.let { determineBasalAdapterAMAJS -> + openapsma_glucosestatus.text = JSONFormatter.format(determineBasalAdapterAMAJS.glucoseStatusParam) + openapsma_currenttemp.text = JSONFormatter.format(determineBasalAdapterAMAJS.currentTempParam) + try { + val iobArray = JSONArray(determineBasalAdapterAMAJS.iobDataParam) + openapsma_iobdata.text = TextUtils.concat(String.format(MainApp.gs(R.string.array_of_elements), iobArray.length()) + "\n", JSONFormatter.format(iobArray.getString(0))) + } catch (e: JSONException) { + log.error("Unhandled exception", e) + openapsma_iobdata.text = "JSONException see log for details" + } + + openapsma_profile.text = JSONFormatter.format(determineBasalAdapterAMAJS.profileParam) + openapsma_mealdata.text = JSONFormatter.format(determineBasalAdapterAMAJS.mealDataParam) + openapsma_scriptdebugdata.text = determineBasalAdapterAMAJS.scriptDebug + } + if (OpenAPSAMAPlugin.getPlugin().lastAPSRun != 0L) { + openapsma_lastrun.text = DateUtil.dateAndTimeFullString(OpenAPSAMAPlugin.getPlugin().lastAPSRun) + } + OpenAPSAMAPlugin.getPlugin().lastAutosensResult?.let { + openapsma_autosensdata.text = JSONFormatter.format(it.json()) + } + } + + private fun updateResultGUI(text: String) { + openapsma_result.text = text + openapsma_glucosestatus.text = "" + openapsma_currenttemp.text = "" + openapsma_iobdata.text = "" + openapsma_profile.text = "" + openapsma_mealdata.text = "" + openapsma_autosensdata.text = "" + openapsma_scriptdebugdata.text = "" + openapsma_request.text = "" + openapsma_lastrun.text = "" + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAPlugin.java index 1f7deebea4..ef31ad599e 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAPlugin.java @@ -6,7 +6,6 @@ import org.slf4j.LoggerFactory; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus; import info.nightscout.androidaps.data.IobTotal; import info.nightscout.androidaps.data.MealData; import info.nightscout.androidaps.data.Profile; @@ -17,17 +16,20 @@ import info.nightscout.androidaps.interfaces.PluginDescription; import info.nightscout.androidaps.interfaces.PluginType; import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.logging.L; -import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; -import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensData; -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult; -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin; import info.nightscout.androidaps.plugins.aps.loop.APSResult; import info.nightscout.androidaps.plugins.aps.loop.ScriptReader; import info.nightscout.androidaps.plugins.aps.openAPSMA.events.EventOpenAPSUpdateGui; import info.nightscout.androidaps.plugins.aps.openAPSMA.events.EventOpenAPSUpdateResultGui; +import info.nightscout.androidaps.plugins.bus.RxBus; +import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; +import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensData; +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult; +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus; +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin; import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; import info.nightscout.androidaps.utils.DateUtil; +import info.nightscout.androidaps.utils.FabricPrivacy; import info.nightscout.androidaps.utils.HardLimits; import info.nightscout.androidaps.utils.Profiler; import info.nightscout.androidaps.utils.Round; @@ -99,21 +101,21 @@ public class OpenAPSAMAPlugin extends PluginBase implements APSInterface { PumpInterface pump = ConfigBuilderPlugin.getPlugin().getActivePump(); if (profile == null) { - MainApp.bus().post(new EventOpenAPSUpdateResultGui(MainApp.gs(R.string.noprofileselected))); + RxBus.INSTANCE.send(new EventOpenAPSUpdateResultGui(MainApp.gs(R.string.noprofileselected))); if (L.isEnabled(L.APS)) log.debug(MainApp.gs(R.string.noprofileselected)); return; } if (!isEnabled(PluginType.APS)) { - MainApp.bus().post(new EventOpenAPSUpdateResultGui(MainApp.gs(R.string.openapsma_disabled))); + RxBus.INSTANCE.send(new EventOpenAPSUpdateResultGui(MainApp.gs(R.string.openapsma_disabled))); if (L.isEnabled(L.APS)) log.debug(MainApp.gs(R.string.openapsma_disabled)); return; } if (glucoseStatus == null) { - MainApp.bus().post(new EventOpenAPSUpdateResultGui(MainApp.gs(R.string.openapsma_noglucosedata))); + RxBus.INSTANCE.send(new EventOpenAPSUpdateResultGui(MainApp.gs(R.string.openapsma_noglucosedata))); if (L.isEnabled(L.APS)) log.debug(MainApp.gs(R.string.openapsma_noglucosedata)); return; @@ -171,7 +173,7 @@ public class OpenAPSAMAPlugin extends PluginBase implements APSInterface { if (MainApp.getConstraintChecker().isAutosensModeEnabled().value()) { AutosensData autosensData = IobCobCalculatorPlugin.getPlugin().getLastAutosensDataSynchronized("OpenAPSPlugin"); if (autosensData == null) { - MainApp.bus().post(new EventOpenAPSUpdateResultGui(MainApp.gs(R.string.openaps_noasdata))); + RxBus.INSTANCE.send(new EventOpenAPSUpdateResultGui(MainApp.gs(R.string.openaps_noasdata))); return; } lastAutosensResult = autosensData.autosensResult; @@ -192,7 +194,8 @@ public class OpenAPSAMAPlugin extends PluginBase implements APSInterface { isTempTarget ); } catch (JSONException e) { - log.error("Unable to set data: " + e.toString()); + FabricPrivacy.logException(e); + return; } @@ -200,23 +203,31 @@ public class OpenAPSAMAPlugin extends PluginBase implements APSInterface { if (L.isEnabled(L.APS)) Profiler.log(log, "AMA calculation", start); // Fix bug determine basal - if (determineBasalResultAMA.rate == 0d && determineBasalResultAMA.duration == 0 && !TreatmentsPlugin.getPlugin().isTempBasalInProgress()) - determineBasalResultAMA.tempBasalRequested = false; + if (determineBasalResultAMA == null) { + if (L.isEnabled(L.APS)) + log.error("SMB calculation returned null"); + lastDetermineBasalAdapterAMAJS = null; + lastAPSResult = null; + lastAPSRun = 0; + } else { + if (determineBasalResultAMA.rate == 0d && determineBasalResultAMA.duration == 0 && !TreatmentsPlugin.getPlugin().isTempBasalInProgress()) + determineBasalResultAMA.tempBasalRequested = false; - determineBasalResultAMA.iob = iobArray[0]; + determineBasalResultAMA.iob = iobArray[0]; - long now = System.currentTimeMillis(); + long now = System.currentTimeMillis(); - try { - determineBasalResultAMA.json.put("timestamp", DateUtil.toISOString(now)); - } catch (JSONException e) { - log.error("Unhandled exception", e); + try { + determineBasalResultAMA.json.put("timestamp", DateUtil.toISOString(now)); + } catch (JSONException e) { + log.error("Unhandled exception", e); + } + + lastDetermineBasalAdapterAMAJS = determineBasalAdapterAMAJS; + lastAPSResult = determineBasalResultAMA; + lastAPSRun = now; } - - lastDetermineBasalAdapterAMAJS = determineBasalAdapterAMAJS; - lastAPSResult = determineBasalResultAMA; - lastAPSRun = now; - MainApp.bus().post(new EventOpenAPSUpdateGui()); + RxBus.INSTANCE.send(new EventOpenAPSUpdateGui()); //deviceStatus.suggested = determineBasalResultAMA.json; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/DetermineBasalAdapterMAJS.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/DetermineBasalAdapterMAJS.java index ae1f390ded..1ded34b7cb 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/DetermineBasalAdapterMAJS.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/DetermineBasalAdapterMAJS.java @@ -14,6 +14,9 @@ import org.slf4j.LoggerFactory; import java.io.IOException; import java.lang.reflect.InvocationTargetException; +import java.nio.charset.StandardCharsets; + +import javax.annotation.Nullable; import info.nightscout.androidaps.Constants; import info.nightscout.androidaps.R; @@ -47,6 +50,7 @@ public class DetermineBasalAdapterMAJS { mScriptReader = scriptReader; } + @Nullable public DetermineBasalResultMA invoke() { DetermineBasalResultMA determineBasalResultMA = null; @@ -207,7 +211,7 @@ public class DetermineBasalAdapterMAJS { private String readFile(String filename) throws IOException { byte[] bytes = mScriptReader.readFile(filename); - String string = new String(bytes, "UTF-8"); + String string = new String(bytes, StandardCharsets.UTF_8); if (string.startsWith("#!/usr/bin/env node")) { string = string.substring(20); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/LoggerCallback.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/LoggerCallback.java index 30ce388f5d..a2596d9174 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/LoggerCallback.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/LoggerCallback.java @@ -36,16 +36,14 @@ public class LoggerCallback extends ScriptableObject { public void jsFunction_log(Object obj1) { if (L.isEnabled(L.APS)) - log.debug(obj1.toString()); + log.debug(obj1.toString().trim()); logBuffer.append(obj1.toString()); - logBuffer.append(' '); } public void jsFunction_error(Object obj1) { if (L.isEnabled(L.APS)) - log.error(obj1.toString()); + log.error(obj1.toString().trim()); errorBuffer.append(obj1.toString()); - errorBuffer.append(' '); } @@ -60,4 +58,4 @@ public class LoggerCallback extends ScriptableObject { } return ret; } -} \ No newline at end of file +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/OpenAPSMAFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/OpenAPSMAFragment.java deleted file mode 100644 index 5d632a44fc..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/OpenAPSMAFragment.java +++ /dev/null @@ -1,116 +0,0 @@ -package info.nightscout.androidaps.plugins.aps.openAPSMA; - -import android.app.Activity; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.Button; -import android.widget.TextView; - -import com.squareup.otto.Subscribe; - -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.plugins.aps.openAPSMA.events.EventOpenAPSUpdateGui; -import info.nightscout.androidaps.plugins.aps.openAPSMA.events.EventOpenAPSUpdateResultGui; -import info.nightscout.androidaps.plugins.common.SubscriberFragment; -import info.nightscout.androidaps.utils.DateUtil; -import info.nightscout.androidaps.utils.FabricPrivacy; -import info.nightscout.androidaps.utils.JSONFormatter; - -public class OpenAPSMAFragment extends SubscriberFragment implements View.OnClickListener { - Button run; - TextView lastRunView; - TextView glucoseStatusView; - TextView currentTempView; - TextView iobDataView; - TextView profileView; - TextView mealDataView; - TextView resultView; - TextView requestView; - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - try { - View view = inflater.inflate(R.layout.openapsma_fragment, container, false); - - run = (Button) view.findViewById(R.id.openapsma_run); - run.setOnClickListener(this); - lastRunView = (TextView) view.findViewById(R.id.openapsma_lastrun); - glucoseStatusView = (TextView) view.findViewById(R.id.openapsma_glucosestatus); - currentTempView = (TextView) view.findViewById(R.id.openapsma_currenttemp); - iobDataView = (TextView) view.findViewById(R.id.openapsma_iobdata); - profileView = (TextView) view.findViewById(R.id.openapsma_profile); - mealDataView = (TextView) view.findViewById(R.id.openapsma_mealdata); - resultView = (TextView) view.findViewById(R.id.openapsma_result); - requestView = (TextView) view.findViewById(R.id.openapsma_request); - - updateGUI(); - return view; - } catch (Exception e) { - FabricPrivacy.logException(e); - } - - return null; - } - - @Override - public void onClick(View view) { - switch (view.getId()) { - case R.id.openapsma_run: - OpenAPSMAPlugin.getPlugin().invoke("OpenAPSMA button", false); - break; - } - - } - - @Subscribe - public void onStatusEvent(final EventOpenAPSUpdateGui ev) { - updateGUI(); - } - - @Subscribe - public void onStatusEvent(final EventOpenAPSUpdateResultGui ev) { - updateResultGUI(ev.text); - } - - @Override - protected void updateGUI() { - Activity activity = getActivity(); - if (activity != null) - activity.runOnUiThread(() -> { - DetermineBasalResultMA lastAPSResult = OpenAPSMAPlugin.getPlugin().lastAPSResult; - if (lastAPSResult != null) { - resultView.setText(JSONFormatter.format(lastAPSResult.json)); - requestView.setText(lastAPSResult.toSpanned()); - } - DetermineBasalAdapterMAJS determineBasalAdapterMAJS = OpenAPSMAPlugin.getPlugin().lastDetermineBasalAdapterMAJS; - if (determineBasalAdapterMAJS != null) { - glucoseStatusView.setText(JSONFormatter.format(determineBasalAdapterMAJS.getGlucoseStatusParam())); - currentTempView.setText(JSONFormatter.format(determineBasalAdapterMAJS.getCurrentTempParam())); - iobDataView.setText(JSONFormatter.format(determineBasalAdapterMAJS.getIobDataParam())); - profileView.setText(JSONFormatter.format(determineBasalAdapterMAJS.getProfileParam())); - mealDataView.setText(JSONFormatter.format(determineBasalAdapterMAJS.getMealDataParam())); - } - if (OpenAPSMAPlugin.getPlugin().lastAPSRun != 0) { - lastRunView.setText(DateUtil.dateAndTimeFullString(OpenAPSMAPlugin.getPlugin().lastAPSRun)); - } - }); - } - - private void updateResultGUI(final String text) { - Activity activity = getActivity(); - if (activity != null) - activity.runOnUiThread(() -> { - resultView.setText(text); - glucoseStatusView.setText(""); - currentTempView.setText(""); - iobDataView.setText(""); - profileView.setText(""); - mealDataView.setText(""); - requestView.setText(""); - lastRunView.setText(""); - }); - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/OpenAPSMAFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/OpenAPSMAFragment.kt new file mode 100644 index 0000000000..1b72f57365 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/OpenAPSMAFragment.kt @@ -0,0 +1,100 @@ +package info.nightscout.androidaps.plugins.aps.openAPSMA + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import info.nightscout.androidaps.R +import info.nightscout.androidaps.logging.L +import info.nightscout.androidaps.plugins.aps.openAPSMA.events.EventOpenAPSUpdateGui +import info.nightscout.androidaps.plugins.aps.openAPSMA.events.EventOpenAPSUpdateResultGui +import info.nightscout.androidaps.plugins.bus.RxBus +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.FabricPrivacy +import info.nightscout.androidaps.utils.JSONFormatter +import info.nightscout.androidaps.utils.plusAssign +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.CompositeDisposable +import kotlinx.android.synthetic.main.openapsama_fragment.* +import org.slf4j.LoggerFactory + +class OpenAPSMAFragment : Fragment() { + private val log = LoggerFactory.getLogger(L.APS) + private var disposable: CompositeDisposable = CompositeDisposable() + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.openapsma_fragment, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + openapsma_run.setOnClickListener { + OpenAPSMAPlugin.getPlugin().invoke("OpenAPSMA button", false) + } + + } + + @Synchronized + override fun onResume() { + super.onResume() + + disposable += RxBus + .toObservable(EventOpenAPSUpdateGui::class.java) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ + updateGUI() + }, { + FabricPrivacy.logException(it) + }) + disposable += RxBus + .toObservable(EventOpenAPSUpdateResultGui::class.java) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ + updateResultGUI(it.text) + }, { + FabricPrivacy.logException(it) + }) + updateGUI() + } + + @Synchronized + override fun onPause() { + super.onPause() + disposable.clear() + } + + @Synchronized + private fun updateGUI() { + if (openapsma_result == null) return + OpenAPSMAPlugin.getPlugin().lastAPSResult?.let { lastAPSResult -> + openapsma_result.text = JSONFormatter.format(lastAPSResult.json) + openapsma_request.text = lastAPSResult.toSpanned() + } + OpenAPSMAPlugin.getPlugin().lastDetermineBasalAdapterMAJS?.let { determineBasalAdapterMAJS -> + openapsma_glucosestatus.text = JSONFormatter.format(determineBasalAdapterMAJS.glucoseStatusParam) + openapsma_currenttemp.text = JSONFormatter.format(determineBasalAdapterMAJS.currentTempParam) + openapsma_iobdata.text = JSONFormatter.format(determineBasalAdapterMAJS.iobDataParam) + openapsma_profile.text = JSONFormatter.format(determineBasalAdapterMAJS.profileParam) + openapsma_mealdata.text = JSONFormatter.format(determineBasalAdapterMAJS.mealDataParam) + } + if (OpenAPSMAPlugin.getPlugin().lastAPSRun != 0L) { + openapsma_lastrun.text = DateUtil.dateAndTimeString(OpenAPSMAPlugin.getPlugin().lastAPSRun) + } + } + + @Synchronized + private fun updateResultGUI(text: String) { + if (openapsma_result == null) return + openapsma_result.text = text + openapsma_glucosestatus.text = "" + openapsma_currenttemp.text = "" + openapsma_iobdata.text = "" + openapsma_profile.text = "" + openapsma_mealdata.text = "" + openapsma_request.text = "" + openapsma_lastrun.text = "" + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/OpenAPSMAPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/OpenAPSMAPlugin.java index 2dd78bdb82..899c0151d8 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/OpenAPSMAPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/OpenAPSMAPlugin.java @@ -6,7 +6,6 @@ import org.slf4j.LoggerFactory; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus; import info.nightscout.androidaps.data.IobTotal; import info.nightscout.androidaps.data.MealData; import info.nightscout.androidaps.data.Profile; @@ -17,14 +16,17 @@ import info.nightscout.androidaps.interfaces.PluginDescription; import info.nightscout.androidaps.interfaces.PluginType; import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.logging.L; -import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; -import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; import info.nightscout.androidaps.plugins.aps.loop.APSResult; import info.nightscout.androidaps.plugins.aps.loop.ScriptReader; import info.nightscout.androidaps.plugins.aps.openAPSMA.events.EventOpenAPSUpdateGui; import info.nightscout.androidaps.plugins.aps.openAPSMA.events.EventOpenAPSUpdateResultGui; +import info.nightscout.androidaps.plugins.bus.RxBus; +import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; +import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus; import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; import info.nightscout.androidaps.utils.DateUtil; +import info.nightscout.androidaps.utils.FabricPrivacy; import info.nightscout.androidaps.utils.HardLimits; import info.nightscout.androidaps.utils.Profiler; import info.nightscout.androidaps.utils.Round; @@ -98,21 +100,21 @@ public class OpenAPSMAPlugin extends PluginBase implements APSInterface { PumpInterface pump = ConfigBuilderPlugin.getPlugin().getActivePump(); if (profile == null) { - MainApp.bus().post(new EventOpenAPSUpdateResultGui(MainApp.gs(R.string.noprofileselected))); + RxBus.INSTANCE.send(new EventOpenAPSUpdateResultGui(MainApp.gs(R.string.noprofileselected))); if (L.isEnabled(L.APS)) log.debug(MainApp.gs(R.string.noprofileselected)); return; } if (!isEnabled(PluginType.APS)) { - MainApp.bus().post(new EventOpenAPSUpdateResultGui(MainApp.gs(R.string.openapsma_disabled))); + RxBus.INSTANCE.send(new EventOpenAPSUpdateResultGui(MainApp.gs(R.string.openapsma_disabled))); if (L.isEnabled(L.APS)) log.debug(MainApp.gs(R.string.openapsma_disabled)); return; } if (glucoseStatus == null) { - MainApp.bus().post(new EventOpenAPSUpdateResultGui(MainApp.gs(R.string.openapsma_noglucosedata))); + RxBus.INSTANCE.send(new EventOpenAPSUpdateResultGui(MainApp.gs(R.string.openapsma_noglucosedata))); if (L.isEnabled(L.APS)) log.debug(MainApp.gs(R.string.openapsma_noglucosedata)); return; @@ -169,7 +171,8 @@ public class OpenAPSMAPlugin extends PluginBase implements APSInterface { try { determineBasalAdapterMAJS.setData(profile, maxIob, maxBasal, minBg, maxBg, targetBg, ConfigBuilderPlugin.getPlugin().getActivePump().getBaseBasalRate(), iobTotal, glucoseStatus, mealData); } catch (JSONException e) { - log.error("Unhandled exception", e); + FabricPrivacy.logException(e); + return; } if (L.isEnabled(L.APS)) Profiler.log(log, "MA calculation", start); @@ -178,22 +181,30 @@ public class OpenAPSMAPlugin extends PluginBase implements APSInterface { long now = System.currentTimeMillis(); DetermineBasalResultMA determineBasalResultMA = determineBasalAdapterMAJS.invoke(); - // Fix bug determinef basal - if (determineBasalResultMA.rate == 0d && determineBasalResultMA.duration == 0 && !TreatmentsPlugin.getPlugin().isTempBasalInProgress()) - determineBasalResultMA.tempBasalRequested = false; + if (determineBasalResultMA == null) { + if (L.isEnabled(L.APS)) + log.error("MA calculation returned null"); + lastDetermineBasalAdapterMAJS = null; + lastAPSResult = null; + lastAPSRun = 0; + } else { + // Fix bug determinef basal + if (determineBasalResultMA.rate == 0d && determineBasalResultMA.duration == 0 && !TreatmentsPlugin.getPlugin().isTempBasalInProgress()) + determineBasalResultMA.tempBasalRequested = false; - determineBasalResultMA.iob = iobTotal; + determineBasalResultMA.iob = iobTotal; - try { - determineBasalResultMA.json.put("timestamp", DateUtil.toISOString(now)); - } catch (JSONException e) { - log.error("Unhandled exception", e); + try { + determineBasalResultMA.json.put("timestamp", DateUtil.toISOString(now)); + } catch (JSONException e) { + log.error("Unhandled exception", e); + } + + lastDetermineBasalAdapterMAJS = determineBasalAdapterMAJS; + lastAPSResult = determineBasalResultMA; + lastAPSRun = now; } - - lastDetermineBasalAdapterMAJS = determineBasalAdapterMAJS; - lastAPSResult = determineBasalResultMA; - lastAPSRun = now; - MainApp.bus().post(new EventOpenAPSUpdateGui()); + RxBus.INSTANCE.send(new EventOpenAPSUpdateGui()); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/events/EventOpenAPSUpdateGui.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/events/EventOpenAPSUpdateGui.java deleted file mode 100644 index de4292025d..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/events/EventOpenAPSUpdateGui.java +++ /dev/null @@ -1,9 +0,0 @@ -package info.nightscout.androidaps.plugins.aps.openAPSMA.events; - -import info.nightscout.androidaps.events.EventUpdateGui; - -/** - * Created by mike on 05.08.2016. - */ -public class EventOpenAPSUpdateGui extends EventUpdateGui { -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/events/EventOpenAPSUpdateGui.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/events/EventOpenAPSUpdateGui.kt new file mode 100644 index 0000000000..2b642c6880 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/events/EventOpenAPSUpdateGui.kt @@ -0,0 +1,5 @@ +package info.nightscout.androidaps.plugins.aps.openAPSMA.events + +import info.nightscout.androidaps.events.EventUpdateGui + +class EventOpenAPSUpdateGui : EventUpdateGui() diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/events/EventOpenAPSUpdateResultGui.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/events/EventOpenAPSUpdateResultGui.java deleted file mode 100644 index fb5ea7e78f..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/events/EventOpenAPSUpdateResultGui.java +++ /dev/null @@ -1,14 +0,0 @@ -package info.nightscout.androidaps.plugins.aps.openAPSMA.events; - -import info.nightscout.androidaps.events.EventUpdateGui; - -/** - * Created by mike on 05.08.2016. - */ -public class EventOpenAPSUpdateResultGui extends EventUpdateGui { - public String text; - - public EventOpenAPSUpdateResultGui(String text) { - this.text = text; - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/events/EventOpenAPSUpdateResultGui.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/events/EventOpenAPSUpdateResultGui.kt new file mode 100644 index 0000000000..4ba02b8755 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/events/EventOpenAPSUpdateResultGui.kt @@ -0,0 +1,5 @@ +package info.nightscout.androidaps.plugins.aps.openAPSMA.events + +import info.nightscout.androidaps.events.EventUpdateGui + +class EventOpenAPSUpdateResultGui(val text: String) : EventUpdateGui() diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.java index bfb75503c1..b7b6d82e5e 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.java @@ -16,6 +16,9 @@ import org.slf4j.LoggerFactory; import java.io.IOException; import java.lang.reflect.InvocationTargetException; +import java.nio.charset.StandardCharsets; + +import javax.annotation.Nullable; import info.nightscout.androidaps.Constants; import info.nightscout.androidaps.MainApp; @@ -68,6 +71,7 @@ public class DetermineBasalAdapterSMBJS { } + @Nullable public DetermineBasalResultSMB invoke() { @@ -340,7 +344,7 @@ public class DetermineBasalAdapterSMBJS { private String readFile(String filename) throws IOException { byte[] bytes = mScriptReader.readFile(filename); - String string = new String(bytes, "UTF-8"); + String string = new String(bytes, StandardCharsets.UTF_8); if (string.startsWith("#!/usr/bin/env node")) { string = string.substring(20); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBFragment.java deleted file mode 100644 index 18828e840e..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBFragment.java +++ /dev/null @@ -1,157 +0,0 @@ -package info.nightscout.androidaps.plugins.aps.openAPSSMB; - -import android.app.Activity; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.Button; -import android.widget.TextView; - -import com.squareup.otto.Subscribe; - -import org.json.JSONArray; -import org.json.JSONException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.OnClick; -import info.nightscout.androidaps.MainApp; -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.logging.L; -import info.nightscout.androidaps.plugins.aps.openAPSMA.events.EventOpenAPSUpdateGui; -import info.nightscout.androidaps.plugins.aps.openAPSMA.events.EventOpenAPSUpdateResultGui; -import info.nightscout.androidaps.plugins.common.SubscriberFragment; -import info.nightscout.androidaps.utils.DateUtil; -import info.nightscout.androidaps.utils.JSONFormatter; - -public class OpenAPSSMBFragment extends SubscriberFragment { - private static Logger log = LoggerFactory.getLogger(L.APS); - - @BindView(R.id.openapsma_run) - Button run; - @BindView(R.id.openapsma_lastrun) - TextView lastRunView; - @BindView(R.id.openapsma_constraints) - TextView constraintsView; - @BindView(R.id.openapsma_glucosestatus) - TextView glucoseStatusView; - @BindView(R.id.openapsma_currenttemp) - TextView currentTempView; - @BindView(R.id.openapsma_iobdata) - TextView iobDataView; - @BindView(R.id.openapsma_profile) - TextView profileView; - @BindView(R.id.openapsma_mealdata) - TextView mealDataView; - @BindView(R.id.openapsma_autosensdata) - TextView autosensDataView; - @BindView(R.id.openapsma_result) - TextView resultView; - @BindView(R.id.openapsma_scriptdebugdata) - TextView scriptdebugView; - @BindView(R.id.openapsma_request) - TextView requestView; - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.openapsama_fragment, container, false); - - unbinder = ButterKnife.bind(this, view); - return view; - } - - @OnClick(R.id.openapsma_run) - public void onRunClick() { - OpenAPSSMBPlugin.getPlugin().invoke("OpenAPSSMB button", false); - } - - @Subscribe - public void onStatusEvent(final EventOpenAPSUpdateGui ev) { - updateGUI(); - } - - @Subscribe - public void onStatusEvent(final EventOpenAPSUpdateResultGui ev) { - updateResultGUI(ev.text); - } - - @Override - protected void updateGUI() { - Activity activity = getActivity(); - if (activity != null) - activity.runOnUiThread(() -> { - synchronized (OpenAPSSMBFragment.this) { - if (!isBound()) return; - OpenAPSSMBPlugin plugin = OpenAPSSMBPlugin.getPlugin(); - DetermineBasalResultSMB lastAPSResult = plugin.lastAPSResult; - if (lastAPSResult != null) { - resultView.setText(JSONFormatter.format(lastAPSResult.json)); - requestView.setText(lastAPSResult.toSpanned()); - } - DetermineBasalAdapterSMBJS determineBasalAdapterSMBJS = plugin.lastDetermineBasalAdapterSMBJS; - if (determineBasalAdapterSMBJS != null) { - glucoseStatusView.setText(JSONFormatter.format(determineBasalAdapterSMBJS.getGlucoseStatusParam()).toString().trim()); - currentTempView.setText(JSONFormatter.format(determineBasalAdapterSMBJS.getCurrentTempParam()).toString().trim()); - try { - JSONArray iobArray = new JSONArray(determineBasalAdapterSMBJS.getIobDataParam()); - iobDataView.setText((String.format(MainApp.gs(R.string.array_of_elements), iobArray.length()) + "\n" + JSONFormatter.format(iobArray.getString(0))).trim()); - } catch (JSONException e) { - log.error("Unhandled exception", e); - iobDataView.setText("JSONException see log for details"); - } - profileView.setText(JSONFormatter.format(determineBasalAdapterSMBJS.getProfileParam()).toString().trim()); - mealDataView.setText(JSONFormatter.format(determineBasalAdapterSMBJS.getMealDataParam()).toString().trim()); - scriptdebugView.setText(determineBasalAdapterSMBJS.getScriptDebug().trim()); - if (lastAPSResult != null && lastAPSResult.inputConstraints != null) - constraintsView.setText(lastAPSResult.inputConstraints.getReasons().trim()); - } - if (plugin.lastAPSRun != 0) { - lastRunView.setText(DateUtil.dateAndTimeFullString(plugin.lastAPSRun)); - } - if (plugin.lastAutosensResult != null) { - autosensDataView.setText(JSONFormatter.format(plugin.lastAutosensResult.json()).toString().trim()); - } - } - }); - } - - void updateResultGUI(final String text) { - Activity activity = getActivity(); - if (activity != null) - activity.runOnUiThread(() -> { - synchronized (OpenAPSSMBFragment.this) { - if (isBound()) { - resultView.setText(text); - glucoseStatusView.setText(""); - currentTempView.setText(""); - iobDataView.setText(""); - profileView.setText(""); - mealDataView.setText(""); - autosensDataView.setText(""); - scriptdebugView.setText(""); - requestView.setText(""); - lastRunView.setText(""); - } - } - }); - } - - private boolean isBound() { - return run != null - && lastRunView != null - && constraintsView != null - && glucoseStatusView != null - && currentTempView != null - && iobDataView != null - && profileView != null - && mealDataView != null - && autosensDataView != null - && resultView != null - && scriptdebugView != null - && requestView != null; - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBFragment.kt new file mode 100644 index 0000000000..06ad0d01fd --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBFragment.kt @@ -0,0 +1,122 @@ +package info.nightscout.androidaps.plugins.aps.openAPSSMB + +import android.annotation.SuppressLint +import android.os.Bundle +import android.text.TextUtils +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import info.nightscout.androidaps.MainApp +import info.nightscout.androidaps.R +import info.nightscout.androidaps.logging.L +import info.nightscout.androidaps.plugins.aps.openAPSMA.events.EventOpenAPSUpdateGui +import info.nightscout.androidaps.plugins.aps.openAPSMA.events.EventOpenAPSUpdateResultGui +import info.nightscout.androidaps.plugins.bus.RxBus +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.FabricPrivacy +import info.nightscout.androidaps.utils.JSONFormatter +import info.nightscout.androidaps.utils.plusAssign +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.CompositeDisposable +import kotlinx.android.synthetic.main.openapsama_fragment.* +import org.json.JSONArray +import org.json.JSONException +import org.slf4j.LoggerFactory + +class OpenAPSSMBFragment : Fragment() { + private val log = LoggerFactory.getLogger(L.APS) + private var disposable: CompositeDisposable = CompositeDisposable() + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.openapsama_fragment, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + openapsma_run.setOnClickListener { + OpenAPSSMBPlugin.getPlugin().invoke("OpenAPSSMB button", false) + } + } + + @Synchronized + override fun onResume() { + super.onResume() + disposable += RxBus + .toObservable(EventOpenAPSUpdateGui::class.java) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ + updateGUI() + }, { + FabricPrivacy.logException(it) + }) + disposable += RxBus + .toObservable(EventOpenAPSUpdateResultGui::class.java) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ + updateResultGUI(it.text) + }, { + FabricPrivacy.logException(it) + }) + + updateGUI() + } + + @Synchronized + override fun onPause() { + super.onPause() + disposable.clear() + } + + @Synchronized + fun updateGUI() { + if (openapsma_result == null) return + val plugin = OpenAPSSMBPlugin.getPlugin() + plugin.lastAPSResult?.let { lastAPSResult -> + openapsma_result.text = JSONFormatter.format(lastAPSResult.json) + openapsma_request.text = lastAPSResult.toSpanned() + } + plugin.lastDetermineBasalAdapterSMBJS?.let { determineBasalAdapterSMBJS -> + openapsma_glucosestatus.text = JSONFormatter.format(determineBasalAdapterSMBJS.glucoseStatusParam) + openapsma_currenttemp.text = JSONFormatter.format(determineBasalAdapterSMBJS.currentTempParam) + try { + val iobArray = JSONArray(determineBasalAdapterSMBJS.iobDataParam) + openapsma_iobdata.text = TextUtils.concat(String.format(MainApp.gs(R.string.array_of_elements), iobArray.length()) + "\n", JSONFormatter.format(iobArray.getString(0))) + } catch (e: JSONException) { + log.error("Unhandled exception", e) + @SuppressLint("SetTextl18n") + openapsma_iobdata.text = "JSONException see log for details" + } + + openapsma_profile.text = JSONFormatter.format(determineBasalAdapterSMBJS.profileParam) + openapsma_mealdata.text = JSONFormatter.format(determineBasalAdapterSMBJS.mealDataParam) + openapsma_scriptdebugdata.text = determineBasalAdapterSMBJS.scriptDebug + plugin.lastAPSResult?.inputConstraints?.let { + openapsma_constraints.text = it.reasons + } + } + if (plugin.lastAPSRun != 0L) { + openapsma_lastrun.text = DateUtil.dateAndTimeFullString(plugin.lastAPSRun) + } + plugin.lastAutosensResult?.let { + openapsma_autosensdata.text = JSONFormatter.format(it.json()) + } + } + + @Synchronized + private fun updateResultGUI(text: String) { + if (openapsma_result == null) return + openapsma_result.text = text + openapsma_glucosestatus.text = "" + openapsma_currenttemp.text = "" + openapsma_iobdata.text = "" + openapsma_profile.text = "" + openapsma_mealdata.text = "" + openapsma_autosensdata.text = "" + openapsma_scriptdebugdata.text = "" + openapsma_request.text = "" + openapsma_lastrun.text = "" + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBPlugin.java index 728e3145d6..d410eba291 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBPlugin.java @@ -6,7 +6,6 @@ import org.slf4j.LoggerFactory; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus; import info.nightscout.androidaps.data.IobTotal; import info.nightscout.androidaps.data.MealData; import info.nightscout.androidaps.data.Profile; @@ -19,18 +18,21 @@ import info.nightscout.androidaps.interfaces.PluginDescription; import info.nightscout.androidaps.interfaces.PluginType; import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.logging.L; -import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; -import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensData; -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult; -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin; import info.nightscout.androidaps.plugins.aps.loop.APSResult; import info.nightscout.androidaps.plugins.aps.loop.ScriptReader; -import info.nightscout.androidaps.plugins.general.nsclient.NSUpload; import info.nightscout.androidaps.plugins.aps.openAPSMA.events.EventOpenAPSUpdateGui; import info.nightscout.androidaps.plugins.aps.openAPSMA.events.EventOpenAPSUpdateResultGui; +import info.nightscout.androidaps.plugins.bus.RxBus; +import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; +import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; +import info.nightscout.androidaps.plugins.general.nsclient.NSUpload; +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensData; +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult; +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus; +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin; import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; import info.nightscout.androidaps.utils.DateUtil; +import info.nightscout.androidaps.utils.FabricPrivacy; import info.nightscout.androidaps.utils.HardLimits; import info.nightscout.androidaps.utils.Profiler; import info.nightscout.androidaps.utils.Round; @@ -103,21 +105,21 @@ public class OpenAPSSMBPlugin extends PluginBase implements APSInterface, Constr PumpInterface pump = ConfigBuilderPlugin.getPlugin().getActivePump(); if (profile == null) { - MainApp.bus().post(new EventOpenAPSUpdateResultGui(MainApp.gs(R.string.noprofileselected))); + RxBus.INSTANCE.send(new EventOpenAPSUpdateResultGui(MainApp.gs(R.string.noprofileselected))); if (L.isEnabled(L.APS)) log.debug(MainApp.gs(R.string.noprofileselected)); return; } if (!isEnabled(PluginType.APS)) { - MainApp.bus().post(new EventOpenAPSUpdateResultGui(MainApp.gs(R.string.openapsma_disabled))); + RxBus.INSTANCE.send(new EventOpenAPSUpdateResultGui(MainApp.gs(R.string.openapsma_disabled))); if (L.isEnabled(L.APS)) log.debug(MainApp.gs(R.string.openapsma_disabled)); return; } if (glucoseStatus == null) { - MainApp.bus().post(new EventOpenAPSUpdateResultGui(MainApp.gs(R.string.openapsma_noglucosedata))); + RxBus.INSTANCE.send(new EventOpenAPSUpdateResultGui(MainApp.gs(R.string.openapsma_noglucosedata))); if (L.isEnabled(L.APS)) log.debug(MainApp.gs(R.string.openapsma_noglucosedata)); return; @@ -139,11 +141,7 @@ public class OpenAPSSMBPlugin extends PluginBase implements APSInterface, Constr long start = System.currentTimeMillis(); long startPart = System.currentTimeMillis(); - IobTotal[] iobArray = IobCobCalculatorPlugin.getPlugin().calculateIobArrayForSMB(profile); - if (L.isEnabled(L.APS)) - Profiler.log(log, "calculateIobArrayInDia()", startPart); - startPart = System.currentTimeMillis(); MealData mealData = TreatmentsPlugin.getPlugin().getMealData(); if (L.isEnabled(L.APS)) Profiler.log(log, "getMealData()", startPart); @@ -181,7 +179,7 @@ public class OpenAPSSMBPlugin extends PluginBase implements APSInterface, Constr if (MainApp.getConstraintChecker().isAutosensModeEnabled().value()) { AutosensData autosensData = IobCobCalculatorPlugin.getPlugin().getLastAutosensDataSynchronized("OpenAPSPlugin"); if (autosensData == null) { - MainApp.bus().post(new EventOpenAPSUpdateResultGui(MainApp.gs(R.string.openaps_noasdata))); + RxBus.INSTANCE.send(new EventOpenAPSUpdateResultGui(MainApp.gs(R.string.openaps_noasdata))); return; } lastAutosensResult = autosensData.autosensResult; @@ -190,6 +188,11 @@ public class OpenAPSSMBPlugin extends PluginBase implements APSInterface, Constr lastAutosensResult.sensResult = "autosens disabled"; } + IobTotal[] iobArray = IobCobCalculatorPlugin.getPlugin().calculateIobArrayForSMB(lastAutosensResult, SMBDefaults.exercise_mode, SMBDefaults.half_basal_exercise_target, isTempTarget); + if (L.isEnabled(L.APS)) + Profiler.log(log, "calculateIobArrayInDia()", startPart); + + startPart = System.currentTimeMillis(); Constraint smbAllowed = new Constraint<>(!tempBasalFallback); MainApp.getConstraintChecker().isSMBModeEnabled(smbAllowed); inputConstraints.copyReasons(smbAllowed); @@ -217,7 +220,7 @@ public class OpenAPSSMBPlugin extends PluginBase implements APSInterface, Constr advancedFiltering.value() ); } catch (JSONException e) { - log.error(e.getMessage()); + FabricPrivacy.logException(e); return; } @@ -226,25 +229,33 @@ public class OpenAPSSMBPlugin extends PluginBase implements APSInterface, Constr DetermineBasalResultSMB determineBasalResultSMB = determineBasalAdapterSMBJS.invoke(); if (L.isEnabled(L.APS)) Profiler.log(log, "SMB calculation", start); - // TODO still needed with oref1? - // Fix bug determine basal - if (determineBasalResultSMB.rate == 0d && determineBasalResultSMB.duration == 0 && !TreatmentsPlugin.getPlugin().isTempBasalInProgress()) - determineBasalResultSMB.tempBasalRequested = false; + if (determineBasalResultSMB == null) { + if (L.isEnabled(L.APS)) + log.error("SMB calculation returned null"); + lastDetermineBasalAdapterSMBJS = null; + lastAPSResult = null; + lastAPSRun = 0; + } else { + // TODO still needed with oref1? + // Fix bug determine basal + if (determineBasalResultSMB.rate == 0d && determineBasalResultSMB.duration == 0 && !TreatmentsPlugin.getPlugin().isTempBasalInProgress()) + determineBasalResultSMB.tempBasalRequested = false; - determineBasalResultSMB.iob = iobArray[0]; + determineBasalResultSMB.iob = iobArray[0]; - try { - determineBasalResultSMB.json.put("timestamp", DateUtil.toISOString(now)); - } catch (JSONException e) { - log.error("Unhandled exception", e); + try { + determineBasalResultSMB.json.put("timestamp", DateUtil.toISOString(now)); + } catch (JSONException e) { + log.error("Unhandled exception", e); + } + + determineBasalResultSMB.inputConstraints = inputConstraints; + + lastDetermineBasalAdapterSMBJS = determineBasalAdapterSMBJS; + lastAPSResult = determineBasalResultSMB; + lastAPSRun = now; } - - determineBasalResultSMB.inputConstraints = inputConstraints; - - lastDetermineBasalAdapterSMBJS = determineBasalAdapterSMBJS; - lastAPSResult = determineBasalResultSMB; - lastAPSRun = now; - MainApp.bus().post(new EventOpenAPSUpdateGui()); + RxBus.INSTANCE.send(new EventOpenAPSUpdateGui()); //deviceStatus.suggested = determineBasalResultAMA.json; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/bus/RxBus.kt b/app/src/main/java/info/nightscout/androidaps/plugins/bus/RxBus.kt new file mode 100644 index 0000000000..1774df1471 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/bus/RxBus.kt @@ -0,0 +1,23 @@ +package info.nightscout.androidaps.plugins.bus + +import info.nightscout.androidaps.events.Event +import io.reactivex.Observable +import io.reactivex.schedulers.Schedulers +import io.reactivex.subjects.PublishSubject + +// Use object so we have a singleton instance +object RxBus { + + private val publisher = PublishSubject.create() + + fun send(event: Event) { + publisher.onNext(event) + } + + // Listen should return an Observable and not the publisher + // Using ofType we filter only events that match that class type + fun toObservable(eventType: Class): Observable = + publisher + .subscribeOn(Schedulers.io()) + .ofType(eventType) +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/common/ManufacturerType.java b/app/src/main/java/info/nightscout/androidaps/plugins/common/ManufacturerType.java new file mode 100644 index 0000000000..0f61dd3320 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/common/ManufacturerType.java @@ -0,0 +1,27 @@ +package info.nightscout.androidaps.plugins.common; + +public enum ManufacturerType { + + AndroidAPS("AndroidAPS"), + Medtronic("Medtronic"), + Sooil("SOOIL"), + + Tandem("Tandem"), + Insulet("Insulet"), + Animas("Animas"), Cellnovo("Cellnovo"), Roche("Roche"); + + + + private String description; + + ManufacturerType(String description) { + + this.description = description; + } + + public String getDescription() { + return description; + } + + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/common/SubscriberFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/common/SubscriberFragment.java deleted file mode 100644 index 3ac877b42d..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/common/SubscriberFragment.java +++ /dev/null @@ -1,32 +0,0 @@ -package info.nightscout.androidaps.plugins.common; - -import android.support.v4.app.Fragment; - -import butterknife.Unbinder; -import info.nightscout.androidaps.MainApp; - -abstract public class SubscriberFragment extends Fragment { - protected Unbinder unbinder; - - @Override - public void onPause() { - super.onPause(); - MainApp.bus().unregister(this); - } - - @Override - public void onResume() { - super.onResume(); - MainApp.bus().register(this); - updateGUI(); - } - - @Override public synchronized void onDestroyView() { - super.onDestroyView(); - if (unbinder != null) - unbinder.unbind(); - } - - - protected abstract void updateGUI(); -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ConfigBuilderFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ConfigBuilderFragment.java deleted file mode 100644 index 47b5d49357..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ConfigBuilderFragment.java +++ /dev/null @@ -1,283 +0,0 @@ -package info.nightscout.androidaps.plugins.configBuilder; - - -import android.content.Intent; -import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.annotation.StringRes; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.Button; -import android.widget.CheckBox; -import android.widget.ImageButton; -import android.widget.LinearLayout; -import android.widget.RadioButton; -import android.widget.ScrollView; -import android.widget.TextView; - -import java.util.ArrayList; -import java.util.List; - -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.OnClick; -import butterknife.Unbinder; -import info.nightscout.androidaps.MainApp; -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.activities.PreferencesActivity; -import info.nightscout.androidaps.events.EventConfigBuilderChange; -import info.nightscout.androidaps.events.EventRefreshGui; -import info.nightscout.androidaps.interfaces.APSInterface; -import info.nightscout.androidaps.interfaces.BgSourceInterface; -import info.nightscout.androidaps.interfaces.ConstraintsInterface; -import info.nightscout.androidaps.interfaces.InsulinInterface; -import info.nightscout.androidaps.interfaces.PluginBase; -import info.nightscout.androidaps.interfaces.PluginType; -import info.nightscout.androidaps.interfaces.ProfileInterface; -import info.nightscout.androidaps.interfaces.PumpInterface; -import info.nightscout.androidaps.interfaces.SensitivityInterface; -import info.nightscout.androidaps.plugins.common.SubscriberFragment; -import info.nightscout.androidaps.plugins.insulin.InsulinOrefRapidActingPlugin; -import info.nightscout.androidaps.plugins.profile.ns.NSProfilePlugin; -import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin; -import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref0Plugin; -import info.nightscout.androidaps.utils.FabricPrivacy; -import info.nightscout.androidaps.utils.PasswordProtection; - - -public class ConfigBuilderFragment extends SubscriberFragment { - - private List pluginViewHolders = new ArrayList<>(); - - @BindView(R.id.categories) - LinearLayout categories; - - @BindView(R.id.main_layout) - ScrollView mainLayout; - @BindView(R.id.unlock) - Button unlock; - - @Override - public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - try { - View view = inflater.inflate(R.layout.configbuilder_fragment, container, false); - unbinder = ButterKnife.bind(this, view); - - if (PasswordProtection.isLocked("settings_password")) - mainLayout.setVisibility(View.GONE); - else unlock.setVisibility(View.GONE); - - createViews(); - - return view; - } catch (Exception e) { - FabricPrivacy.logException(e); - } - - return null; - } - - @OnClick(R.id.unlock) - void onClickUnlock() { - PasswordProtection.QueryPassword(getContext(), R.string.settings_password, "settings_password", () -> { - mainLayout.setVisibility(View.VISIBLE); - unlock.setVisibility(View.GONE); - }, null); - } - - @Override - public void onDestroyView() { - super.onDestroyView(); - for (PluginViewHolder pluginViewHolder : pluginViewHolders) pluginViewHolder.unbind(); - pluginViewHolders.clear(); - } - - @Override - protected void updateGUI() { - for (PluginViewHolder pluginViewHolder : pluginViewHolders) pluginViewHolder.update(); - } - - private void createViews() { - createViewsForPlugins(R.string.configbuilder_profile, R.string.configbuilder_profile_description, PluginType.PROFILE, MainApp.getSpecificPluginsVisibleInListByInterface(ProfileInterface.class, PluginType.PROFILE)); - createViewsForPlugins(R.string.configbuilder_insulin, R.string.configbuilder_insulin_description, PluginType.INSULIN, MainApp.getSpecificPluginsVisibleInListByInterface(InsulinInterface.class, PluginType.INSULIN)); - createViewsForPlugins(R.string.configbuilder_bgsource, R.string.configbuilder_bgsource_description, PluginType.BGSOURCE, MainApp.getSpecificPluginsVisibleInListByInterface(BgSourceInterface.class, PluginType.BGSOURCE)); - createViewsForPlugins(R.string.configbuilder_pump, R.string.configbuilder_pump_description, PluginType.PUMP, MainApp.getSpecificPluginsVisibleInList(PluginType.PUMP)); - createViewsForPlugins(R.string.configbuilder_sensitivity, R.string.configbuilder_sensitivity_description, PluginType.SENSITIVITY, MainApp.getSpecificPluginsVisibleInListByInterface(SensitivityInterface.class, PluginType.SENSITIVITY)); - createViewsForPlugins(R.string.configbuilder_aps, R.string.configbuilder_aps_description, PluginType.APS, MainApp.getSpecificPluginsVisibleInList(PluginType.APS)); - createViewsForPlugins(R.string.configbuilder_loop, R.string.configbuilder_loop_description, PluginType.LOOP, MainApp.getSpecificPluginsVisibleInList(PluginType.LOOP)); - createViewsForPlugins(R.string.constraints, R.string.configbuilder_constraints_description, PluginType.CONSTRAINTS, MainApp.getSpecificPluginsVisibleInListByInterface(ConstraintsInterface.class, PluginType.CONSTRAINTS)); - createViewsForPlugins(R.string.configbuilder_treatments, R.string.configbuilder_treatments_description, PluginType.TREATMENT, MainApp.getSpecificPluginsVisibleInList(PluginType.TREATMENT)); - createViewsForPlugins(R.string.configbuilder_general, R.string.configbuilder_general_description, PluginType.GENERAL, MainApp.getSpecificPluginsVisibleInList(PluginType.GENERAL)); - } - - private void createViewsForPlugins(@StringRes int title, @StringRes int description, PluginType pluginType, List plugins) { - if (plugins.size() == 0) return; - LinearLayout parent = (LinearLayout) getLayoutInflater().inflate(R.layout.configbuilder_single_category, null); - ((TextView) parent.findViewById(R.id.category_title)).setText(MainApp.gs(title)); - ((TextView) parent.findViewById(R.id.category_description)).setText(MainApp.gs(description)); - LinearLayout pluginContainer = parent.findViewById(R.id.category_plugins); - for (PluginBase plugin: plugins) { - PluginViewHolder pluginViewHolder = new PluginViewHolder(pluginType, plugin); - pluginContainer.addView(pluginViewHolder.getBaseView()); - pluginViewHolders.add(pluginViewHolder); - } - categories.addView(parent); - } - - private boolean areMultipleSelectionsAllowed(PluginType type) { - return type == PluginType.GENERAL || type == PluginType.CONSTRAINTS ||type == PluginType.LOOP; - } - - public static void processOnEnabledCategoryChanged(PluginBase changedPlugin, PluginType type) { - ArrayList pluginsInCategory = null; - switch (type) { - // Multiple selection allowed - case GENERAL: - case CONSTRAINTS: - case LOOP: - break; - // Single selection allowed - case INSULIN: - pluginsInCategory = MainApp.getSpecificPluginsListByInterface(InsulinInterface.class); - break; - case SENSITIVITY: - pluginsInCategory = MainApp.getSpecificPluginsListByInterface(SensitivityInterface.class); - break; - case APS: - pluginsInCategory = MainApp.getSpecificPluginsListByInterface(APSInterface.class); - break; - case PROFILE: - pluginsInCategory = MainApp.getSpecificPluginsListByInterface(ProfileInterface.class); - break; - case BGSOURCE: - pluginsInCategory = MainApp.getSpecificPluginsListByInterface(BgSourceInterface.class); - break; - case TREATMENT: - case PUMP: - pluginsInCategory = MainApp.getSpecificPluginsListByInterface(PumpInterface.class); - break; - } - if (pluginsInCategory != null) { - boolean newSelection = changedPlugin.isEnabled(type); - if (newSelection) { // new plugin selected -> disable others - for (PluginBase p : pluginsInCategory) { - if (p.getName().equals(changedPlugin.getName())) { - // this is new selected - } else { - p.setPluginEnabled(type, false); - p.setFragmentVisible(type, false); - } - } - } else { // enable first plugin in list - if (type == PluginType.PUMP) - VirtualPumpPlugin.getPlugin().setPluginEnabled(type, true); - else if (type == PluginType.INSULIN) - InsulinOrefRapidActingPlugin.getPlugin().setPluginEnabled(type, true); - else if (type == PluginType.SENSITIVITY) - SensitivityOref0Plugin.getPlugin().setPluginEnabled(type, true); - else if (type == PluginType.PROFILE) - NSProfilePlugin.getPlugin().setPluginEnabled(type, true); - else - pluginsInCategory.get(0).setPluginEnabled(type, true); - } - } - } - - public class PluginViewHolder { - - private Unbinder unbinder; - private PluginType pluginType; - private PluginBase plugin; - - LinearLayout baseView; - @BindView(R.id.plugin_enabled_exclusive) - RadioButton enabledExclusive; - @BindView(R.id.plugin_enabled_inclusive) - CheckBox enabledInclusive; - @BindView(R.id.plugin_name) - TextView pluginName; - @BindView(R.id.plugin_description) - TextView pluginDescription; - @BindView(R.id.plugin_preferences) - ImageButton pluginPreferences; - @BindView(R.id.plugin_visibility) - CheckBox pluginVisibility; - - public PluginViewHolder(PluginType pluginType, PluginBase plugin) { - this.pluginType = pluginType; - this.plugin = plugin; - baseView = (LinearLayout) getLayoutInflater().inflate(R.layout.configbuilder_single_plugin, null); - unbinder = ButterKnife.bind(this, baseView); - update(); - } - - public LinearLayout getBaseView() { - return baseView; - } - - public void update() { - enabledExclusive.setVisibility(areMultipleSelectionsAllowed(pluginType) ? View.GONE : View.VISIBLE); - enabledInclusive.setVisibility(areMultipleSelectionsAllowed(pluginType) ? View.VISIBLE : View.GONE); - enabledExclusive.setChecked(plugin.isEnabled(pluginType)); - enabledInclusive.setChecked(plugin.isEnabled(pluginType)); - enabledInclusive.setEnabled(!plugin.pluginDescription.alwaysEnabled); - enabledExclusive.setEnabled(!plugin.pluginDescription.alwaysEnabled); - pluginName.setText(plugin.getName()); - if (plugin.getDescription() == null) pluginDescription.setVisibility(View.GONE); - else { - pluginDescription.setVisibility(View.VISIBLE); - pluginDescription.setText(plugin.getDescription()); - } - pluginPreferences.setVisibility(plugin.getPreferencesId() == -1 || !plugin.isEnabled(pluginType) ? View.INVISIBLE : View.VISIBLE); - pluginVisibility.setVisibility(plugin.hasFragment() ? View.VISIBLE : View.INVISIBLE); - pluginVisibility.setEnabled(!(plugin.pluginDescription.neverVisible || plugin.pluginDescription.alwayVisible) && plugin.isEnabled(pluginType)); - pluginVisibility.setChecked(plugin.isFragmentVisible()); - } - - @OnClick(R.id.plugin_visibility) - void onVisibilityChanged() { - plugin.setFragmentVisible(pluginType, pluginVisibility.isChecked()); - ConfigBuilderPlugin.getPlugin().storeSettings("CheckedCheckboxVisible"); - MainApp.bus().post(new EventRefreshGui()); - ConfigBuilderPlugin.getPlugin().logPluginStatus(); - } - - @OnClick({R.id.plugin_enabled_exclusive, R.id.plugin_enabled_inclusive}) - void onEnabledChanged() { - plugin.switchAllowed(new PluginSwitcher(), getActivity()); - } - - @OnClick(R.id.plugin_preferences) - void onPluginPreferencesClicked() { - PasswordProtection.QueryPassword(getContext(), R.string.settings_password, "settings_password", () -> { - Intent i = new Intent(getContext(), PreferencesActivity.class); - i.putExtra("id", plugin.getPreferencesId()); - startActivity(i); - }, null); - } - - public void unbind() { - unbinder.unbind(); - } - - public class PluginSwitcher { - public void invoke() { - boolean enabled = enabledExclusive.getVisibility() == View.VISIBLE ? enabledExclusive.isChecked() : enabledInclusive.isChecked(); - plugin.setPluginEnabled(pluginType, enabled); - plugin.setFragmentVisible(pluginType, enabled); - processOnEnabledCategoryChanged(plugin, pluginType); - updateGUI(); - ConfigBuilderPlugin.getPlugin().storeSettings("CheckedCheckboxEnabled"); - MainApp.bus().post(new EventRefreshGui()); - MainApp.bus().post(new EventConfigBuilderChange()); - ConfigBuilderPlugin.getPlugin().logPluginStatus(); - } - - public void cancel(){ - updateGUI(); - } - } - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ConfigBuilderFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ConfigBuilderFragment.kt new file mode 100644 index 0000000000..059bec1444 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ConfigBuilderFragment.kt @@ -0,0 +1,98 @@ +package info.nightscout.androidaps.plugins.configBuilder + + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.LinearLayout +import android.widget.TextView +import androidx.annotation.StringRes +import androidx.fragment.app.Fragment +import info.nightscout.androidaps.MainApp +import info.nightscout.androidaps.R +import info.nightscout.androidaps.interfaces.* +import info.nightscout.androidaps.plugins.bus.RxBus +import info.nightscout.androidaps.utils.FabricPrivacy +import info.nightscout.androidaps.utils.PasswordProtection +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.CompositeDisposable +import kotlinx.android.synthetic.main.configbuilder_fragment.* +import java.util.* + +class ConfigBuilderFragment : Fragment() { + + private var disposable: CompositeDisposable = CompositeDisposable() + private val pluginViewHolders = ArrayList() + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.configbuilder_fragment, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + if (PasswordProtection.isLocked("settings_password")) + configbuilder_main_layout.visibility = View.GONE + else + unlock.visibility = View.GONE + + unlock.setOnClickListener { + PasswordProtection.QueryPassword(context, R.string.settings_password, "settings_password", { + configbuilder_main_layout.visibility = View.VISIBLE + unlock.visibility = View.GONE + }, null) + } + } + + @Synchronized + override fun onResume() { + super.onResume() + disposable.add(RxBus + .toObservable(EventConfigBuilderUpdateGui::class.java) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ + for (pluginViewHolder in pluginViewHolders) pluginViewHolder.update() + }, { + FabricPrivacy.logException(it) + })) + updateGUI() + } + + @Synchronized + override fun onPause() { + super.onPause() + disposable.clear() + } + + @Synchronized + private fun updateGUI() { + configbuilder_categories.removeAllViews() + createViewsForPlugins(R.string.configbuilder_profile, R.string.configbuilder_profile_description, PluginType.PROFILE, MainApp.getSpecificPluginsVisibleInListByInterface(ProfileInterface::class.java, PluginType.PROFILE)) + createViewsForPlugins(R.string.configbuilder_insulin, R.string.configbuilder_insulin_description, PluginType.INSULIN, MainApp.getSpecificPluginsVisibleInListByInterface(InsulinInterface::class.java, PluginType.INSULIN)) + createViewsForPlugins(R.string.configbuilder_bgsource, R.string.configbuilder_bgsource_description, PluginType.BGSOURCE, MainApp.getSpecificPluginsVisibleInListByInterface(BgSourceInterface::class.java, PluginType.BGSOURCE)) + createViewsForPlugins(R.string.configbuilder_pump, R.string.configbuilder_pump_description, PluginType.PUMP, MainApp.getSpecificPluginsVisibleInList(PluginType.PUMP)) + createViewsForPlugins(R.string.configbuilder_sensitivity, R.string.configbuilder_sensitivity_description, PluginType.SENSITIVITY, MainApp.getSpecificPluginsVisibleInListByInterface(SensitivityInterface::class.java, PluginType.SENSITIVITY)) + createViewsForPlugins(R.string.configbuilder_aps, R.string.configbuilder_aps_description, PluginType.APS, MainApp.getSpecificPluginsVisibleInList(PluginType.APS)) + createViewsForPlugins(R.string.configbuilder_loop, R.string.configbuilder_loop_description, PluginType.LOOP, MainApp.getSpecificPluginsVisibleInList(PluginType.LOOP)) + createViewsForPlugins(R.string.constraints, R.string.configbuilder_constraints_description, PluginType.CONSTRAINTS, MainApp.getSpecificPluginsVisibleInListByInterface(ConstraintsInterface::class.java, PluginType.CONSTRAINTS)) + createViewsForPlugins(R.string.configbuilder_treatments, R.string.configbuilder_treatments_description, PluginType.TREATMENT, MainApp.getSpecificPluginsVisibleInList(PluginType.TREATMENT)) + createViewsForPlugins(R.string.configbuilder_general, R.string.configbuilder_general_description, PluginType.GENERAL, MainApp.getSpecificPluginsVisibleInList(PluginType.GENERAL)) + } + + private fun createViewsForPlugins(@StringRes title: Int, @StringRes description: Int, pluginType: PluginType, plugins: List) { + if (plugins.size == 0) return + val parent = layoutInflater.inflate(R.layout.configbuilder_single_category, null) as LinearLayout + (parent.findViewById(R.id.category_title) as TextView).text = MainApp.gs(title) + (parent.findViewById(R.id.category_description) as TextView).text = MainApp.gs(description) + val pluginContainer = parent.findViewById(R.id.category_plugins) + for (plugin in plugins) { + val pluginViewHolder = PluginViewHolder(this, pluginType, plugin) + pluginContainer.addView(pluginViewHolder.baseView) + pluginViewHolders.add(pluginViewHolder) + } + configbuilder_categories.addView(parent) + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ConfigBuilderPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ConfigBuilderPlugin.java index 3c3531da86..f43513fbf5 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ConfigBuilderPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ConfigBuilderPlugin.java @@ -1,6 +1,6 @@ package info.nightscout.androidaps.plugins.configBuilder; -import android.support.annotation.Nullable; +import androidx.annotation.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -20,7 +20,9 @@ import info.nightscout.androidaps.interfaces.ProfileInterface; import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.interfaces.SensitivityInterface; import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.insulin.InsulinOrefRapidActingPlugin; +import info.nightscout.androidaps.plugins.profile.ns.NSProfilePlugin; import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin; import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref0Plugin; import info.nightscout.androidaps.queue.CommandQueue; @@ -57,7 +59,7 @@ public class ConfigBuilderPlugin extends PluginBase { .fragmentClass(ConfigBuilderFragment.class.getName()) .showInList(true) .alwaysEnabled(true) - .alwayVisible(false) + .alwaysVisible(false) .pluginName(R.string.configbuilder) .shortName(R.string.configbuilder_shortname) .description(R.string.description_config_builder) @@ -66,14 +68,12 @@ public class ConfigBuilderPlugin extends PluginBase { @Override protected void onStart() { - MainApp.bus().register(this); super.onStart(); } @Override protected void onStop() { super.onStop(); - MainApp.bus().unregister(this); } @@ -82,7 +82,7 @@ public class ConfigBuilderPlugin extends PluginBase { upgradeSettings(); loadSettings(); setAlwaysEnabledPluginsEnabled(); - MainApp.bus().post(new EventAppInitialized()); + RxBus.INSTANCE.send(new EventAppInitialized()); } private void setAlwaysEnabledPluginsEnabled() { @@ -102,7 +102,7 @@ public class ConfigBuilderPlugin extends PluginBase { for (PluginBase p : pluginList) { PluginType type = p.getType(); - if (p.pluginDescription.alwaysEnabled && p.pluginDescription.alwayVisible) + if (p.pluginDescription.alwaysEnabled && p.pluginDescription.alwaysVisible) continue; if (p.pluginDescription.alwaysEnabled && p.pluginDescription.neverVisible) continue; @@ -232,31 +232,37 @@ public class ConfigBuilderPlugin extends PluginBase { return commandQueue; } + @Nullable public BgSourceInterface getActiveBgSource() { return activeBgSource; } + @Nullable public ProfileInterface getActiveProfileInterface() { return activeProfile; } + @Nullable public InsulinInterface getActiveInsulin() { return activeInsulin; } + @Nullable public APSInterface getActiveAPS() { return activeAPS; } + @Nullable public PumpInterface getActivePump() { return activePump; } + @Nullable public SensitivityInterface getActiveSensitivity() { return activeSensitivity; } - void logPluginStatus() { + public void logPluginStatus() { if (L.isEnabled(L.CONFIGBUILDER)) for (PluginBase p : pluginList) { log.debug(p.getName() + ":" + @@ -399,4 +405,58 @@ public class ConfigBuilderPlugin extends PluginBase { return found; } + public void processOnEnabledCategoryChanged(PluginBase changedPlugin, PluginType type) { + ArrayList pluginsInCategory = null; + switch (type) { + // Multiple selection allowed + case GENERAL: + case CONSTRAINTS: + case LOOP: + break; + // Single selection allowed + case INSULIN: + pluginsInCategory = MainApp.getSpecificPluginsListByInterface(InsulinInterface.class); + break; + case SENSITIVITY: + pluginsInCategory = MainApp.getSpecificPluginsListByInterface(SensitivityInterface.class); + break; + case APS: + pluginsInCategory = MainApp.getSpecificPluginsListByInterface(APSInterface.class); + break; + case PROFILE: + pluginsInCategory = MainApp.getSpecificPluginsListByInterface(ProfileInterface.class); + break; + case BGSOURCE: + pluginsInCategory = MainApp.getSpecificPluginsListByInterface(BgSourceInterface.class); + break; + case TREATMENT: + case PUMP: + pluginsInCategory = MainApp.getSpecificPluginsListByInterface(PumpInterface.class); + break; + } + if (pluginsInCategory != null) { + boolean newSelection = changedPlugin.isEnabled(type); + if (newSelection) { // new plugin selected -> disable others + for (PluginBase p : pluginsInCategory) { + if (p.getName().equals(changedPlugin.getName())) { + // this is new selected + } else { + p.setPluginEnabled(type, false); + p.setFragmentVisible(type, false); + } + } + } else { // enable first plugin in list + if (type == PluginType.PUMP) + VirtualPumpPlugin.getPlugin().setPluginEnabled(type, true); + else if (type == PluginType.INSULIN) + InsulinOrefRapidActingPlugin.getPlugin().setPluginEnabled(type, true); + else if (type == PluginType.SENSITIVITY) + SensitivityOref0Plugin.getPlugin().setPluginEnabled(type, true); + else if (type == PluginType.PROFILE) + NSProfilePlugin.getPlugin().setPluginEnabled(type, true); + else + pluginsInCategory.get(0).setPluginEnabled(type, true); + } + } + } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/DetailedBolusInfoStorage.java b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/DetailedBolusInfoStorage.java deleted file mode 100644 index b19948fc5e..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/DetailedBolusInfoStorage.java +++ /dev/null @@ -1,44 +0,0 @@ -package info.nightscout.androidaps.plugins.configBuilder; - -import android.support.annotation.Nullable; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.ArrayList; -import java.util.List; - -import info.nightscout.androidaps.data.DetailedBolusInfo; -import info.nightscout.androidaps.logging.L; - -/** - * Created by mike on 08.08.2017. - */ - -public class DetailedBolusInfoStorage { - private static Logger log = LoggerFactory.getLogger(L.PUMP); - private static List store = new ArrayList<>(); - - public static synchronized void add(DetailedBolusInfo detailedBolusInfo) { - log.debug("Stored bolus info: " + detailedBolusInfo); - store.add(detailedBolusInfo); - } - - @Nullable - public static synchronized DetailedBolusInfo findDetailedBolusInfo(long bolustime) { - DetailedBolusInfo found = null; - for (int i = 0; i < store.size(); i++) { - long infoTime = store.get(i).date; - if (L.isEnabled(L.PUMP)) - log.debug("Existing bolus info: " + store.get(i)); - if (bolustime > infoTime - 60 * 1000 && bolustime < infoTime + 60 * 1000) { - found = store.get(i); - if (L.isEnabled(L.PUMP)) - log.debug("Using & removing bolus info: " + store.get(i)); - store.remove(i); - break; - } - } - return found; - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/EventConfigBuilderUpdateGui.kt b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/EventConfigBuilderUpdateGui.kt new file mode 100644 index 0000000000..e57fd79983 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/EventConfigBuilderUpdateGui.kt @@ -0,0 +1,6 @@ +package info.nightscout.androidaps.plugins.configBuilder + +import info.nightscout.androidaps.events.EventUpdateGui + +class EventConfigBuilderUpdateGui : EventUpdateGui() { +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/PluginViewHolder.kt b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/PluginViewHolder.kt new file mode 100644 index 0000000000..86e0f39437 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/PluginViewHolder.kt @@ -0,0 +1,82 @@ +package info.nightscout.androidaps.plugins.configBuilder + +import android.content.Intent +import android.view.View +import android.widget.* +import info.nightscout.androidaps.R +import info.nightscout.androidaps.activities.PreferencesActivity +import info.nightscout.androidaps.events.EventRebuildTabs +import info.nightscout.androidaps.interfaces.PluginBase +import info.nightscout.androidaps.interfaces.PluginType +import info.nightscout.androidaps.plugins.bus.RxBus +import info.nightscout.androidaps.utils.PasswordProtection + +class PluginViewHolder internal constructor(private val fragment: ConfigBuilderFragment, + private val pluginType: PluginType, + private val plugin: PluginBase) { + + val baseView: LinearLayout = fragment.layoutInflater.inflate(R.layout.configbuilder_single_plugin, null) as LinearLayout + private val enabledExclusive: RadioButton + private val enabledInclusive: CheckBox + private val pluginName: TextView + private val pluginDescription: TextView + private val pluginPreferences: ImageButton + private val pluginVisibility: CheckBox + + init { + enabledExclusive = baseView.findViewById(R.id.plugin_enabled_exclusive) + enabledInclusive = baseView.findViewById(R.id.plugin_enabled_inclusive) + pluginName = baseView.findViewById(R.id.plugin_name) + pluginDescription = baseView.findViewById(R.id.plugin_description) + pluginPreferences = baseView.findViewById(R.id.plugin_preferences) + pluginVisibility = baseView.findViewById(R.id.plugin_visibility) + + pluginVisibility.setOnClickListener { + plugin.setFragmentVisible(pluginType, pluginVisibility.isChecked) + ConfigBuilderPlugin.getPlugin().storeSettings("CheckedCheckboxVisible") + RxBus.send(EventRebuildTabs()) + ConfigBuilderPlugin.getPlugin().logPluginStatus() + } + + enabledExclusive.setOnClickListener { + plugin.switchAllowed(if (enabledExclusive.visibility == View.VISIBLE) enabledExclusive.isChecked else enabledInclusive.isChecked, fragment.activity, pluginType) + } + enabledInclusive.setOnClickListener { + plugin.switchAllowed(if (enabledExclusive.visibility == View.VISIBLE) enabledExclusive.isChecked else enabledInclusive.isChecked, fragment.activity, pluginType) + } + + pluginPreferences.setOnClickListener { + PasswordProtection.QueryPassword(fragment.context, R.string.settings_password, "settings_password", { + val i = Intent(fragment.context, PreferencesActivity::class.java) + i.putExtra("id", plugin.preferencesId) + fragment.startActivity(i) + }, null) + } + update() + } + + fun update() { + enabledExclusive.visibility = if (areMultipleSelectionsAllowed(pluginType)) View.GONE else View.VISIBLE + enabledInclusive.visibility = if (areMultipleSelectionsAllowed(pluginType)) View.VISIBLE else View.GONE + enabledExclusive.isChecked = plugin.isEnabled(pluginType) + enabledInclusive.isChecked = plugin.isEnabled(pluginType) + enabledInclusive.isEnabled = !plugin.pluginDescription.alwaysEnabled + enabledExclusive.isEnabled = !plugin.pluginDescription.alwaysEnabled + pluginName.text = plugin.name + if (plugin.description == null) + pluginDescription.visibility = View.GONE + else { + pluginDescription.visibility = View.VISIBLE + pluginDescription.text = plugin.description + } + pluginPreferences.visibility = if (plugin.preferencesId == -1 || !plugin.isEnabled(pluginType)) View.INVISIBLE else View.VISIBLE + pluginVisibility.visibility = if (plugin.hasFragment()) View.VISIBLE else View.INVISIBLE + pluginVisibility.isEnabled = !(plugin.pluginDescription.neverVisible || plugin.pluginDescription.alwaysVisible) && plugin.isEnabled(pluginType) + pluginVisibility.isChecked = plugin.isFragmentVisible + } + + private fun areMultipleSelectionsAllowed(type: PluginType): Boolean { + return type == PluginType.GENERAL || type == PluginType.CONSTRAINTS || type == PluginType.LOOP + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ProfileFunctions.java b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ProfileFunctions.java index 0360ba4a68..025d8dac1e 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ProfileFunctions.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ProfileFunctions.java @@ -2,10 +2,10 @@ package info.nightscout.androidaps.plugins.configBuilder; import android.content.Intent; import android.os.Bundle; -import android.support.annotation.Nullable; + +import androidx.annotation.Nullable; import com.google.firebase.analytics.FirebaseAnalytics; -import com.squareup.otto.Subscribe; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -23,13 +23,18 @@ import info.nightscout.androidaps.events.EventProfileNeedsUpdate; import info.nightscout.androidaps.interfaces.ProfileInterface; import info.nightscout.androidaps.interfaces.TreatmentsInterface; import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.general.overview.dialogs.ErrorHelperActivity; import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; import info.nightscout.androidaps.queue.Callback; import info.nightscout.androidaps.utils.FabricPrivacy; +import info.nightscout.androidaps.utils.SP; +import io.reactivex.disposables.CompositeDisposable; +import io.reactivex.schedulers.Schedulers; public class ProfileFunctions { private static Logger log = LoggerFactory.getLogger(L.PROFILE); + private CompositeDisposable disposable = new CompositeDisposable(); private static ProfileFunctions profileFunctions = null; @@ -43,29 +48,30 @@ public class ProfileFunctions { ProfileFunctions.getInstance(); // register to bus at start } - ProfileFunctions() { - MainApp.bus().register(this); - } - - @Subscribe - public void onProfileSwitch(EventProfileNeedsUpdate ignored) { - if (L.isEnabled(L.PROFILE)) - log.debug("onProfileSwitch"); - ConfigBuilderPlugin.getPlugin().getCommandQueue().setProfile(getProfile(), new Callback() { - @Override - public void run() { - if (!result.success) { - Intent i = new Intent(MainApp.instance(), ErrorHelperActivity.class); - i.putExtra("soundid", R.raw.boluserror); - i.putExtra("status", result.comment); - i.putExtra("title", MainApp.gs(R.string.failedupdatebasalprofile)); - i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - MainApp.instance().startActivity(i); - } - if (result.enacted) - MainApp.bus().post(new EventNewBasalProfile()); - } - }); + private ProfileFunctions() { + disposable.add(RxBus.INSTANCE + .toObservable(EventProfileNeedsUpdate.class) + .observeOn(Schedulers.io()) + .subscribe(event -> { + if (L.isEnabled(L.PROFILE)) + log.debug("onProfileSwitch"); + ConfigBuilderPlugin.getPlugin().getCommandQueue().setProfile(getProfile(), new Callback() { + @Override + public void run() { + if (!result.success) { + Intent i = new Intent(MainApp.instance(), ErrorHelperActivity.class); + i.putExtra("soundid", R.raw.boluserror); + i.putExtra("status", result.comment); + i.putExtra("title", MainApp.gs(R.string.failedupdatebasalprofile)); + i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + MainApp.instance().startActivity(i); + } + if (result.enacted) + RxBus.INSTANCE.send(new EventNewBasalProfile()); + } + }); + }, FabricPrivacy::logException) + ); } public String getProfileName() { @@ -101,7 +107,8 @@ public class ProfileFunctions { } public boolean isProfileValid(String from) { - return getProfile() != null && getProfile().isValid(from); + Profile profile = getProfile(); + return profile != null && profile.isValid(from); } @Nullable @@ -159,6 +166,8 @@ public class ProfileFunctions { public static void doProfileSwitch(final ProfileStore profileStore, final String profileName, final int duration, final int percentage, final int timeshift) { ProfileSwitch profileSwitch = prepareProfileSwitch(profileStore, profileName, duration, percentage, timeshift, System.currentTimeMillis()); TreatmentsPlugin.getPlugin().addToHistoryProfileSwitch(profileSwitch); + if (percentage == 90 && duration == 10) + SP.putBoolean(R.string.key_objectiveuseprofileswitch, true); } public static void doProfileSwitch(final int duration, final int percentage, final int timeshift) { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/dstHelper/DstHelperPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/dstHelper/DstHelperPlugin.java index ec3ef44881..e026d6589d 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/dstHelper/DstHelperPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/dstHelper/DstHelperPlugin.java @@ -15,6 +15,7 @@ import info.nightscout.androidaps.interfaces.PluginType; import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.logging.L; import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification; import info.nightscout.androidaps.plugins.general.overview.notifications.Notification; @@ -89,6 +90,6 @@ public class DstHelperPlugin extends PluginBase implements ConstraintsInterface private void warnUser(int id, String warningText) { Notification notification = new Notification(id, warningText, Notification.LOW); - MainApp.bus().post(new EventNewNotification(notification)); + RxBus.INSTANCE.send(new EventNewNotification(notification)); } } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesFragment.java deleted file mode 100644 index b943571089..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesFragment.java +++ /dev/null @@ -1,229 +0,0 @@ -package info.nightscout.androidaps.plugins.constraints.objectives; - -import android.app.Activity; -import android.os.Bundle; -import android.os.Handler; -import android.os.Looper; -import android.support.annotation.NonNull; -import android.support.v7.widget.CardView; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.LinearSmoothScroller; -import android.support.v7.widget.RecyclerView; -import android.text.Html; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.Button; -import android.widget.CheckBox; -import android.widget.LinearLayout; -import android.widget.TextView; - -import java.util.Date; - -import info.nightscout.androidaps.MainApp; -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.plugins.common.SubscriberFragment; -import info.nightscout.androidaps.plugins.constraints.objectives.objectives.Objective; -import info.nightscout.androidaps.utils.FabricPrivacy; - -public class ObjectivesFragment extends SubscriberFragment { - RecyclerView recyclerView; - CheckBox enableFake; - TextView reset; - ObjectivesAdapter objectivesAdapter = new ObjectivesAdapter(); - Handler handler = new Handler(Looper.getMainLooper()); - - private Runnable objectiveUpdater = new Runnable() { - @Override - public void run() { - handler.postDelayed(this, 60 * 1000); - updateGUI(); - } - }; - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - try { - View view = inflater.inflate(R.layout.objectives_fragment, container, false); - - recyclerView = view.findViewById(R.id.objectives_recyclerview); - recyclerView.setLayoutManager(new LinearLayoutManager(view.getContext())); - recyclerView.setAdapter(objectivesAdapter); - enableFake = view.findViewById(R.id.objectives_fake); - reset = view.findViewById(R.id.objectives_reset); - enableFake.setOnClickListener(v -> updateGUI()); - reset.setOnClickListener(v -> { - ObjectivesPlugin.getPlugin().reset(); - ObjectivesPlugin.getPlugin().saveProgress(); - recyclerView.getAdapter().notifyDataSetChanged(); - scrollToCurrentObjective(); - }); - scrollToCurrentObjective(); - startUpdateTimer(); - return view; - } catch (Exception e) { - FabricPrivacy.logException(e); - } - - return null; - } - - @Override - public synchronized void onDestroyView() { - super.onDestroyView(); - handler.removeCallbacks(objectiveUpdater); - } - - private void startUpdateTimer() { - handler.removeCallbacks(objectiveUpdater); - for (Objective objective : ObjectivesPlugin.getPlugin().getObjectives()) { - if (objective.isStarted() && !objective.isAccomplished()) { - long timeTillNextMinute = (System.currentTimeMillis() - objective.getStartedOn().getTime()) % (60 * 1000); - handler.postDelayed(objectiveUpdater, timeTillNextMinute); - break; - } - } - } - - private void scrollToCurrentObjective() { - for (int i = 0; i < ObjectivesPlugin.getPlugin().getObjectives().size(); i++) { - Objective objective = ObjectivesPlugin.getPlugin().getObjectives().get(i); - if (!objective.isStarted() || !objective.isAccomplished()) { - RecyclerView.SmoothScroller smoothScroller = new LinearSmoothScroller(getContext()) { - @Override - protected int getVerticalSnapPreference() { - return LinearSmoothScroller.SNAP_TO_START; - } - - @Override - protected int calculateTimeForScrolling(int dx) { - return super.calculateTimeForScrolling(dx) * 4; - } - }; - smoothScroller.setTargetPosition(i); - recyclerView.getLayoutManager().startSmoothScroll(smoothScroller); - break; - } - } - } - - private class ObjectivesAdapter extends RecyclerView.Adapter { - - @NonNull - @Override - public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - return new ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.objectives_item, parent, false)); - } - - @Override - public void onBindViewHolder(@NonNull ViewHolder holder, int position) { - Objective objective = ObjectivesPlugin.getPlugin().getObjectives().get(position); - holder.title.setText(MainApp.gs(R.string.nth_objective, position + 1)); - holder.revert.setVisibility(View.INVISIBLE); - if (objective.getObjective() != 0) { - holder.objective.setVisibility(View.VISIBLE); - holder.objective.setText(MainApp.gs(objective.getObjective())); - } else holder.objective.setVisibility(View.GONE); - if (objective.getGate() != 0) { - holder.gate.setVisibility(View.VISIBLE); - holder.gate.setText(MainApp.gs(objective.getGate())); - } else holder.gate.setVisibility(View.GONE); - if (!objective.isStarted()) { - holder.gate.setTextColor(0xFFFFFFFF); - holder.verify.setVisibility(View.GONE); - holder.progress.setVisibility(View.GONE); - if (position == 0 || ObjectivesPlugin.getPlugin().getObjectives().get(position - 1).isAccomplished()) - holder.start.setVisibility(View.VISIBLE); - else holder.start.setVisibility(View.GONE); - } else if (objective.isAccomplished()) { - holder.gate.setTextColor(0xFF4CAF50); - holder.verify.setVisibility(View.GONE); - holder.progress.setVisibility(View.GONE); - holder.start.setVisibility(View.GONE); - } else if (objective.isStarted()) { - holder.gate.setTextColor(0xFFFFFFFF); - holder.verify.setVisibility(View.VISIBLE); - holder.verify.setEnabled(objective.isCompleted() || enableFake.isChecked()); - holder.start.setVisibility(View.GONE); - if(objective.isRevertable()) { - holder.revert.setVisibility(View.VISIBLE); - } - holder.progress.setVisibility(View.VISIBLE); - holder.progress.removeAllViews(); - for (Objective.Task task : objective.getTasks()) { - if (task.shouldBeIgnored()) continue; - TextView textView = new TextView(holder.progress.getContext()); - textView.setTextColor(0xFFFFFFFF); - String basicHTML = "%2$s: %3$s"; - String formattedHTML = String.format(basicHTML, task.isCompleted() ? "#4CAF50" : "#FF9800", MainApp.gs(task.getTask()), task.getProgress()); - textView.setText(Html.fromHtml(formattedHTML)); - holder.progress.addView(textView, LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT); - } - } - holder.verify.setOnClickListener((view) -> { - objective.setAccomplishedOn(new Date()); - notifyDataSetChanged(); - scrollToCurrentObjective(); - startUpdateTimer(); - }); - holder.start.setOnClickListener((view) -> { - objective.setStartedOn(new Date()); - notifyDataSetChanged(); - scrollToCurrentObjective(); - startUpdateTimer(); - }); - holder.revert.setOnClickListener((view) -> { - objective.setAccomplishedOn(null); - objective.setStartedOn(null); - if (position > 0) { - Objective prevObj = ObjectivesPlugin.getPlugin().getObjectives().get(position - 1); - prevObj.setAccomplishedOn(null); - } - notifyDataSetChanged(); - scrollToCurrentObjective(); - }); - } - - - - @Override - public int getItemCount() { - return ObjectivesPlugin.getPlugin().getObjectives().size(); - } - - public class ViewHolder extends RecyclerView.ViewHolder { - - public CardView cardView; - public TextView title; - public TextView objective; - public TextView gate; - public LinearLayout progress; - public Button verify; - public Button start; - public Button revert; - - public ViewHolder(View itemView) { - super(itemView); - cardView = (CardView) itemView; - title = itemView.findViewById(R.id.objective_title); - objective = itemView.findViewById(R.id.objective_objective); - gate = itemView.findViewById(R.id.objective_gate); - progress = itemView.findViewById(R.id.objective_progress); - verify = itemView.findViewById(R.id.objective_verify); - start = itemView.findViewById(R.id.objective_start); - revert = itemView.findViewById(R.id.objective_back); - } - } - } - - @Override - public void updateGUI() { - Activity activity = getActivity(); - if (activity != null) - activity.runOnUiThread(() -> { - objectivesAdapter.notifyDataSetChanged(); - }); - } - -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesFragment.kt new file mode 100644 index 0000000000..70242d3433 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesFragment.kt @@ -0,0 +1,324 @@ +package info.nightscout.androidaps.plugins.constraints.objectives + +import android.graphics.Color +import android.os.Bundle +import android.os.Handler +import android.os.Looper +import android.view.Gravity +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Button +import android.widget.EditText +import android.widget.LinearLayout +import android.widget.TextView +import androidx.fragment.app.Fragment +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.LinearSmoothScroller +import androidx.recyclerview.widget.RecyclerView +import info.nightscout.androidaps.MainApp +import info.nightscout.androidaps.R +import info.nightscout.androidaps.logging.L +import info.nightscout.androidaps.plugins.bus.RxBus +import info.nightscout.androidaps.plugins.constraints.objectives.activities.ObjectivesExamDialog +import info.nightscout.androidaps.plugins.constraints.objectives.events.EventObjectivesUpdateGui +import info.nightscout.androidaps.plugins.constraints.objectives.objectives.Objective.ExamTask +import info.nightscout.androidaps.receivers.NetworkChangeReceiver +import info.nightscout.androidaps.utils.* +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.CompositeDisposable +import kotlinx.android.synthetic.main.objectives_fragment.* +import org.slf4j.LoggerFactory + +class ObjectivesFragment : Fragment() { + private val log = LoggerFactory.getLogger(L.CONSTRAINTS) + private val objectivesAdapter = ObjectivesAdapter() + private val handler = Handler(Looper.getMainLooper()) + + private var disposable: CompositeDisposable = CompositeDisposable() + + private val objectiveUpdater = object : Runnable { + override fun run() { + handler.postDelayed(this, (60 * 1000).toLong()) + updateGUI() + } + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.objectives_fragment, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + objectives_recyclerview.layoutManager = LinearLayoutManager(view.context) + objectives_recyclerview.adapter = objectivesAdapter + objectives_fake.setOnClickListener { updateGUI() } + objectives_reset.setOnClickListener { + ObjectivesPlugin.reset() + objectives_recyclerview.adapter?.notifyDataSetChanged() + scrollToCurrentObjective() + } + scrollToCurrentObjective() + startUpdateTimer() + } + + @Synchronized + override fun onResume() { + super.onResume() + disposable.add(RxBus + .toObservable(EventObjectivesUpdateGui::class.java) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ + objectives_recyclerview.adapter?.notifyDataSetChanged() + }, { + FabricPrivacy.logException(it) + }) + ) + } + + @Synchronized + override fun onPause() { + super.onPause() + disposable.clear() + } + + @Synchronized + override fun onDestroyView() { + super.onDestroyView() + handler.removeCallbacks(objectiveUpdater) + } + + private fun startUpdateTimer() { + handler.removeCallbacks(objectiveUpdater) + for (objective in ObjectivesPlugin.objectives) { + if (objective.isStarted && !objective.isAccomplished) { + val timeTillNextMinute = (System.currentTimeMillis() - objective.startedOn) % (60 * 1000) + handler.postDelayed(objectiveUpdater, timeTillNextMinute) + break + } + } + } + + private fun scrollToCurrentObjective() { + for (i in 0 until ObjectivesPlugin.objectives.size) { + val objective = ObjectivesPlugin.objectives[i] + if (!objective.isStarted || !objective.isAccomplished) { + val smoothScroller = object : LinearSmoothScroller(context!!) { + override fun getVerticalSnapPreference(): Int { + return SNAP_TO_START + } + + override fun calculateTimeForScrolling(dx: Int): Int { + return super.calculateTimeForScrolling(dx) * 4 + } + } + smoothScroller.targetPosition = i + objectives_recyclerview.layoutManager?.startSmoothScroll(smoothScroller) + break + } + } + } + + private inner class ObjectivesAdapter : RecyclerView.Adapter() { + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + return ViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.objectives_item, parent, false)) + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + val objective = ObjectivesPlugin.objectives[position] + holder.title.text = MainApp.gs(R.string.nth_objective, position + 1) + holder.revert.visibility = View.GONE + if (objective.objective != 0) { + holder.objective.visibility = View.VISIBLE + holder.objective.text = MainApp.gs(objective.objective) + } else + holder.objective.visibility = View.GONE + if (objective.gate != 0) { + holder.gate.visibility = View.VISIBLE + holder.gate.text = MainApp.gs(objective.gate) + } else + holder.gate.visibility = View.GONE + if (!objective.isStarted) { + holder.gate.setTextColor(-0x1) + holder.verify.visibility = View.GONE + holder.progress.visibility = View.GONE + holder.accomplished.visibility = View.GONE + if (position == 0 || ObjectivesPlugin.objectives[position - 1].isAccomplished) + holder.start.visibility = View.VISIBLE + else + holder.start.visibility = View.GONE + } else if (objective.isAccomplished) { + holder.gate.setTextColor(-0xb350b0) + holder.verify.visibility = View.GONE + holder.progress.visibility = View.GONE + holder.start.visibility = View.GONE + holder.accomplished.visibility = View.VISIBLE + } else if (objective.isStarted) { + holder.gate.setTextColor(-0x1) + holder.verify.visibility = View.VISIBLE + holder.verify.isEnabled = objective.isCompleted || objectives_fake.isChecked + holder.start.visibility = View.GONE + holder.accomplished.visibility = View.GONE + if (objective.isRevertable) { + holder.revert.visibility = View.VISIBLE + } + holder.progress.visibility = View.VISIBLE + holder.progress.removeAllViews() + for (task in objective.tasks) { + if (task.shouldBeIgnored()) continue + // name + val name = TextView(holder.progress.context) + name.text = MainApp.gs(task.task) + ":" + name.setTextColor(-0x1) + holder.progress.addView(name, LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT) + // hint + task.hints.forEach { h -> + if (!task.isCompleted) + holder.progress.addView(h.generate(context)) + } + // state + val state = TextView(holder.progress.context) + state.setTextColor(-0x1) + val basicHTML = "%2\$s" + val formattedHTML = String.format(basicHTML, if (task.isCompleted) "#4CAF50" else "#FF9800", task.progress) + state.text = HtmlHelper.fromHtml(formattedHTML) + state.gravity = Gravity.END + holder.progress.addView(state, LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT) + if (task is ExamTask) { + state.setOnClickListener { + val dialog = ObjectivesExamDialog() + val bundle = Bundle() + val taskPosition = objective.tasks.indexOf(task) + bundle.putInt("currentTask", taskPosition) + dialog.arguments = bundle + ObjectivesExamDialog.objective = objective + fragmentManager?.let { dialog.show(it, "ObjectivesFragment") } + } + } + // horizontal line + val separator = View(holder.progress.context) + separator.setBackgroundColor(Color.DKGRAY) + holder.progress.addView(separator, LinearLayout.LayoutParams.MATCH_PARENT, 2) + } + } + holder.accomplished.text = MainApp.gs(R.string.accomplished, DateUtil.dateAndTimeString(objective.accomplishedOn)) + holder.accomplished.setTextColor(-0x3e3e3f) + holder.verify.setOnClickListener { + holder.verify.visibility = View.INVISIBLE + NetworkChangeReceiver.fetch() + if (objectives_fake.isChecked) { + objective.accomplishedOn = DateUtil.now() + scrollToCurrentObjective() + startUpdateTimer() + RxBus.send(EventObjectivesUpdateGui()) + } else + SntpClient.ntpTime(object : SntpClient.Callback() { + override fun run() { + activity?.runOnUiThread { + holder.verify.visibility = View.VISIBLE + log.debug("NTP time: $time System time: ${DateUtil.now()}") + if (!networkConnected) { + ToastUtils.showToastInUiThread(context, R.string.notconnected) + } else if (success) { + if (objective.isCompleted(time)) { + objective.accomplishedOn = time + scrollToCurrentObjective() + startUpdateTimer() + RxBus.send(EventObjectivesUpdateGui()) + } else { + ToastUtils.showToastInUiThread(context, R.string.requirementnotmet) + } + } else { + ToastUtils.showToastInUiThread(context, R.string.failedretrievetime) + } + } + } + }, NetworkChangeReceiver.isConnected()) + } + holder.start.setOnClickListener { + holder.start.visibility = View.INVISIBLE + NetworkChangeReceiver.fetch() + if (objectives_fake.isChecked) { + objective.startedOn = DateUtil.now() + scrollToCurrentObjective() + startUpdateTimer() + RxBus.send(EventObjectivesUpdateGui()) + } else + SntpClient.ntpTime(object : SntpClient.Callback() { + override fun run() { + activity?.runOnUiThread { + holder.start.visibility = View.VISIBLE + log.debug("NTP time: $time System time: ${DateUtil.now()}") + if (!networkConnected) { + ToastUtils.showToastInUiThread(context, R.string.notconnected) + } else if (success) { + objective.startedOn = time + scrollToCurrentObjective() + startUpdateTimer() + RxBus.send(EventObjectivesUpdateGui()) + } else { + ToastUtils.showToastInUiThread(context, R.string.failedretrievetime) + } + } + } + }, NetworkChangeReceiver.isConnected()) + } + holder.revert.setOnClickListener { + objective.accomplishedOn = 0 + objective.startedOn = 0 + if (position > 0) { + val prevObj = ObjectivesPlugin.objectives[position - 1] + prevObj.accomplishedOn = 0 + } + scrollToCurrentObjective() + RxBus.send(EventObjectivesUpdateGui()) + } + if (objective.hasSpecialInput && !objective.isAccomplished && objective.isStarted) { + // generate random request code if none exists + val request = SP.getString(R.string.key_objectives_request_code, String.format("%1$05d", (Math.random() * 99999).toInt())) + SP.putString(R.string.key_objectives_request_code, request) + holder.requestCode.text = MainApp.gs(R.string.requestcode, request) + holder.requestCode.visibility = View.VISIBLE + holder.enterButton.visibility = View.VISIBLE + holder.input.visibility = View.VISIBLE + holder.inputHint.visibility = View.VISIBLE + holder.enterButton.setOnClickListener { + val input = holder.input.text.toString() + objective.specialAction(activity, input) + RxBus.send(EventObjectivesUpdateGui()) + } + } else { + holder.enterButton.visibility = View.GONE + holder.input.visibility = View.GONE + holder.inputHint.visibility = View.GONE + holder.requestCode.visibility = View.GONE + } + } + + + override fun getItemCount(): Int { + return ObjectivesPlugin.objectives.size + } + + inner class ViewHolder internal constructor(itemView: View) : RecyclerView.ViewHolder(itemView) { + val title: TextView = itemView.findViewById(R.id.objective_title) + val objective: TextView = itemView.findViewById(R.id.objective_objective) + val gate: TextView = itemView.findViewById(R.id.objective_gate) + val accomplished: TextView = itemView.findViewById(R.id.objective_accomplished) + val progress: LinearLayout = itemView.findViewById(R.id.objective_progress) + val verify: Button = itemView.findViewById(R.id.objective_verify) + val start: Button = itemView.findViewById(R.id.objective_start) + val revert: Button = itemView.findViewById(R.id.objective_back) + val inputHint: TextView = itemView.findViewById(R.id.objective_inputhint) + val input: EditText = itemView.findViewById(R.id.objective_input) + val enterButton: Button = itemView.findViewById(R.id.objective_enterbutton) + val requestCode: TextView = itemView.findViewById(R.id.objective_requestcode) + } + } + + fun updateGUI() { + activity?.runOnUiThread { objectivesAdapter.notifyDataSetChanged() } + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesPlugin.java deleted file mode 100644 index 1d18cd14b4..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesPlugin.java +++ /dev/null @@ -1,164 +0,0 @@ -package info.nightscout.androidaps.plugins.constraints.objectives; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.ArrayList; -import java.util.List; - -import info.nightscout.androidaps.Config; -import info.nightscout.androidaps.MainApp; -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.interfaces.Constraint; -import info.nightscout.androidaps.interfaces.ConstraintsInterface; -import info.nightscout.androidaps.interfaces.PluginBase; -import info.nightscout.androidaps.interfaces.PluginDescription; -import info.nightscout.androidaps.interfaces.PluginType; -import info.nightscout.androidaps.interfaces.PumpInterface; -import info.nightscout.androidaps.logging.L; -import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; -import info.nightscout.androidaps.plugins.constraints.objectives.events.EventObjectivesSaved; -import info.nightscout.androidaps.plugins.constraints.objectives.objectives.Objective; -import info.nightscout.androidaps.plugins.constraints.objectives.objectives.Objective1; -import info.nightscout.androidaps.plugins.constraints.objectives.objectives.Objective2; -import info.nightscout.androidaps.plugins.constraints.objectives.objectives.Objective3; -import info.nightscout.androidaps.plugins.constraints.objectives.objectives.Objective4; -import info.nightscout.androidaps.plugins.constraints.objectives.objectives.Objective5; -import info.nightscout.androidaps.plugins.constraints.objectives.objectives.Objective6; -import info.nightscout.androidaps.plugins.constraints.objectives.objectives.Objective7; -import info.nightscout.androidaps.plugins.constraints.objectives.objectives.Objective8; -import info.nightscout.androidaps.utils.SP; - -/** - * Created by mike on 05.08.2016. - */ -public class ObjectivesPlugin extends PluginBase implements ConstraintsInterface { - private static Logger log = LoggerFactory.getLogger(L.CONSTRAINTS); - - private static ObjectivesPlugin objectivesPlugin; - - public List objectives = new ArrayList<>(); - public boolean bgIsAvailableInNS = false; - public boolean pumpStatusIsAvailableInNS = false; - public Integer manualEnacts = 0; - - public static ObjectivesPlugin getPlugin() { - if (objectivesPlugin == null) { - objectivesPlugin = new ObjectivesPlugin(); - } - return objectivesPlugin; - } - - private ObjectivesPlugin() { - super(new PluginDescription() - .mainType(PluginType.CONSTRAINTS) - .fragmentClass(ObjectivesFragment.class.getName()) - .alwaysEnabled(!Config.NSCLIENT) - .showInList(!Config.NSCLIENT) - .pluginName(R.string.objectives) - .shortName(R.string.objectives_shortname) - .description(R.string.description_objectives) - ); - setupObjectives(); - loadProgress(); - } - - @Override - public boolean specialEnableCondition() { - PumpInterface pump = ConfigBuilderPlugin.getPlugin().getActivePump(); - return pump == null || pump.getPumpDescription().isTempBasalCapable; - } - - private void setupObjectives() { - objectives.add(new Objective1()); - objectives.add(new Objective2()); - objectives.add(new Objective3()); - objectives.add(new Objective4()); - objectives.add(new Objective5()); - objectives.add(new Objective6()); - objectives.add(new Objective7()); - objectives.add(new Objective8()); - } - - public void reset() { - for (Objective objective : objectives) { - objective.setStartedOn(null); - objective.setAccomplishedOn(null); - } - bgIsAvailableInNS = false; - pumpStatusIsAvailableInNS = false; - manualEnacts = 0; - saveProgress(); - } - - public void saveProgress() { - SP.putBoolean("Objectives" + "bgIsAvailableInNS", bgIsAvailableInNS); - SP.putBoolean("Objectives" + "pumpStatusIsAvailableInNS", pumpStatusIsAvailableInNS); - SP.putString("Objectives" + "manualEnacts", Integer.toString(manualEnacts)); - if (L.isEnabled(L.CONSTRAINTS)) - log.debug("Objectives stored"); - MainApp.bus().post(new EventObjectivesSaved()); - } - - private void loadProgress() { - bgIsAvailableInNS = SP.getBoolean("Objectives" + "bgIsAvailableInNS", false); - pumpStatusIsAvailableInNS = SP.getBoolean("Objectives" + "pumpStatusIsAvailableInNS", false); - try { - manualEnacts = SP.getInt("Objectives" + "manualEnacts", 0); - } catch (Exception e) { - log.error("Unhandled exception", e); - } - if (L.isEnabled(L.CONSTRAINTS)) - log.debug("Objectives loaded"); - } - - public List getObjectives() { - return objectives; - } - - /** - * Constraints interface - **/ - @Override - public Constraint isLoopInvocationAllowed(Constraint value) { - if (!objectives.get(0).isStarted()) - value.set(false, String.format(MainApp.gs(R.string.objectivenotstarted), 1), this); - return value; - } - - @Override - public Constraint isClosedLoopAllowed(Constraint value) { - if (!objectives.get(3).isStarted()) - value.set(false, String.format(MainApp.gs(R.string.objectivenotstarted), 4), this); - return value; - } - - @Override - public Constraint isAutosensModeEnabled(Constraint value) { - if (!objectives.get(5).isStarted()) - value.set(false, String.format(MainApp.gs(R.string.objectivenotstarted), 6), this); - return value; - } - - @Override - public Constraint isAMAModeEnabled(Constraint value) { - if (!objectives.get(6).isStarted()) - value.set(false, String.format(MainApp.gs(R.string.objectivenotstarted), 7), this); - return value; - } - - @Override - public Constraint isSMBModeEnabled(Constraint value) { - if (!objectives.get(7).isStarted()) - value.set(false, String.format(MainApp.gs(R.string.objectivenotstarted), 8), this); - return value; - } - - @Override - public Constraint applyMaxIOBConstraints(Constraint maxIob) { - if (objectives.get(3).isStarted() && !objectives.get(3).isAccomplished()) - maxIob.set(0d, String.format(MainApp.gs(R.string.objectivenotfinished), 4), this); - return maxIob; - } - -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesPlugin.kt new file mode 100644 index 0000000000..ca3e1d3126 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesPlugin.kt @@ -0,0 +1,170 @@ +package info.nightscout.androidaps.plugins.constraints.objectives + +import android.app.Activity +import com.google.common.base.Charsets +import com.google.common.hash.Hashing +import info.nightscout.androidaps.BuildConfig +import info.nightscout.androidaps.Config +import info.nightscout.androidaps.MainApp +import info.nightscout.androidaps.R +import info.nightscout.androidaps.interfaces.* +import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin +import info.nightscout.androidaps.plugins.constraints.objectives.objectives.* +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.OKDialog +import info.nightscout.androidaps.utils.SP +import java.util.* + +/** + * Created by mike on 05.08.2016. + */ +object ObjectivesPlugin : PluginBase(PluginDescription() + .mainType(PluginType.CONSTRAINTS) + .fragmentClass(ObjectivesFragment::class.qualifiedName) + .alwaysEnabled(Config.APS) + .showInList(Config.APS) + .pluginName(R.string.objectives) + .shortName(R.string.objectives_shortname) + .description(R.string.description_objectives)), ConstraintsInterface { + + var objectives: MutableList = ArrayList() + + val FIRST_OBJECTIVE = 0 + val USAGE_OBJECTIVE = 1 + val EXAM_OBJECTIVE = 2 + val OPENLOOP_OBJECTIVE = 3 + val MAXBASAL_OBJECTIVE = 4 + val MAXIOB_ZERO_CL_OBJECTIVE = 5 + val MAXIOB_OBJECTIVE = 6 + val AUTOSENS_OBJECTIVE = 7 + val AMA_OBJECTIVE = 8 + val SMB_OBJECTIVE = 9 + + init { + convertSP() + setupObjectives() + } + + override fun specialEnableCondition(): Boolean { + val pump = ConfigBuilderPlugin.getPlugin().activePump + return pump == null || pump.pumpDescription.isTempBasalCapable + } + + // convert 2.3 SP version + private fun convertSP() { + doConvertSP(0, "config") + doConvertSP(1, "openloop") + doConvertSP(2, "maxbasal") + doConvertSP(3, "maxiobzero") + doConvertSP(4, "maxiob") + doConvertSP(5, "autosens") + doConvertSP(6, "ama") + doConvertSP(7, "smb") + } + + private fun doConvertSP(number: Int, name: String) { + if (!SP.contains("Objectives_" + name + "_started")) { + SP.putLong("Objectives_" + name + "_started", SP.getLong("Objectives" + number + "started", 0L)) + SP.putLong("Objectives_" + name + "_accomplished", SP.getLong("Objectives" + number + "accomplished", 0L)) + } + // TODO: we can remove Objectives1accomplished sometimes later + } + + private fun setupObjectives() { + objectives.clear() + objectives.add(Objective0()) + objectives.add(Objective1()) + objectives.add(Objective2()) + objectives.add(Objective3()) + objectives.add(Objective4()) + objectives.add(Objective5()) + objectives.add(Objective6()) + objectives.add(Objective7()) + objectives.add(Objective8()) + objectives.add(Objective9()) + } + + fun reset() { + for (objective in objectives) { + objective.startedOn = 0 + objective.accomplishedOn = 0 + } + SP.putBoolean(R.string.key_ObjectivesbgIsAvailableInNS, false) + SP.putBoolean(R.string.key_ObjectivespumpStatusIsAvailableInNS, false) + SP.putInt(R.string.key_ObjectivesmanualEnacts, 0) + SP.putBoolean(R.string.key_objectiveuseprofileswitch, false) + SP.putBoolean(R.string.key_objectiveusedisconnect, false) + SP.putBoolean(R.string.key_objectiveusereconnect, false) + SP.putBoolean(R.string.key_objectiveusetemptarget, false) + SP.putBoolean(R.string.key_objectiveuseactions, false) + SP.putBoolean(R.string.key_objectiveuseloop, false) + SP.putBoolean(R.string.key_objectiveusescale, false) + } + + fun completeObjectives(activity: Activity, request: String) { + val requestCode = SP.getString(R.string.key_objectives_request_code, "") + var url = SP.getString(R.string.key_nsclientinternal_url, "").toLowerCase() + if (!url.endsWith("\"")) url = "$url/" + val hashNS = Hashing.sha1().hashString(url + BuildConfig.APPLICATION_ID + "/" + requestCode, Charsets.UTF_8).toString() + if (request.equals(hashNS.substring(0, 10), ignoreCase = true)) { + SP.putLong("Objectives_" + "openloop" + "_started", DateUtil.now()) + SP.putLong("Objectives_" + "openloop" + "_accomplished", DateUtil.now()) + SP.putLong("Objectives_" + "maxbasal" + "_started", DateUtil.now()) + SP.putLong("Objectives_" + "maxbasal" + "_accomplished", DateUtil.now()) + SP.putLong("Objectives_" + "maxiobzero" + "_started", DateUtil.now()) + SP.putLong("Objectives_" + "maxiobzero" + "_accomplished", DateUtil.now()) + SP.putLong("Objectives_" + "maxiob" + "_started", DateUtil.now()) + SP.putLong("Objectives_" + "maxiob" + "_accomplished", DateUtil.now()) + SP.putLong("Objectives_" + "autosens" + "_started", DateUtil.now()) + SP.putLong("Objectives_" + "autosens" + "_accomplished", DateUtil.now()) + SP.putLong("Objectives_" + "ama" + "_started", DateUtil.now()) + SP.putLong("Objectives_" + "ama" + "_accomplished", DateUtil.now()) + SP.putLong("Objectives_" + "smb" + "_started", DateUtil.now()) + SP.putLong("Objectives_" + "smb" + "_accomplished", DateUtil.now()) + setupObjectives() + OKDialog.show(activity, "", MainApp.gs(R.string.codeaccepted), null) + } else { + OKDialog.show(activity, "", MainApp.gs(R.string.codeinvalid), null) + } + } + + /** + * Constraints interface + */ + override fun isLoopInvocationAllowed(value: Constraint): Constraint { + if (!objectives[FIRST_OBJECTIVE].isStarted) + value.set(false, String.format(MainApp.gs(R.string.objectivenotstarted), FIRST_OBJECTIVE + 1), this) + return value + } + + override fun isClosedLoopAllowed(value: Constraint): Constraint { + if (!objectives[MAXIOB_ZERO_CL_OBJECTIVE].isStarted) + value.set(false, String.format(MainApp.gs(R.string.objectivenotstarted), MAXIOB_ZERO_CL_OBJECTIVE + 1), this) + return value + } + + override fun isAutosensModeEnabled(value: Constraint): Constraint { + if (!objectives[AUTOSENS_OBJECTIVE].isStarted) + value.set(false, String.format(MainApp.gs(R.string.objectivenotstarted), AUTOSENS_OBJECTIVE + 1), this) + return value + } + + override fun isAMAModeEnabled(value: Constraint): Constraint { + if (!objectives[AMA_OBJECTIVE].isStarted) + value.set(false, String.format(MainApp.gs(R.string.objectivenotstarted), AMA_OBJECTIVE + 1), this) + return value + } + + override fun isSMBModeEnabled(value: Constraint): Constraint { + if (!objectives[SMB_OBJECTIVE].isStarted) + value.set(false, String.format(MainApp.gs(R.string.objectivenotstarted), SMB_OBJECTIVE + 1), this) + return value + } + + override fun applyMaxIOBConstraints(maxIob: Constraint): Constraint { + if (objectives[MAXIOB_ZERO_CL_OBJECTIVE].isStarted && !objectives[MAXIOB_ZERO_CL_OBJECTIVE].isAccomplished) + maxIob.set(0.0, String.format(MainApp.gs(R.string.objectivenotfinished), MAXIOB_ZERO_CL_OBJECTIVE + 1), this) + return maxIob + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/activities/ObjectivesExamDialog.kt b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/activities/ObjectivesExamDialog.kt new file mode 100644 index 0000000000..1428652be5 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/activities/ObjectivesExamDialog.kt @@ -0,0 +1,130 @@ +package info.nightscout.androidaps.plugins.constraints.objectives.activities + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.DialogFragment +import info.nightscout.androidaps.MainApp +import info.nightscout.androidaps.R +import info.nightscout.androidaps.plugins.bus.RxBus +import info.nightscout.androidaps.plugins.constraints.objectives.events.EventObjectivesUpdateGui +import info.nightscout.androidaps.plugins.constraints.objectives.objectives.Objective +import info.nightscout.androidaps.plugins.constraints.objectives.objectives.Objective.* +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.OKDialog +import info.nightscout.androidaps.utils.T +import info.nightscout.androidaps.utils.ToastUtils +import kotlinx.android.synthetic.main.objectives_exam_fragment.* + +class ObjectivesExamDialog : DialogFragment() { + companion object { + var objective: Objective? = null + } + + var currentTask = 0 + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + // load data from bundle + (savedInstanceState ?: arguments)?.let { bundle -> + currentTask = bundle.getInt("currentTask", 0) + } + + return inflater.inflate(R.layout.objectives_exam_fragment, container, false) + } + + override fun onStart() { + super.onStart() + dialog?.setCanceledOnTouchOutside(false) + dialog?.window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); + } + + override fun onResume() { + super.onResume() + updateGui() + } + + override fun onSaveInstanceState(bundle: Bundle) { + super.onSaveInstanceState(bundle) + bundle.putInt("currentTask", currentTask) + } + + fun updateGui() { + objective?.let { objective -> + val task: ExamTask = objective.tasks[currentTask] as ExamTask + objectives_exam_name.setText(task.task) + objectives_exam_question.setText(task.question) + // Options + objectives_exam_options.removeAllViews() + task.options.forEach { + val cb = it.generate(context) + if (task.answered) { + cb.isEnabled = false + if (it.isCorrect) + cb.isChecked = true + } + objectives_exam_options.addView(cb) + } + // Hints + objectives_exam_hints.removeAllViews() + for (h in task.hints) { + objectives_exam_hints.addView(h.generate(context)) + } + // Disabled to + objectives_exam_disabledto.text = MainApp.gs(R.string.answerdisabledto, DateUtil.timeString(task.disabledTo)) + objectives_exam_disabledto.visibility = if (task.isEnabledAnswer) View.GONE else View.VISIBLE + // Buttons + objectives_exam_verify.isEnabled = !task.answered && task.isEnabledAnswer + objectives_exam_verify.setOnClickListener { + var result = true + for (o in task.options) { + val option: Option = o as Option; + result = result && option.evaluate() + } + task.setAnswered(result); + if (!result) { + task.disabledTo = DateUtil.now() + T.hours(1).msecs() + ToastUtils.showToastInUiThread(context, R.string.wronganswer) + } else task.disabledTo = 0 + updateGui() + RxBus.send(EventObjectivesUpdateGui()) + } + close.setOnClickListener { dismiss() } + objectives_exam_reset.setOnClickListener { + task.answered = false + //task.disabledTo = 0 + updateGui() + RxBus.send(EventObjectivesUpdateGui()) + } + objectives_back_button.isEnabled = currentTask != 0 + objectives_back_button.setOnClickListener { + currentTask-- + updateGui() + } + objectives_next_button.isEnabled = currentTask != objective.tasks.size - 1 + objectives_next_button.setOnClickListener { + currentTask++ + updateGui() + } + + objectives_next_unanswered_button.isEnabled = !objective.isCompleted + objectives_next_unanswered_button.setOnClickListener { + for (i in (currentTask + 1)..(objective.tasks.size - 1)) { + if (!objective.tasks[i].isCompleted) { + currentTask = i + updateGui() + return@setOnClickListener + } + } + for (i in 0..currentTask) { + if (!objective.tasks[i].isCompleted) { + currentTask = i + updateGui() + return@setOnClickListener + } + } + } + } + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/events/EventObjectivesSaved.java b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/events/EventObjectivesSaved.java deleted file mode 100644 index d4dbb0251d..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/events/EventObjectivesSaved.java +++ /dev/null @@ -1,6 +0,0 @@ -package info.nightscout.androidaps.plugins.constraints.objectives.events; - -import info.nightscout.androidaps.events.Event; - -public class EventObjectivesSaved extends Event { -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/events/EventObjectivesUpdateGui.kt b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/events/EventObjectivesUpdateGui.kt new file mode 100644 index 0000000000..fd59db7d9f --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/events/EventObjectivesUpdateGui.kt @@ -0,0 +1,5 @@ +package info.nightscout.androidaps.plugins.constraints.objectives.events + +import info.nightscout.androidaps.events.EventUpdateGui + +class EventObjectivesUpdateGui : EventUpdateGui() \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/objectives/Objective.java b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/objectives/Objective.java index 83abe15688..c882a921f5 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/objectives/Objective.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/objectives/Objective.java @@ -1,35 +1,46 @@ package info.nightscout.androidaps.plugins.constraints.objectives.objectives; -import android.support.annotation.StringRes; +import android.app.Activity; +import android.content.Context; +import android.graphics.Color; +import android.text.util.Linkify; +import android.view.View; +import android.widget.CheckBox; +import android.widget.TextView; + +import androidx.annotation.StringRes; import java.util.ArrayList; -import java.util.Date; import java.util.List; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; +import info.nightscout.androidaps.utils.DateUtil; import info.nightscout.androidaps.utils.SP; import info.nightscout.androidaps.utils.T; public abstract class Objective { - private int number; + private String spName; @StringRes private int objective; @StringRes private int gate; - private Date startedOn; - private Date accomplishedOn; - private List tasks = new ArrayList<>(); + private long startedOn; + private long accomplishedOn; + List tasks = new ArrayList<>(); + public boolean hasSpecialInput = false; - public Objective(int number, @StringRes int objective, @StringRes int gate) { - this.number = number; + public Objective(String spName, @StringRes int objective, @StringRes int gate) { + this.spName = spName; this.objective = objective; this.gate = gate; - startedOn = new Date(SP.getLong("Objectives" + number + "started", 0L)); - if (startedOn.getTime() == 0L) startedOn = null; - accomplishedOn = new Date(SP.getLong("Objectives" + number + "accomplished", 0L)); - if (accomplishedOn.getTime() == 0L) accomplishedOn = null; + startedOn = SP.getLong("Objectives_" + spName + "_started", 0L); + accomplishedOn = SP.getLong("Objectives_" + spName + "_accomplished", 0L); + if ((accomplishedOn - DateUtil.now()) > T.hours(3).msecs() || (startedOn - DateUtil.now()) > T.hours(3).msecs()) { // more than 3 hours in the future + startedOn = 0; + accomplishedOn = 0; + } setupTasks(tasks); for (Task task : tasks) task.objective = this; } @@ -42,19 +53,27 @@ public abstract class Objective { return true; } + public boolean isCompleted(long trueTime) { + for (Task task : tasks) { + if (!task.shouldBeIgnored() && !task.isCompleted(trueTime)) + return false; + } + return true; + } + public boolean isRevertable() { return false; } public boolean isAccomplished() { - return accomplishedOn != null; + return accomplishedOn != 0 && accomplishedOn < DateUtil.now(); } public boolean isStarted() { - return startedOn != null; + return startedOn != 0; } - public Date getStartedOn() { + public long getStartedOn() { return startedOn; } @@ -66,14 +85,18 @@ public abstract class Objective { return gate; } - public void setStartedOn(Date startedOn) { + public void setStartedOn(long startedOn) { this.startedOn = startedOn; - SP.putLong("Objectives" + number + "started", startedOn == null ? 0 : startedOn.getTime()); + SP.putLong("Objectives_" + spName + "_started", startedOn); } - public void setAccomplishedOn(Date accomplishedOn) { + public void setAccomplishedOn(long accomplishedOn) { this.accomplishedOn = accomplishedOn; - SP.putLong("Objectives" + number + "accomplished", accomplishedOn == null ? 0 : accomplishedOn.getTime()); + SP.putLong("Objectives_" + spName + "_accomplished", accomplishedOn); + } + + public long getAccomplishedOn() { + return accomplishedOn; } protected void setupTasks(List tasks) { @@ -84,16 +107,19 @@ public abstract class Objective { return tasks; } + public void specialAction(Activity activity, String input) {} + public abstract class Task { @StringRes private int task; private Objective objective; + ArrayList hints = new ArrayList<>(); public Task(@StringRes int task) { this.task = task; } - public int getTask() { + public @StringRes int getTask() { return task; } @@ -102,11 +128,21 @@ public abstract class Objective { } public abstract boolean isCompleted(); + public boolean isCompleted(long trueTime) { return isCompleted(); }; public String getProgress() { return MainApp.gs(isCompleted() ? R.string.completed_well_done : R.string.not_completed_yet); } + Task hint(Hint hint) { + hints.add(hint); + return this; + } + + public ArrayList getHints() { + return hints; + } + public boolean shouldBeIgnored() { return false; } @@ -116,19 +152,24 @@ public abstract class Objective { private long minimumDuration; - public MinimumDurationTask(long minimumDuration) { + MinimumDurationTask(long minimumDuration) { super(R.string.time_elapsed); this.minimumDuration = minimumDuration; } @Override public boolean isCompleted() { - return getObjective().isStarted() && System.currentTimeMillis() - getObjective().getStartedOn().getTime() >= minimumDuration; + return getObjective().isStarted() && System.currentTimeMillis() - getObjective().getStartedOn() >= minimumDuration; + } + + @Override + public boolean isCompleted(long trueTime) { + return getObjective().isStarted() && trueTime - getObjective().getStartedOn() >= minimumDuration; } @Override public String getProgress() { - return getDurationText(System.currentTimeMillis() - getObjective().getStartedOn().getTime()) + return getDurationText(System.currentTimeMillis() - getObjective().getStartedOn()) + " / " + getDurationText(minimumDuration); } @@ -142,4 +183,107 @@ public abstract class Objective { } } + public class ExamTask extends Task { + @StringRes + int question; + ArrayList

* Most of this defintions is intended for VirtualPump only, but they can be used by other plugins. */ public enum PumpType { - GenericAAPS("Generic AAPS", 0.1d, null, // - new DoseSettings(0.05d, 30, 8*60, 0.05d), // + GenericAAPS("Generic AAPS", ManufacturerType.AndroidAPS, "VirutalPump", 0.1d, null, // + new DoseSettings(0.05d, 30, 8 * 60, 0.05d), // PumpTempBasalType.Percent, // - new DoseSettings(10,30, 24*60, 0d, 500d), PumpCapability.BasalRate_Duration15and30minAllowed, // + new DoseSettings(10, 30, 24 * 60, 0d, 500d), PumpCapability.BasalRate_Duration15and30minAllowed, // 0.01d, 0.01d, null, PumpCapability.VirtualPumpCapabilities), // // Cellnovo - Cellnovo1("Cellnovo", 0.05d, null, // - new DoseSettings(0.05d, 30, 24*60, 1d, null), + Cellnovo1("Cellnovo", ManufacturerType.Cellnovo, "Cellnovo", 0.05d, null, // + new DoseSettings(0.05d, 30, 24 * 60, 1d, null), PumpTempBasalType.Percent, - new DoseSettings(5,30, 24*60, 0d, 200d), PumpCapability.BasalRate_Duration30minAllowed, // + new DoseSettings(5, 30, 24 * 60, 0d, 200d), PumpCapability.BasalRate_Duration30minAllowed, // 0.05d, 0.05d, null, PumpCapability.VirtualPumpCapabilities), // // Accu-Chek - AccuChekCombo("Accu-Chek Combo", 0.1d, null, // - new DoseSettings(0.1d, 15, 12*60, 0.1d), // + AccuChekCombo("Accu-Chek Combo", ManufacturerType.Roche, "Combo", 0.1d, null, // + new DoseSettings(0.1d, 15, 12 * 60, 0.1d), // PumpTempBasalType.Percent, - new DoseSettings(10, 15, 12*60,0d, 500d), PumpCapability.BasalRate_Duration15and30minAllowed, // + new DoseSettings(10, 15, 12 * 60, 0d, 500d), PumpCapability.BasalRate_Duration15and30minAllowed, // 0.01d, 0.01d, DoseStepSize.ComboBasal, PumpCapability.ComboCapabilities), // - AccuChekSpirit("Accu-Chek Spirit", 0.1d, null, // - new DoseSettings(0.1d, 15, 12*60, 0.1d), // + AccuChekSpirit("Accu-Chek Spirit", ManufacturerType.Roche, "Spirit", 0.1d, null, // + new DoseSettings(0.1d, 15, 12 * 60, 0.1d), // PumpTempBasalType.Percent, - new DoseSettings(10, 15, 12*60,0d, 500d), PumpCapability.BasalRate_Duration15and30minAllowed, // + new DoseSettings(10, 15, 12 * 60, 0d, 500d), PumpCapability.BasalRate_Duration15and30minAllowed, // 0.01d, 0.1d, null, PumpCapability.VirtualPumpCapabilities), // - AccuChekInsight("Accu-Chek Insight", 0.05d, DoseStepSize.InsightBolus, // - new DoseSettings(0.05d, 15, 24*60, 0.05d), // + AccuChekInsight("Accu-Chek Insight", ManufacturerType.Roche, "Insight", 0.05d, DoseStepSize.InsightBolus, // + new DoseSettings(0.05d, 15, 24 * 60, 0.05d), // PumpTempBasalType.Percent, - new DoseSettings(10, 15, 24*60,0d, 250d), PumpCapability.BasalRate_Duration15and30minAllowed, // + new DoseSettings(10, 15, 24 * 60, 0d, 250d), PumpCapability.BasalRate_Duration15and30minAllowed, // 0.02d, 0.01d, null, PumpCapability.InsightCapabilities), // - AccuChekInsightBluetooth("Accu-Chek Insight", 0.01d, null, // - new DoseSettings(0.01d, 15, 24*60, 0.05d), // + AccuChekInsightBluetooth("Accu-Chek Insight", ManufacturerType.Roche, "Insight", 0.01d, null, // + new DoseSettings(0.01d, 15, 24 * 60, 0.05d), // PumpTempBasalType.Percent, - new DoseSettings(10, 15, 24*60,0d, 250d), PumpCapability.BasalRate_Duration15and30minAllowed, // + new DoseSettings(10, 15, 24 * 60, 0d, 250d), PumpCapability.BasalRate_Duration15and30minAllowed, // 0.02d, 0.01d, DoseStepSize.InsightBolus, PumpCapability.InsightCapabilities), // // Animas - AnimasVibe("Animas Vibe", 0.05d, null, // AnimasBolus? - new DoseSettings(0.05d, 30, 12*60, 0.05d), // + AnimasVibe("Animas Vibe", ManufacturerType.Animas, "Vibe", 0.05d, null, // AnimasBolus? + new DoseSettings(0.05d, 30, 12 * 60, 0.05d), // PumpTempBasalType.Percent, // - new DoseSettings(10, 30, 24*60, 0d, 300d), PumpCapability.BasalRate_Duration30minAllowed, // + new DoseSettings(10, 30, 24 * 60, 0d, 300d), PumpCapability.BasalRate_Duration30minAllowed, // 0.025d, 5d, 0d, null, PumpCapability.VirtualPumpCapabilities), // - AnimasPing("Animas Ping", AnimasVibe), + AnimasPing("Animas Ping", "Ping", AnimasVibe), // Dana - DanaR("DanaR", 0.05d, null, // - new DoseSettings(0.05d, 30, 8*60, 0.05d), // + DanaR("DanaR", ManufacturerType.Sooil, "DanaR", 0.05d, null, // + new DoseSettings(0.05d, 30, 8 * 60, 0.05d), // PumpTempBasalType.Percent, // - new DoseSettings(10d, 60, 24*60, 0d, 200d), PumpCapability.BasalRate_Duration15and30minNotAllowed, // + new DoseSettings(10d, 60, 24 * 60, 0d, 200d), PumpCapability.BasalRate_Duration15and30minNotAllowed, // 0.04d, 0.01d, null, PumpCapability.DanaCapabilities), - DanaRKorean("DanaR Korean", 0.05d, null, // - new DoseSettings(0.05d, 30, 8*60, 0.05d), // + DanaRKorean("DanaR Korean", ManufacturerType.Sooil, "DanaRKorean", 0.05d, null, // + new DoseSettings(0.05d, 30, 8 * 60, 0.05d), // PumpTempBasalType.Percent, // - new DoseSettings(10d, 60, 24*60, 0d, 200d), PumpCapability.BasalRate_Duration15and30minNotAllowed, // + new DoseSettings(10d, 60, 24 * 60, 0d, 200d), PumpCapability.BasalRate_Duration15and30minNotAllowed, // 0.1d, 0.01d, null, PumpCapability.DanaCapabilities), - DanaRS("DanaRS", 0.05d, null, // - new DoseSettings(0.05d, 30, 8*60, 0.05d), // + DanaRS("DanaRS", ManufacturerType.Sooil, "DanaRS", 0.05d, null, // + new DoseSettings(0.05d, 30, 8 * 60, 0.05d), // PumpTempBasalType.Percent, // - new DoseSettings(10d, 60, 24*60, 0d, 200d), PumpCapability.BasalRate_Duration15and30minAllowed, // + new DoseSettings(10d, 60, 24 * 60, 0d, 200d), PumpCapability.BasalRate_Duration15and30minAllowed, // 0.04d, 0.01d, null, PumpCapability.DanaWithHistoryCapabilities), - DanaRv2("DanaRv2", DanaRS), + DanaRv2("DanaRv2", "DanaRv2", DanaRS), // Insulet - Insulet_Omnipod("Insulet Omnipod", 0.05d, null, // - new DoseSettings(0.05d, 30, 8*60, 0.05d), // + Insulet_Omnipod("Insulet Omnipod", ManufacturerType.Insulet, "Omnipod", 0.05d, null, // + new DoseSettings(0.05d, 30, 8 * 60, 0.05d), // PumpTempBasalType.Absolute, // - new DoseSettings(0.05d, 30, 12*60, 0d, 30.0d), PumpCapability.BasalRate_Duration30minAllowed, // cannot exceed max basal rate 30u/hr + new DoseSettings(0.05d, 30, 12 * 60, 0d, 30.0d), PumpCapability.BasalRate_Duration30minAllowed, // cannot exceed max basal rate 30u/hr 0.05d, 0.05d, null, PumpCapability.VirtualPumpCapabilities), // Medtronic - Medtronic_512_712("Medtronic 512/712", 0.05d, null, // - new DoseSettings(0.05d, 30, 8*60, 0.05d), // + Medtronic_512_712("Medtronic 512/712", ManufacturerType.Medtronic, "512/712", 0.1d, null, // + new DoseSettings(0.05d, 30, 8 * 60, 0.05d), // PumpTempBasalType.Absolute, // - new DoseSettings(0.05d, 30, 24*60, 0d, 35d), PumpCapability.BasalRate_Duration30minAllowed, // - 0.05d, 0.05d, null, PumpCapability.VirtualPumpCapabilities), // TODO + new DoseSettings(0.05d, 30, 24 * 60, 0d, 35d), PumpCapability.BasalRate_Duration30minAllowed, // + 0.05d, 0.05d, null, PumpCapability.MedtronicCapabilities), // - Medtronic_515_715("Medtronic 515/715", Medtronic_512_712), - Medtronic_522_722("Medtronic 522/722", Medtronic_512_712), + Medtronic_515_715("Medtronic 515/715", "515/715", Medtronic_512_712), + Medtronic_522_722("Medtronic 522/722", "522/722", Medtronic_512_712), - Medtronic_523_723_Revel("Medtronic 523/723 (Revel)", 0.05d, null, // - new DoseSettings(0.05d, 30, 8*60, 0.05d), // + Medtronic_523_723_Revel("Medtronic 523/723 (Revel)", ManufacturerType.Medtronic, "523/723 (Revel)", 0.05d, null, // + new DoseSettings(0.05d, 30, 8 * 60, 0.05d), // PumpTempBasalType.Absolute, // - new DoseSettings(0.05d, 30, 24*60, 0d, 35d), PumpCapability.BasalRate_Duration30minAllowed, // - 0.025d, 0.025d, DoseStepSize.MedtronicVeoBasal, PumpCapability.VirtualPumpCapabilities), // + new DoseSettings(0.05d, 30, 24 * 60, 0d, 35d), PumpCapability.BasalRate_Duration30minAllowed, // + 0.025d, 0.025d, DoseStepSize.MedtronicVeoBasal, PumpCapability.MedtronicCapabilities), // - Medtronic_554_754_Veo("Medtronic 554/754 (Veo)", Medtronic_523_723_Revel), // TODO + Medtronic_554_754_Veo("Medtronic 554/754 (Veo)", "554/754 (Veo)", Medtronic_523_723_Revel), // TODO - Medtronic_640G("Medtronic 640G", 0.025d, null, // - new DoseSettings(0.05d, 30, 8*60, 0.05d), // + Medtronic_640G("Medtronic 640G", ManufacturerType.Medtronic, "640G", 0.025d, null, // + new DoseSettings(0.05d, 30, 8 * 60, 0.05d), // PumpTempBasalType.Absolute, // - new DoseSettings(0.05d, 30, 24*60, 0d, 35d), PumpCapability.BasalRate_Duration30minAllowed, // + new DoseSettings(0.05d, 30, 24 * 60, 0d, 35d), PumpCapability.BasalRate_Duration30minAllowed, // 0.025d, 0.025d, DoseStepSize.MedtronicVeoBasal, PumpCapability.VirtualPumpCapabilities), // // Tandem - TandemTSlim("Tandem t:slim", 0.01d, null, // - new DoseSettings(0.01d,15, 8*60, 0.4d), + TandemTSlim("Tandem t:slim", ManufacturerType.Tandem, "t:slim", 0.01d, null, // + new DoseSettings(0.01d, 15, 8 * 60, 0.4d), PumpTempBasalType.Percent, - new DoseSettings(1,15, 8*60, 0d, 250d), PumpCapability.BasalRate_Duration15and30minAllowed, // + new DoseSettings(1, 15, 8 * 60, 0d, 250d), PumpCapability.BasalRate_Duration15and30minAllowed, // 0.1d, 0.001d, null, PumpCapability.VirtualPumpCapabilities), - TandemTFlex("Tandem t:flex", TandemTSlim), // - TandemTSlimG4("Tandem t:slim G4", TandemTSlim), // - TandemTSlimX2("Tandem t:slim X2", TandemTSlim), // - ; + TandemTFlex("Tandem t:flex", "t:flex", TandemTSlim), // + TandemTSlimG4("Tandem t:slim G4", "t:slim G4", TandemTSlim), // + TandemTSlimX2("Tandem t:slim X2", "t:slim X2", TandemTSlim), // + + // MDI + MDI("MDI", ManufacturerType.AndroidAPS, "MDI"); + private String description; + private ManufacturerType manufacturer; + private String model; private double bolusSize; private DoseStepSize specialBolusSize; private DoseSettings extendedBolusSettings; @@ -146,10 +152,9 @@ public enum PumpType { private PumpCapability pumpCapability; private PumpType parent; - private static Map mapByDescription; + private static Map mapByDescription; - static - { + static { mapByDescription = new HashMap<>(); for (PumpType pumpType : values()) { @@ -158,33 +163,41 @@ public enum PumpType { } - PumpType(String description, PumpType parent) - { + PumpType(String description, String model, PumpType parent) { this.description = description; this.parent = parent; + this.model = model; } - PumpType(String description, PumpType parent, PumpCapability pumpCapability) - { + + PumpType(String description, ManufacturerType manufacturer, String model) { + this.description = description; + this.manufacturer = manufacturer; + this.model = model; + } + + + PumpType(String description, String model, PumpType parent, PumpCapability pumpCapability) { this.description = description; this.parent = parent; this.pumpCapability = pumpCapability; + parent.model = model; } - PumpType(String description, double bolusSize, DoseStepSize specialBolusSize, // - DoseSettings extendedBolusSettings, // - PumpTempBasalType pumpTempBasalType, DoseSettings tbrSettings, PumpCapability specialBasalDurations, // - double baseBasalMinValue, double baseBasalStep, DoseStepSize baseBasalSpecialSteps, PumpCapability pumpCapability) - { - this(description, bolusSize, specialBolusSize, extendedBolusSettings, pumpTempBasalType, tbrSettings, specialBasalDurations, baseBasalMinValue, null, baseBasalStep, baseBasalSpecialSteps, pumpCapability); - } - - PumpType(String description, double bolusSize, DoseStepSize specialBolusSize, // + PumpType(String description, ManufacturerType manufacturer, String model, double bolusSize, DoseStepSize specialBolusSize, // DoseSettings extendedBolusSettings, // PumpTempBasalType pumpTempBasalType, DoseSettings tbrSettings, PumpCapability specialBasalDurations, // - double baseBasalMinValue, Double baseBasalMaxValue, double baseBasalStep, DoseStepSize baseBasalSpecialSteps, PumpCapability pumpCapability) - { + double baseBasalMinValue, double baseBasalStep, DoseStepSize baseBasalSpecialSteps, PumpCapability pumpCapability) { + this(description, manufacturer, model, bolusSize, specialBolusSize, extendedBolusSettings, pumpTempBasalType, tbrSettings, specialBasalDurations, baseBasalMinValue, null, baseBasalStep, baseBasalSpecialSteps, pumpCapability); + } + + PumpType(String description, ManufacturerType manufacturer, String model, double bolusSize, DoseStepSize specialBolusSize, // + DoseSettings extendedBolusSettings, // + PumpTempBasalType pumpTempBasalType, DoseSettings tbrSettings, PumpCapability specialBasalDurations, // + double baseBasalMinValue, Double baseBasalMaxValue, double baseBasalStep, DoseStepSize baseBasalSpecialSteps, PumpCapability pumpCapability) { this.description = description; + this.manufacturer = manufacturer; + this.model = model; this.bolusSize = bolusSize; this.specialBolusSize = specialBolusSize; this.extendedBolusSettings = extendedBolusSettings; @@ -203,6 +216,14 @@ public enum PumpType { return description; } + public ManufacturerType getManufacturer() { + return isParentSet() ? parent.manufacturer : manufacturer; + } + + public String getModel() { + return isParentSet() ? parent.model : model; + } + public PumpCapability getPumpCapability() { if (isParentSet()) @@ -261,20 +282,15 @@ public enum PumpType { } - private boolean isParentSet() - { - return this.parent!=null; + private boolean isParentSet() { + return this.parent != null; } - public static PumpType getByDescription(String desc) - { - if (mapByDescription.containsKey(desc)) - { + public static PumpType getByDescription(String desc) { + if (mapByDescription.containsKey(desc)) { return mapByDescription.get(desc); - } - else - { + } else { return PumpType.GenericAAPS; } } @@ -282,7 +298,7 @@ public enum PumpType { public String getFullDescription(String i18nTemplate, boolean hasExtendedBasals) { - String unit = getPumpTempBasalType()==PumpTempBasalType.Percent ? "%" : ""; + String unit = getPumpTempBasalType() == PumpTempBasalType.Percent ? "%" : ""; DoseSettings eb = getExtendedBolusSettings(); DoseSettings tbr = getTbrSettings(); @@ -291,24 +307,22 @@ public enum PumpType { return String.format(i18nTemplate, // getStep("" + getBolusSize(), getSpecialBolusSize()), // - eb.getStep(), eb.getDurationStep(), eb.getMaxDuration()/60, // + eb.getStep(), eb.getDurationStep(), eb.getMaxDuration() / 60, // getStep(getBaseBasalRange(), getBaseBasalSpecialSteps()), // tbr.getMinDose() + unit + "-" + tbr.getMaxDose() + unit, tbr.getStep() + unit, - tbr.getDurationStep(), tbr.getMaxDuration()/60, extendedNote); + tbr.getDurationStep(), tbr.getMaxDuration() / 60, extendedNote); } - private String getBaseBasalRange() - { + private String getBaseBasalRange() { Double maxValue = getBaseBasalMaxValue(); - return maxValue==null ? "" + getBaseBasalMinValue() : getBaseBasalMinValue() + "-" + maxValue; + return maxValue == null ? "" + getBaseBasalMinValue() : getBaseBasalMinValue() + "-" + maxValue; } - private String getStep(String step, DoseStepSize stepSize) - { - if (stepSize!=null) + private String getStep(String step, DoseStepSize stepSize) { + if (stepSize != null) return step + " [" + stepSize.getDescription() + "] *"; else return "" + step; @@ -316,18 +330,15 @@ public enum PumpType { public boolean hasExtendedBasals() { - return ((getBaseBasalSpecialSteps() !=null) || (getSpecialBolusSize() != null)); + return ((getBaseBasalSpecialSteps() != null) || (getSpecialBolusSize() != null)); } public PumpCapability getSpecialBasalDurations() { - if (isParentSet()) - { + if (isParentSet()) { return parent.getSpecialBasalDurations(); - } - else - { + } else { return specialBasalDurations == null ? // PumpCapability.BasalRate_Duration15and30minNotAllowed : specialBasalDurations; } @@ -338,20 +349,24 @@ public enum PumpType { return bolusAmount; } - double bolusStepSize; + double bolusStepSize = getBolusSize(); - if (getSpecialBolusSize() == null) { - bolusStepSize = getBolusSize(); - } else { + if (getSpecialBolusSize() != null) { DoseStepSize specialBolusSize = getSpecialBolusSize(); - - bolusStepSize = specialBolusSize.getStepSizeForAmount((double)bolusAmount); + bolusStepSize = specialBolusSize.getStepSizeForAmount(bolusAmount); } - return Math.round(bolusAmount / bolusStepSize) * bolusStepSize; + return Round.roundTo(bolusAmount, bolusStepSize); } + public double determineCorrectBolusStepSize(double bolusAmount) { + DoseStepSize specialBolusSize = getSpecialBolusSize(); + if (specialBolusSize != null) + return specialBolusSize.getStepSizeForAmount(bolusAmount); + return getBolusSize(); + } + public double determineCorrectExtendedBolusSize(double bolusAmount) { if (bolusAmount == 0.0d) { return bolusAmount; @@ -371,7 +386,7 @@ public enum PumpType { bolusAmount = extendedBolusSettings.getMaxDose(); } - return Math.round(bolusAmount / bolusStepSize) * bolusStepSize; + return Round.roundTo(bolusAmount, bolusStepSize); } @@ -393,7 +408,7 @@ public enum PumpType { if (basalAmount > getTbrSettings().getMaxDose()) basalAmount = getTbrSettings().getMaxDose().doubleValue(); - return Math.round(basalAmount / basalStepSize) * basalStepSize; + return Round.roundTo(basalAmount, basalStepSize); } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/dialog/RefreshableInterface.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/dialog/RefreshableInterface.java new file mode 100644 index 0000000000..df84750152 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/dialog/RefreshableInterface.java @@ -0,0 +1,11 @@ +package info.nightscout.androidaps.plugins.pump.common.dialog; + +/** + * Created by andy on 5/19/18. + */ + +public interface RefreshableInterface { + + void refreshData(); + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/dialog/RileyLinkBLEScanActivity.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/dialog/RileyLinkBLEScanActivity.java new file mode 100644 index 0000000000..ae0a0d34f7 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/dialog/RileyLinkBLEScanActivity.java @@ -0,0 +1,419 @@ +package info.nightscout.androidaps.plugins.pump.common.dialog; + +import android.Manifest; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.le.BluetoothLeScanner; +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.content.Intent; +import android.content.pm.PackageManager; +import android.os.Bundle; +import android.os.Handler; +import android.os.ParcelUuid; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.ListView; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.widget.Toolbar; +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; + +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.activities.NoSplashAppCompatActivity; +import info.nightscout.androidaps.plugins.bus.RxBus; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkConst; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkUtil; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data.GattAttributes; +import info.nightscout.androidaps.plugins.pump.common.utils.LocationHelper; +import info.nightscout.androidaps.plugins.pump.medtronic.driver.MedtronicPumpStatus; +import info.nightscout.androidaps.plugins.pump.medtronic.events.EventMedtronicPumpConfigurationChanged; +import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil; +import info.nightscout.androidaps.utils.SP; + +// IMPORTANT: This activity needs to be called from RileyLinkSelectPreference (see pref_medtronic.xml as example) +public class RileyLinkBLEScanActivity extends NoSplashAppCompatActivity { + + private static final Logger LOG = LoggerFactory.getLogger(RileyLinkBLEScanActivity.class); + + private static final int PERMISSION_REQUEST_COARSE_LOCATION = 30241; // arbitrary. + private static final int REQUEST_ENABLE_BT = 30242; // arbitrary + + private static String TAG = "RileyLinkBLEScanActivity"; + + // Stops scanning after 30 seconds. + private static final long SCAN_PERIOD = 30000; + public boolean mScanning; + public ScanSettings settings; + public List filters; + public ListView listBTScan; + public Toolbar toolbarBTScan; + public Context mContext = this; + private BluetoothAdapter mBluetoothAdapter; + private BluetoothLeScanner mLEScanner; + private LeDeviceListAdapter mLeDeviceListAdapter; + private Handler mHandler; + + private String actionTitleStart, actionTitleStop; + private MenuItem menuItem; + + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.rileylink_scan_activity); + + // Initializes Bluetooth adapter. + mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); + mHandler = new Handler(); + + mLeDeviceListAdapter = new LeDeviceListAdapter(); + listBTScan = (ListView) findViewById(R.id.rileylink_listBTScan); + listBTScan.setAdapter(mLeDeviceListAdapter); + listBTScan.setOnItemClickListener((parent, view, position, id) -> { + + // stop scanning if still active + if (mScanning) { + mScanning = false; + mLEScanner.stopScan(mScanCallback2); + } + + TextView textview = (TextView) view.findViewById(R.id.rileylink_device_address); + String bleAddress = textview.getText().toString(); + + SP.putString(RileyLinkConst.Prefs.RileyLinkAddress, bleAddress); + + RileyLinkUtil.getRileyLinkSelectPreference().setSummary(bleAddress); + + MedtronicPumpStatus pumpStatus = MedtronicUtil.getPumpStatus(); + pumpStatus.verifyConfiguration(); // force reloading of address + + RxBus.INSTANCE.send(new EventMedtronicPumpConfigurationChanged()); + + finish(); + }); + + toolbarBTScan = (Toolbar) findViewById(R.id.rileylink_toolbarBTScan); + toolbarBTScan.setTitle(R.string.rileylink_scanner_title); + setSupportActionBar(toolbarBTScan); + + prepareForScanning(); + } + + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.menu_rileylink_ble_scan, menu); + + actionTitleStart = MainApp.gs(R.string.rileylink_scanner_scan_scan); + actionTitleStop = MainApp.gs(R.string.rileylink_scanner_scan_stop); + + menuItem = menu.getItem(0); + + menuItem.setTitle(actionTitleStart); + + return true; + } + + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.rileylink_miScan: { + scanLeDevice(menuItem.getTitle().equals(actionTitleStart)); + return true; + } + + default: + return super.onOptionsItemSelected(item); + } + } + + + public void prepareForScanning() { + // https://developer.android.com/training/permissions/requesting.html + // http://developer.radiusnetworks.com/2015/09/29/is-your-beacon-app-ready-for-android-6.html + if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) { + Toast.makeText(this, R.string.rileylink_scanner_ble_not_supported, Toast.LENGTH_SHORT).show(); + } else { + // Use this check to determine whether BLE is supported on the device. Then + // you can selectively disable BLE-related features. + if (ContextCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { + // your code that requires permission + ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, + PERMISSION_REQUEST_COARSE_LOCATION); + } + + // Ensures Bluetooth is available on the device and it is enabled. If not, + // displays a dialog requesting user permission to enable Bluetooth. + if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) { + Toast.makeText(this, R.string.rileylink_scanner_ble_not_enabled, Toast.LENGTH_SHORT).show(); + } else { + + // Will request that GPS be enabled for devices running Marshmallow or newer. + if (!LocationHelper.isLocationEnabled(this)) { + LocationHelper.requestLocationForBluetooth(this); + } + + mLEScanner = mBluetoothAdapter.getBluetoothLeScanner(); + settings = new ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build(); + filters = Arrays.asList(new ScanFilter.Builder().setServiceUuid( + ParcelUuid.fromString(GattAttributes.SERVICE_RADIO)).build()); + + } + } + + // disable currently selected RL, so that we can discover it + RileyLinkUtil.sendBroadcastMessage(RileyLinkConst.Intents.RileyLinkDisconnect); + } + + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + if (requestCode == REQUEST_ENABLE_BT) { + if (resultCode == RESULT_OK) { + // User allowed Bluetooth to turn on + } else if (resultCode == RESULT_CANCELED) { + // Error, or user said "NO" + finish(); + } + } + } + + private ScanCallback mScanCallback2 = new ScanCallback() { + + @Override + public void onScanResult(int callbackType, final ScanResult scanRecord) { + + Log.d(TAG, scanRecord.toString()); + + runOnUiThread(() -> { + if (addDevice(scanRecord)) + mLeDeviceListAdapter.notifyDataSetChanged(); + }); + } + + + @Override + public void onBatchScanResults(final List results) { + + runOnUiThread(() -> { + + boolean added = false; + + for (ScanResult result : results) { + if (addDevice(result)) + added = true; + } + + if (added) + mLeDeviceListAdapter.notifyDataSetChanged(); + }); + } + + + private boolean addDevice(ScanResult result) { + + BluetoothDevice device = result.getDevice(); + + List serviceUuids = result.getScanRecord().getServiceUuids(); + + if (serviceUuids == null || serviceUuids.size() == 0) { + Log.v(TAG, "Device " + device.getAddress() + " has no serviceUuids (Not RileyLink)."); + } else if (serviceUuids.size() > 1) { + Log.v(TAG, "Device " + device.getAddress() + " has too many serviceUuids (Not RileyLink)."); + } else { + + String uuid = serviceUuids.get(0).getUuid().toString().toLowerCase(); + + if (uuid.equals(GattAttributes.SERVICE_RADIO)) { + Log.i(TAG, "Found RileyLink with address: " + device.getAddress()); + mLeDeviceListAdapter.addDevice(result); + return true; + } else { + Log.v(TAG, "Device " + device.getAddress() + " has incorrect uuid (Not RileyLink)."); + } + } + + return false; + } + + + private String getDeviceDebug(BluetoothDevice device) { + return "BluetoothDevice [name=" + device.getName() + ", address=" + device.getAddress() + // + ", type=" + device.getType(); // + ", alias=" + device.getAlias(); + } + + + @Override + public void onScanFailed(int errorCode) { + Log.e("Scan Failed", "Error Code: " + errorCode); + Toast.makeText(mContext, MainApp.gs(R.string.rileylink_scanner_scanning_error, errorCode), + Toast.LENGTH_LONG).show(); + } + + }; + + + private void scanLeDevice(final boolean enable) { + + if (mLEScanner == null) + return; + + if (enable) { + + mLeDeviceListAdapter.clear(); + mLeDeviceListAdapter.notifyDataSetChanged(); + + // Stops scanning after a pre-defined scan period. + mHandler.postDelayed(() -> { + + if (mScanning) { + mScanning = false; + mLEScanner.stopScan(mScanCallback2); + LOG.debug("scanLeDevice: Scanning Stop"); + Toast.makeText(mContext, R.string.rileylink_scanner_scanning_finished, Toast.LENGTH_SHORT).show(); + menuItem.setTitle(actionTitleStart); + } + }, SCAN_PERIOD); + + mScanning = true; + mLEScanner.startScan(filters, settings, mScanCallback2); + LOG.debug("scanLeDevice: Scanning Start"); + Toast.makeText(this, R.string.rileylink_scanner_scanning, Toast.LENGTH_SHORT).show(); + + menuItem.setTitle(actionTitleStop); + + } else { + if (mScanning) { + mScanning = false; + mLEScanner.stopScan(mScanCallback2); + + LOG.debug("scanLeDevice: Scanning Stop"); + Toast.makeText(this, R.string.rileylink_scanner_scanning_finished, Toast.LENGTH_SHORT).show(); + + menuItem.setTitle(actionTitleStart); + } + } + } + + private class LeDeviceListAdapter extends BaseAdapter { + + private ArrayList mLeDevices; + private Map rileyLinkDevices; + private LayoutInflater mInflator; + String currentlySelectedAddress; + + + public LeDeviceListAdapter() { + super(); + mLeDevices = new ArrayList<>(); + rileyLinkDevices = new HashMap<>(); + mInflator = RileyLinkBLEScanActivity.this.getLayoutInflater(); + currentlySelectedAddress = SP.getString(RileyLinkConst.Prefs.RileyLinkAddress, ""); + } + + + public void addDevice(ScanResult result) { + + if (!mLeDevices.contains(result.getDevice())) { + mLeDevices.add(result.getDevice()); + } + rileyLinkDevices.put(result.getDevice(), result.getRssi()); + notifyDataSetChanged(); + } + + + public void clear() { + mLeDevices.clear(); + rileyLinkDevices.clear(); + notifyDataSetChanged(); + } + + + @Override + public int getCount() { + return mLeDevices.size(); + } + + + @Override + public Object getItem(int i) { + return mLeDevices.get(i); + } + + + @Override + public long getItemId(int i) { + return i; + } + + + @Override + public View getView(int i, View view, ViewGroup viewGroup) { + + ViewHolder viewHolder; + // General ListView optimization code. + if (view == null) { + view = mInflator.inflate(R.layout.rileylink_scan_item, null); + viewHolder = new ViewHolder(); + viewHolder.deviceAddress = (TextView) view.findViewById(R.id.rileylink_device_address); + viewHolder.deviceName = (TextView) view.findViewById(R.id.rileylink_device_name); + view.setTag(viewHolder); + } else { + viewHolder = (ViewHolder) view.getTag(); + } + + BluetoothDevice device = mLeDevices.get(i); + String deviceName = device.getName(); + + if (StringUtils.isBlank(deviceName)) { + deviceName = "RileyLink"; + } + + deviceName += " [" + rileyLinkDevices.get(device).intValue() + "]"; + + if (currentlySelectedAddress.equals(device.getAddress())) { + // viewHolder.deviceName.setTextColor(getColor(R.color.secondary_text_light)); + // viewHolder.deviceAddress.setTextColor(getColor(R.color.secondary_text_light)); + deviceName += " (" + getResources().getString(R.string.rileylink_scanner_selected_device) + ")"; + } + + viewHolder.deviceName.setText(deviceName); + viewHolder.deviceAddress.setText(device.getAddress()); + + return view; + } + + } + + static class ViewHolder { + + TextView deviceName; + TextView deviceAddress; + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/RileyLinkCommunicationManager.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/RileyLinkCommunicationManager.java new file mode 100644 index 0000000000..69837afd7c --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/RileyLinkCommunicationManager.java @@ -0,0 +1,439 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import android.content.Context; + +import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.pump.common.data.PumpStatus; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.RFSpy; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.RileyLinkCommunicationException; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data.FrequencyScanResults; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data.FrequencyTrial; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data.RFSpyResponse; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data.RLMessage; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data.RadioPacket; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data.RadioResponse; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.RLMessageType; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.RileyLinkBLEError; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.RileyLinkServiceData; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.tasks.ServiceTaskExecutor; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.tasks.WakeAndTuneTask; +import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil; +import info.nightscout.androidaps.plugins.pump.medtronic.defs.PumpDeviceState; +import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil; +import info.nightscout.androidaps.utils.SP; + +/** + * This is abstract class for RileyLink Communication, this one needs to be extended by specific "Pump" class. + *

+ * Created by andy on 5/10/18. + */ +public abstract class RileyLinkCommunicationManager { + + private static final Logger LOG = LoggerFactory.getLogger(L.PUMPCOMM); + + private static final int SCAN_TIMEOUT = 1500; + private static final int ALLOWED_PUMP_UNREACHABLE = 10 * 60 * 1000; // 10 minutes + + protected final RFSpy rfspy; + protected final Context context; + protected int receiverDeviceAwakeForMinutes = 1; // override this in constructor of specific implementation + protected String receiverDeviceID; // String representation of receiver device (ex. Pump (xxxxxx) or Pod (yyyyyy)) + protected long lastGoodReceiverCommunicationTime = 0; + protected PumpStatus pumpStatus; + protected RileyLinkServiceData rileyLinkServiceData; + private long nextWakeUpRequired = 0L; + + // internal flag + private boolean showPumpMessages = true; + private int timeoutCount = 0; + + + public RileyLinkCommunicationManager(Context context, RFSpy rfspy) { + this.context = context; + this.rfspy = rfspy; + this.rileyLinkServiceData = RileyLinkUtil.getRileyLinkServiceData(); + RileyLinkUtil.setRileyLinkCommunicationManager(this); + + configurePumpSpecificSettings(); + } + + + protected abstract void configurePumpSpecificSettings(); + + + // All pump communications go through this function. + protected E sendAndListen(RLMessage msg, int timeout_ms, Class clazz) + throws RileyLinkCommunicationException { + + if (showPumpMessages) { + if (isLogEnabled()) + LOG.info("Sent:" + ByteUtil.shortHexString(msg.getTxData())); + } + + RFSpyResponse rfSpyResponse = rfspy.transmitThenReceive(new RadioPacket(msg.getTxData()), timeout_ms); + + RadioResponse radioResponse = rfSpyResponse.getRadioResponse(); + + E response = createResponseMessage(rfSpyResponse.getRadioResponse().getPayload(), clazz); + + if (response.isValid()) { + // Mark this as the last time we heard from the pump. + rememberLastGoodDeviceCommunicationTime(); + } else { + LOG.warn("isDeviceReachable. Response is invalid ! [interrupted={}, timeout={}, unknownCommand={}, invalidParam={}]", rfSpyResponse.wasInterrupted(), + rfSpyResponse.wasTimeout(), rfSpyResponse.isUnknownCommand(), rfSpyResponse.isInvalidParam()); + + if (rfSpyResponse.wasTimeout()) { + timeoutCount++; + + long diff = System.currentTimeMillis() - pumpStatus.lastConnection; + + if (diff > ALLOWED_PUMP_UNREACHABLE) { + LOG.warn("We reached max time that Pump can be unreachable. Starting Tuning."); + ServiceTaskExecutor.startTask(new WakeAndTuneTask()); + timeoutCount = 0; + } + + throw new RileyLinkCommunicationException(RileyLinkBLEError.Timeout); + } else if (rfSpyResponse.wasInterrupted()) { + throw new RileyLinkCommunicationException(RileyLinkBLEError.Interrupted); + } + } + + if (showPumpMessages) { + if (isLogEnabled()) + LOG.info("Received:" + ByteUtil.shortHexString(rfSpyResponse.getRadioResponse().getPayload())); + } + + return response; + } + + + public abstract E createResponseMessage(byte[] payload, Class clazz); + + + public void wakeUp(boolean force) { + wakeUp(receiverDeviceAwakeForMinutes, force); + } + + + public int getNotConnectedCount() { + return rfspy != null ? rfspy.notConnectedCount : 0; + } + + + + // FIXME change wakeup + // TODO we might need to fix this. Maybe make pump awake for shorter time (battery factor for pump) - Andy + public void wakeUp(int duration_minutes, boolean force) { + // If it has been longer than n minutes, do wakeup. Otherwise assume pump is still awake. + // **** FIXME: this wakeup doesn't seem to work well... must revisit + // receiverDeviceAwakeForMinutes = duration_minutes; + + MedtronicUtil.setPumpDeviceState(PumpDeviceState.WakingUp); + + if (force) + nextWakeUpRequired = 0L; + + if (System.currentTimeMillis() > nextWakeUpRequired) { + if (isLogEnabled()) + LOG.info("Waking pump..."); + + byte[] pumpMsgContent = createPumpMessageContent(RLMessageType.ReadSimpleData); // simple + RFSpyResponse resp = rfspy.transmitThenReceive(new RadioPacket(pumpMsgContent), (byte)0, (byte)200, + (byte)0, (byte)0, 25000, (byte)0); + if (isLogEnabled()) + LOG.info("wakeup: raw response is " + ByteUtil.shortHexString(resp.getRaw())); + + // FIXME wakeUp successful !!!!!!!!!!!!!!!!!! + + nextWakeUpRequired = System.currentTimeMillis() + (receiverDeviceAwakeForMinutes * 60 * 1000); + } else { + if (isLogEnabled()) + LOG.trace("Last pump communication was recent, not waking pump."); + } + + // long lastGoodPlus = getLastGoodReceiverCommunicationTime() + (receiverDeviceAwakeForMinutes * 60 * 1000); + // + // if (System.currentTimeMillis() > lastGoodPlus || force) { + // LOG.info("Waking pump..."); + // + // byte[] pumpMsgContent = createPumpMessageContent(RLMessageType.PowerOn); + // RFSpyResponse resp = rfspy.transmitThenReceive(new RadioPacket(pumpMsgContent), (byte) 0, (byte) 200, (byte) + // 0, (byte) 0, 15000, (byte) 0); + // LOG.info("wakeup: raw response is " + ByteUtil.shortHexString(resp.getRaw())); + // } else { + // LOG.trace("Last pump communication was recent, not waking pump."); + // } + } + + + public void setRadioFrequencyForPump(double freqMHz) { + rfspy.setBaseFrequency(freqMHz); + } + + + public double tuneForDevice() { + return scanForDevice(RileyLinkUtil.getRileyLinkTargetFrequency().getScanFrequencies()); + } + + + /** + * If user changes pump and one pump is running in US freq, and other in WW, then previously set frequency would be + * invalid, + * so we would need to retune. This checks that saved frequency is correct range. + * + * @param frequency + * @return + */ + public boolean isValidFrequency(double frequency) { + + double[] scanFrequencies = RileyLinkUtil.getRileyLinkTargetFrequency().getScanFrequencies(); + + if (scanFrequencies.length == 1) { + return RileyLinkUtil.isSame(scanFrequencies[0], frequency); + } else { + return (scanFrequencies[0] <= frequency && scanFrequencies[scanFrequencies.length - 1] >= frequency); + } + } + + + /** + * Do device connection, with wakeup + * + * @return + */ + public abstract boolean tryToConnectToDevice(); + + + public double scanForDevice(double[] frequencies) { + if (isLogEnabled()) + LOG.info("Scanning for receiver ({})", receiverDeviceID); + wakeUp(receiverDeviceAwakeForMinutes, false); + FrequencyScanResults results = new FrequencyScanResults(); + + for (int i = 0; i < frequencies.length; i++) { + int tries = 3; + FrequencyTrial trial = new FrequencyTrial(); + trial.frequencyMHz = frequencies[i]; + rfspy.setBaseFrequency(frequencies[i]); + + int sumRSSI = 0; + for (int j = 0; j < tries; j++) { + + byte[] pumpMsgContent = createPumpMessageContent(RLMessageType.ReadSimpleData); + RFSpyResponse resp = rfspy.transmitThenReceive(new RadioPacket(pumpMsgContent), (byte)0, (byte)0, + (byte)0, (byte)0, 1250, (byte)0); + if (resp.wasTimeout()) { + LOG.error("scanForPump: Failed to find pump at frequency {}", frequencies[i]); + } else if (resp.looksLikeRadioPacket()) { + RadioResponse radioResponse = new RadioResponse(); + + try { + + radioResponse.init(resp.getRaw()); + + if (radioResponse.isValid()) { + int rssi = calculateRssi(radioResponse.rssi); + sumRSSI += rssi; + trial.rssiList.add(rssi); + trial.successes++; + } else { + LOG.warn("Failed to parse radio response: " + ByteUtil.shortHexString(resp.getRaw())); + trial.rssiList.add(-99); + } + + } catch (RileyLinkCommunicationException rle) { + LOG.warn("Failed to decode radio response: " + ByteUtil.shortHexString(resp.getRaw())); + trial.rssiList.add(-99); + } + + } else { + LOG.error("scanForPump: raw response is " + ByteUtil.shortHexString(resp.getRaw())); + trial.rssiList.add(-99); + } + trial.tries++; + } + sumRSSI += -99.0 * (trial.tries - trial.successes); + trial.averageRSSI2 = (double)(sumRSSI) / (double)(trial.tries); + + trial.calculateAverage(); + + results.trials.add(trial); + } + + results.dateTime = System.currentTimeMillis(); + + StringBuilder stringBuilder = new StringBuilder("Scan results:\n"); + + for (int k = 0; k < results.trials.size(); k++) { + FrequencyTrial one = results.trials.get(k); + + stringBuilder.append(String.format("Scan Result[%s]: Freq=%s, avg RSSI = %s\n", "" + k, "" + + one.frequencyMHz, "" + one.averageRSSI + ", RSSIs =" + one.rssiList)); + } + + LOG.info(stringBuilder.toString()); + + results.sort(); // sorts in ascending order + + FrequencyTrial bestTrial = results.trials.get(results.trials.size() - 1); + results.bestFrequencyMHz = bestTrial.frequencyMHz; + if (bestTrial.successes > 0) { + rfspy.setBaseFrequency(results.bestFrequencyMHz); + if (isLogEnabled()) + LOG.debug("Best frequency found: " + results.bestFrequencyMHz); + return results.bestFrequencyMHz; + } else { + LOG.error("No pump response during scan."); + return 0.0; + } + } + + + private int calculateRssi(int rssiIn) { + int rssiOffset = 73; + int outRssi = 0; + if (rssiIn >= 128) { + outRssi = ((rssiIn - 256) / 2) - rssiOffset; + } else { + outRssi = (rssiIn / 2) - rssiOffset; + } + + return outRssi; + } + + + public abstract byte[] createPumpMessageContent(RLMessageType type); + + + private int tune_tryFrequency(double freqMHz) { + rfspy.setBaseFrequency(freqMHz); + // RLMessage msg = makeRLMessage(RLMessageType.ReadSimpleData); + byte[] pumpMsgContent = createPumpMessageContent(RLMessageType.ReadSimpleData); + RadioPacket pkt = new RadioPacket(pumpMsgContent); + RFSpyResponse resp = rfspy.transmitThenReceive(pkt, (byte)0, (byte)0, (byte)0, (byte)0, SCAN_TIMEOUT, (byte)0); + if (resp.wasTimeout()) { + LOG.warn("tune_tryFrequency: no pump response at frequency {}", freqMHz); + } else if (resp.looksLikeRadioPacket()) { + RadioResponse radioResponse = new RadioResponse(); + try { + radioResponse.init(resp.getRaw()); + + if (radioResponse.isValid()) { + LOG.warn("tune_tryFrequency: saw response level {} at frequency {}", radioResponse.rssi, freqMHz); + return calculateRssi(radioResponse.rssi); + } else { + LOG.warn("tune_tryFrequency: invalid radio response:" + + ByteUtil.shortHexString(radioResponse.getPayload())); + } + + } catch (RileyLinkCommunicationException e) { + LOG.warn("Failed to decode radio response: " + ByteUtil.shortHexString(resp.getRaw())); + } + } + + return 0; + } + + + public double quickTuneForPump(double startFrequencyMHz) { + double betterFrequency = startFrequencyMHz; + double stepsize = 0.05; + for (int tries = 0; tries < 4; tries++) { + double evenBetterFrequency = quickTunePumpStep(betterFrequency, stepsize); + if (evenBetterFrequency == 0.0) { + // could not see the pump at all. + // Try again at larger step size + stepsize += 0.05; + } else { + if ((int)(evenBetterFrequency * 100) == (int)(betterFrequency * 100)) { + // value did not change, so we're done. + break; + } + betterFrequency = evenBetterFrequency; // and go again. + } + } + if (betterFrequency == 0.0) { + // we've failed... caller should try a full scan for pump + if (isLogEnabled()) + LOG.error("quickTuneForPump: failed to find pump"); + } else { + rfspy.setBaseFrequency(betterFrequency); + if (betterFrequency != startFrequencyMHz) { + if (isLogEnabled()) + LOG.info("quickTuneForPump: new frequency is {}MHz", betterFrequency); + } else { + if (isLogEnabled()) + LOG.info("quickTuneForPump: pump frequency is the same: {}MHz", startFrequencyMHz); + } + } + return betterFrequency; + } + + + private double quickTunePumpStep(double startFrequencyMHz, double stepSizeMHz) { + if (isLogEnabled()) + LOG.info("Doing quick radio tune for receiver ({})", receiverDeviceID); + wakeUp(false); + int startRssi = tune_tryFrequency(startFrequencyMHz); + double lowerFrequency = startFrequencyMHz - stepSizeMHz; + int lowerRssi = tune_tryFrequency(lowerFrequency); + double higherFrequency = startFrequencyMHz + stepSizeMHz; + int higherRssi = tune_tryFrequency(higherFrequency); + + if ((higherRssi == 0.0) && (lowerRssi == 0.0) && (startRssi == 0.0)) { + // we can't see the pump at all... + return 0.0; + } + if (higherRssi > startRssi) { + // need to move higher + return higherFrequency; + } else if (lowerRssi > startRssi) { + // need to move lower. + return lowerFrequency; + } + return startFrequencyMHz; + } + + + protected void rememberLastGoodDeviceCommunicationTime() { + lastGoodReceiverCommunicationTime = System.currentTimeMillis(); + + SP.putLong(RileyLinkConst.Prefs.LastGoodDeviceCommunicationTime, lastGoodReceiverCommunicationTime); + pumpStatus.setLastCommunicationToNow(); + } + + + private long getLastGoodReceiverCommunicationTime() { + // If we have a value of zero, we need to load from prefs. + if (lastGoodReceiverCommunicationTime == 0L) { + lastGoodReceiverCommunicationTime = SP.getLong(RileyLinkConst.Prefs.LastGoodDeviceCommunicationTime, 0L); + // Might still be zero, but that's fine. + } + double minutesAgo = (System.currentTimeMillis() - lastGoodReceiverCommunicationTime) / (1000.0 * 60.0); + if (isLogEnabled()) + LOG.trace("Last good pump communication was " + minutesAgo + " minutes ago."); + return lastGoodReceiverCommunicationTime; + } + + + public PumpStatus getPumpStatus() { + return pumpStatus; + } + + + public void clearNotConnectedCount() { + if (rfspy != null) { + rfspy.notConnectedCount = 0; + } + } + + private boolean isLogEnabled() { + return L.isEnabled(L.PUMPCOMM); + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/RileyLinkConst.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/RileyLinkConst.java new file mode 100644 index 0000000000..bff2a39240 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/RileyLinkConst.java @@ -0,0 +1,50 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink; + +import info.nightscout.androidaps.R; + +/** + * Created by andy on 16/05/2018. + */ + +public class RileyLinkConst { + + static final String Prefix = "AAPS.RileyLink."; + + public class Intents { + + public static final String RileyLinkReady = Prefix + "RileyLink_Ready"; + public static final String RileyLinkGattFailed = Prefix + "RileyLink_Gatt_Failed"; + + public static final String BluetoothConnected = Prefix + "Bluetooth_Connected"; + public static final String BluetoothReconnected = Prefix + "Bluetooth_Reconnected"; + public static final String BluetoothDisconnected = Prefix + "Bluetooth_Disconnected"; + public static final String RileyLinkDisconnected = Prefix + "RileyLink_Disconnected"; + + public static final String RileyLinkNewAddressSet = Prefix + "NewAddressSet"; + + public static final String INTENT_NEW_rileylinkAddressKey = Prefix + "INTENT_NEW_rileylinkAddressKey"; + public static final String INTENT_NEW_pumpIDKey = Prefix + "INTENT_NEW_pumpIDKey"; + public static final String RileyLinkDisconnect = Prefix + "RileyLink_Disconnect"; + } + + public class Prefs { + + //public static final String PrefPrefix = "pref_rileylink_"; + //public static final String RileyLinkAddress = PrefPrefix + "mac_address"; // pref_rileylink_mac_address + public static final int RileyLinkAddress = R.string.key_rileylink_mac_address; + public static final String LastGoodDeviceCommunicationTime = Prefix + "lastGoodDeviceCommunicationTime"; + public static final String LastGoodDeviceFrequency = Prefix + "LastGoodDeviceFrequency"; + } + + public class IPC { + + // needs to br renamed (and maybe removed) + public static final String MSG_PUMP_quickTune = Prefix + "MSG_PUMP_quickTune"; + public static final String MSG_PUMP_tunePump = Prefix + "MSG_PUMP_tunePump"; + public static final String MSG_PUMP_fetchHistory = Prefix + "MSG_PUMP_fetchHistory"; + public static final String MSG_PUMP_fetchSavedHistory = Prefix + "MSG_PUMP_fetchSavedHistory"; + + public static final String MSG_ServiceCommand = Prefix + "MSG_ServiceCommand"; + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/RileyLinkUtil.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/RileyLinkUtil.java new file mode 100644 index 0000000000..3c7ba730a7 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/RileyLinkUtil.java @@ -0,0 +1,328 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink; + +import android.content.Context; +import android.content.Intent; + +import androidx.localbroadcastmanager.content.LocalBroadcastManager; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.bus.RxBus; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.RileyLinkBLE; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data.encoding.Encoding4b6b; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data.encoding.Encoding4b6bGeoff; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.RileyLinkEncodingType; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.RileyLinkFirmwareVersion; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.RileyLinkTargetFrequency; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.data.BleAdvertisedData; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.data.RLHistoryItem; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkError; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkServiceState; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkTargetDevice; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.RileyLinkService; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.RileyLinkServiceData; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.data.ServiceNotification; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.data.ServiceResult; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.data.ServiceTransport; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.tasks.ServiceTask; +import info.nightscout.androidaps.plugins.pump.common.ui.RileyLinkSelectPreference; +import info.nightscout.androidaps.plugins.pump.medtronic.events.EventMedtronicDeviceStatusChange; + +/** + * Created by andy on 17/05/2018. + */ + +public class RileyLinkUtil { + + private static final Logger LOG = LoggerFactory.getLogger(L.PUMP); + protected static List historyRileyLink = new ArrayList<>(); + protected static RileyLinkCommunicationManager rileyLinkCommunicationManager; + static ServiceTask currentTask; + private static Context context; + private static RileyLinkBLE rileyLinkBLE; + private static RileyLinkServiceData rileyLinkServiceData; + private static RileyLinkService rileyLinkService; + private static RileyLinkTargetFrequency rileyLinkTargetFrequency; + + private static RileyLinkTargetDevice targetDevice; + private static RileyLinkEncodingType encoding; + private static RileyLinkSelectPreference rileyLinkSelectPreference; + private static Encoding4b6b encoding4b6b; + private static RileyLinkFirmwareVersion firmwareVersion; + + + public static void setContext(Context contextIn) { + RileyLinkUtil.context = contextIn; + } + + + public static RileyLinkEncodingType getEncoding() { + return encoding; + } + + + public static void setEncoding(RileyLinkEncodingType encoding) { + RileyLinkUtil.encoding = encoding; + + if (encoding == RileyLinkEncodingType.FourByteSixByteLocal) { + RileyLinkUtil.encoding4b6b = new Encoding4b6bGeoff(); + } + } + + + public static void sendBroadcastMessage(String message) { + if (context != null) { + Intent intent = new Intent(message); + LocalBroadcastManager.getInstance(RileyLinkUtil.context).sendBroadcast(intent); + } + } + + + public static void setServiceState(RileyLinkServiceState newState) { + setServiceState(newState, null); + } + + + public static RileyLinkError getError() { + if (RileyLinkUtil.rileyLinkServiceData != null) + return RileyLinkUtil.rileyLinkServiceData.errorCode; + else + return null; + } + + + public static RileyLinkServiceState getServiceState() { + return workWithServiceState(null, null, false); + } + + + public static void setServiceState(RileyLinkServiceState newState, RileyLinkError errorCode) { + workWithServiceState(newState, errorCode, true); + } + + + private static synchronized RileyLinkServiceState workWithServiceState(RileyLinkServiceState newState, + RileyLinkError errorCode, boolean set) { + + if (set) { + + RileyLinkUtil.rileyLinkServiceData.serviceState = newState; + RileyLinkUtil.rileyLinkServiceData.errorCode = errorCode; + + if (L.isEnabled(L.PUMP)) + LOG.info("RileyLink State Changed: {} {}", newState, errorCode == null ? "" : " - Error State: " + + errorCode.name()); + + RileyLinkUtil.historyRileyLink.add(new RLHistoryItem(RileyLinkUtil.rileyLinkServiceData.serviceState, + RileyLinkUtil.rileyLinkServiceData.errorCode, targetDevice)); + RxBus.INSTANCE.send(new EventMedtronicDeviceStatusChange(newState, errorCode)); + return null; + + } else { + return (RileyLinkUtil.rileyLinkServiceData == null || RileyLinkUtil.rileyLinkServiceData.serviceState == null) ? // + RileyLinkServiceState.NotStarted + : RileyLinkUtil.rileyLinkServiceData.serviceState; + } + + } + + + public static RileyLinkBLE getRileyLinkBLE() { + return RileyLinkUtil.rileyLinkBLE; + } + + + public static void setRileyLinkBLE(RileyLinkBLE rileyLinkBLEIn) { + RileyLinkUtil.rileyLinkBLE = rileyLinkBLEIn; + } + + + public static RileyLinkServiceData getRileyLinkServiceData() { + return RileyLinkUtil.rileyLinkServiceData; + } + + + public static void setRileyLinkServiceData(RileyLinkServiceData rileyLinkServiceData) { + RileyLinkUtil.rileyLinkServiceData = rileyLinkServiceData; + } + + + public static boolean hasPumpBeenTunned() { + return RileyLinkUtil.rileyLinkServiceData.tuneUpDone; + } + + + public static RileyLinkService getRileyLinkService() { + return RileyLinkUtil.rileyLinkService; + } + + + public static void setRileyLinkService(RileyLinkService rileyLinkService) { + RileyLinkUtil.rileyLinkService = rileyLinkService; + } + + + public static RileyLinkCommunicationManager getRileyLinkCommunicationManager() { + return RileyLinkUtil.rileyLinkCommunicationManager; + } + + + public static void setRileyLinkCommunicationManager(RileyLinkCommunicationManager rileyLinkCommunicationManager) { + RileyLinkUtil.rileyLinkCommunicationManager = rileyLinkCommunicationManager; + } + + + public static boolean sendNotification(ServiceNotification notification, Integer clientHashcode) { + return false; + } + + + // FIXME remove ? + public static void setCurrentTask(ServiceTask task) { + if (currentTask == null) { + currentTask = task; + } else { + //LOG.error("setCurrentTask: Cannot replace current task"); + } + } + + + public static void finishCurrentTask(ServiceTask task) { + if (task != currentTask) { + //LOG.error("finishCurrentTask: task does not match"); + } + // hack to force deep copy of transport contents + ServiceTransport transport = task.getServiceTransport().clone(); + + if (transport.hasServiceResult()) { + sendServiceTransportResponse(transport, transport.getServiceResult()); + } + currentTask = null; + } + + + public static void sendServiceTransportResponse(ServiceTransport transport, ServiceResult serviceResult) { + // get the key (hashcode) of the client who requested this + Integer clientHashcode = transport.getSenderHashcode(); + // make a new bundle to send as the message data + transport.setServiceResult(serviceResult); + // FIXME + // transport.setTransportType(RT2Const.IPC.MSG_ServiceResult); + // rileyLinkIPCConnection.sendTransport(transport, clientHashcode); + } + + + public static RileyLinkTargetFrequency getRileyLinkTargetFrequency() { + return RileyLinkUtil.rileyLinkTargetFrequency; + } + + + public static void setRileyLinkTargetFrequency(RileyLinkTargetFrequency rileyLinkTargetFrequency) { + RileyLinkUtil.rileyLinkTargetFrequency = rileyLinkTargetFrequency; + } + + + public static boolean isSame(Double d1, Double d2) { + double diff = d1 - d2; + + return (Math.abs(diff) <= 0.000001); + } + + + @Deprecated + public static BleAdvertisedData parseAdertisedData(byte[] advertisedData) { + List uuids = new ArrayList(); + String name = null; + if (advertisedData == null) { + return new BleAdvertisedData(uuids, name); + } + + ByteBuffer buffer = ByteBuffer.wrap(advertisedData).order(ByteOrder.LITTLE_ENDIAN); + while (buffer.remaining() > 2) { + byte length = buffer.get(); + if (length == 0) + break; + + byte type = buffer.get(); + switch (type) { + case 0x02: // Partial list of 16-bit UUIDs + case 0x03: // Complete list of 16-bit UUIDs + while (length >= 2) { + uuids + .add(UUID.fromString(String.format("%08x-0000-1000-8000-00805f9b34fb", buffer.getShort()))); + length -= 2; + } + break; + case 0x06: // Partial list of 128-bit UUIDs + case 0x07: // Complete list of 128-bit UUIDs + while (length >= 16) { + long lsb = buffer.getLong(); + long msb = buffer.getLong(); + uuids.add(new UUID(msb, lsb)); + length -= 16; + } + break; + case 0x09: + byte[] nameBytes = new byte[length - 1]; + buffer.get(nameBytes); + name = new String(nameBytes, StandardCharsets.UTF_8); + break; + default: + buffer.position(buffer.position() + length - 1); + break; + } + } + return new BleAdvertisedData(uuids, name); + } + + + public static List getRileyLinkHistory() { + return historyRileyLink; + } + + + public static RileyLinkTargetDevice getTargetDevice() { + return targetDevice; + } + + + public static void setTargetDevice(RileyLinkTargetDevice targetDevice) { + RileyLinkUtil.targetDevice = targetDevice; + } + + + public static void setRileyLinkSelectPreference(RileyLinkSelectPreference rileyLinkSelectPreference) { + + RileyLinkUtil.rileyLinkSelectPreference = rileyLinkSelectPreference; + } + + + public static RileyLinkSelectPreference getRileyLinkSelectPreference() { + + return rileyLinkSelectPreference; + } + + + public static Encoding4b6b getEncoding4b6b() { + return RileyLinkUtil.encoding4b6b; + } + + + public static void setFirmwareVersion(RileyLinkFirmwareVersion firmwareVersion) { + RileyLinkUtil.firmwareVersion = firmwareVersion; + } + + + public static RileyLinkFirmwareVersion getFirmwareVersion() { + return firmwareVersion; + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/RFSpy.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/RFSpy.java new file mode 100644 index 0000000000..1fb24821e1 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/RFSpy.java @@ -0,0 +1,438 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble; + +import android.os.SystemClock; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.UUID; + +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkUtil; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.command.Reset; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.command.RileyLinkCommand; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.command.SendAndListen; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.command.SetHardwareEncoding; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.command.SetPreamble; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.command.UpdateRegister; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data.GattAttributes; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data.RFSpyResponse; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data.RadioPacket; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.CC111XRegister; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.RXFilterMode; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.RileyLinkCommandType; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.RileyLinkEncodingType; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.RileyLinkFirmwareVersion; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.RileyLinkTargetFrequency; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.operations.BLECommOperationResult; +import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil; +import info.nightscout.androidaps.plugins.pump.common.utils.StringUtil; +import info.nightscout.androidaps.plugins.pump.common.utils.ThreadUtil; +import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicConst; +import info.nightscout.androidaps.utils.SP; + +/** + * Created by geoff on 5/26/16. + */ +public class RFSpy { + + public static final long RILEYLINK_FREQ_XTAL = 24000000; + public static final int EXPECTED_MAX_BLUETOOTH_LATENCY_MS = 7500; // 1500 + private static final Logger LOG = LoggerFactory.getLogger(L.PUMPBTCOMM); + public int notConnectedCount = 0; + private RileyLinkBLE rileyLinkBle; + private RFSpyReader reader; + private RileyLinkTargetFrequency selectedTargetFrequency; + private UUID radioServiceUUID = UUID.fromString(GattAttributes.SERVICE_RADIO); + private UUID radioDataUUID = UUID.fromString(GattAttributes.CHARA_RADIO_DATA); + private UUID radioVersionUUID = UUID.fromString(GattAttributes.CHARA_RADIO_VERSION); + private UUID responseCountUUID = UUID.fromString(GattAttributes.CHARA_RADIO_RESPONSE_COUNT); + private RileyLinkFirmwareVersion firmwareVersion; + private String bleVersion; // We don't use it so no need of sofisticated logic + Double currentFrequencyMHz; + + + public RFSpy(RileyLinkBLE rileyLinkBle) { + this.rileyLinkBle = rileyLinkBle; + reader = new RFSpyReader(rileyLinkBle); + } + + + public RileyLinkFirmwareVersion getRLVersionCached() { + return firmwareVersion; + } + + + public String getBLEVersionCached() { + return bleVersion; + } + + + // Call this after the RL services are discovered. + // Starts an async task to read when data is available + public void startReader() { + rileyLinkBle.registerRadioResponseCountNotification(new Runnable() { + + @Override + public void run() { + newDataIsAvailable(); + } + }); + reader.start(); + } + + + // Here should go generic RL initialisation + protocol adjustments depending on + // firmware version + public void initializeRileyLink() { + bleVersion = getVersion(); + firmwareVersion = getFirmwareVersion(); + RileyLinkUtil.setFirmwareVersion(firmwareVersion); + } + + + // Call this from the "response count" notification handler. + public void newDataIsAvailable() { + // pass the message to the reader (which should be internal to RFSpy) + reader.newDataIsAvailable(); + } + + + // This gets the version from the BLE113, not from the CC1110. + // I.e., this gets the version from the BLE interface, not from the radio. + public String getVersion() { + BLECommOperationResult result = rileyLinkBle.readCharacteristic_blocking(radioServiceUUID, radioVersionUUID); + if (result.resultCode == BLECommOperationResult.RESULT_SUCCESS) { + String version = StringUtil.fromBytes(result.value); + if (isLogEnabled()) + LOG.debug("BLE Version: " + version); + return version; + } else { + LOG.error("getVersion failed with code: " + result.resultCode); + return "(null)"; + } + } + + + public RileyLinkFirmwareVersion getFirmwareVersion() { + + if (isLogEnabled()) + LOG.debug("Firmware Version. Get Version - Start"); + + for (int i = 0; i < 5; i++) { + // We have to call raw version of communication to get firmware version + // So that we can adjust other commands accordingly afterwords + + byte[] getVersionRaw = getByteArray(RileyLinkCommandType.GetVersion.code); + byte[] response = writeToDataRaw(getVersionRaw, 5000); + + if (isLogEnabled()) + LOG.debug("Firmware Version. GetVersion [response={}]", ByteUtil.shortHexString(response)); + + if (response != null) { // && response[0] == (byte) 0xDD) { + + String versionString = StringUtil.fromBytes(response); + + RileyLinkFirmwareVersion version = RileyLinkFirmwareVersion.getByVersionString(StringUtil + .fromBytes(response)); + + if (isLogEnabled()) + LOG.trace("Firmware Version string: {}, resolved to {}.", versionString, version); + + if (version != RileyLinkFirmwareVersion.UnknownVersion) + return version; + + SystemClock.sleep(1000); + } + } + + LOG.error("Firmware Version can't be determined. Checking with BLE Version [{}].", bleVersion); + + if (bleVersion.contains(" 2.")) { + return RileyLinkFirmwareVersion.Version_2_0; + } + + return RileyLinkFirmwareVersion.UnknownVersion; + } + + + private byte[] writeToDataRaw(byte[] bytes, int responseTimeout_ms) { + SystemClock.sleep(100); + // FIXME drain read queue? + byte[] junkInBuffer = reader.poll(0); + + while (junkInBuffer != null) { + LOG.warn(ThreadUtil.sig() + "writeToData: draining read queue, found this: " + + ByteUtil.shortHexString(junkInBuffer)); + junkInBuffer = reader.poll(0); + } + + // prepend length, and send it. + byte[] prepended = ByteUtil.concat(new byte[]{(byte) (bytes.length)}, bytes); + + LOG.debug("writeToData (raw={})", ByteUtil.shortHexString(prepended)); + + BLECommOperationResult writeCheck = rileyLinkBle.writeCharacteristic_blocking(radioServiceUUID, radioDataUUID, + prepended); + if (writeCheck.resultCode != BLECommOperationResult.RESULT_SUCCESS) { + LOG.error("BLE Write operation failed, code=" + writeCheck.resultCode); + return null; // will be a null (invalid) response + } + SystemClock.sleep(100); + // Log.i(TAG,ThreadUtil.sig()+String.format(" writeToData:(timeout %d) %s",(responseTimeout_ms),ByteUtil.shortHexString(prepended))); + byte[] rawResponse = reader.poll(responseTimeout_ms); + return rawResponse; + + } + + + // The caller has to know how long the RFSpy will be busy with what was sent to it. + private RFSpyResponse writeToData(RileyLinkCommand command, int responseTimeout_ms) { + + byte[] bytes = command.getRaw(); + byte[] rawResponse = writeToDataRaw(bytes, responseTimeout_ms); + + RFSpyResponse resp = new RFSpyResponse(command, rawResponse); + if (rawResponse == null) { + LOG.error("writeToData: No response from RileyLink"); + notConnectedCount++; + } else { + if (resp.wasInterrupted()) { + LOG.error("writeToData: RileyLink was interrupted"); + } else if (resp.wasTimeout()) { + LOG.error("writeToData: RileyLink reports timeout"); + notConnectedCount++; + } else if (resp.isOK()) { + LOG.warn("writeToData: RileyLink reports OK"); + resetNotConnectedCount(); + } else { + if (resp.looksLikeRadioPacket()) { + // RadioResponse radioResp = resp.getRadioResponse(); + // byte[] responsePayload = radioResp.getPayload(); + if (isLogEnabled()) + LOG.trace("writeToData: received radio response. Will decode at upper level"); + resetNotConnectedCount(); + } + // Log.i(TAG, "writeToData: raw response is " + ByteUtil.shortHexString(rawResponse)); + } + } + return resp; + } + + + private void resetNotConnectedCount() { + this.notConnectedCount = 0; + } + + + private byte[] getByteArray(byte... input) { + return input; + } + + + private byte[] getCommandArray(RileyLinkCommandType command, byte[] body) { + int bodyLength = body == null ? 0 : body.length; + + byte[] output = new byte[bodyLength + 1]; + + output[0] = command.code; + + if (body != null) { + for (int i = 0; i < body.length; i++) { + output[i + 1] = body[i]; + } + } + + return output; + } + + + public RFSpyResponse transmitThenReceive(RadioPacket pkt, byte sendChannel, byte repeatCount, byte delay_ms, + byte listenChannel, int timeout_ms, byte retryCount) { + return transmitThenReceive(pkt, sendChannel, repeatCount, delay_ms, listenChannel, timeout_ms, retryCount, null); + } + + + public RFSpyResponse transmitThenReceive(RadioPacket pkt, int timeout_ms) { + return transmitThenReceive(pkt, (byte) 0, (byte) 0, (byte) 0, (byte) 0, timeout_ms, (byte) 0); + } + + + public RFSpyResponse transmitThenReceive(RadioPacket pkt, byte sendChannel, byte repeatCount, byte delay_ms, + byte listenChannel, int timeout_ms, byte retryCount, Integer extendPreamble_ms) { + + int sendDelay = repeatCount * delay_ms; + int receiveDelay = timeout_ms * (retryCount + 1); + + SendAndListen command = new SendAndListen(sendChannel, repeatCount, delay_ms, listenChannel, timeout_ms, + retryCount, extendPreamble_ms, pkt); + + return writeToData(command, sendDelay + receiveDelay + EXPECTED_MAX_BLUETOOTH_LATENCY_MS); + } + + + public RFSpyResponse updateRegister(CC111XRegister reg, int val) { + RFSpyResponse resp = writeToData(new UpdateRegister(reg, (byte) val), EXPECTED_MAX_BLUETOOTH_LATENCY_MS); + return resp; + } + + + public void setBaseFrequency(double freqMHz) { + int value = (int) (freqMHz * 1000000 / ((double) (RILEYLINK_FREQ_XTAL) / Math.pow(2.0, 16.0))); + updateRegister(CC111XRegister.freq0, (byte) (value & 0xff)); + updateRegister(CC111XRegister.freq1, (byte) ((value >> 8) & 0xff)); + updateRegister(CC111XRegister.freq2, (byte) ((value >> 16) & 0xff)); + LOG.info("Set frequency to {} MHz", freqMHz); + + this.currentFrequencyMHz = freqMHz; + + configureRadioForRegion(RileyLinkUtil.getRileyLinkTargetFrequency()); + } + + + private void configureRadioForRegion(RileyLinkTargetFrequency frequency) { + + // we update registers only on first run, or if region changed + + switch (frequency) { + case Medtronic_WorldWide: { + // updateRegister(CC111X_MDMCFG4, (byte) 0x59); + setRXFilterMode(RXFilterMode.Wide); + // updateRegister(CC111X_MDMCFG3, (byte) 0x66); + // updateRegister(CC111X_MDMCFG2, (byte) 0x33); + updateRegister(CC111XRegister.mdmcfg1, 0x62); + updateRegister(CC111XRegister.mdmcfg0, 0x1A); + updateRegister(CC111XRegister.deviatn, 0x13); + setMedtronicEncoding(); + } + break; + + case Medtronic_US: { + // updateRegister(CC111X_MDMCFG4, (byte) 0x99); + setRXFilterMode(RXFilterMode.Narrow); + // updateRegister(CC111X_MDMCFG3, (byte) 0x66); + // updateRegister(CC111X_MDMCFG2, (byte) 0x33); + updateRegister(CC111XRegister.mdmcfg1, 0x61); + updateRegister(CC111XRegister.mdmcfg0, 0x7E); + updateRegister(CC111XRegister.deviatn, 0x15); + setMedtronicEncoding(); + } + break; + + case Omnipod: { + RFSpyResponse r = null; + // RL initialization for Omnipod is a copy/paste from OmniKit implementation. + // Last commit from original repository: 5c3beb4144 + // so if something is terribly wrong, please check git diff PodCommsSession.swift since that commit + r = updateRegister(CC111XRegister.pktctrl1, 0x20); + r = updateRegister(CC111XRegister.agcctrl0, 0x00); + r = updateRegister(CC111XRegister.fsctrl1, 0x06); + r = updateRegister(CC111XRegister.mdmcfg4, 0xCA); + r = updateRegister(CC111XRegister.mdmcfg3, 0xBC); + r = updateRegister(CC111XRegister.mdmcfg2, 0x06); + r = updateRegister(CC111XRegister.mdmcfg1, 0x70); + r = updateRegister(CC111XRegister.mdmcfg0, 0x11); + r = updateRegister(CC111XRegister.deviatn, 0x44); + r = updateRegister(CC111XRegister.mcsm0, 0x18); + r = updateRegister(CC111XRegister.foccfg, 0x17); + r = updateRegister(CC111XRegister.fscal3, 0xE9); + r = updateRegister(CC111XRegister.fscal2, 0x2A); + r = updateRegister(CC111XRegister.fscal1, 0x00); + r = updateRegister(CC111XRegister.fscal0, 0x1F); + + r = updateRegister(CC111XRegister.test1, 0x31); + r = updateRegister(CC111XRegister.test0, 0x09); + r = updateRegister(CC111XRegister.paTable0, 0x84); + r = updateRegister(CC111XRegister.sync1, 0xA5); + r = updateRegister(CC111XRegister.sync0, 0x5A); + + r = setRileyLinkEncoding(RileyLinkEncodingType.Manchester); + r = setPreamble(0x6665); + + } + break; + default: + LOG.warn("No region configuration for RfSpy and {}", frequency.name()); + break; + + } + + this.selectedTargetFrequency = frequency; + } + + + private void setMedtronicEncoding() { + RileyLinkEncodingType encoding = RileyLinkEncodingType.FourByteSixByteLocal; + + if (RileyLinkFirmwareVersion.isSameVersion(this.firmwareVersion, RileyLinkFirmwareVersion.Version2AndHigher)) { + if (SP.getString(MedtronicConst.Prefs.Encoding, "None").equals(MainApp.gs(R.string.key_medtronic_pump_encoding_4b6b_rileylink))) { + encoding = RileyLinkEncodingType.FourByteSixByteRileyLink; + } + } + + setRileyLinkEncoding(encoding); + + if (isLogEnabled()) + LOG.debug("Set Encoding for Medtronic: " + encoding.name()); + } + + + private RFSpyResponse setPreamble(int preamble) { + RFSpyResponse resp = null; + try { + resp = writeToData(new SetPreamble(preamble), EXPECTED_MAX_BLUETOOTH_LATENCY_MS); + } catch (Exception e) { + e.toString(); + } + return resp; + } + + + public RFSpyResponse setRileyLinkEncoding(RileyLinkEncodingType encoding) { + RFSpyResponse resp = writeToData(new SetHardwareEncoding(encoding), EXPECTED_MAX_BLUETOOTH_LATENCY_MS); + + if (resp.isOK()) { + reader.setRileyLinkEncodingType(encoding); + RileyLinkUtil.setEncoding(encoding); + } + + return resp; + } + + + private void setRXFilterMode(RXFilterMode mode) { + + byte drate_e = (byte) 0x9; // exponent of symbol rate (16kbps) + byte chanbw = mode.value; + + updateRegister(CC111XRegister.mdmcfg4, (byte) (chanbw | drate_e)); + } + + /** + * Reset RileyLink Configuration (set all updateRegisters) + */ + public void resetRileyLinkConfiguration() { + if (this.currentFrequencyMHz != null) + this.setBaseFrequency(this.currentFrequencyMHz); + } + + + public RFSpyResponse resetRileyLink() { + RFSpyResponse resp = null; + try { + resp = writeToData(new Reset(), EXPECTED_MAX_BLUETOOTH_LATENCY_MS); + if (isLogEnabled()) + LOG.debug("Reset command send, response: {}", resp); + } catch (Exception e) { + e.toString(); + } + return resp; + } + + + private boolean isLogEnabled() { + return L.isEnabled(L.PUMPBTCOMM); + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/RFSpyReader.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/RFSpyReader.java new file mode 100644 index 0000000000..063cdb877b --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/RFSpyReader.java @@ -0,0 +1,150 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble; + +import java.util.UUID; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import android.os.AsyncTask; +import android.os.SystemClock; + +import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkUtil; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data.GattAttributes; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.RileyLinkEncodingType; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.operations.BLECommOperationResult; +import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil; +import info.nightscout.androidaps.plugins.pump.common.utils.ThreadUtil; + +/** + * Created by geoff on 5/26/16. + */ +public class RFSpyReader { + + private static final Logger LOG = LoggerFactory.getLogger(L.PUMPBTCOMM); + private static AsyncTask readerTask; + private RileyLinkBLE rileyLinkBle; + private Semaphore waitForRadioData = new Semaphore(0, true); + private LinkedBlockingQueue mDataQueue = new LinkedBlockingQueue<>(); + private int acquireCount = 0; + private int releaseCount = 0; + private boolean stopAtNull = true; + + + public RFSpyReader(RileyLinkBLE rileyLinkBle) { + this.rileyLinkBle = rileyLinkBle; + } + + + public void init(RileyLinkBLE rileyLinkBLE) { + this.rileyLinkBle = rileyLinkBLE; + } + + + public void setRileyLinkBle(RileyLinkBLE rileyLinkBle) { + if (readerTask != null) { + readerTask.cancel(true); + } + this.rileyLinkBle = rileyLinkBle; + } + + public void setRileyLinkEncodingType(RileyLinkEncodingType encodingType) { + stopAtNull = !(encodingType == RileyLinkEncodingType.Manchester || // + encodingType == RileyLinkEncodingType.FourByteSixByteRileyLink); + } + + + // This timeout must be coordinated with the length of the RFSpy radio operation or Bad Things Happen. + public byte[] poll(int timeout_ms) { + if (isLogEnabled()) + LOG.trace(ThreadUtil.sig() + "Entering poll at t==" + SystemClock.uptimeMillis() + ", timeout is " + timeout_ms + + " mDataQueue size is " + mDataQueue.size()); + + if (mDataQueue.isEmpty()) + try { + // block until timeout or data available. + // returns null if timeout. + byte[] dataFromQueue = mDataQueue.poll(timeout_ms, TimeUnit.MILLISECONDS); + if (dataFromQueue != null) { + if (isLogEnabled()) + LOG.debug("Got data [" + ByteUtil.shortHexString(dataFromQueue) + "] at t==" + + SystemClock.uptimeMillis()); + } else { + if (isLogEnabled()) + LOG.debug("Got data [null] at t==" + SystemClock.uptimeMillis()); + } + return dataFromQueue; + } catch (InterruptedException e) { + LOG.error("poll: Interrupted waiting for data"); + } + return null; + } + + + // Call this from the "response count" notification handler. + public void newDataIsAvailable() { + releaseCount++; + + if (isLogEnabled()) + LOG.trace(ThreadUtil.sig() + "waitForRadioData released(count=" + releaseCount + ") at t=" + + SystemClock.uptimeMillis()); + waitForRadioData.release(); + } + + + public void start() { + readerTask = new AsyncTask() { + + @Override + protected Void doInBackground(Void... voids) { + UUID serviceUUID = UUID.fromString(GattAttributes.SERVICE_RADIO); + UUID radioDataUUID = UUID.fromString(GattAttributes.CHARA_RADIO_DATA); + BLECommOperationResult result; + while (true) { + try { + acquireCount++; + waitForRadioData.acquire(); + if (isLogEnabled()) + LOG.trace(ThreadUtil.sig() + "waitForRadioData acquired (count=" + acquireCount + ") at t=" + + SystemClock.uptimeMillis()); + SystemClock.sleep(100); + SystemClock.sleep(1); + result = rileyLinkBle.readCharacteristic_blocking(serviceUUID, radioDataUUID); + SystemClock.sleep(100); + + if (result.resultCode == BLECommOperationResult.RESULT_SUCCESS) { + if (stopAtNull) { + // only data up to the first null is valid + for (int i = 0; i < result.value.length; i++) { + if (result.value[i] == 0) { + result.value = ByteUtil.substring(result.value, 0, i); + break; + } + } + } + mDataQueue.add(result.value); + } else if (result.resultCode == BLECommOperationResult.RESULT_INTERRUPTED) { + LOG.error("Read operation was interrupted"); + } else if (result.resultCode == BLECommOperationResult.RESULT_TIMEOUT) { + LOG.error("Read operation on Radio Data timed out"); + } else if (result.resultCode == BLECommOperationResult.RESULT_BUSY) { + LOG.error("FAIL: RileyLinkBLE reports operation already in progress"); + } else if (result.resultCode == BLECommOperationResult.RESULT_NONE) { + LOG.error("FAIL: got invalid result code: " + result.resultCode); + } + } catch (InterruptedException e) { + LOG.error("Interrupted while waiting for data"); + } + } + } + }.execute(); + } + + private boolean isLogEnabled() { + return L.isEnabled(L.PUMPBTCOMM); + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/RileyLinkBLE.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/RileyLinkBLE.java new file mode 100644 index 0000000000..aab5e35142 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/RileyLinkBLE.java @@ -0,0 +1,589 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble; + +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.BluetoothGattService; +import android.bluetooth.BluetoothProfile; +import android.content.Context; +import android.os.SystemClock; + +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; +import java.util.UUID; +import java.util.concurrent.Semaphore; + +import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkConst; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkUtil; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data.GattAttributes; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.operations.BLECommOperation; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.operations.BLECommOperationResult; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.operations.CharacteristicReadOperation; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.operations.CharacteristicWriteOperation; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.operations.DescriptorWriteOperation; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkError; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkServiceState; +import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil; +import info.nightscout.androidaps.plugins.pump.common.utils.ThreadUtil; + +/** + * Created by geoff on 5/26/16. + * Added: State handling, configuration of RF for different configuration ranges, connection handling + */ +public class RileyLinkBLE { + + private static final Logger LOG = LoggerFactory.getLogger(L.PUMPBTCOMM); + + private final Context context; + public boolean gattDebugEnabled = true; + boolean manualDisconnect = false; + private BluetoothAdapter bluetoothAdapter; + private BluetoothGattCallback bluetoothGattCallback; + private BluetoothDevice rileyLinkDevice; + private BluetoothGatt bluetoothConnectionGatt = null; + private BLECommOperation mCurrentOperation; + private Semaphore gattOperationSema = new Semaphore(1, true); + private Runnable radioResponseCountNotified; + private boolean mIsConnected = false; + + + public RileyLinkBLE(final Context context) { + this.context = context; + this.bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); + + if (isLogEnabled()) + LOG.debug("BT Adapter: " + this.bluetoothAdapter); + bluetoothGattCallback = new BluetoothGattCallback() { + + @Override + public void onCharacteristicChanged(final BluetoothGatt gatt, + final BluetoothGattCharacteristic characteristic) { + super.onCharacteristicChanged(gatt, characteristic); + if (gattDebugEnabled && isLogEnabled()) { + LOG.trace(ThreadUtil.sig() + "onCharacteristicChanged " + + GattAttributes.lookup(characteristic.getUuid()) + " " + + ByteUtil.getHex(characteristic.getValue())); + if (characteristic.getUuid().equals(UUID.fromString(GattAttributes.CHARA_RADIO_RESPONSE_COUNT))) { + LOG.debug("Response Count is " + ByteUtil.shortHexString(characteristic.getValue())); + } + } + if (radioResponseCountNotified != null) { + radioResponseCountNotified.run(); + } + } + + + @Override + public void onCharacteristicRead(final BluetoothGatt gatt, + final BluetoothGattCharacteristic characteristic, int status) { + super.onCharacteristicRead(gatt, characteristic, status); + + final String statusMessage = getGattStatusMessage(status); + if (gattDebugEnabled && isLogEnabled()) { + LOG.trace(ThreadUtil.sig() + "onCharacteristicRead (" + + GattAttributes.lookup(characteristic.getUuid()) + ") " + statusMessage + ":" + + ByteUtil.getHex(characteristic.getValue())); + } + mCurrentOperation.gattOperationCompletionCallback(characteristic.getUuid(), characteristic.getValue()); + } + + + @Override + public void onCharacteristicWrite(final BluetoothGatt gatt, + final BluetoothGattCharacteristic characteristic, int status) { + super.onCharacteristicWrite(gatt, characteristic, status); + + final String uuidString = GattAttributes.lookup(characteristic.getUuid()); + if (gattDebugEnabled && isLogEnabled()) { + LOG.trace(ThreadUtil.sig() + "onCharacteristicWrite " + getGattStatusMessage(status) + " " + + uuidString + " " + ByteUtil.shortHexString(characteristic.getValue())); + } + mCurrentOperation.gattOperationCompletionCallback(characteristic.getUuid(), characteristic.getValue()); + } + + + @Override + public void onConnectionStateChange(final BluetoothGatt gatt, final int status, final int newState) { + super.onConnectionStateChange(gatt, status, newState); + + // https://github.com/NordicSemiconductor/puck-central-android/blob/master/PuckCentral/app/src/main/java/no/nordicsemi/puckcentral/bluetooth/gatt/GattManager.java#L117 + if (status == 133) { + LOG.error("Got the status 133 bug, closing gatt"); + disconnect(); + SystemClock.sleep(500); + return; + } + + if (gattDebugEnabled) { + final String stateMessage; + if (newState == BluetoothProfile.STATE_CONNECTED) { + stateMessage = "CONNECTED"; + } else if (newState == BluetoothProfile.STATE_CONNECTING) { + stateMessage = "CONNECTING"; + } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { + stateMessage = "DISCONNECTED"; + } else if (newState == BluetoothProfile.STATE_DISCONNECTING) { + stateMessage = "DISCONNECTING"; + } else { + stateMessage = "UNKNOWN newState (" + newState + ")"; + } + + if (isLogEnabled()) + LOG.warn("onConnectionStateChange " + getGattStatusMessage(status) + " " + stateMessage); + } + + if (newState == BluetoothProfile.STATE_CONNECTED) { + if (status == BluetoothGatt.GATT_SUCCESS) { + RileyLinkUtil.sendBroadcastMessage(RileyLinkConst.Intents.BluetoothConnected); + } else { + if (isLogEnabled()) + LOG.debug("BT State connected, GATT status {} ({})", status, getGattStatusMessage(status)); + } + + } else if ((newState == BluetoothProfile.STATE_CONNECTING) || // + (newState == BluetoothProfile.STATE_DISCONNECTING)) { + // LOG.debug("We are in {} state.", status == BluetoothProfile.STATE_CONNECTING ? "Connecting" : + // "Disconnecting"); + } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { + RileyLinkUtil.sendBroadcastMessage(RileyLinkConst.Intents.RileyLinkDisconnected); + if (manualDisconnect) + close(); + LOG.warn("RileyLink Disconnected."); + } else { + LOG.warn("Some other state: (status={},newState={})", status, newState); + } + } + + + @Override + public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { + super.onDescriptorWrite(gatt, descriptor, status); + if (gattDebugEnabled && isLogEnabled()) { + LOG.warn("onDescriptorWrite " + GattAttributes.lookup(descriptor.getUuid()) + " " + + getGattStatusMessage(status) + " written: " + ByteUtil.getHex(descriptor.getValue())); + } + mCurrentOperation.gattOperationCompletionCallback(descriptor.getUuid(), descriptor.getValue()); + } + + + @Override + public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { + super.onDescriptorRead(gatt, descriptor, status); + mCurrentOperation.gattOperationCompletionCallback(descriptor.getUuid(), descriptor.getValue()); + if (gattDebugEnabled && isLogEnabled()) { + LOG.warn("onDescriptorRead " + getGattStatusMessage(status) + " status " + descriptor); + } + } + + + @Override + public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) { + super.onMtuChanged(gatt, mtu, status); + if (gattDebugEnabled && isLogEnabled()) { + LOG.warn("onMtuChanged " + mtu + " status " + status); + } + } + + + @Override + public void onReadRemoteRssi(final BluetoothGatt gatt, int rssi, int status) { + super.onReadRemoteRssi(gatt, rssi, status); + if (gattDebugEnabled && isLogEnabled()) { + LOG.warn("onReadRemoteRssi " + getGattStatusMessage(status) + ": " + rssi); + } + } + + + @Override + public void onReliableWriteCompleted(BluetoothGatt gatt, int status) { + super.onReliableWriteCompleted(gatt, status); + if (gattDebugEnabled && isLogEnabled()) { + LOG.warn("onReliableWriteCompleted status " + status); + } + } + + + @Override + public void onServicesDiscovered(final BluetoothGatt gatt, int status) { + super.onServicesDiscovered(gatt, status); + + if (status == BluetoothGatt.GATT_SUCCESS) { + final List services = gatt.getServices(); + + boolean rileyLinkFound = false; + + for (BluetoothGattService service : services) { + final UUID uuidService = service.getUuid(); + + if (isAnyRileyLinkServiceFound(service)) { + rileyLinkFound = true; + } + + if (gattDebugEnabled) { + debugService(service, 0); + } + } + + if (gattDebugEnabled && isLogEnabled()) { + LOG.warn("onServicesDiscovered " + getGattStatusMessage(status)); + } + + LOG.info("Gatt device is RileyLink device: " + rileyLinkFound); + + if (rileyLinkFound) { + mIsConnected = true; + RileyLinkUtil.sendBroadcastMessage(RileyLinkConst.Intents.RileyLinkReady); + // RileyLinkUtil.sendNotification(new + // ServiceNotification(RileyLinkConst.Intents.RileyLinkReady), null); + } else { + mIsConnected = false; + RileyLinkUtil.setServiceState(RileyLinkServiceState.RileyLinkError, + RileyLinkError.DeviceIsNotRileyLink); + } + + } else { + if (isLogEnabled()) + LOG.debug("onServicesDiscovered " + getGattStatusMessage(status)); + RileyLinkUtil.sendBroadcastMessage(RileyLinkConst.Intents.RileyLinkGattFailed); + } + } + }; + } + + + private boolean isAnyRileyLinkServiceFound(BluetoothGattService service) { + + boolean found = false; + + found = GattAttributes.isRileyLink(service.getUuid()); + + if (found) { + return true; + } else { + List includedServices = service.getIncludedServices(); + + for (BluetoothGattService serviceI : includedServices) { + if (isAnyRileyLinkServiceFound(serviceI)) { + return true; + } + + } + } + + return false; + } + + + public BluetoothDevice getRileyLinkDevice() { + return this.rileyLinkDevice; + } + + + public void debugService(BluetoothGattService service, int indentCount) { + + String indentString = StringUtils.repeat(' ', indentCount); + + final UUID uuidService = service.getUuid(); + + if (gattDebugEnabled) { + final String uuidServiceString = uuidService.toString(); + + StringBuilder stringBuilder = new StringBuilder(); + + stringBuilder.append(indentString); + stringBuilder.append(GattAttributes.lookup(uuidServiceString, "Unknown service")); + stringBuilder.append(" (" + uuidServiceString + ")"); + + for (BluetoothGattCharacteristic character : service.getCharacteristics()) { + final String uuidCharacteristicString = character.getUuid().toString(); + + stringBuilder.append("\n "); + stringBuilder.append(indentString); + stringBuilder.append(" - " + GattAttributes.lookup(uuidCharacteristicString, "Unknown Characteristic")); + stringBuilder.append(" (" + uuidCharacteristicString + ")"); + } + + stringBuilder.append("\n\n"); + + LOG.warn(stringBuilder.toString()); + + List includedServices = service.getIncludedServices(); + + for (BluetoothGattService serviceI : includedServices) { + debugService(serviceI, indentCount + 4); + } + } + } + + + public void registerRadioResponseCountNotification(Runnable notifier) { + radioResponseCountNotified = notifier; + } + + + public boolean isConnected() { + return mIsConnected; + } + + + public boolean discoverServices() { + + if (bluetoothConnectionGatt == null) { + // shouldn't happen, but if it does we exit + return false; + } + + if (bluetoothConnectionGatt.discoverServices()) { + if (isLogEnabled()) + LOG.warn("Starting to discover GATT Services."); + return true; + } else { + LOG.error("Cannot discover GATT Services."); + return false; + } + } + + + public boolean enableNotifications() { + BLECommOperationResult result = setNotification_blocking(UUID.fromString(GattAttributes.SERVICE_RADIO), // + UUID.fromString(GattAttributes.CHARA_RADIO_RESPONSE_COUNT)); + if (result.resultCode != BLECommOperationResult.RESULT_SUCCESS) { + LOG.error("Error setting response count notification"); + return false; + } + return true; + } + + + public void findRileyLink(String RileyLinkAddress) { + if (isLogEnabled()) + LOG.debug("RileyLink address: " + RileyLinkAddress); + // Must verify that this is a valid MAC, or crash. + + rileyLinkDevice = bluetoothAdapter.getRemoteDevice(RileyLinkAddress); + // if this succeeds, we get a connection state change callback? + + if (rileyLinkDevice!=null) { + connectGatt(); + } else { + LOG.error("RileyLink device not found with address: " + RileyLinkAddress); + } + } + + + // This function must be run on UI thread. + public void connectGatt() { + if (this.rileyLinkDevice==null) { + LOG.error("RileyLink device is null, can't do connectGatt."); + return; + } + + bluetoothConnectionGatt = rileyLinkDevice.connectGatt(context, true, bluetoothGattCallback); + // , BluetoothDevice.TRANSPORT_LE + if (bluetoothConnectionGatt == null) { + LOG.error("Failed to connect to Bluetooth Low Energy device at " + bluetoothAdapter.getAddress()); + } else { + if (gattDebugEnabled) { + if (isLogEnabled()) + LOG.debug("Gatt Connected."); + } + } + } + + + public void disconnect() { + mIsConnected = false; + if (isLogEnabled()) + LOG.warn("Closing GATT connection"); + // Close old conenction + if (bluetoothConnectionGatt != null) { + // Not sure if to disconnect or to close first.. + bluetoothConnectionGatt.disconnect(); + manualDisconnect = true; + } + } + + + public void close() { + if (bluetoothConnectionGatt != null) { + bluetoothConnectionGatt.close(); + bluetoothConnectionGatt = null; + } + } + + + public BLECommOperationResult setNotification_blocking(UUID serviceUUID, UUID charaUUID) { + BLECommOperationResult rval = new BLECommOperationResult(); + if (bluetoothConnectionGatt != null) { + + try { + gattOperationSema.acquire(); + SystemClock.sleep(1); // attempting to yield thread, to make sequence of events easier to follow + } catch (InterruptedException e) { + LOG.error("setNotification_blocking: interrupted waiting for gattOperationSema"); + return rval; + } + if (mCurrentOperation != null) { + rval.resultCode = BLECommOperationResult.RESULT_BUSY; + } else { + if (bluetoothConnectionGatt.getService(serviceUUID) == null) { + // Catch if the service is not supported by the BLE device + rval.resultCode = BLECommOperationResult.RESULT_NONE; + LOG.error("BT Device not supported"); + // TODO: 11/07/2016 UI update for user + } else { + BluetoothGattCharacteristic chara = bluetoothConnectionGatt.getService(serviceUUID) + .getCharacteristic(charaUUID); + // Tell Android that we want the notifications + bluetoothConnectionGatt.setCharacteristicNotification(chara, true); + List list = chara.getDescriptors(); + if (gattDebugEnabled) { + for (int i = 0; i < list.size(); i++) { + if (isLogEnabled()) + LOG.debug("Found descriptor: " + list.get(i).toString()); + } + } + BluetoothGattDescriptor descr = list.get(0); + // Tell the remote device to send the notifications + mCurrentOperation = new DescriptorWriteOperation(bluetoothConnectionGatt, descr, + BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); + mCurrentOperation.execute(this); + if (mCurrentOperation.timedOut) { + rval.resultCode = BLECommOperationResult.RESULT_TIMEOUT; + } else if (mCurrentOperation.interrupted) { + rval.resultCode = BLECommOperationResult.RESULT_INTERRUPTED; + } else { + rval.resultCode = BLECommOperationResult.RESULT_SUCCESS; + } + } + mCurrentOperation = null; + gattOperationSema.release(); + } + } else { + LOG.error("setNotification_blocking: not configured!"); + rval.resultCode = BLECommOperationResult.RESULT_NOT_CONFIGURED; + } + return rval; + } + + + // call from main + public BLECommOperationResult writeCharacteristic_blocking(UUID serviceUUID, UUID charaUUID, byte[] value) { + BLECommOperationResult rval = new BLECommOperationResult(); + if (bluetoothConnectionGatt != null) { + rval.value = value; + try { + gattOperationSema.acquire(); + SystemClock.sleep(1); // attempting to yield thread, to make sequence of events easier to follow + } catch (InterruptedException e) { + LOG.error("writeCharacteristic_blocking: interrupted waiting for gattOperationSema"); + return rval; + } + + if (mCurrentOperation != null) { + rval.resultCode = BLECommOperationResult.RESULT_BUSY; + } else { + if (bluetoothConnectionGatt.getService(serviceUUID) == null) { + // Catch if the service is not supported by the BLE device + // GGW: Tue Jul 12 01:14:01 UTC 2016: This can also happen if the + // app that created the bluetoothConnectionGatt has been destroyed/created, + // e.g. when the user switches from portrait to landscape. + rval.resultCode = BLECommOperationResult.RESULT_NONE; + LOG.error("BT Device not supported"); + // TODO: 11/07/2016 UI update for user + } else { + BluetoothGattCharacteristic chara = bluetoothConnectionGatt.getService(serviceUUID) + .getCharacteristic(charaUUID); + mCurrentOperation = new CharacteristicWriteOperation(bluetoothConnectionGatt, chara, value); + mCurrentOperation.execute(this); + if (mCurrentOperation.timedOut) { + rval.resultCode = BLECommOperationResult.RESULT_TIMEOUT; + } else if (mCurrentOperation.interrupted) { + rval.resultCode = BLECommOperationResult.RESULT_INTERRUPTED; + } else { + rval.resultCode = BLECommOperationResult.RESULT_SUCCESS; + } + } + mCurrentOperation = null; + gattOperationSema.release(); + } + } else { + LOG.error("writeCharacteristic_blocking: not configured!"); + rval.resultCode = BLECommOperationResult.RESULT_NOT_CONFIGURED; + } + return rval; + } + + + public BLECommOperationResult readCharacteristic_blocking(UUID serviceUUID, UUID charaUUID) { + BLECommOperationResult rval = new BLECommOperationResult(); + if (bluetoothConnectionGatt != null) { + try { + gattOperationSema.acquire(); + SystemClock.sleep(1); // attempting to yield thread, to make sequence of events easier to follow + } catch (InterruptedException e) { + LOG.error("readCharacteristic_blocking: Interrupted waiting for gattOperationSema"); + return rval; + } + if (mCurrentOperation != null) { + rval.resultCode = BLECommOperationResult.RESULT_BUSY; + } else { + if (bluetoothConnectionGatt.getService(serviceUUID) == null) { + // Catch if the service is not supported by the BLE device + rval.resultCode = BLECommOperationResult.RESULT_NONE; + LOG.error("BT Device not supported"); + // TODO: 11/07/2016 UI update for user + } else { + BluetoothGattCharacteristic chara = bluetoothConnectionGatt.getService(serviceUUID).getCharacteristic( + charaUUID); + mCurrentOperation = new CharacteristicReadOperation(bluetoothConnectionGatt, chara); + mCurrentOperation.execute(this); + if (mCurrentOperation.timedOut) { + rval.resultCode = BLECommOperationResult.RESULT_TIMEOUT; + } else if (mCurrentOperation.interrupted) { + rval.resultCode = BLECommOperationResult.RESULT_INTERRUPTED; + } else { + rval.resultCode = BLECommOperationResult.RESULT_SUCCESS; + rval.value = mCurrentOperation.getValue(); + } + } + } + mCurrentOperation = null; + gattOperationSema.release(); + } else { + LOG.error("readCharacteristic_blocking: not configured!"); + rval.resultCode = BLECommOperationResult.RESULT_NOT_CONFIGURED; + } + return rval; + } + + + private String getGattStatusMessage(final int status) { + final String statusMessage; + if (status == BluetoothGatt.GATT_SUCCESS) { + statusMessage = "SUCCESS"; + } else if (status == BluetoothGatt.GATT_FAILURE) { + statusMessage = "FAILED"; + } else if (status == BluetoothGatt.GATT_WRITE_NOT_PERMITTED) { + statusMessage = "NOT PERMITTED"; + } else if (status == 133) { + statusMessage = "Found the strange 133 bug"; + } else { + statusMessage = "UNKNOWN (" + status + ")"; + } + + return statusMessage; + } + + private boolean isLogEnabled() { + return L.isEnabled(L.PUMPBTCOMM); + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/RileyLinkCommunicationException.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/RileyLinkCommunicationException.java new file mode 100644 index 0000000000..e694609571 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/RileyLinkCommunicationException.java @@ -0,0 +1,30 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble; + +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.RileyLinkBLEError; + +/** + * Created by andy on 11/23/18. + */ + +public class RileyLinkCommunicationException extends Exception { + + String extendedErrorText; + private RileyLinkBLEError errorCode; + + + public RileyLinkCommunicationException(RileyLinkBLEError errorCode, String extendedErrorText) { + super(errorCode.getDescription()); + + this.errorCode = errorCode; + this.extendedErrorText = extendedErrorText; + } + + + public RileyLinkCommunicationException(RileyLinkBLEError errorCode) { + super(errorCode.getDescription()); + + this.errorCode = errorCode; + // this.extendedErrorText = extendedErrorText; + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/command/GetVersion.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/command/GetVersion.java new file mode 100644 index 0000000000..757be66aac --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/command/GetVersion.java @@ -0,0 +1,22 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.command; + +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.RileyLinkCommandType; + +public class GetVersion extends RileyLinkCommand { + + public GetVersion() { + super(); + } + + + @Override + public RileyLinkCommandType getCommandType() { + return RileyLinkCommandType.GetVersion; + } + + + @Override + public byte[] getRaw() { + return super.getRawSimple(); + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/command/Reset.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/command/Reset.java new file mode 100644 index 0000000000..bd556d6695 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/command/Reset.java @@ -0,0 +1,22 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.command; + +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.RileyLinkCommandType; + +public class Reset extends RileyLinkCommand { + + public Reset() { + super(); + } + + + @Override + public RileyLinkCommandType getCommandType() { + return RileyLinkCommandType.Reset; + } + + + @Override + public byte[] getRaw() { + return super.getRawSimple(); + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/command/ResetRadioConfig.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/command/ResetRadioConfig.java new file mode 100644 index 0000000000..c37d4da35c --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/command/ResetRadioConfig.java @@ -0,0 +1,22 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.command; + +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.RileyLinkCommandType; + +public class ResetRadioConfig extends RileyLinkCommand { + + public ResetRadioConfig() { + super(); + } + + + @Override + public RileyLinkCommandType getCommandType() { + return RileyLinkCommandType.ResetRadioConfig; + } + + + @Override + public byte[] getRaw() { + return super.getRawSimple(); + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/command/RileyLinkCommand.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/command/RileyLinkCommand.java new file mode 100644 index 0000000000..4eeffc6439 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/command/RileyLinkCommand.java @@ -0,0 +1,27 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.command; + +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.RileyLinkCommandType; + +public abstract class RileyLinkCommand { + + public RileyLinkCommand() { + } + + + public abstract RileyLinkCommandType getCommandType(); + + + public abstract byte[] getRaw(); + + + protected byte[] getRawSimple() { + return getByteArray(getCommandType().code); + + } + + + protected byte[] getByteArray(byte... input) { + return input; + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/command/SendAndListen.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/command/SendAndListen.java new file mode 100644 index 0000000000..900275a05f --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/command/SendAndListen.java @@ -0,0 +1,96 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.command; + +import java.nio.ByteBuffer; +import java.util.ArrayList; + +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkUtil; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data.RadioPacket; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.RileyLinkCommandType; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.RileyLinkFirmwareVersion; +import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil; + +public class SendAndListen extends RileyLinkCommand { + + private byte sendChannel; + private byte repeatCount; + private int delayBetweenPackets_ms; + private byte listenChannel; + private int timeout_ms; + private byte retryCount; + private Integer preambleExtension_ms; + private RadioPacket packetToSend; + + + public SendAndListen(byte sendChannel, byte repeatCount, byte delayBetweenPackets_ms, byte listenChannel, + int timeout_ms, byte retryCount, RadioPacket packetToSend + + ) { + this(sendChannel, repeatCount, delayBetweenPackets_ms, listenChannel, timeout_ms, retryCount, null, + packetToSend); + } + + + public SendAndListen(byte sendChannel, byte repeatCount, int delayBetweenPackets_ms, byte listenChannel, + int timeout_ms, byte retryCount, Integer preambleExtension_ms, RadioPacket packetToSend + + ) { + super(); + this.sendChannel = sendChannel; + this.repeatCount = repeatCount; + this.delayBetweenPackets_ms = delayBetweenPackets_ms; + this.listenChannel = listenChannel; + this.timeout_ms = timeout_ms; + this.retryCount = retryCount; + this.preambleExtension_ms = preambleExtension_ms == null ? 0 : preambleExtension_ms; + this.packetToSend = packetToSend; + } + + + @Override + public RileyLinkCommandType getCommandType() { + return RileyLinkCommandType.SendAndListen; + } + + + @Override + public byte[] getRaw() { + + // If firmware version is not set (error reading version from device, shouldn't happen), + // we will default to version 2 + boolean isPacketV2 = RileyLinkUtil.getFirmwareVersion() != null ? RileyLinkUtil.getFirmwareVersion() + .isSameVersion(RileyLinkFirmwareVersion.Version2AndHigher) : true; + + ArrayList bytes = new ArrayList(); + bytes.add(this.getCommandType().code); + bytes.add(this.sendChannel); + bytes.add(this.repeatCount); + + if (isPacketV2) { // delay is unsigned 16-bit integer + byte[] delayBuff = ByteBuffer.allocate(4).putInt(delayBetweenPackets_ms).array(); + bytes.add(delayBuff[2]); + bytes.add(delayBuff[3]); + } else { + bytes.add((byte)delayBetweenPackets_ms); + } + + bytes.add(this.listenChannel); + + byte[] timeoutBuff = ByteBuffer.allocate(4).putInt(timeout_ms).array(); + + bytes.add(timeoutBuff[0]); + bytes.add(timeoutBuff[1]); + bytes.add(timeoutBuff[2]); + bytes.add(timeoutBuff[3]); + + bytes.add(retryCount); + + if (isPacketV2) { // 2.x (and probably higher versions) support preamble extension + byte[] preambleBuf = ByteBuffer.allocate(4).putInt(preambleExtension_ms).array(); + bytes.add(preambleBuf[2]); + bytes.add(preambleBuf[3]); + } + + return ByteUtil.concat(ByteUtil.getByteArrayFromList(bytes), packetToSend.getEncoded()); + + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/command/SetHardwareEncoding.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/command/SetHardwareEncoding.java new file mode 100644 index 0000000000..8019563ee9 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/command/SetHardwareEncoding.java @@ -0,0 +1,27 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.command; + +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.RileyLinkCommandType; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.RileyLinkEncodingType; + +public class SetHardwareEncoding extends RileyLinkCommand { + + private final RileyLinkEncodingType encoding; + + + public SetHardwareEncoding(RileyLinkEncodingType encoding) { + super(); + this.encoding = encoding; + } + + + @Override + public RileyLinkCommandType getCommandType() { + return RileyLinkCommandType.SetHardwareEncoding; + } + + + @Override + public byte[] getRaw() { + return getByteArray(getCommandType().code, encoding.value); + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/command/SetPreamble.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/command/SetPreamble.java new file mode 100644 index 0000000000..7333459f2b --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/command/SetPreamble.java @@ -0,0 +1,42 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.command; + +import java.nio.ByteBuffer; + +import org.apache.commons.lang3.NotImplementedException; + +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkUtil; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.RileyLinkCommandType; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.RileyLinkFirmwareVersion; + +public class SetPreamble extends RileyLinkCommand { + + private int preamble; + + + public SetPreamble(int preamble) throws Exception { + super(); + + // this command was not supported before 2.0 + if (!RileyLinkUtil.getFirmwareVersion().isSameVersion(RileyLinkFirmwareVersion.Version2AndHigher)) { + throw new NotImplementedException("Old firmware does not support SetPreamble command"); + } + + if (preamble < 0 || preamble > 0xFFFF) { + throw new Exception("preamble value is out of range"); + } + this.preamble = preamble; + } + + + @Override + public RileyLinkCommandType getCommandType() { + return RileyLinkCommandType.SetPreamble; + } + + + @Override + public byte[] getRaw() { + byte[] bytes = ByteBuffer.allocate(4).putInt(preamble).array(); + return getByteArray(this.getCommandType().code, bytes[2], bytes[3]); + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/command/UpdateRegister.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/command/UpdateRegister.java new file mode 100644 index 0000000000..71ae922ba8 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/command/UpdateRegister.java @@ -0,0 +1,29 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.command; + +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.CC111XRegister; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.RileyLinkCommandType; + +public class UpdateRegister extends RileyLinkCommand { + + CC111XRegister register; + byte registerValue; + + + public UpdateRegister(CC111XRegister register, byte registerValue) { + super(); + this.register = register; + this.registerValue = registerValue; + } + + + @Override + public RileyLinkCommandType getCommandType() { + return RileyLinkCommandType.UpdateRegister; + } + + + @Override + public byte[] getRaw() { + return getByteArray(getCommandType().code, register.value, registerValue); + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/data/FrequencyScanResults.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/data/FrequencyScanResults.java new file mode 100644 index 0000000000..88850e315e --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/data/FrequencyScanResults.java @@ -0,0 +1,30 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Created by geoff on 5/30/16. + * changed by Andy 10/20/18 + */ +public class FrequencyScanResults { + + public List trials = new ArrayList<>(); + public double bestFrequencyMHz = 0.0; + public long dateTime; + + + public void sort() { + Collections.sort(trials, (trial1, trial2) -> { + int res = trial1.averageRSSI.compareTo(trial2.averageRSSI); + + if (res == 0) { + return (int)(trial1.frequencyMHz - trial2.frequencyMHz); + } else + return res; + + }); + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/data/FrequencyTrial.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/data/FrequencyTrial.java new file mode 100644 index 0000000000..af7dce6a98 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/data/FrequencyTrial.java @@ -0,0 +1,35 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by geoff on 5/30/16. + * changed by Andy 10/20/18 + */ +public class FrequencyTrial { + + public int tries = 0; + public int successes = 0; + public Double averageRSSI = 0.0; + public double frequencyMHz = 0.0; + public List rssiList = new ArrayList<>(); + public double averageRSSI2; + + + public void calculateAverage() { + int sum = 0; + int count = 0; + for (Integer rssi : rssiList) { + sum += Math.abs(rssi); + count++; + } + + double avg = (sum / (count * 1.0d)); + + if (count != 0) + this.averageRSSI = avg * (-1); + else + this.averageRSSI = -99.0d; + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/data/GattAttributes.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/data/GattAttributes.java new file mode 100644 index 0000000000..4a76d25d1c --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/data/GattAttributes.java @@ -0,0 +1,87 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +/** + * Created by geoff on 5/21/16. + */ +public class GattAttributes { + + // NOTE: these uuid strings must be lower case! + + public static String PREFIX = "0000"; + public static String SUFFIX = "-0000-1000-8000-00805f9b34fb"; + public static String SERVICE_GAP = PREFIX + "1800" + SUFFIX; + public static String CHARA_GAP_NAME = PREFIX + "2a00" + SUFFIX; // RileyLink RFSpy + public static String CHARA_GAP_NUM = PREFIX + "2a01" + SUFFIX; // 0000 + public static String CHARA_GAP_UNK = PREFIX + "2a01" + SUFFIX; // a + + public static String SERVICE_BATTERY = PREFIX + "180f" + SUFFIX; // Battery + public static String CHARA_BATTERY_UNK = PREFIX + "2a19" + SUFFIX; + + // RileyLink Radio Service + public static String SERVICE_RADIO = "0235733b-99c5-4197-b856-69219c2a3845"; + public static String CHARA_RADIO_DATA = "c842e849-5028-42e2-867c-016adada9155"; + public static String CHARA_RADIO_RESPONSE_COUNT = "6e6c7910-b89e-43a5-a0fe-50c5e2b81f4a"; + public static String CHARA_RADIO_TIMER_TICK = "6e6c7910-b89e-43a5-78af-50c5e2b86f7e"; + public static String CHARA_RADIO_CUSTOM_NAME = "d93b2af0-1e28-11e4-8c21-0800200c9a66"; + public static String CHARA_RADIO_VERSION = "30d99dc9-7c91-4295-a051-0a104d238cf2"; + public static String CHARA_RADIO_LED_MODE = "c6d84241-f1a7-4f9c-a25f-fce16732f14e"; + + private static Map attributes; + private static Map attributesRileyLinkSpecific; + + // table of names for uuids + static { + attributes = new HashMap<>(); + + attributes.put(SERVICE_GAP, "Device Information Service"); + attributes.put(CHARA_GAP_NAME, "Name"); // + attributes.put(CHARA_GAP_NUM, "Number"); // + + attributes.put(SERVICE_BATTERY, "Battery Service"); + + attributes.put(SERVICE_RADIO, "Radio Interface"); // a + attributes.put(CHARA_RADIO_CUSTOM_NAME, "Custom Name"); + attributes.put(CHARA_RADIO_DATA, "Data"); + attributes.put(CHARA_RADIO_RESPONSE_COUNT, "Response Count"); + attributes.put(CHARA_RADIO_TIMER_TICK, "Timer Tick"); + attributes.put(CHARA_RADIO_VERSION, "Version"); // firmwareVersion + attributes.put(CHARA_RADIO_LED_MODE, "Led Mode"); + + attributesRileyLinkSpecific = new HashMap<>(); + + attributesRileyLinkSpecific.put(SERVICE_RADIO, "Radio Interface"); // a + attributesRileyLinkSpecific.put(CHARA_RADIO_CUSTOM_NAME, "Custom Name"); + attributesRileyLinkSpecific.put(CHARA_RADIO_DATA, "Data"); + attributesRileyLinkSpecific.put(CHARA_RADIO_RESPONSE_COUNT, "Response Count"); + attributesRileyLinkSpecific.put(CHARA_RADIO_TIMER_TICK, "Timer Tick"); + attributesRileyLinkSpecific.put(CHARA_RADIO_VERSION, "Version"); // firmwareVersion + attributesRileyLinkSpecific.put(CHARA_RADIO_LED_MODE, "Led Mode"); + } + + + public static String lookup(UUID uuid) { + return lookup(uuid.toString()); + } + + + public static String lookup(String uuid) { + return lookup(uuid, uuid); + } + + + public static String lookup(String uuid, String defaultName) { + String name = attributes.get(uuid); + return name == null ? defaultName : name; + } + + + // we check for specific UUID (Radio ones, because thoose seem to be unique + public static boolean isRileyLink(UUID uuid) { + return attributesRileyLinkSpecific.containsKey(uuid.toString()); + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/data/RFSpyResponse.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/data/RFSpyResponse.java new file mode 100644 index 0000000000..cf5362427c --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/data/RFSpyResponse.java @@ -0,0 +1,134 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data; + +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.RileyLinkCommunicationException; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.command.RileyLinkCommand; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.RFSpyRLResponse; + +/** + * Created by geoff on 5/26/16. + */ +public class RFSpyResponse { + + // 0xaa == timeout + // 0xbb == interrupted + // 0xcc == zero-data + // 0xdd == success + // 0x11 == invalidParam + // 0x22 == unknownCommand + + protected byte[] raw; + protected RadioResponse radioResponse; + private RileyLinkCommand command; + + + public RFSpyResponse() { + init(new byte[0]); + } + + + public RFSpyResponse(byte[] bytes) { + init(bytes); + } + + + public RFSpyResponse(RileyLinkCommand command, byte[] rawResponse) { + + this.command = command; + init(rawResponse); + } + + + public void init(byte[] bytes) { + if (bytes == null) { + raw = new byte[0]; + } else { + raw = bytes; + } + + } + + + public RadioResponse getRadioResponse() throws RileyLinkCommunicationException { + if (looksLikeRadioPacket()) { + radioResponse = new RadioResponse(command); + radioResponse.init(raw); + } else { + radioResponse = new RadioResponse(); + } + return radioResponse; + } + + + public boolean wasTimeout() { + if ((raw.length == 1) || (raw.length == 2)) { + if (raw[0] == (byte)0xaa) { + return true; + } + } + return false; + } + + + public boolean wasInterrupted() { + if ((raw.length == 1) || (raw.length == 2)) { + if (raw[0] == (byte)0xbb) { + return true; + } + } + return false; + } + + + public boolean isInvalidParam() { + if ((raw.length == 1) || (raw.length == 2)) { + if (raw[0] == (byte)0x11) { + return true; + } + } + return false; + } + + + public boolean isUnknownCommand() { + if ((raw.length == 1) || (raw.length == 2)) { + if (raw[0] == (byte)0x22) { + return true; + } + } + return false; + } + + + public boolean isOK() { + if ((raw.length == 1) || (raw.length == 2)) { + if (raw[0] == (byte)0x01 || raw[0] == (byte)0xDD) { + return true; + } + } + return false; + } + + + public boolean looksLikeRadioPacket() { + if (raw.length > 2) { + return true; + } + return false; + } + + + @Override + public String toString() { + if (raw.length > 2) { + return "Radio packet"; + } else { + RFSpyRLResponse r = RFSpyRLResponse.fromByte(raw[0]); + return r.toString(); + } + } + + + public byte[] getRaw() { + return raw; + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/data/RLMessage.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/data/RLMessage.java new file mode 100644 index 0000000000..b52c3eb264 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/data/RLMessage.java @@ -0,0 +1,13 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data; + +/** + * Created by andy on 5/6/18. + */ +public interface RLMessage { + + byte[] getTxData(); + + + boolean isValid(); + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/data/RLMessageType.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/data/RLMessageType.java new file mode 100644 index 0000000000..4fbc3144bd --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/data/RLMessageType.java @@ -0,0 +1,11 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data; + +/** + * Created by andy on 5/6/18. + */ + +public enum RLMessageType { + PowerOn, // for powering on the pump (wakeup) + ReadSimpleData, // for checking if pump is readable (for Medtronic we can use GetModel) + ; +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/data/RadioPacket.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/data/RadioPacket.java new file mode 100644 index 0000000000..a48e3de208 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/data/RadioPacket.java @@ -0,0 +1,57 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data; + +import org.apache.commons.lang3.NotImplementedException; + +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkUtil; +import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil; +import info.nightscout.androidaps.plugins.pump.common.utils.CRC; + +/** + * Created by geoff on 5/22/16. + */ + +public class RadioPacket { + + protected byte[] pkt; + + + public RadioPacket(byte[] pkt) { + this.pkt = pkt; + } + + + public byte[] getRaw() { + return pkt; + } + + + public byte[] getWithCRC() { + byte[] withCRC = ByteUtil.concat(pkt, CRC.crc8(pkt)); + return withCRC; + } + + + public byte[] getEncoded() { + + switch (RileyLinkUtil.getEncoding()) { + case Manchester: { // We have this encoding in RL firmware + return pkt; + } + + case FourByteSixByteLocal: { + byte[] withCRC = getWithCRC(); + + byte[] encoded = RileyLinkUtil.getEncoding4b6b().encode4b6b(withCRC); + return ByteUtil.concat(encoded, (byte)0); + } + + case FourByteSixByteRileyLink: { + return getWithCRC(); + } + + default: + throw new NotImplementedException(("Encoding not supported: " + RileyLinkUtil.getEncoding().toString())); + } + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/data/RadioResponse.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/data/RadioResponse.java new file mode 100644 index 0000000000..646d7aac9a --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/data/RadioResponse.java @@ -0,0 +1,142 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data; + +import org.apache.commons.lang3.NotImplementedException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkUtil; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.RileyLinkCommunicationException; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.command.RileyLinkCommand; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.RileyLinkBLEError; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.RileyLinkCommandType; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.RileyLinkFirmwareVersion; +import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil; +import info.nightscout.androidaps.plugins.pump.common.utils.CRC; + +/** + * Created by geoff on 5/30/16. + */ +public class RadioResponse { + + private static final Logger LOG = LoggerFactory.getLogger(L.PUMPBTCOMM); + + public boolean decodedOK = false; + public int rssi; + public int responseNumber; + public byte[] decodedPayload = new byte[0]; + public byte receivedCRC; + private RileyLinkCommand command; + + + public RadioResponse() { + + } + + + // public RadioResponse(byte[] rxData) { + // init(rxData); + // } + + public RadioResponse(RileyLinkCommand command /* , byte[] raw */) { + this.command = command; + // init(raw); + } + + + public boolean isValid() { + + // We should check for all listening commands, but only one is actually used + if (command != null && command.getCommandType() != RileyLinkCommandType.SendAndListen) { + return true; + } + + if (!decodedOK) { + return false; + } + if (decodedPayload != null) { + if (receivedCRC == CRC.crc8(decodedPayload)) { + return true; + } + } + return false; + } + + + public void init(byte[] rxData) throws RileyLinkCommunicationException { + + if (rxData == null) { + return; + } + if (rxData.length < 3) { + // This does not look like something valid heard from a RileyLink device + return; + } + byte[] encodedPayload; + + if (RileyLinkFirmwareVersion.isSameVersion(RileyLinkUtil.getRileyLinkServiceData().versionCC110, + RileyLinkFirmwareVersion.Version2)) { + encodedPayload = ByteUtil.substring(rxData, 3, rxData.length - 3); + rssi = rxData[1]; + responseNumber = rxData[2]; + } else { + encodedPayload = ByteUtil.substring(rxData, 2, rxData.length - 2); + rssi = rxData[0]; + responseNumber = rxData[1]; + } + + try { + + // for non-radio commands we just return the raw response + // well, for non-radio commands we shouldn't even reach this point + // but getVersion is kind of exception + if (command != null && // + command.getCommandType() != RileyLinkCommandType.SendAndListen) { + decodedOK = true; + decodedPayload = encodedPayload; + return; + } + + switch (RileyLinkUtil.getEncoding()) { + + case Manchester: + case FourByteSixByteRileyLink: { + decodedOK = true; + decodedPayload = encodedPayload; + } + break; + + case FourByteSixByteLocal: { + byte[] decodeThis = RileyLinkUtil.getEncoding4b6b().decode4b6b(encodedPayload); + + if (decodeThis != null && decodeThis.length > 2) { + decodedOK = true; + + decodedPayload = ByteUtil.substring(decodeThis, 0, decodeThis.length - 1); + receivedCRC = decodeThis[decodeThis.length - 1]; + byte calculatedCRC = CRC.crc8(decodedPayload); + if (receivedCRC != calculatedCRC) { + LOG.error(String.format("RadioResponse: CRC mismatch, calculated 0x%02x, received 0x%02x", + calculatedCRC, receivedCRC)); + } + } else { + throw new RileyLinkCommunicationException(RileyLinkBLEError.TooShortOrNullResponse); + } + } + break; + + default: + throw new NotImplementedException("this {" + RileyLinkUtil.getEncoding().toString() + + "} encoding is not supported"); + } + } catch (NumberFormatException e) { + decodedOK = false; + LOG.error("Failed to decode radio data: " + ByteUtil.shortHexString(encodedPayload)); + } + } + + + public byte[] getPayload() { + return decodedPayload; + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/data/encoding/Encoding4b6b.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/data/encoding/Encoding4b6b.java new file mode 100644 index 0000000000..a05a0be401 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/data/encoding/Encoding4b6b.java @@ -0,0 +1,16 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data.encoding; + +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.RileyLinkCommunicationException; + +/** + * Created by andy on 11/24/18. + */ + +public interface Encoding4b6b { + + byte[] encode4b6b(byte[] data); + + + byte[] decode4b6b(byte[] data) throws RileyLinkCommunicationException; + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/data/encoding/Encoding4b6bAbstract.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/data/encoding/Encoding4b6bAbstract.java new file mode 100644 index 0000000000..3bab1a6d05 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/data/encoding/Encoding4b6bAbstract.java @@ -0,0 +1,69 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data.encoding; + +import org.slf4j.Logger; + +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.RileyLinkCommunicationException; +import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil; + + +/** + * Created by andy on 11/24/18. + */ + +public abstract class Encoding4b6bAbstract implements Encoding4b6b { + + /** + * encode4b6bMap is an ordered list of translations 6bits -> 4 bits, in order from 0x0 to 0xF + * The 6 bit codes are what is used on the RF side of the RileyLink to communicate + * with a Medtronic pump. + */ + public static final byte[] encode4b6bList = new byte[] { + 0x15, 0x31, 0x32, 0x23, 0x34, 0x25, 0x26, 0x16, 0x1a, 0x19, 0x2a, 0x0b, 0x2c, 0x0d, 0x0e, 0x1c }; + + + // 21, 49, 50, 35, 52, 37, 38, 22, 26, 25, 42, 11, 44, 13, 14, 28 + + public abstract byte[] encode4b6b(byte[] data); + + + public abstract byte[] decode4b6b(byte[] data) throws RileyLinkCommunicationException; + + + protected short convertUnsigned(byte x) { + short ss = x; + + if (ss < 0) { + ss += 256; + } + + return ss; + } + + + /* O(n) lookup. Run on an O(n) translation of a byte-stream, gives O(n**2) performance. Sigh. */ + public static int encode4b6bListIndex(byte b) { + for (int i = 0; i < encode4b6bList.length; i++) { + if (b == encode4b6bList[i]) { + return i; + } + } + return -1; + } + + + public void writeError(Logger LOG, byte[] raw, String errorData) { + + LOG.error("\n=============================================================================\n" + // + " Decoded payload length is zero.\n" + + " encodedPayload: {}\n" + + " errors: {}\n" + + "=============================================================================", // + ByteUtil.getHex(raw), errorData); + + //FabricUtil.createEvent("MedtronicDecode4b6bError", null); + + return; + + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/data/encoding/Encoding4b6bGeoff.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/data/encoding/Encoding4b6bGeoff.java new file mode 100644 index 0000000000..40f24cf135 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/data/encoding/Encoding4b6bGeoff.java @@ -0,0 +1,249 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data.encoding; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; + +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.RileyLinkCommunicationException; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.RileyLinkBLEError; +import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil; + +/** + * Created by andy on 11/24/18. + */ + +public class Encoding4b6bGeoff extends Encoding4b6bAbstract { + + public static final Logger LOG = LoggerFactory.getLogger(Encoding4b6bGeoff.class); + + + public byte[] encode4b6b(byte[] data) { + // if ((data.length % 2) != 0) { + // LOG.error("Warning: data is odd number of bytes"); + // } + // use arraylists because byte[] is annoying. + List inData = ByteUtil.getListFromByteArray(data); + List outData = new ArrayList<>(); + + int acc = 0; + int bitcount = 0; + int i; + for (i = 0; i < inData.size(); i++) { + acc <<= 6; + acc |= encode4b6bList[(inData.get(i) >> 4) & 0x0f]; + bitcount += 6; + + acc <<= 6; + acc |= encode4b6bList[inData.get(i) & 0x0f]; + bitcount += 6; + + while (bitcount >= 8) { + byte outByte = (byte) (acc >> (bitcount - 8) & 0xff); + outData.add(outByte); + bitcount -= 8; + acc &= (0xffff >> (16 - bitcount)); + } + } + if (bitcount > 0) { + acc <<= 6; + acc |= 0x14; // marks uneven packet boundary. + bitcount += 6; + if (bitcount >= 8) { + byte outByte = (byte) ((acc >> (bitcount - 8)) & 0xff); + outData.add(outByte); + bitcount -= 8; + // acc &= (0xffff >> (16 - bitcount)); + } + while (bitcount >= 8) { + outData.add((byte) 0); + bitcount -= 8; + } + } + + // convert back to byte[] + byte[] rval = ByteUtil.getByteArrayFromList(outData); + + return rval; + + } + + + /** + * Decode by Geoff + * + * @param raw + * @return + * @throws NumberFormatException + */ + public byte[] decode4b6b(byte[] raw) throws RileyLinkCommunicationException { + + StringBuilder errorMessageBuilder = new StringBuilder(); + + errorMessageBuilder.append("Input data: " + ByteUtil.shortHexString(raw) + "\n"); + + if ((raw.length % 2) != 0) { + errorMessageBuilder.append("Warn: odd number of bytes.\n"); + } + + byte[] rval = new byte[]{}; + int availableBits = 0; + int codingErrors = 0; + int x = 0; + // Log.w(TAG,"decode4b6b: untested code"); + // Log.w(TAG,String.format("Decoding %d bytes: %s",raw.length,ByteUtil.shortHexString(raw))); + for (int i = 0; i < raw.length; i++) { + int unsignedValue = raw[i]; + if (unsignedValue < 0) { + unsignedValue += 256; + } + x = (x << 8) + unsignedValue; + availableBits += 8; + if (availableBits >= 12) { + // take top six + int highcode = (x >> (availableBits - 6)) & 0x3F; + int highIndex = encode4b6bListIndex((byte) (highcode)); + // take bottom six + int lowcode = (x >> (availableBits - 12)) & 0x3F; + int lowIndex = encode4b6bListIndex((byte) (lowcode)); + // special case at end of transmission on uneven boundaries: + if ((highIndex >= 0) && (lowIndex >= 0)) { + byte decoded = (byte) ((highIndex << 4) + lowIndex); + rval = ByteUtil.concat(rval, decoded); + /* + * LOG.debug(String.format( + * "i=%d,x=0x%08X,0x%02X->0x%02X, 0x%02X->0x%02X, result: 0x%02X, %d bits remaining, errors %d, bytes remaining: %s" + * , + * i,x,highcode,highIndex, lowcode, + * lowIndex,decoded,availableBits,codingErrors,ByteUtil.shortHexString + * (ByteUtil.substring(raw,i+1,raw.length-i-1)))); + */ + } else { + // LOG.debug(String.format("i=%d,x=%08X, coding error: highcode=0x%02X, lowcode=0x%02X, %d bits remaining",i,x,highcode,lowcode,availableBits)); + errorMessageBuilder.append(String.format( + "decode4b6b: i=%d,x=%08X, coding error: highcode=0x%02X, lowcode=0x%02X, %d bits remaining.\n", + i, x, highcode, lowcode, availableBits)); + codingErrors++; + } + + availableBits -= 12; + x = x & (0x0000ffff >> (16 - availableBits)); + } + } + + if (availableBits != 0) { + if ((availableBits == 4) && (x == 0x05)) { + // normal end + } else { + // LOG.error("decode4b6b: failed clean decode -- extra bits available (not marker)(" + availableBits + + // ")"); + errorMessageBuilder.append("decode4b6b: failed clean decode -- extra bits available (not marker)(" + + availableBits + ")\n"); + codingErrors++; + } + } else { + // also normal end. + } + + if (codingErrors > 0) { + // LOG.error("decode4b6b: " + codingErrors + " coding errors encountered."); + errorMessageBuilder.append("decode4b6b: " + codingErrors + " coding errors encountered."); + writeError(LOG, raw, errorMessageBuilder.toString()); + throw new RileyLinkCommunicationException(RileyLinkBLEError.CodingErrors, errorMessageBuilder.toString()); + } + return rval; + } + + // public static RFTools.DecodeResponseDto decode4b6bWithoutException(byte[] raw) { + // /* + // * if ((raw.length % 2) != 0) { + // * LOG.error("Warning: data is odd number of bytes"); + // * } + // */ + // + // RFTools.DecodeResponseDto response = new RFTools.DecodeResponseDto(); + // + // StringBuilder errorMessageBuilder = new StringBuilder(); + // + // errorMessageBuilder.append("Input data: " + ByteUtil.getHex(raw) + "\n"); + // + // if ((raw.length % 2) != 0) { + // errorMessageBuilder.append("Warn: odd number of bytes."); + // } + // + // byte[] rval = new byte[] {}; + // int availableBits = 0; + // int codingErrors = 0; + // int x = 0; + // // Log.w(TAG,"decode4b6b: untested code"); + // // Log.w(TAG,String.format("Decoding %d bytes: %s",raw.length,ByteUtil.shortHexString(raw))); + // for (int i = 0; i < raw.length; i++) { + // int unsignedValue = raw[i]; + // if (unsignedValue < 0) { + // unsignedValue += 256; + // } + // x = (x << 8) + unsignedValue; + // availableBits += 8; + // if (availableBits >= 12) { + // // take top six + // int highcode = (x >> (availableBits - 6)) & 0x3F; + // int highIndex = encode4b6bListIndex((byte)(highcode)); + // // take bottom six + // int lowcode = (x >> (availableBits - 12)) & 0x3F; + // int lowIndex = encode4b6bListIndex((byte)(lowcode)); + // // special case at end of transmission on uneven boundaries: + // if ((highIndex >= 0) && (lowIndex >= 0)) { + // byte decoded = (byte)((highIndex << 4) + lowIndex); + // rval = ByteUtil.concat(rval, decoded); + // /* + // * LOG.debug(String.format( + // * + // "i=%d,x=0x%08X,0x%02X->0x%02X, 0x%02X->0x%02X, result: 0x%02X, %d bits remaining, errors %d, bytes remaining: %s" + // * , + // * i,x,highcode,highIndex, lowcode, + // * lowIndex,decoded,availableBits,codingErrors,ByteUtil.shortHexString + // * (ByteUtil.substring(raw,i+1,raw.length-i-1)))); + // */ + // } else { + // // + // LOG.debug(String.format("i=%d,x=%08X, coding error: highcode=0x%02X, lowcode=0x%02X, %d bits remaining",i,x,highcode,lowcode,availableBits)); + // errorMessageBuilder.append(String.format( + // "decode4b6b: i=%d,x=%08X, coding error: highcode=0x%02X, lowcode=0x%02X, %d bits remaining.\n", + // i, x, highcode, lowcode, availableBits)); + // codingErrors++; + // } + // + // availableBits -= 12; + // x = x & (0x0000ffff >> (16 - availableBits)); + // } else { + // // LOG.debug(String.format("i=%d, skip: x=0x%08X, available bits %d",i,x,availableBits)); + // } + // } + // + // if (availableBits != 0) { + // if ((availableBits == 4) && (x == 0x05)) { + // // normal end + // } else { + // LOG.error("decode4b6b: failed clean decode -- extra bits available (not marker)(" + availableBits + ")"); + // errorMessageBuilder.append("decode4b6b: failed clean decode -- extra bits available (not marker)(" + // + availableBits + ")\n"); + // codingErrors++; + // } + // } else { + // // also normal end. + // } + // + // if (codingErrors > 0) { + // LOG.error("decode4b6b: " + codingErrors + " coding errors encountered."); + // errorMessageBuilder.append("decode4b6b: " + codingErrors + " coding errors encountered."); + // + // response.errorData = errorMessageBuilder.toString(); + // } else { + // response.data = rval; + // } + // + // return response; + // } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/data/encoding/Encoding4b6bGo.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/data/encoding/Encoding4b6bGo.java new file mode 100644 index 0000000000..6609138011 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/data/encoding/Encoding4b6bGo.java @@ -0,0 +1,163 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data.encoding; + +import java.util.HashMap; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.RileyLinkCommunicationException; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.RileyLinkBLEError; +import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil; + +/** + * Created by andy on 11/24/18. + */ + +public class Encoding4b6bGo extends Encoding4b6bAbstract { + + public static final Logger LOG = LoggerFactory.getLogger(Encoding4b6bGo.class); + private static Map decodeGoMap; + + + public byte[] encode4b6b(byte[] src) { + // 2 input bytes produce 3 output bytes. + // Odd final input byte, if any, produces 2 output bytes. + int n = src.length; + byte[] dst = new byte[3 * (n / 2) + 2 * (n % 2)]; + int j = 0; + + for (int i = 0; i < n; i += 2, j = j + 3) { + short x = convertUnsigned(src[i]); + short a = encode4b6bList[hi(4, x)]; + short b = encode4b6bList[lo(4, x)]; + dst[j] = (byte)(a << 2 | hi(4, b)); + if (i + 1 < n) { + short y = convertUnsigned(src[i + 1]); + short c = encode4b6bList[hi(4, y)]; + short d = encode4b6bList[lo(4, y)]; + dst[j + 1] = (byte)(lo(4, b) << 4 | hi(6, c)); + dst[j + 2] = (byte)(lo(2, c) << 6 | d); + } else { + // Fill final nibble with 5 to match pump behavior. + dst[j + 1] = (byte)(lo(4, b) << 4 | 0x5); + } + + } + return dst; + } + + + /** + * Decode from Go code by ecc1. NOT WORKING + * + * @param src + * @return + */ + public byte[] decode4b6b(byte[] src) throws RileyLinkCommunicationException { + int n = src.length; + + if (decodeGoMap == null) + initDecodeGo(); + + StringBuilder errorMessageBuilder = new StringBuilder(); + + errorMessageBuilder.append("Input data: " + ByteUtil.getHex(src) + "\n"); + int codingErrors = 0; + + // Check for valid packet length. + if (n % 3 == 1) { + errorMessageBuilder.append("Invalid package length " + n); + codingErrors++; + // return nil, ErrDecoding + } + // 3 input bytes produce 2 output bytes. + // Final 2 input bytes, if any, produce 1 output byte. + byte[] dst = new byte[2 * (n / 3) + (n % 3) / 2]; + + int j = 0; + for (int i = 0; i < n; i = i + 3, j = j + 2) { + if (i + 1 >= n) { + errorMessageBuilder.append("Overflow in i (" + i + ")"); + } + short x = convertUnsigned(src[i]); + short y = convertUnsigned(src[i + 1]); + short a = decode6b_goMap(hi(6, x)); + short b = decode6b_goMap(lo(2, x) << 4 | hi(4, y)); + if (a == 0xFF || b == 0xFF) { + errorMessageBuilder.append("Error decoding "); + codingErrors++; + } + dst[j] = (byte)(a << 4 | b); + if (i + 2 < n) { + short z = convertUnsigned(src[i + 2]); + short c = decode6b_goMap(lo(4, y) << 2 | hi(2, z)); + short d = decode6b_goMap(lo(6, z)); + if (c == 0xFF || d == 0xFF) { + errorMessageBuilder.append("Error decoding "); + codingErrors++; + } + dst[j + 1] = (byte)(c << 4 | d); + } + } + + if (codingErrors > 0) { + errorMessageBuilder.append("decode4b6b: " + codingErrors + " coding errors encountered."); + writeError(LOG, dst, errorMessageBuilder.toString()); + throw new RileyLinkCommunicationException(RileyLinkBLEError.CodingErrors, errorMessageBuilder.toString()); + } + + return dst; + } + + + static short hi(int n, short x) { + // x = convertUnsigned(x); + return (short)(x >> (8 - n)); + } + + + static short lo(int n, short x) { + // byte b = (byte)x; + return (short)(x & ((1 << n) - 1)); + } + + + public static void initDecodeGo() { + + decodeGoMap = new HashMap<>(); + + putToMap(0x0B, 0x0B); + putToMap(0x0D, 0x0D); + putToMap(0x0E, 0x0E); + putToMap(0x15, 0x00); + putToMap(0x16, 0x07); + putToMap(0x19, 0x09); + putToMap(0x1A, 0x08); + putToMap(0x1C, 0x0F); + putToMap(0x23, 0x03); + putToMap(0x25, 0x05); + putToMap(0x26, 0x06); + putToMap(0x2A, 0x0A); + putToMap(0x2C, 0x0C); + putToMap(0x31, 0x01); + putToMap(0x32, 0x02); + putToMap(0x34, 0x04); + + } + + + private static short decode6b_goMap(int value) { + short val = (short)value; + if (decodeGoMap.containsKey(val)) + return decodeGoMap.get(val); + else + return (short)0xff; + } + + + private static void putToMap(int val1, int val2) { + decodeGoMap.put((short)val1, (short)val2); + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/data/encoding/Encoding4b6bLoop.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/data/encoding/Encoding4b6bLoop.java new file mode 100644 index 0000000000..3e551b6ccf --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/data/encoding/Encoding4b6bLoop.java @@ -0,0 +1,136 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data.encoding; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.RileyLinkCommunicationException; +import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil; + +/** + * Created by andy on 11/24/18. + */ + +public class Encoding4b6bLoop extends Encoding4b6bAbstract { + + public static final Logger LOG = LoggerFactory.getLogger(Encoding4b6bLoop.class); + public Map codesRev = null; + + + public Encoding4b6bLoop() { + createCodeRev(); + } + + + /** + * This method is almost 1:1 with same method from Loop, only change is unsigning of element and |05 added for + * last byte. It should work better than original one, which is really different than this one. + * + * @param data + * @return + */ + public byte[] encode4b6b(byte[] data) { + + List buffer = new ArrayList(); + int bitAccumulator = 0x0; + int bitcount = 0; + + for (byte element : data) { + + short element2 = element; + + if (element2 < 0) { + element2 += 256; + } + + bitAccumulator <<= 6; + bitAccumulator |= encode4b6bList[element2 >> 4]; + bitcount += 6; + + bitAccumulator <<= 6; + bitAccumulator |= encode4b6bList[element2 & 0x0f]; + bitcount += 6; + + while (bitcount >= 8) { + buffer.add((byte)((bitAccumulator >> (bitcount - 8)) & 0xff)); + bitcount -= 8; + bitAccumulator &= (0xffff >> (16 - bitcount)); + } + } + + if (bitcount > 0) { + bitAccumulator <<= (8 - bitcount); + buffer.add((byte)((bitAccumulator | 0x5) & 0xff)); + } + + return ByteUtil.getByteArrayFromList(buffer); + } + + + /** + * DOESN'T WORK YET + * + * @param data + * @return + * @throws RileyLinkCommunicationException + */ + public byte[] decode4b6b(byte[] data) throws RileyLinkCommunicationException { + List buffer = new ArrayList(); + int availBits = 0; + int bitAccumulator = 0; + + for (byte element2 : data) { + + short element = convertUnsigned(element2); + + // if (element < 0) { + // element += 255; + // } + + if (element == 0) { + break; + } + + bitAccumulator = (bitAccumulator << 8) + element; + availBits += 8; + + if (availBits >= 12) { + + int hiNibble; + int loNibble; + + try { + int index = (bitAccumulator >> (availBits - 6)); + int index2 = ((bitAccumulator >> (availBits - 12)) & 0b111111); + hiNibble = codesRev.get((bitAccumulator >> (availBits - 6))); + loNibble = codesRev.get(((bitAccumulator >> (availBits - 12)) & 0b111111)); + } catch (Exception ex) { + System.out.println("Exception: " + ex.getMessage()); + ex.printStackTrace(); + return null; + } + + int decoded = ((hiNibble << 4) + loNibble); + buffer.add((byte)decoded); + availBits -= 12; + bitAccumulator = bitAccumulator & (0xffff >> (16 - availBits)); + } + } + + return ByteUtil.getByteArrayFromList(buffer); + } + + + private void createCodeRev() { + codesRev = new HashMap<>(); + + for (int i = 0; i < encode4b6bList.length; i++) { + codesRev.put(i, encode4b6bList[i]); + } + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/defs/CC111XRegister.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/defs/CC111XRegister.java new file mode 100644 index 0000000000..02694979d9 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/defs/CC111XRegister.java @@ -0,0 +1,48 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs; + +/** + * Created by andy on 21/05/2018. + */ + +public enum CC111XRegister { + + sync1(0x00), // + sync0(0x01), // + pktlen(0x02), // + pktctrl1(0x03), // + pktctrl0(0x04), // + fsctrl1(0x07), // + freq2(0x09), // + freq1(0x0a), // + freq0(0x0b), // + mdmcfg4(0x0c), // + mdmcfg3(0x0d), // + mdmcfg2(0x0e), // + mdmcfg1(0x0f), // + mdmcfg0(0x10), // + deviatn(0x11), // + mcsm0(0x14), // + foccfg(0x15), // + agcctrl2(0x17), // + agcctrl1(0x18), // + agcctrl0(0x19), // + frend1(0x1a), // + frend0(0x1b), // + fscal3(0x1c), // + fscal2(0x1d), // + fscal1(0x1e), // + fscal0(0x1f), // + test1(0x24), // + test0(0x25), // + paTable0(0x2e), // + + ; + + public byte value; + + + CC111XRegister(int value) { + this.value = (byte)value; + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/defs/RFSpyCommand.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/defs/RFSpyCommand.java new file mode 100644 index 0000000000..b2bbe81e9a --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/defs/RFSpyCommand.java @@ -0,0 +1,42 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs; + +/** + * Created by andy on 22/05/2018. + */ + +public enum RFSpyCommand { + + GetState(1), // + GetVersion(2, false), // + GetPacket(3), // aka Listen, receive + Send(4), // + SendAndListen(5), // + UpdateRegister(6), // + Reset(7), // + + ; + + public byte code; + private boolean encoded = true; + + + RFSpyCommand(int code) { + this.code = (byte)code; + } + + + RFSpyCommand(int code, boolean encoded) { + this.code = (byte)code; + this.encoded = encoded; + } + + + public boolean isEncoded() { + return encoded; + } + + + public void setEncoded(boolean encoded) { + this.encoded = encoded; + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/defs/RFSpyRLResponse.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/defs/RFSpyRLResponse.java new file mode 100644 index 0000000000..d1435d470d --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/defs/RFSpyRLResponse.java @@ -0,0 +1,37 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs; + +public enum RFSpyRLResponse { + // 0xaa == timeout + // 0xbb == interrupted + // 0xcc == zero-data + // 0xdd == success + // 0x11 == invalidParam + // 0x22 == unknownCommand + + Invalid(0), // default, just fail + Timeout(0xAA), + Interrupted(0xBB), + ZeroData(0xCC), + Success(0xDD), + OldSuccess(0x01), + InvalidParam(0x11), + UnknownCommand(0x22), ; + + byte value; + + + RFSpyRLResponse(int value) { + this.value = (byte)value; + } + + + public static RFSpyRLResponse fromByte(byte input) { + for (RFSpyRLResponse type : values()) { + if (type.value == input) { + return type; + } + } + return null; + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/defs/RLMessageType.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/defs/RLMessageType.java new file mode 100644 index 0000000000..a55fa1db19 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/defs/RLMessageType.java @@ -0,0 +1,11 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs; + +/** + * Created by andy on 5/6/18. + */ + +public enum RLMessageType { + PowerOn, // for powering on the pump (wakeup) + ReadSimpleData, // for checking if pump is readable (for Medtronic we can use GetModel) + ; +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/defs/RXFilterMode.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/defs/RXFilterMode.java new file mode 100644 index 0000000000..e50929ebfe --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/defs/RXFilterMode.java @@ -0,0 +1,19 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs; + +/** + * Created by andy on 21/05/2018. + */ + +public enum RXFilterMode { + + Wide(0x50), // + Narrow(0x90) // + ; + + public byte value; + + + RXFilterMode(int value) { + this.value = (byte)value; + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/defs/RileyLinkBLEError.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/defs/RileyLinkBLEError.java new file mode 100644 index 0000000000..8d32445718 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/defs/RileyLinkBLEError.java @@ -0,0 +1,25 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs; + +/** + * Created by andy on 11/24/18. + */ + +public enum RileyLinkBLEError { + CodingErrors("Coding Errors encountered during decode of RileyLink packet."), // + Timeout("Timeout"), // + Interrupted("Interrupted"), + TooShortOrNullResponse("Too short or null decoded response."); + + private String description; + + + RileyLinkBLEError(String description) { + + this.description = description; + } + + + public String getDescription() { + return description; + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/defs/RileyLinkCommandType.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/defs/RileyLinkCommandType.java new file mode 100644 index 0000000000..e241f0a83a --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/defs/RileyLinkCommandType.java @@ -0,0 +1,30 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs; + +/** + * Created by andy on 22/05/2018. + */ + +public enum RileyLinkCommandType { + + GetState(1), // + GetVersion(2), // + GetPacket(3), // aka Listen, receive + Send(4), // + SendAndListen(5), // + UpdateRegister(6), // + Reset(7), // + Led(8), + ReadRegister(9), + SetModeRegisters(10), + SetHardwareEncoding(11), + SetPreamble(12), + ResetRadioConfig(13), + GetStatistics(14), ; + + public byte code; + + + RileyLinkCommandType(int code) { + this.code = (byte)code; + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/defs/RileyLinkEncodingType.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/defs/RileyLinkEncodingType.java new file mode 100644 index 0000000000..5482e224e3 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/defs/RileyLinkEncodingType.java @@ -0,0 +1,52 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs; + +import java.util.HashMap; +import java.util.Map; + +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.MainApp; + +public enum RileyLinkEncodingType { + + None(0x00, null), // No encoding on RL + Manchester(0x01, null), // Manchester encoding on RL (for Omnipod) + FourByteSixByteRileyLink(0x02, R.string.key_medtronic_pump_encoding_4b6b_rileylink), // 4b6b encoding on RL (for Medtronic) + FourByteSixByteLocal(0x00, R.string.key_medtronic_pump_encoding_4b6b_local), // No encoding on RL, but 4b6b encoding in code + ; + + public byte value; + public Integer resourceId; + public String description; + + private static Map encodingTypeMap; + + static { + encodingTypeMap = new HashMap<>(); + + for (RileyLinkEncodingType encType : values()) { + if (encType.resourceId!=null) { + encodingTypeMap.put(MainApp.gs(encType.resourceId), encType); + } + } + } + + + RileyLinkEncodingType(int value) { + this.value = (byte)value; + } + + + RileyLinkEncodingType(int value, Integer resourceId) { + this.value = (byte)value; + this.resourceId = resourceId; + } + + public static RileyLinkEncodingType getByDescription(String description) { + if (encodingTypeMap.containsKey(description)) { + return encodingTypeMap.get(description); + } + + return RileyLinkEncodingType.FourByteSixByteLocal; + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/defs/RileyLinkFirmwareVersion.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/defs/RileyLinkFirmwareVersion.java new file mode 100644 index 0000000000..b6d0eab3cd --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/defs/RileyLinkFirmwareVersion.java @@ -0,0 +1,104 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs; + +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public enum RileyLinkFirmwareVersion { + + Version_0_0(0, 0, "0.0"), // just for defaulting + Version_0_9(0, 9, "0.9"), // + Version_1_0(1, 0, "1.0"), // + Version_2_0(2, 0, "2.0"), // + Version_2_2(2, 2, "2.2"), // + Version_3_0(3, 0, "3.0"), // + UnknownVersion(0, 0, "???"), // + Version1(Version_0_0, Version_0_9, Version_1_0), // + Version2(Version_2_0, Version_2_2), // + Version2AndHigher(Version_2_0, Version_2_2, Version_3_0), // + ; + + private static final String FIRMWARE_IDENTIFICATION_PREFIX = "subg_rfspy "; + private static final Pattern _version_pattern = Pattern.compile(FIRMWARE_IDENTIFICATION_PREFIX + + "([0-9]+)\\.([0-9]+)"); + static Map mapByVersion; + + static { + mapByVersion = new HashMap<>(); + for (RileyLinkFirmwareVersion version : values()) { + if (version.familyMembers == null) { + mapByVersion.put(version.versionKey, version); + } + } + } + + protected RileyLinkFirmwareVersion[] familyMembers; + private int major; + private int minor; + private String versionKey = ""; + + + RileyLinkFirmwareVersion(int major, int minor, String versionKey) { + this.major = major; + this.minor = minor; + this.versionKey = versionKey; + } + + + RileyLinkFirmwareVersion(RileyLinkFirmwareVersion... familyMembers) { + this.familyMembers = familyMembers; + } + + + public static boolean isSameVersion(RileyLinkFirmwareVersion versionWeCheck, RileyLinkFirmwareVersion versionSources) { + if (versionSources.familyMembers != null) { + for (RileyLinkFirmwareVersion vrs : versionSources.familyMembers) { + if (vrs == versionWeCheck) + return true; + } + } else { + return (versionWeCheck == versionSources); + } + return false; + } + + + public static RileyLinkFirmwareVersion getByVersionString(String versionString) { + if (versionString != null) { + Matcher m = _version_pattern.matcher(versionString); + if (m.find()) { + int major = Integer.parseInt(m.group(1)); + int minor = Integer.parseInt(m.group(2)); + String versionKey = major + "." + minor; + if (mapByVersion.containsKey(versionKey)) { + return mapByVersion.get(versionKey); + } else { + return defaultToLowestMajorVersion(major); // just in case there is new release that we don't cover + // example: 2.3 etc + } + } + } + + return RileyLinkFirmwareVersion.UnknownVersion; + } + + + private static RileyLinkFirmwareVersion defaultToLowestMajorVersion(int major) { + if (mapByVersion.containsKey(major + ".0")) { + return mapByVersion.get(major + ".0"); + } + return UnknownVersion; + } + + + public boolean isSameVersion(RileyLinkFirmwareVersion versionSources) { + return isSameVersion(this, versionSources); + } + + + @Override + public String toString() { + return FIRMWARE_IDENTIFICATION_PREFIX + versionKey; + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/defs/RileyLinkTargetFrequency.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/defs/RileyLinkTargetFrequency.java new file mode 100644 index 0000000000..0d7f341584 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/defs/RileyLinkTargetFrequency.java @@ -0,0 +1,25 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs; + +/** + * Created by andy on 6/7/18. + */ + +public enum RileyLinkTargetFrequency { + + Medtronic_WorldWide(868.25, 868.3, 868.35, 868.4, 868.45, 868.5, 868.55, 868.6, 868.65), // + Medtronic_US(916.45, 916.5, 916.55, 916.6, 916.65, 916.7, 916.75, 916.8), // + Omnipod(433.91), // + ; + + double[] frequencies; + + + RileyLinkTargetFrequency(double... frequencies) { + this.frequencies = frequencies; + } + + + public double[] getScanFrequencies() { + return frequencies; + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/operations/BLECommOperation.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/operations/BLECommOperation.java new file mode 100644 index 0000000000..81fe52f890 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/operations/BLECommOperation.java @@ -0,0 +1,38 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.operations; + +import java.util.UUID; +import java.util.concurrent.Semaphore; + +import android.bluetooth.BluetoothGatt; + +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.RileyLinkBLE; + +/** + * Created by geoff on 5/26/16. + */ +public abstract class BLECommOperation { + + public boolean timedOut = false; + public boolean interrupted = false; + protected byte[] value; + protected BluetoothGatt gatt; + protected Semaphore operationComplete = new Semaphore(0, true); + + + // This is to be run on the main thread + public abstract void execute(RileyLinkBLE comm); + + + public void gattOperationCompletionCallback(UUID uuid, byte[] value) { + } + + + public int getGattOperationTimeout_ms() { + return 22000; + } + + + public byte[] getValue() { + return value; + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/operations/BLECommOperationResult.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/operations/BLECommOperationResult.java new file mode 100644 index 0000000000..06f1e4e94e --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/operations/BLECommOperationResult.java @@ -0,0 +1,16 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.operations; + +/** + * Created by geoff on 5/26/16. + */ +public class BLECommOperationResult { + + public static final int RESULT_NONE = 0; + public static final int RESULT_SUCCESS = 1; + public static final int RESULT_TIMEOUT = 2; + public static final int RESULT_BUSY = 3; + public static final int RESULT_INTERRUPTED = 4; + public static final int RESULT_NOT_CONFIGURED = 5; + public byte[] value; + public int resultCode; +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/operations/CharacteristicReadOperation.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/operations/CharacteristicReadOperation.java new file mode 100644 index 0000000000..6c12a8ef77 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/operations/CharacteristicReadOperation.java @@ -0,0 +1,71 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.operations; + +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import android.bluetooth.BluetoothGatt; +import android.bluetooth.BluetoothGattCharacteristic; +import android.os.SystemClock; + +import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.RileyLinkBLE; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data.GattAttributes; + +/** + * Created by geoff on 5/26/16. + */ +public class CharacteristicReadOperation extends BLECommOperation { + + private static final Logger LOG = LoggerFactory.getLogger(L.PUMPBTCOMM); + + private BluetoothGattCharacteristic characteristic; + + + public CharacteristicReadOperation(BluetoothGatt gatt, BluetoothGattCharacteristic chara) { + this.gatt = gatt; + this.characteristic = chara; + } + + + @Override + public void execute(RileyLinkBLE comm) { + gatt.readCharacteristic(characteristic); + // wait here for callback to notify us that value was read. + try { + boolean didAcquire = operationComplete.tryAcquire(getGattOperationTimeout_ms(), TimeUnit.MILLISECONDS); + if (didAcquire) { + SystemClock.sleep(1); // This is to allow the IBinder thread to exit before we continue, allowing easier + // understanding of the sequence of events. + // success + } else { + LOG.error("Timeout waiting for gatt write operation to complete"); + timedOut = true; + } + } catch (InterruptedException e) { + if (isLogEnabled()) + LOG.error("Interrupted while waiting for gatt write operation to complete"); + interrupted = true; + } + value = characteristic.getValue(); + } + + + @Override + public void gattOperationCompletionCallback(UUID uuid, byte[] value) { + super.gattOperationCompletionCallback(uuid, value); + if (!characteristic.getUuid().equals(uuid)) { + LOG.error(String.format( + "Completion callback: UUID does not match! out of sequence? Found: %s, should be %s", + GattAttributes.lookup(characteristic.getUuid()), GattAttributes.lookup(uuid))); + } + operationComplete.release(); + } + + private boolean isLogEnabled() { + return L.isEnabled(L.PUMPBTCOMM); + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/operations/CharacteristicWriteOperation.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/operations/CharacteristicWriteOperation.java new file mode 100644 index 0000000000..488b03af27 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/operations/CharacteristicWriteOperation.java @@ -0,0 +1,74 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.operations; + +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import android.bluetooth.BluetoothGatt; +import android.bluetooth.BluetoothGattCharacteristic; +import android.os.SystemClock; + +import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.RileyLinkBLE; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data.GattAttributes; + +/** + * Created by geoff on 5/26/16. + */ +public class CharacteristicWriteOperation extends BLECommOperation { + + private static final Logger LOG = LoggerFactory.getLogger(L.PUMPBTCOMM); + + private BluetoothGattCharacteristic characteristic; + + + public CharacteristicWriteOperation(BluetoothGatt gatt, BluetoothGattCharacteristic chara, byte[] value) { + this.gatt = gatt; + this.characteristic = chara; + this.value = value; + } + + + @Override + public void execute(RileyLinkBLE comm) { + + characteristic.setValue(value); + gatt.writeCharacteristic(characteristic); + // wait here for callback to notify us that value was written. + try { + boolean didAcquire = operationComplete.tryAcquire(getGattOperationTimeout_ms(), TimeUnit.MILLISECONDS); + if (didAcquire) { + SystemClock.sleep(1); // This is to allow the IBinder thread to exit before we continue, allowing easier + // understanding of the sequence of events. + // success + } else { + LOG.error("Timeout waiting for gatt write operation to complete"); + timedOut = true; + } + } catch (InterruptedException e) { + LOG.error("Interrupted while waiting for gatt write operation to complete"); + interrupted = true; + } + + } + + + // This will be run on the IBinder thread + @Override + public void gattOperationCompletionCallback(UUID uuid, byte[] value) { + if (!characteristic.getUuid().equals(uuid)) { + LOG.error(String.format( + "Completion callback: UUID does not match! out of sequence? Found: %s, should be %s", + GattAttributes.lookup(characteristic.getUuid()), GattAttributes.lookup(uuid))); + } + operationComplete.release(); + } + + + private boolean isLogEnabled() { + return L.isEnabled(L.PUMPBTCOMM); + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/operations/DescriptorWriteOperation.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/operations/DescriptorWriteOperation.java new file mode 100644 index 0000000000..9e631c1c7c --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/ble/operations/DescriptorWriteOperation.java @@ -0,0 +1,59 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.operations; + +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import android.bluetooth.BluetoothGatt; +import android.bluetooth.BluetoothGattDescriptor; +import android.os.SystemClock; + +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.RileyLinkBLE; + +/** + * Created by geoff on 5/26/16. + */ +public class DescriptorWriteOperation extends BLECommOperation { + + private static final Logger LOG = LoggerFactory.getLogger(DescriptorWriteOperation.class); + + private BluetoothGattDescriptor descr; + + + public DescriptorWriteOperation(BluetoothGatt gatt, BluetoothGattDescriptor descr, byte[] value) { + this.gatt = gatt; + this.descr = descr; + this.value = value; + } + + + @Override + public void gattOperationCompletionCallback(UUID uuid, byte[] value) { + super.gattOperationCompletionCallback(uuid, value); + operationComplete.release(); + } + + + @Override + public void execute(RileyLinkBLE comm) { + descr.setValue(value); + gatt.writeDescriptor(descr); + // wait here for callback to notify us that value was read. + try { + boolean didAcquire = operationComplete.tryAcquire(getGattOperationTimeout_ms(), TimeUnit.MILLISECONDS); + if (didAcquire) { + SystemClock.sleep(1); // This is to allow the IBinder thread to exit before we continue, allowing easier + // understanding of the sequence of events. + // success + } else { + LOG.error("Timeout waiting for descriptor write operation to complete"); + timedOut = true; + } + } catch (InterruptedException e) { + LOG.error("Interrupted while waiting for descriptor write operation to complete"); + interrupted = true; + } + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/data/BleAdvertisedData.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/data/BleAdvertisedData.java new file mode 100644 index 0000000000..8a2bbdfec7 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/data/BleAdvertisedData.java @@ -0,0 +1,35 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.data; + +import java.util.List; +import java.util.UUID; + +/** + * Created by andy on 9/10/18. + */ + +public class BleAdvertisedData { + + private List mUuids; + private String mName; + + + public BleAdvertisedData(List uuids, String name) { + mUuids = uuids; + mName = name; + } + + + public List getUuids() { + return mUuids; + } + + + public String getName() { + return mName; + } + + + public String toString() { + return "BleAdvertisedData [name=" + mName + ", UUIDs=" + mUuids + "]"; + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/data/CommandValueDefinition.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/data/CommandValueDefinition.java new file mode 100644 index 0000000000..fceb0609aa --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/data/CommandValueDefinition.java @@ -0,0 +1,14 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.data; + +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.CommandValueDefinitionType; + +/** + * Created by andy on 4/5/19. + */ + +public class CommandValueDefinition { + + public CommandValueDefinitionType definitionType; + public String value; + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/data/RLHistoryItem.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/data/RLHistoryItem.java new file mode 100644 index 0000000000..a887fede5c --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/data/RLHistoryItem.java @@ -0,0 +1,115 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.data; + +import org.joda.time.LocalDateTime; + +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkError; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkServiceState; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkTargetDevice; +import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicCommandType; +import info.nightscout.androidaps.plugins.pump.medtronic.defs.PumpDeviceState; + +/** + * Created by andy on 5/19/18. + */ + +public class RLHistoryItem { + + private MedtronicCommandType medtronicCommandType; + private LocalDateTime dateTime; + private RLHistoryItemSource source; + private RileyLinkServiceState serviceState; + private RileyLinkError errorCode; + + private RileyLinkTargetDevice targetDevice; + private PumpDeviceState pumpDeviceState; + + + public RLHistoryItem(RileyLinkServiceState serviceState, RileyLinkError errorCode, + RileyLinkTargetDevice targetDevice) { + this.targetDevice = targetDevice; + this.dateTime = new LocalDateTime(); + this.serviceState = serviceState; + this.errorCode = errorCode; + this.source = RLHistoryItemSource.RileyLink; + } + + + public RLHistoryItem(PumpDeviceState pumpDeviceState, RileyLinkTargetDevice targetDevice) { + this.pumpDeviceState = pumpDeviceState; + this.dateTime = new LocalDateTime(); + this.targetDevice = targetDevice; + this.source = RLHistoryItemSource.MedtronicPump; + } + + + public RLHistoryItem(MedtronicCommandType medtronicCommandType) { + this.dateTime = new LocalDateTime(); + this.medtronicCommandType = medtronicCommandType; + source = RLHistoryItemSource.MedtronicCommand; + } + + + public LocalDateTime getDateTime() { + return dateTime; + } + + + public RileyLinkServiceState getServiceState() { + return serviceState; + } + + + public RileyLinkError getErrorCode() { + return errorCode; + } + + + public String getDescription() { + + // TODO extend when we have Omnipod + switch (this.source) { + case RileyLink: + return "State: " + MainApp.gs(serviceState.getResourceId(targetDevice)) + + (this.errorCode == null ? "" : ", Error Code: " + errorCode); + + case MedtronicPump: + return MainApp.gs(pumpDeviceState.getResourceId()); + + case MedtronicCommand: + return medtronicCommandType.name(); + + default: + return "Unknown Description"; + } + } + + + public RLHistoryItemSource getSource() { + return source; + } + + + public PumpDeviceState getPumpDeviceState() { + return pumpDeviceState; + } + + public enum RLHistoryItemSource { + RileyLink("RileyLink"), // + MedtronicPump("Medtronic"), // + MedtronicCommand("Medtronic"); + + private String desc; + + + RLHistoryItemSource(String desc) { + this.desc = desc; + } + + + public String getDesc() { + return desc; + } + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/defs/CommandValueDefinitionRLType.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/defs/CommandValueDefinitionRLType.java new file mode 100644 index 0000000000..5be3087b49 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/defs/CommandValueDefinitionRLType.java @@ -0,0 +1,32 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs; + +/** + * Created by andy on 4/5/19. + */ + +public enum CommandValueDefinitionRLType implements CommandValueDefinitionType { + Name, // + Firmware, // + SignalStrength, // + ConnectionState, // + Frequency, // + ; + + @Override + public String getName() { + return this.name(); + } + + + @Override + public String getDescription() { + return null; + } + + + @Override + public String commandAction() { + return null; + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/defs/CommandValueDefinitionType.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/defs/CommandValueDefinitionType.java new file mode 100644 index 0000000000..db5c5b2b91 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/defs/CommandValueDefinitionType.java @@ -0,0 +1,17 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs; + +/** + * Created by andy on 4/5/19. + */ + +public interface CommandValueDefinitionType { + + String getName(); + + + String getDescription(); + + + String commandAction(); + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/defs/RileyLinkError.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/defs/RileyLinkError.java new file mode 100644 index 0000000000..07867a781a --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/defs/RileyLinkError.java @@ -0,0 +1,52 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs; + +import info.nightscout.androidaps.R; + +/** + * Created by andy on 14/05/2018. + */ + +public enum RileyLinkError { + + // Configuration + + // BT + NoBluetoothAdapter(R.string.rileylink_error_no_bt_adapter), // + BluetoothDisabled(R.string.rileylink_error_bt_disabled), // + + // RileyLink + RileyLinkUnreachable(R.string.rileylink_error_unreachable), // + DeviceIsNotRileyLink(R.string.rileylink_error_not_rl), // + + // Device + TuneUpOfDeviceFailed(R.string.rileylink_error_tuneup_failed), // + NoContactWithDevice(R.string.rileylink_error_pump_unreachable, R.string.rileylink_error_pod_unreachable), // + ; + + int resourceId; + Integer resourceIdPod; + + + RileyLinkError(int resourceId) { + this.resourceId = resourceId; + } + + + RileyLinkError(int resourceId, int resourceIdPod) { + this.resourceId = resourceId; + this.resourceIdPod = resourceIdPod; + } + + + public int getResourceId(RileyLinkTargetDevice targetDevice) { + if (this.resourceIdPod != null) { + + return targetDevice == RileyLinkTargetDevice.MedtronicPump ? // + this.resourceId + : this.resourceIdPod; + } else { + return this.resourceId; + } + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/defs/RileyLinkServiceState.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/defs/RileyLinkServiceState.java new file mode 100644 index 0000000000..2ae66b53c9 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/defs/RileyLinkServiceState.java @@ -0,0 +1,95 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs; + +import info.nightscout.androidaps.R; + +/** + * Created by andy on 14/05/2018. + */ + +public enum RileyLinkServiceState { + + NotStarted(R.string.rileylink_state_not_started), // + + // Bluetooth + BluetoothInitializing(R.string.rileylink_state_bt_init), // (S) init BT (if error no BT interface -> Disabled, BT + // not enabled -> BluetoothError) + // BluetoothNotAvailable, // (E) BT not available, would happen only if device has no BT + BluetoothError(R.string.rileylink_state_bt_error), // (E) if BT gets disabled ( -> EnableBluetooth) + BluetoothReady(R.string.rileylink_state_bt_ready), // (OK) + + // RileyLink + RileyLinkInitializing(R.string.rileylink_state_rl_init), // (S) start Gatt discovery (OK -> RileyLinkReady, Error -> + // BluetoothEnabled) ?? + RileyLinkError(R.string.rileylink_state_rl_error), // (E) + RileyLinkReady(R.string.rileylink_state_connected), // (OK) if tunning was already done we go to PumpConnectorReady + + // Tunning + TuneUpDevice(R.string.rileylink_state_pc_tune_up), // (S) + PumpConnectorError(R.string.rileylink_state_pc_error), // either TuneUp Error or pump couldn't not be contacted + // error + PumpConnectorReady(R.string.rileylink_state_connected), // (OK) RileyLink Ready for Pump Communication + + // Initializing, // get all parameters required for connection (if not possible -> Disabled, if sucessful -> + // EnableBluetooth) + + // EnableBlueTooth, // enable BT (if error no BT interface -> Disabled, BT not enabled -> BluetoothError) + // BlueToothEnabled, // -> InitializeRileyLink + // RileyLinkInitialized, // + + // RileyLinkConnected, // -> TuneUpPump (on 1st), else PumpConnectorReady + + // PumpConnected, // + + ; + + int resourceId; + Integer resourceIdPod; + + + RileyLinkServiceState(int resourceId) { + this.resourceId = resourceId; + } + + + RileyLinkServiceState(int resourceId, int resourceIdPod) { + this.resourceId = resourceId; + this.resourceIdPod = resourceIdPod; + } + + + public static boolean isReady(RileyLinkServiceState serviceState) { + return (/* serviceState == RileyLinkReady || */serviceState == PumpConnectorReady); + } + + + public int getResourceId(RileyLinkTargetDevice targetDevice) { + if (this.resourceIdPod != null) { + + return (targetDevice == null || targetDevice == RileyLinkTargetDevice.MedtronicPump) ? // + this.resourceId + : this.resourceIdPod; + } else { + return this.resourceId; + } + } + + + public boolean isConnecting() { + + return (this == RileyLinkServiceState.BluetoothInitializing || // + // this == RileyLinkServiceState.BluetoothError || // + this == RileyLinkServiceState.BluetoothReady || // + this == RileyLinkServiceState.RileyLinkInitializing || // + this == RileyLinkReady + // this == RileyLinkServiceState.RileyLinkBLEError + ); + } + + + public boolean isError() { + + return (this == RileyLinkServiceState.BluetoothError || // + // this == RileyLinkServiceState.PumpConnectorError || // + this == RileyLinkServiceState.RileyLinkError); + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/defs/RileyLinkTargetDevice.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/defs/RileyLinkTargetDevice.java new file mode 100644 index 0000000000..2d0dea69f7 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/defs/RileyLinkTargetDevice.java @@ -0,0 +1,32 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs; + +import info.nightscout.androidaps.R; + +/** + * Created by andy on 5/19/18. + */ + +public enum RileyLinkTargetDevice { + MedtronicPump(R.string.rileylink_target_device_medtronic, true), // + Omnipod(R.string.rileylink_target_device_omnipod, false), // + ; + + private int resourceId; + private boolean tuneUpEnabled; + + + RileyLinkTargetDevice(int resourceId, boolean tuneUpEnabled) { + this.resourceId = resourceId; + this.tuneUpEnabled = tuneUpEnabled; + } + + + public boolean isTuneUpEnabled() { + return tuneUpEnabled; + } + + + public int getResourceId() { + return resourceId; + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/dialog/RileyLinkStatusActivity.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/dialog/RileyLinkStatusActivity.java new file mode 100644 index 0000000000..37a6656a6e --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/dialog/RileyLinkStatusActivity.java @@ -0,0 +1,161 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.dialog; + +import android.os.Bundle; +import android.widget.TextView; + +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentPagerAdapter; +import androidx.viewpager.widget.ViewPager; + +import com.google.android.material.floatingactionbutton.FloatingActionButton; +import com.google.android.material.tabs.TabLayout; + +import java.util.ArrayList; +import java.util.List; + +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.activities.NoSplashAppCompatActivity; +import info.nightscout.androidaps.plugins.pump.common.dialog.RefreshableInterface; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkUtil; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.RileyLinkServiceData; + +public class RileyLinkStatusActivity extends NoSplashAppCompatActivity { + + TextView connectionStatus; + TextView configuredAddress; + TextView connectedDevice; + TextView connectionError; + RileyLinkServiceData rileyLinkServiceData; + + private SectionsPagerAdapter mSectionsPagerAdapter; + private FloatingActionButton floatingActionButton; + private TabLayout tabLayout; + /** + * The {@link ViewPager} that will host the section contents. + */ + private ViewPager mViewPager; + + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.rileylink_status); + + // Create the adapter that will return a fragment for each of the three + // primary sections of the activity. + + // Set up the ViewPager with the sections adapter. + mViewPager = (ViewPager) findViewById(R.id.rileylink_settings_container); + // mViewPager.setAdapter(mSectionsPagerAdapter); + setupViewPager(mViewPager); + + tabLayout = (TabLayout) findViewById(R.id.rileylink_settings_tabs); + tabLayout.setupWithViewPager(mViewPager); + + floatingActionButton = (FloatingActionButton) findViewById(R.id.rileylink_settings_fab); + floatingActionButton.setOnClickListener(v -> { + + RefreshableInterface selectableInterface = (RefreshableInterface) mSectionsPagerAdapter + .getItem(tabLayout.getSelectedTabPosition()); + selectableInterface.refreshData(); + + // refreshData(tabLayout.getSelectedTabPosition()); + + // Toast.makeText(getApplicationContext(), "Test pos: " + tabLayout.getSelectedTabPosition(), + // Toast.LENGTH_LONG); + }); + + this.connectionStatus = findViewById(R.id.rls_t1_connection_status); + this.configuredAddress = findViewById(R.id.rls_t1_configured_address); + this.connectedDevice = findViewById(R.id.rls_t1_connected_device); + this.connectionError = findViewById(R.id.rls_t1_connection_error); + + rileyLinkServiceData = RileyLinkUtil.getRileyLinkServiceData(); + + // // 7-12 + // int[] ids = {R.id.rls_t1_tv02, R.id.rls_t1_tv03, R.id.rls_t1_tv04, R.id.rls_t1_tv05, R.id.rls_t1_tv07, // + // R.id.rls_t1_tv08, R.id.rls_t1_tv09, R.id.rls_t1_tv10, R.id.rls_t1_tv11, R.id.rls_t1_tv12}; + // + // for (int id : ids) { + // + // TextView tv = (TextView) findViewById(id); + // tv.setText(tv.getText() + ":"); + // } + + // refreshData(0); + // refreshData(1); + + } + + + public void refreshData(int position) { + if (position == 0) { + // FIXME i18n + this.connectionStatus.setText(rileyLinkServiceData.serviceState.name()); + this.configuredAddress.setText(rileyLinkServiceData.rileylinkAddress); + // FIXME + this.connectedDevice.setText("???"); + // FIXME i18n + this.connectionError.setText(rileyLinkServiceData.errorCode.name()); + } else { + + } + + } + + + public void setupViewPager(ViewPager pager) { + + mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager()); + + mSectionsPagerAdapter.addFragment(new RileyLinkStatusGeneral(), MainApp.gs(R.string.rileylink_settings_tab1)); + mSectionsPagerAdapter.addFragment(new RileyLinkStatusHistory(), MainApp.gs(R.string.rileylink_settings_tab2)); + //mSectionsPagerAdapter.addFragment(new RileyLinkStatusDevice(), "Medtronic"); + + mViewPager.setAdapter(mSectionsPagerAdapter); + } + + /** + * A {@link FragmentPagerAdapter} that returns a fragment corresponding to + * one of the sections/tabs/pages. + */ + public class SectionsPagerAdapter extends FragmentPagerAdapter { + + List fragmentList = new ArrayList<>(); + List fragmentTitle = new ArrayList<>(); + int lastSelectedPosition = 0; + + + public SectionsPagerAdapter(FragmentManager fm) { + super(fm); + } + + + @Override + public Fragment getItem(int position) { + this.lastSelectedPosition = position; + return fragmentList.get(position); + } + + + @Override + public int getCount() { + // Show 3 total pages. + return fragmentList.size(); + } + + + public void addFragment(Fragment fragment, String title) { + this.fragmentList.add(fragment); + this.fragmentTitle.add(title); + } + + + @Override + public CharSequence getPageTitle(int position) { + return fragmentTitle.get(position); + } + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/dialog/RileyLinkStatusDevice.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/dialog/RileyLinkStatusDevice.java new file mode 100644 index 0000000000..226d7fc622 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/dialog/RileyLinkStatusDevice.java @@ -0,0 +1,154 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.dialog; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import android.os.Bundle; +import androidx.fragment.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.Button; +import android.widget.ListView; +import android.widget.TextView; + +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.plugins.pump.common.dialog.RefreshableInterface; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.data.CommandValueDefinition; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.CommandValueDefinitionType; +//import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.data.CommandValueDefinition; +//import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.data.RLHistoryItem; +//import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.CommandValueDefinitionType; +//import info.nightscout.androidaps.plugins.pump.common.utils.StringUtil; + +/** + * Created by andy on 5/19/18. + */ + +// FIXME needs to be implemented + +public class RileyLinkStatusDevice extends Fragment implements RefreshableInterface { + + ListView listView; + + RileyLinkCommandListAdapter adapter; + + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View rootView = inflater.inflate(R.layout.rileylink_status_device, container, false); + + adapter = new RileyLinkCommandListAdapter(); + + return rootView; + } + + + @Override + public void onStart() { + super.onStart(); + + this.listView = (ListView)getActivity().findViewById(R.id.rileyLinkDeviceList); + + listView.setAdapter(adapter); + + setElements(); + } + + + private void setElements() { + + } + + + @Override + public void refreshData() { + // adapter.addItemsAndClean(RileyLinkUtil.getRileyLinkHistory()); + } + + static class ViewHolder { + + TextView itemDescription; + Button itemValue; + } + + private class RileyLinkCommandListAdapter extends BaseAdapter { + + private List commandValueList; + private Map commandValueMap; + private LayoutInflater mInflator; + + + public RileyLinkCommandListAdapter() { + super(); + commandValueList = new ArrayList<>(); + mInflator = RileyLinkStatusDevice.this.getLayoutInflater(); + } + + + public void addItems(List list) { + commandValueList.addAll(list); + + for (CommandValueDefinition commandValueDefinition : list) { + commandValueMap.put(commandValueDefinition.definitionType, commandValueDefinition); + } + + notifyDataSetChanged(); + } + + + public CommandValueDefinition getCommandValueItem(int position) { + return commandValueList.get(position); + } + + + public void clear() { + commandValueList.clear(); + notifyDataSetChanged(); + } + + + @Override + public int getCount() { + return commandValueList.size(); + } + + + @Override + public Object getItem(int i) { + return commandValueList.get(i); + } + + + @Override + public long getItemId(int i) { + return i; + } + + + @Override + public View getView(int i, View view, ViewGroup viewGroup) { + RileyLinkStatusDevice.ViewHolder viewHolder; + // General ListView optimization code. + if (view == null) { + view = mInflator.inflate(R.layout.rileylink_status_device_item, null); + viewHolder = new RileyLinkStatusDevice.ViewHolder(); + viewHolder.itemDescription = (TextView)view.findViewById(R.id.rileylink_device_label); + viewHolder.itemValue = (Button)view.findViewById(R.id.rileylink_device_action); + view.setTag(viewHolder); + } else { + viewHolder = (RileyLinkStatusDevice.ViewHolder)view.getTag(); + } + // Z + // RLHistoryItem item = historyItemList.get(i); + // viewHolder.itemTime.setText(StringUtil.toDateTimeString(item.getDateTime())); + // viewHolder.itemSource.setText("Riley Link"); // for now + // viewHolder.itemDescription.setText(item.getDescription()); + + return view; + } + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/dialog/RileyLinkStatusGeneral.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/dialog/RileyLinkStatusGeneral.java new file mode 100644 index 0000000000..021d1ee068 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/dialog/RileyLinkStatusGeneral.java @@ -0,0 +1,150 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.dialog; + +import android.os.Bundle; +import androidx.fragment.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import org.joda.time.LocalDateTime; + +import java.util.Locale; + +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.plugins.pump.common.dialog.RefreshableInterface; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkUtil; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.RileyLinkFirmwareVersion; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkServiceState; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkTargetDevice; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.RileyLinkServiceData; +import info.nightscout.androidaps.plugins.pump.common.utils.StringUtil; +import info.nightscout.androidaps.plugins.pump.medtronic.driver.MedtronicPumpStatus; +import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil; + +/** + * Created by andy on 5/19/18. + */ + +public class RileyLinkStatusGeneral extends Fragment implements RefreshableInterface { + + TextView connectionStatus; + TextView configuredAddress; + TextView connectedDevice; + TextView connectionError; + TextView deviceType; + TextView deviceModel; + TextView serialNumber; + TextView pumpFrequency; + TextView lastUsedFrequency; + TextView lastDeviceContact; + TextView firmwareVersion; + + RileyLinkServiceData rileyLinkServiceData; + + MedtronicPumpStatus medtronicPumpStatus; + boolean first = false; + + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View rootView = inflater.inflate(R.layout.rileylink_status_general, container, false); + + return rootView; + } + + + @Override + public void onStart() { + super.onStart(); + rileyLinkServiceData = RileyLinkUtil.getRileyLinkServiceData(); + + this.connectionStatus = getActivity().findViewById(R.id.rls_t1_connection_status); + this.configuredAddress = getActivity().findViewById(R.id.rls_t1_configured_address); + this.connectedDevice = getActivity().findViewById(R.id.rls_t1_connected_device); + this.connectionError = getActivity().findViewById(R.id.rls_t1_connection_error); + this.deviceType = getActivity().findViewById(R.id.rls_t1_device_type); + this.deviceModel = getActivity().findViewById(R.id.rls_t1_device_model); + this.serialNumber = getActivity().findViewById(R.id.rls_t1_serial_number); + this.pumpFrequency = getActivity().findViewById(R.id.rls_t1_pump_frequency); + this.lastUsedFrequency = getActivity().findViewById(R.id.rls_t1_last_used_frequency); + this.lastDeviceContact = getActivity().findViewById(R.id.rls_t1_last_device_contact); + this.firmwareVersion = getActivity().findViewById(R.id.rls_t1_firmware_version); + + if (!first) { + + // 7-12 + int[] ids = {R.id.rls_t1_tv02, R.id.rls_t1_tv03, R.id.rls_t1_tv04, R.id.rls_t1_tv05, R.id.rls_t1_tv07, // + R.id.rls_t1_tv08, R.id.rls_t1_tv09, R.id.rls_t1_tv10, R.id.rls_t1_tv11, R.id.rls_t1_tv12, R.id.rls_t1_tv13}; + + for (int id : ids) { + + TextView tv = (TextView) getActivity().findViewById(id); + tv.setText(tv.getText() + ":"); + } + + first = true; + } + + refreshData(); + } + + + public void refreshData() { + + RileyLinkTargetDevice targetDevice = RileyLinkUtil.getTargetDevice(); + + if (RileyLinkUtil.getServiceState()==null) + this.connectionStatus.setText(MainApp.gs(RileyLinkServiceState.NotStarted.getResourceId(targetDevice))); + else + this.connectionStatus.setText(MainApp.gs(RileyLinkUtil.getServiceState().getResourceId(targetDevice))); + + if (rileyLinkServiceData != null) { + this.configuredAddress.setText(rileyLinkServiceData.rileylinkAddress); + this.connectionError.setText(rileyLinkServiceData.errorCode == null ? // + "-" + : MainApp.gs(rileyLinkServiceData.errorCode.getResourceId(targetDevice))); + + + RileyLinkFirmwareVersion firmwareVersion = rileyLinkServiceData.versionCC110; + + if (firmwareVersion==null) { + this.firmwareVersion.setText("BLE113: -\nCC110: -"); + } else { + this.firmwareVersion.setText("BLE113: " + rileyLinkServiceData.versionBLE113 + // + "\nCC110: " + firmwareVersion.toString()); + } + + } + + // TODO add handling for Omnipod pump status + this.medtronicPumpStatus = MedtronicUtil.getPumpStatus(); + + if (medtronicPumpStatus != null) { + this.deviceType.setText(MainApp.gs(RileyLinkTargetDevice.MedtronicPump.getResourceId())); + this.deviceModel.setText(medtronicPumpStatus.pumpType.getDescription()); + this.serialNumber.setText(medtronicPumpStatus.serialNumber); + this.pumpFrequency.setText(MainApp.gs(medtronicPumpStatus.pumpFrequency.equals("medtronic_pump_frequency_us_ca") ? R.string.medtronic_pump_frequency_us_ca : R.string.medtronic_pump_frequency_worldwide)); + + // TODO extend when Omnipod used + + if (MedtronicUtil.getMedtronicPumpModel() != null) + this.connectedDevice.setText("Medtronic " + MedtronicUtil.getMedtronicPumpModel().getPumpModel()); + else + this.connectedDevice.setText("???"); + + if (rileyLinkServiceData.lastGoodFrequency != null) + this.lastUsedFrequency.setText(String.format(Locale.ENGLISH, "%.2f MHz", + rileyLinkServiceData.lastGoodFrequency)); + + if (medtronicPumpStatus.lastConnection != 0) + this.lastDeviceContact.setText(StringUtil.toDateTimeString(new LocalDateTime( + medtronicPumpStatus.lastDataTime))); + else + this.lastDeviceContact.setText("Never"); + } + + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/dialog/RileyLinkStatusHistory.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/dialog/RileyLinkStatusHistory.java new file mode 100644 index 0000000000..d6af799550 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/dialog/RileyLinkStatusHistory.java @@ -0,0 +1,161 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.dialog; + +import java.util.ArrayList; +import java.util.List; + +import android.os.Bundle; +import androidx.fragment.app.Fragment; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.plugins.pump.common.dialog.RefreshableInterface; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkUtil; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.data.RLHistoryItem; +import info.nightscout.androidaps.plugins.pump.common.utils.StringUtil; +import info.nightscout.androidaps.plugins.pump.medtronic.defs.PumpDeviceState; + +/** + * Created by andy on 5/19/18. + */ + +public class RileyLinkStatusHistory extends Fragment implements RefreshableInterface { + + RecyclerView recyclerView; + RecyclerViewAdapter recyclerViewAdapter; + + LinearLayoutManager llm; + List filteredHistoryList = new ArrayList<>(); + + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View rootView = inflater.inflate(R.layout.rileylink_status_history, container, false); + + recyclerView = (RecyclerView)rootView.findViewById(R.id.rileylink_history_list); + + recyclerView.setHasFixedSize(true); + llm = new LinearLayoutManager(getActivity().getApplicationContext()); + recyclerView.setLayoutManager(llm); + + recyclerViewAdapter = new RecyclerViewAdapter(filteredHistoryList); + recyclerView.setAdapter(recyclerViewAdapter); + + return rootView; + } + + + @Override + public void onStart() { + super.onStart(); + + refreshData(); + } + + + @Override + public void refreshData() { + if (RileyLinkUtil.getRileyLinkHistory()!=null) { + recyclerViewAdapter.addItemsAndClean(RileyLinkUtil.getRileyLinkHistory()); + } + } + + + public static class RecyclerViewAdapter extends RecyclerView.Adapter { + + List historyList; + + + RecyclerViewAdapter(List historyList) { + this.historyList = historyList; + } + + + public void setHistoryList(List historyList) { + this.historyList = historyList; + } + + + public void addItemsAndClean(List items) { + this.historyList.clear(); + + for (RLHistoryItem item : items) { + + if (!historyList.contains(item) && isValidItem(item)) { + historyList.add(item); + } + } + + notifyDataSetChanged(); + } + + + private boolean isValidItem(RLHistoryItem item) { + + PumpDeviceState pumpState = item.getPumpDeviceState(); + + if ((pumpState != null) && // + (pumpState == PumpDeviceState.Sleeping || // + pumpState == PumpDeviceState.Active || // + pumpState == PumpDeviceState.WakingUp // + )) + return false; + + return true; + + } + + + @Override + public RecyclerViewAdapter.HistoryViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) { + View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.rileylink_status_history_item, // + viewGroup, false); + return new RecyclerViewAdapter.HistoryViewHolder(v); + } + + + @Override + public void onBindViewHolder(RecyclerViewAdapter.HistoryViewHolder holder, int position) { + RLHistoryItem item = historyList.get(position); + + if (item != null) { + holder.timeView.setText(StringUtil.toDateTimeString(item.getDateTime())); + holder.typeView.setText(item.getSource().getDesc()); + holder.valueView.setText(item.getDescription()); + } + } + + + @Override + public int getItemCount() { + return historyList.size(); + } + + + @Override + public void onAttachedToRecyclerView(RecyclerView recyclerView) { + super.onAttachedToRecyclerView(recyclerView); + } + + static class HistoryViewHolder extends RecyclerView.ViewHolder { + + TextView timeView; + TextView typeView; + TextView valueView; + + + HistoryViewHolder(View itemView) { + super(itemView); + + timeView = (TextView)itemView.findViewById(R.id.rileylink_history_time); + typeView = (TextView)itemView.findViewById(R.id.rileylink_history_source); + valueView = (TextView)itemView.findViewById(R.id.rileylink_history_description); + } + } + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/service/RileyLinkBluetoothStateReceiver.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/service/RileyLinkBluetoothStateReceiver.java new file mode 100644 index 0000000000..78b7def1af --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/service/RileyLinkBluetoothStateReceiver.java @@ -0,0 +1,58 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service; + +import android.bluetooth.BluetoothAdapter; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.interfaces.PumpInterface; +import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkConst; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkUtil; + +public class RileyLinkBluetoothStateReceiver extends BroadcastReceiver { + + private static Logger LOG = LoggerFactory.getLogger(L.PUMP); + + @Override + public void onReceive(Context context, Intent intent) { + final String action = intent.getAction(); + + PumpInterface activePump = ConfigBuilderPlugin.getPlugin().getActivePump(); + + if (action != null && activePump != null) { + + final int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR); + switch (state) { + case BluetoothAdapter.STATE_OFF: + case BluetoothAdapter.STATE_TURNING_OFF: + case BluetoothAdapter.STATE_TURNING_ON: + break; + + case BluetoothAdapter.STATE_ON: { + LOG.debug("RileyLinkBluetoothStateReceiver: Bluetooth back on. Sending broadcast to RileyLink Framework"); + RileyLinkUtil.sendBroadcastMessage(RileyLinkConst.Intents.BluetoothReconnected); + } + break; + } + } + } + + + public void unregisterBroadcasts() { + MainApp.instance().unregisterReceiver(this); + } + + + public void registerBroadcasts() { + IntentFilter filter = new IntentFilter(); + filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); + MainApp.instance().registerReceiver(this, filter); + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/service/RileyLinkBroadcastReceiver.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/service/RileyLinkBroadcastReceiver.java new file mode 100644 index 0000000000..d8a897955e --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/service/RileyLinkBroadcastReceiver.java @@ -0,0 +1,260 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service; + +/** + * Created by andy on 10/23/18. + */ + +import android.bluetooth.BluetoothAdapter; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import androidx.localbroadcastmanager.content.LocalBroadcastManager; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkConst; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkUtil; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.RileyLinkFirmwareVersion; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkError; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkServiceState; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.tasks.DiscoverGattServicesTask; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.tasks.InitializePumpManagerTask; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.tasks.ServiceTask; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.tasks.ServiceTaskExecutor; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.tasks.WakeAndTuneTask; +import info.nightscout.androidaps.utils.SP; + +/** + * I added this class outside of RileyLinkService, because for now it's very important part of RL framework and + * where we get a lot of problems. Especially merging between AAPS and RileyLinkAAPS. I might put it back at + * later time + */ +public class RileyLinkBroadcastReceiver extends BroadcastReceiver { + + private static final Logger LOG = LoggerFactory.getLogger(L.PUMPCOMM); + + RileyLinkService serviceInstance; + protected Map> broadcastIdentifiers = null; + String deviceSpecificPrefix; + Context context; + + + public RileyLinkBroadcastReceiver(RileyLinkService serviceInstance, Context context) { + this.serviceInstance = serviceInstance; + this.context = context; + + createBroadcastIdentifiers(); + } + + + private void createBroadcastIdentifiers() { + + this.broadcastIdentifiers = new HashMap<>(); + + // Bluetooth + this.broadcastIdentifiers.put("Bluetooth", Arrays.asList( // + RileyLinkConst.Intents.BluetoothConnected, // + RileyLinkConst.Intents.BluetoothReconnected)); + + // TuneUp + this.broadcastIdentifiers.put("TuneUp", Arrays.asList( // + RileyLinkConst.IPC.MSG_PUMP_tunePump, // + RileyLinkConst.IPC.MSG_PUMP_quickTune)); + + // RileyLink + this.broadcastIdentifiers.put("RileyLink", Arrays.asList( // + RileyLinkConst.Intents.RileyLinkDisconnected, // + RileyLinkConst.Intents.RileyLinkReady, // + RileyLinkConst.Intents.RileyLinkDisconnected, // + RileyLinkConst.Intents.RileyLinkNewAddressSet, // + RileyLinkConst.Intents.RileyLinkDisconnect)); + + // Device Specific + deviceSpecificPrefix = serviceInstance.getDeviceSpecificBroadcastsIdentifierPrefix(); + + // Application specific + + } + + + @Override + public void onReceive(Context context, Intent intent) { + + if (intent == null) { + LOG.error("onReceive: received null intent"); + } else { + String action = intent.getAction(); + if (action == null) { + LOG.error("onReceive: null action"); + } else { + if (isLoggingEnabled()) + LOG.debug("Received Broadcast: " + action); + + if (!processBluetoothBroadcasts(action) && // + !processRileyLinkBroadcasts(action) && // + !processTuneUpBroadcasts(action) && // + !processDeviceSpecificBroadcasts(action, intent) && // + !processApplicationSpecificBroadcasts(action, intent) // + ) { + LOG.error("Unhandled broadcast: action=" + action); + } + } + } + } + + + public void registerBroadcasts() { + + IntentFilter intentFilter = new IntentFilter(); + + for (Map.Entry> stringListEntry : broadcastIdentifiers.entrySet()) { + + for (String intentKey : stringListEntry.getValue()) { + intentFilter.addAction(intentKey); + } + } + + if (deviceSpecificPrefix != null) { + serviceInstance.registerDeviceSpecificBroadcasts(intentFilter); + } + + LocalBroadcastManager.getInstance(context).registerReceiver(this, intentFilter); + } + + + private boolean processRileyLinkBroadcasts(String action) { + + if (action.equals(RileyLinkConst.Intents.RileyLinkDisconnected)) { + if (BluetoothAdapter.getDefaultAdapter().isEnabled()) { + RileyLinkUtil + .setServiceState(RileyLinkServiceState.BluetoothError, RileyLinkError.RileyLinkUnreachable); + } else { + RileyLinkUtil.setServiceState(RileyLinkServiceState.BluetoothError, RileyLinkError.BluetoothDisabled); + } + + return true; + } else if (action.equals(RileyLinkConst.Intents.RileyLinkReady)) { + + if (isLoggingEnabled()) + LOG.warn("MedtronicConst.Intents.RileyLinkReady"); + // sendIPCNotification(RT2Const.IPC.MSG_note_WakingPump); + + if (this.serviceInstance.rileyLinkBLE == null) + return false; + + this.serviceInstance.rileyLinkBLE.enableNotifications(); + this.serviceInstance.rfspy.startReader(); // call startReader from outside? + + this.serviceInstance.rfspy.initializeRileyLink(); + String bleVersion = this.serviceInstance.rfspy.getBLEVersionCached(); + RileyLinkFirmwareVersion rlVersion = this.serviceInstance.rfspy.getRLVersionCached(); + +// if (isLoggingEnabled()) + LOG.debug("RfSpy version (BLE113): " + bleVersion); + this.serviceInstance.rileyLinkServiceData.versionBLE113 = bleVersion; + +// if (isLoggingEnabled()) + LOG.debug("RfSpy Radio version (CC110): " + rlVersion.name()); + this.serviceInstance.rileyLinkServiceData.versionCC110 = rlVersion; + + ServiceTask task = new InitializePumpManagerTask(RileyLinkUtil.getTargetDevice()); + ServiceTaskExecutor.startTask(task); + if (isLoggingEnabled()) + LOG.info("Announcing RileyLink open For business"); + + return true; + } else if (action.equals(RileyLinkConst.Intents.RileyLinkNewAddressSet)) { + String RileylinkBLEAddress = SP.getString(RileyLinkConst.Prefs.RileyLinkAddress, ""); + if (RileylinkBLEAddress.equals("")) { + LOG.error("No Rileylink BLE Address saved in app"); + } else { + // showBusy("Configuring Service", 50); + // rileyLinkBLE.findRileyLink(RileylinkBLEAddress); + this.serviceInstance.reconfigureRileyLink(RileylinkBLEAddress); + // MainApp.getServiceClientConnection().setThisRileylink(RileylinkBLEAddress); + } + + return true; + } else if (action.equals(RileyLinkConst.Intents.RileyLinkDisconnect)) { + this.serviceInstance.disconnectRileyLink(); + + return true; + } else { + return false; + } + + } + + + public boolean processBluetoothBroadcasts(String action) { + + if (action.equals(RileyLinkConst.Intents.BluetoothConnected)) { + if (isLoggingEnabled()) + LOG.debug("Bluetooth - Connected"); + ServiceTaskExecutor.startTask(new DiscoverGattServicesTask()); + + return true; + + } else if (action.equals(RileyLinkConst.Intents.BluetoothReconnected)) { + if (isLoggingEnabled()) + LOG.debug("Bluetooth - Reconnecting"); + + serviceInstance.bluetoothInit(); + ServiceTaskExecutor.startTask(new DiscoverGattServicesTask(true)); + + return true; + } else { + + return false; + } + + } + + + private boolean processTuneUpBroadcasts(String action) { + + if (this.broadcastIdentifiers.get("TuneUp").contains(action)) { + if (serviceInstance.getRileyLinkTargetDevice().isTuneUpEnabled()) { + ServiceTaskExecutor.startTask(new WakeAndTuneTask()); + } + return true; + } else { + return false; + } + } + + + public boolean processDeviceSpecificBroadcasts(String action, Intent intent) { + + if (this.deviceSpecificPrefix == null) { + return false; + } + + if (action.startsWith(this.deviceSpecificPrefix)) { + return this.serviceInstance.handleDeviceSpecificBroadcasts(intent); + } else + return false; + } + + + public boolean processApplicationSpecificBroadcasts(String action, Intent intent) { + return false; + } + + + public boolean isLoggingEnabled() { + return (L.isEnabled(L.PUMPCOMM)); + } + + public void unregisterBroadcasts() { + LocalBroadcastManager.getInstance(context).unregisterReceiver(this); + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/service/RileyLinkService.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/service/RileyLinkService.java new file mode 100644 index 0000000000..29781d191b --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/service/RileyLinkService.java @@ -0,0 +1,289 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service; + +import static info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkUtil.getRileyLinkCommunicationManager; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import android.app.Service; +import android.bluetooth.BluetoothAdapter; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; + +import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkCommunicationManager; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkConst; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkUtil; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.RFSpy; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.RileyLinkBLE; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.RileyLinkEncodingType; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkError; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkServiceState; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkTargetDevice; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.data.ServiceResult; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.data.ServiceTransport; +import info.nightscout.androidaps.plugins.pump.medtronic.defs.PumpDeviceState; +import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil; +import info.nightscout.androidaps.utils.SP; + +/** + * Created by andy on 5/6/18. + * Split from original file and renamed. + */ +public abstract class RileyLinkService extends Service { + + private static final Logger LOG = LoggerFactory.getLogger(L.PUMPCOMM); + + public RileyLinkBLE rileyLinkBLE; // android-bluetooth management + protected BluetoothAdapter bluetoothAdapter; + protected RFSpy rfspy; // interface for RL xxx Mhz radio. + protected Context context; + protected RileyLinkBroadcastReceiver mBroadcastReceiver; + protected RileyLinkServiceData rileyLinkServiceData; + protected RileyLinkBluetoothStateReceiver bluetoothStateReceiver; + + public RileyLinkService(Context context) { + super(); + this.context = context; + RileyLinkUtil.setContext(this.context); + RileyLinkUtil.setRileyLinkService(this); + RileyLinkUtil.setEncoding(getEncoding()); + initRileyLinkServiceData(); + } + + + /** + * Get Encoding for RileyLink communication + */ + public abstract RileyLinkEncodingType getEncoding(); + + + /** + * If you have customized RileyLinkServiceData you need to override this + */ + public abstract void initRileyLinkServiceData(); + + + @Override + public boolean onUnbind(Intent intent) { + //LOG.warn("onUnbind"); + return super.onUnbind(intent); + } + + + @Override + public void onRebind(Intent intent) { + //LOG.warn("onRebind"); + super.onRebind(intent); + } + + + @Override + public void onDestroy() { + super.onDestroy(); + //LOG.error("I die! I die!"); + + if (rileyLinkBLE != null) { + rileyLinkBLE.disconnect(); // dispose of Gatt (disconnect and close) + rileyLinkBLE = null; + } + + if (mBroadcastReceiver!=null) { + mBroadcastReceiver.unregisterBroadcasts(); + } + + if (bluetoothStateReceiver!=null) { + bluetoothStateReceiver.unregisterBroadcasts(); + } + + } + + + @Override + public void onCreate() { + super.onCreate(); + //LOG.debug("onCreate"); + + mBroadcastReceiver = new RileyLinkBroadcastReceiver(this, this.context); + mBroadcastReceiver.registerBroadcasts(); + + + bluetoothStateReceiver = new RileyLinkBluetoothStateReceiver(); + bluetoothStateReceiver.registerBroadcasts(); + + //LOG.debug("onCreate(): It's ALIVE!"); + } + + + /** + * Prefix for Device specific broadcast identifier prefix (for example MSG_PUMP_ for pump or + * MSG_POD_ for Omnipod) + * + * @return + */ + public abstract String getDeviceSpecificBroadcastsIdentifierPrefix(); + + + public abstract boolean handleDeviceSpecificBroadcasts(Intent intent); + + + public abstract void registerDeviceSpecificBroadcasts(IntentFilter intentFilter); + + + public abstract RileyLinkCommunicationManager getDeviceCommunicationManager(); + + + // Here is where the wake-lock begins: + // We've received a service startCommand, we grab the lock. + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + RileyLinkUtil.setContext(getApplicationContext()); + return (START_STICKY); + } + + + public boolean bluetoothInit() { + if (isLogEnabled()) + LOG.debug("bluetoothInit: attempting to get an adapter"); + RileyLinkUtil.setServiceState(RileyLinkServiceState.BluetoothInitializing); + + bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); + + if (bluetoothAdapter == null) { + LOG.error("Unable to obtain a BluetoothAdapter."); + RileyLinkUtil.setServiceState(RileyLinkServiceState.BluetoothError, RileyLinkError.NoBluetoothAdapter); + } else { + + if (!bluetoothAdapter.isEnabled()) { + LOG.error("Bluetooth is not enabled."); + RileyLinkUtil.setServiceState(RileyLinkServiceState.BluetoothError, RileyLinkError.BluetoothDisabled); + } else { + RileyLinkUtil.setServiceState(RileyLinkServiceState.BluetoothReady); + return true; + } + } + + return false; + } + + + // returns true if our Rileylink configuration changed + public boolean reconfigureRileyLink(String deviceAddress) { + + if (rileyLinkBLE == null) { + RileyLinkUtil.setServiceState(RileyLinkServiceState.BluetoothInitializing); + return false; + } + + RileyLinkUtil.setServiceState(RileyLinkServiceState.RileyLinkInitializing); + + if (rileyLinkBLE.isConnected()) { + if (deviceAddress.equals(rileyLinkServiceData.rileylinkAddress)) { + if (isLogEnabled()) + LOG.info("No change to RL address. Not reconnecting."); + return false; + } else { + if (isLogEnabled()) + LOG.warn("Disconnecting from old RL (" + rileyLinkServiceData.rileylinkAddress + + "), reconnecting to new: " + deviceAddress); + + rileyLinkBLE.disconnect(); + // prolly need to shut down listening thread too? + // SP.putString(MedtronicConst.Prefs.RileyLinkAddress, deviceAddress); + + rileyLinkServiceData.rileylinkAddress = deviceAddress; + rileyLinkBLE.findRileyLink(rileyLinkServiceData.rileylinkAddress); + return true; + } + } else { + if (isLogEnabled()) + LOG.debug("Using RL " + deviceAddress); + + if (RileyLinkUtil.getServiceState() == RileyLinkServiceState.NotStarted) { + if (!bluetoothInit()) { + LOG.error("RileyLink can't get activated, Bluetooth is not functioning correctly. {}", + RileyLinkUtil.getError() != null ? RileyLinkUtil.getError().name() : "Unknown error (null)"); + return false; + } + } + + rileyLinkBLE.findRileyLink(deviceAddress); + + return true; + } + } + + + public void sendServiceTransportResponse(ServiceTransport transport, ServiceResult serviceResult) { + } + + + // FIXME: This needs to be run in a session so that is interruptable, has a separate thread, etc. + public void doTuneUpDevice() { + + RileyLinkUtil.setServiceState(RileyLinkServiceState.TuneUpDevice); + MedtronicUtil.setPumpDeviceState(PumpDeviceState.Sleeping); + + double lastGoodFrequency = 0.0d; + + if (rileyLinkServiceData.lastGoodFrequency == null) { + lastGoodFrequency = SP.getDouble(RileyLinkConst.Prefs.LastGoodDeviceFrequency, 0.0d); + } else { + lastGoodFrequency = rileyLinkServiceData.lastGoodFrequency; + } + + double newFrequency; + + newFrequency = getDeviceCommunicationManager().tuneForDevice(); + + if ((newFrequency != 0.0) && (newFrequency != lastGoodFrequency)) { + if (isLogEnabled()) + LOG.info("Saving new pump frequency of {} MHz", newFrequency); + SP.putDouble(RileyLinkConst.Prefs.LastGoodDeviceFrequency, newFrequency); + rileyLinkServiceData.lastGoodFrequency = newFrequency; + rileyLinkServiceData.tuneUpDone = true; + rileyLinkServiceData.lastTuneUpTime = System.currentTimeMillis(); + } + + if (newFrequency == 0.0d) { + // error tuning pump, pump not present ?? + RileyLinkUtil + .setServiceState(RileyLinkServiceState.PumpConnectorError, RileyLinkError.TuneUpOfDeviceFailed); + } else { + getRileyLinkCommunicationManager().clearNotConnectedCount(); + RileyLinkUtil.setServiceState(RileyLinkServiceState.PumpConnectorReady); + } + } + + + public void disconnectRileyLink() { + + if (this.rileyLinkBLE != null && this.rileyLinkBLE.isConnected()) { + this.rileyLinkBLE.disconnect(); + rileyLinkServiceData.rileylinkAddress = null; + } + + RileyLinkUtil.setServiceState(RileyLinkServiceState.BluetoothReady); + } + + + /** + * Get Target Device for Service + */ + public RileyLinkTargetDevice getRileyLinkTargetDevice() { + return this.rileyLinkServiceData.targetDevice; + } + + + private boolean isLogEnabled() { + return L.isEnabled(L.PUMPCOMM); + } + + + public void changeRileyLinkEncoding(RileyLinkEncodingType encodingType) { + if (rfspy != null) { + rfspy.setRileyLinkEncoding(encodingType); + } + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/service/RileyLinkServiceData.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/service/RileyLinkServiceData.java new file mode 100644 index 0000000000..dd66255da4 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/service/RileyLinkServiceData.java @@ -0,0 +1,43 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service; + +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.RileyLinkFirmwareVersion; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkError; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkServiceState; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkTargetDevice; + +/** + * Created by andy on 16/05/2018. + */ + +public class RileyLinkServiceData { + + public boolean tuneUpDone = false; + public RileyLinkError errorCode; + public RileyLinkServiceState serviceState = RileyLinkServiceState.NotStarted; + public String rileylinkAddress; + public long lastTuneUpTime = 0L; + public Double lastGoodFrequency; + + // bt version + public String versionBLE113; + // radio version + public RileyLinkFirmwareVersion versionCC110; + + public RileyLinkTargetDevice targetDevice; + + // Medtronic Pump + public String pumpID; + public byte[] pumpIDBytes; + + + public RileyLinkServiceData(RileyLinkTargetDevice targetDevice) { + this.targetDevice = targetDevice; + } + + + public void setPumpID(String pumpId, byte[] pumpIdBytes) { + this.pumpID = pumpId; + this.pumpIDBytes = pumpIdBytes; + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/service/data/ServiceCommand.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/service/data/ServiceCommand.java new file mode 100644 index 0000000000..4f43013328 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/service/data/ServiceCommand.java @@ -0,0 +1,67 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.data; + +import android.os.Bundle; + +/** + * Created by geoff on 6/25/16. + */ +public class ServiceCommand extends ServiceMessage { + + public ServiceCommand() { + map = new Bundle(); + } + + + // commandID is a string that the client can set on the message. + // The service does not use this value, but passes it back with the result + // so that the client can identify it. + public ServiceCommand(String commandName, String commandID) { + init(); + map.putString("command", commandName); + map.putString("commandID", commandID); + } + + + public ServiceCommand(Bundle commandBundle) { + if (commandBundle != null) { + map = commandBundle; + } else { + map = new Bundle(); + init(); + map.putString("command", "(null)"); + map.putString("commandID", "(null"); + } + } + + + @Override + public void init() { + map.putString("ServiceMessageType", "ServiceCommand"); + } + + + public String getCommandID() { + return map.getString("commandID"); + } + + + public String getCommandName() { + return map.getString("command"); + } + + + public boolean isPumpCommand() { + switch (getCommandName()) { + case "FetchPumpHistory": + case "ReadPumpClock": + case "RetrieveHistoryPage": + case "ReadISFProfile": + case "ReadBolusWizardCarbProfile": + case "UpdatePumpStatus": + case "WakeAndTune": + return true; + default: + return false; + } + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/service/data/ServiceMessage.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/service/data/ServiceMessage.java new file mode 100644 index 0000000000..83e2f57ade --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/service/data/ServiceMessage.java @@ -0,0 +1,40 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.data; + +import android.os.Bundle; + +/** + * Created by geoff on 7/4/16. + *

+ * Base class for all messages passed between service and client + */ +public class ServiceMessage { + + protected Bundle map = new Bundle(); + + + public ServiceMessage() { + init(); + } + + + public void init() { + map.putString("ServiceMessageClass", this.getClass().getCanonicalName()); + map.putString("ServiceMessageType", this.getClass().getSimpleName()); + } + + + public Bundle getMap() { + return map; + } + + + public void setMap(Bundle map) { + this.map = map; + } + + + public String getServiceMessageType() { + return map.getString("ServiceMessageType"); + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/service/data/ServiceNotification.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/service/data/ServiceNotification.java new file mode 100644 index 0000000000..aad45093f1 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/service/data/ServiceNotification.java @@ -0,0 +1,48 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.data; + +import android.os.Bundle; + +/** + * Created by geoff on 7/6/16. + *

+ * These are "one liner" messages between client and service. Must still be contained within ServiceTransports + */ +public class ServiceNotification extends ServiceMessage { + + public ServiceNotification() { + } + + + public ServiceNotification(Bundle b) { + if (b != null) { + if ("ServiceNotification".equals(b.getString("ServiceMessageType"))) { + setMap(b); + } else { + throw new IllegalArgumentException(); + } + } + } + + + public ServiceNotification(String notificationType) { + setNotificationType(notificationType); + } + + + @Override + public void init() { + super.init(); + map.putString("ServiceMessageType", "ServiceNotification"); + } + + + public String getNotificationType() { + return map.getString("NotificationType", ""); + } + + + public void setNotificationType(String notificationType) { + map.putString("NotificationType", notificationType); + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/service/data/ServiceResult.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/service/data/ServiceResult.java new file mode 100644 index 0000000000..ecf7d54f99 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/service/data/ServiceResult.java @@ -0,0 +1,96 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.data; + +import android.os.Bundle; + +/** + * Created by geoff on 6/25/16. + */ +public class ServiceResult extends ServiceMessage { + + public static final int ERROR_MALFORMED_PUMP_RESPONSE = 1; + public static final int ERROR_NULL_PUMP_RESPONSE = 2; + public static final int ERROR_INVALID_PUMP_RESPONSE = 3; + public static final int ERROR_PUMP_BUSY = 4; + + + public ServiceResult() { + init(); + } + + + public ServiceResult(Bundle resultBundle) { + if (resultBundle != null) { + setMap(resultBundle); + } else { + init(); + } + } + + + public static final String getErrorDescription(int errorCode) { + switch (errorCode) { + case ERROR_MALFORMED_PUMP_RESPONSE: + return "Malformed Pump Response"; + case ERROR_NULL_PUMP_RESPONSE: + return "Null pump response"; + case ERROR_INVALID_PUMP_RESPONSE: + return "Invalid pump response"; + case ERROR_PUMP_BUSY: + return "A pump command session is already in progress"; + default: + return "Unknown error code (" + errorCode + ")"; + } + } + + + @Override + public void init() { + super.init(); + map.putString("ServiceMessageType", "ServiceResult"); + setServiceResultType(this.getClass().getSimpleName()); + setResultError(0, "Uninitialized ServiceResult"); + } + + + public String getServiceResultType() { + return map.getString("ServiceResultType", "ServiceResult"); + } + + + public void setServiceResultType(String serviceResultType) { + map.putString("ServiceResultType", serviceResultType); + } + + + public void setResultOK() { + map.putString("result", "OK"); + } + + + public void setResultError(int errorCode) { + setResultError(errorCode, getErrorDescription(errorCode)); + } + + + public void setResultError(int errorCode, String errorDescription) { + map.putString("result", "error"); + map.putInt("errorCode", errorCode); + map.putString("errorDescription", errorDescription); + } + + + public boolean resultIsOK() { + return ("OK".equals(map.getString("result", ""))); + } + + + public String getErrorDescription() { + return map.getString("errorDescription", ""); + } + + + public String getResult() { + return map.getString("result", ""); + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/service/data/ServiceTransport.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/service/data/ServiceTransport.java new file mode 100644 index 0000000000..c2043708fe --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/service/data/ServiceTransport.java @@ -0,0 +1,150 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.data; + +import android.os.Bundle; +import android.os.Parcel; + +/** + * Created by geoff on 7/6/16. + *

+ * This class exists to hold a ServiceCommand along with transport variables such as time sent, time received, sender. + * May also contain result, if the command is completed. + */ +public class ServiceTransport extends ServiceMessage { + + private ServiceTransportType serviceTransportType = ServiceTransportType.Undefined; + + + public ServiceTransport() { + } + + + public ServiceTransport(Bundle b) { + if (b != null) { + if ("ServiceTransport".equals(b.getString("ServiceMessageType"))) { + setMap(b); + } else { + throw new IllegalArgumentException(); + } + } + } + + + @Override + public void init() { + super.init(); + map.putString("ServiceMessageType", "ServiceTransport"); + setTransportType("unknown"); + setSenderHashcode(0); + } + + + public Integer getSenderHashcode() { + return map.getInt("senderHashCode", 0); + } + + + public void setSenderHashcode(Integer senderHashcode) { + map.putInt("senderHashcode", senderHashcode); + } + + + public ServiceCommand getServiceCommand() { + return new ServiceCommand(map.getBundle("ServiceCommand")); + } + + + public void setServiceCommand(ServiceCommand serviceCommand) { + map.putBundle("ServiceCommand", serviceCommand.getMap()); + this.serviceTransportType = ServiceTransportType.ServiceCommand; + } + + + public boolean hasServiceCommand() { + return (getMap().containsKey("ServiceCommand")); + } + + + public String getTransportType() { + return map.getString("transportType", "unknown"); + } + + + // On remote end, this will be converted to the "action" of a local Intent, + // so can be used for separating types of messages to different internal handlers. + public void setTransportType(String transportType) { + map.putString("transportType", transportType); + } + + + public ServiceResult getServiceResult() { + return new ServiceResult(map.getBundle("ServiceResult")); + } + + + public void setServiceResult(ServiceResult serviceResult) { + map.putBundle("ServiceResult", serviceResult.getMap()); + this.serviceTransportType = ServiceTransportType.ServiceResult; + } + + + public boolean hasServiceResult() { + return (getMap().containsKey("ServiceResult")); + } + + + public ServiceNotification getServiceNotification() { + return new ServiceNotification(map.getBundle("ServiceNotification")); + } + + + public void setServiceNotification(ServiceNotification notification) { + map.putBundle("ServiceNotification", notification.getMap()); + this.serviceTransportType = ServiceTransportType.ServiceNotification; + } + + + public boolean hasServiceNotification() { + return (map.containsKey("ServiceNotification")); + } + + + public boolean commandDidCompleteOK() { + return getServiceResult().resultIsOK(); + } + + + public String getOriginalCommandName() { + return getServiceCommand().getCommandName(); + } + + + public String describeContentsShort() { + String rval = ""; + rval += getTransportType(); + + if (this.serviceTransportType == ServiceTransportType.ServiceNotification) { + rval += "note: " + getServiceNotification().getNotificationType(); + } else if (this.serviceTransportType == ServiceTransportType.ServiceCommand) { + rval += ", cmd=" + getOriginalCommandName(); + } else if (this.serviceTransportType == ServiceTransportType.ServiceResult) { + rval += ", cmd=" + getOriginalCommandName(); + rval += ", rslt=" + getServiceResult().getResult(); + rval += ", err=" + getServiceResult().getErrorDescription(); + } + return rval; + } + + + public ServiceTransport clone() { + Parcel p = Parcel.obtain(); + Parcel p2 = Parcel.obtain(); + getMap().writeToParcel(p, 0); + byte[] bytes = p.marshall(); + p2.unmarshall(bytes, 0, bytes.length); + p2.setDataPosition(0); + Bundle b = p2.readBundle(); + ServiceTransport rval = new ServiceTransport(); + rval.setMap(b); + return rval; + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/service/data/ServiceTransportType.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/service/data/ServiceTransportType.java new file mode 100644 index 0000000000..135c217996 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/service/data/ServiceTransportType.java @@ -0,0 +1,15 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.data; + +/** + * Created by andy on 31/05/18. + */ + +public enum ServiceTransportType { + + Undefined, // + ServiceNotification, // + + ServiceCommand, // + ServiceResult + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/service/tasks/DiscoverGattServicesTask.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/service/tasks/DiscoverGattServicesTask.java new file mode 100644 index 0000000000..5f807c3e89 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/service/tasks/DiscoverGattServicesTask.java @@ -0,0 +1,30 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.tasks; + +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkUtil; + +/** + * Created by geoff on 7/9/16. + */ +public class DiscoverGattServicesTask extends ServiceTask { + + public boolean needToConnect = false; + + + public DiscoverGattServicesTask() { + } + + + public DiscoverGattServicesTask(boolean needToConnect) { + this.needToConnect = needToConnect; + } + + + @Override + public void run() { + + if (needToConnect) + RileyLinkUtil.getRileyLinkBLE().connectGatt(); + + RileyLinkUtil.getRileyLinkBLE().discoverServices(); + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/service/tasks/InitializePumpManagerTask.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/service/tasks/InitializePumpManagerTask.java new file mode 100644 index 0000000000..9856b21b66 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/service/tasks/InitializePumpManagerTask.java @@ -0,0 +1,83 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.tasks; + +import android.util.Log; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkConst; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkUtil; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkError; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkServiceState; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkTargetDevice; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.data.ServiceTransport; +import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicConst; +import info.nightscout.androidaps.utils.SP; + +/** + * Created by geoff on 7/9/16. + *

+ * This class is intended to be run by the Service, for the Service. Not intended for clients to run. + */ +public class InitializePumpManagerTask extends ServiceTask { + + private static final String TAG = "InitPumpManagerTask"; + private RileyLinkTargetDevice targetDevice; + private static final Logger LOG = LoggerFactory.getLogger(L.PUMPCOMM); + + public InitializePumpManagerTask(RileyLinkTargetDevice targetDevice) { + super(); + this.targetDevice = targetDevice; + } + + + public InitializePumpManagerTask(ServiceTransport transport) { + super(transport); + } + + + @Override + public void run() { + + double lastGoodFrequency = 0.0d; + + if (RileyLinkUtil.getRileyLinkServiceData().lastGoodFrequency==null) { + + lastGoodFrequency = SP.getDouble(RileyLinkConst.Prefs.LastGoodDeviceFrequency, 0.0d); + lastGoodFrequency = Math.round(lastGoodFrequency * 1000d) / 1000d; + + RileyLinkUtil.getRileyLinkServiceData().lastGoodFrequency = lastGoodFrequency; + +// if (RileyLinkUtil.getRileyLinkTargetFrequency() == null) { +// String pumpFrequency = SP.getString(MedtronicConst.Prefs.PumpFrequency, null); +// } + } else { + lastGoodFrequency = RileyLinkUtil.getRileyLinkServiceData().lastGoodFrequency; + } + + if ((lastGoodFrequency > 0.0d) + && RileyLinkUtil.getRileyLinkCommunicationManager().isValidFrequency(lastGoodFrequency)) { + + RileyLinkUtil.setServiceState(RileyLinkServiceState.RileyLinkReady); + + if (L.isEnabled(L.PUMPCOMM)) + LOG.info("Setting radio frequency to {} MHz", lastGoodFrequency); + + RileyLinkUtil.getRileyLinkCommunicationManager().setRadioFrequencyForPump(lastGoodFrequency); + + boolean foundThePump = RileyLinkUtil.getRileyLinkCommunicationManager().tryToConnectToDevice(); + + if (foundThePump) { + RileyLinkUtil.setServiceState(RileyLinkServiceState.PumpConnectorReady); + } else { + RileyLinkUtil.setServiceState(RileyLinkServiceState.PumpConnectorError, + RileyLinkError.NoContactWithDevice); + RileyLinkUtil.sendBroadcastMessage(RileyLinkConst.IPC.MSG_PUMP_tunePump); + } + + } else { + RileyLinkUtil.sendBroadcastMessage(RileyLinkConst.IPC.MSG_PUMP_tunePump); + } + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/service/tasks/PumpTask.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/service/tasks/PumpTask.java new file mode 100644 index 0000000000..c307a43590 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/service/tasks/PumpTask.java @@ -0,0 +1,18 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.tasks; + +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.data.ServiceTransport; + +/** + * Created by geoff on 7/10/16. + */ +public class PumpTask extends ServiceTask { + + public PumpTask() { + super(); + } + + + public PumpTask(ServiceTransport transport) { + super(transport); + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/service/tasks/ResetRileyLinkConfigurationTask.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/service/tasks/ResetRileyLinkConfigurationTask.java new file mode 100644 index 0000000000..cce127ffdf --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/service/tasks/ResetRileyLinkConfigurationTask.java @@ -0,0 +1,35 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.tasks; + +import info.nightscout.androidaps.plugins.bus.RxBus; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.data.ServiceTransport; +import info.nightscout.androidaps.plugins.pump.medtronic.MedtronicPumpPlugin; +import info.nightscout.androidaps.plugins.pump.medtronic.events.EventRefreshButtonState; +import info.nightscout.androidaps.plugins.pump.medtronic.service.RileyLinkMedtronicService; + +/** + * Created by geoff on 7/16/16. + */ +public class ResetRileyLinkConfigurationTask extends PumpTask { + + private static final String TAG = "ResetRileyLinkTask"; + + + public ResetRileyLinkConfigurationTask() { + } + + + public ResetRileyLinkConfigurationTask(ServiceTransport transport) { + super(transport); + } + + + @Override + public void run() { + RxBus.INSTANCE.send(new EventRefreshButtonState(false)); + MedtronicPumpPlugin.isBusy = true; + RileyLinkMedtronicService.getInstance().resetRileyLinkConfiguration(); + MedtronicPumpPlugin.isBusy = false; + RxBus.INSTANCE.send(new EventRefreshButtonState(true)); + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/service/tasks/ServiceTask.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/service/tasks/ServiceTask.java new file mode 100644 index 0000000000..e287b63e17 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/service/tasks/ServiceTask.java @@ -0,0 +1,54 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.tasks; + +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.data.ServiceTransport; + +/** + * Created by geoff on 7/9/16. + */ +public class ServiceTask implements Runnable { + + private static final String TAG = "ServiceTask(base)"; + public boolean completed = false; + protected ServiceTransport mTransport; + + + public ServiceTask() { + init(new ServiceTransport()); + } + + + public ServiceTask(ServiceTransport transport) { + init(transport); + } + + + public void init(ServiceTransport transport) { + mTransport = transport; + } + + + @Override + public void run() { + } + + + public void preOp() { + // This function is called by UI thread before running asynch thread. + } + + + public void postOp() { + // This function is called by UI thread after running asynch thread. + } + + + public ServiceTransport getServiceTransport() { + return mTransport; + } + + /* + * protected void sendResponse(ServiceResult result) { + * RoundtripService.getInstance().sendServiceTransportResponse(mTransport,result); + * } + */ +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/service/tasks/ServiceTaskExecutor.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/service/tasks/ServiceTaskExecutor.java new file mode 100644 index 0000000000..1c6f33b071 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/service/tasks/ServiceTaskExecutor.java @@ -0,0 +1,59 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.tasks; + +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import android.util.Log; + +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkUtil; + +/** + * Created by geoff on 7/9/16. + */ +public class ServiceTaskExecutor extends ThreadPoolExecutor { + + private static final String TAG = "ServiceTaskExecutor"; + private static ServiceTaskExecutor instance; + private static LinkedBlockingQueue taskQueue = new LinkedBlockingQueue<>(); + + static { + instance = new ServiceTaskExecutor(); + } + + + private ServiceTaskExecutor() { + super(1, 1, 10000, TimeUnit.MILLISECONDS, taskQueue); + } + + + public static ServiceTaskExecutor getInstance() { + return instance; + } + + + public static ServiceTask startTask(ServiceTask task) { + instance.execute(task); // task will be run on async thread from pool. + return task; + } + + + // FIXME + protected void beforeExecute(Thread t, Runnable r) { + // This is run on either caller UI thread or Service UI thread. + ServiceTask task = (ServiceTask)r; + Log.v(TAG, "About to run task " + task.getClass().getSimpleName()); + RileyLinkUtil.setCurrentTask(task); + task.preOp(); + } + + + // FIXME + protected void afterExecute(Runnable r, Throwable t) { + // This is run on either caller UI thread or Service UI thread. + ServiceTask task = (ServiceTask)r; + task.postOp(); + Log.v(TAG, "Finishing task " + task.getClass().getSimpleName()); + RileyLinkUtil.finishCurrentTask(task); + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/service/tasks/WakeAndTuneTask.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/service/tasks/WakeAndTuneTask.java new file mode 100644 index 0000000000..5feb5ab405 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/hw/rileylink/service/tasks/WakeAndTuneTask.java @@ -0,0 +1,34 @@ +package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.tasks; + +import info.nightscout.androidaps.plugins.bus.RxBus; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.data.ServiceTransport; +import info.nightscout.androidaps.plugins.pump.medtronic.MedtronicPumpPlugin; +import info.nightscout.androidaps.plugins.pump.medtronic.events.EventRefreshButtonState; +import info.nightscout.androidaps.plugins.pump.medtronic.service.RileyLinkMedtronicService; + +/** + * Created by geoff on 7/16/16. + */ +public class WakeAndTuneTask extends PumpTask { + + private static final String TAG = "WakeAndTuneTask"; + + + public WakeAndTuneTask() { + } + + + public WakeAndTuneTask(ServiceTransport transport) { + super(transport); + } + + + @Override + public void run() { + RxBus.INSTANCE.send(new EventRefreshButtonState(false)); + MedtronicPumpPlugin.isBusy = true; + RileyLinkMedtronicService.getInstance().doTuneUpDevice(); + MedtronicPumpPlugin.isBusy = false; + RxBus.INSTANCE.send(new EventRefreshButtonState(true)); + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/ui/RileyLinkSelectPreference.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/ui/RileyLinkSelectPreference.java new file mode 100644 index 0000000000..de9d28d46b --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/ui/RileyLinkSelectPreference.java @@ -0,0 +1,40 @@ +package info.nightscout.androidaps.plugins.pump.common.ui; + +import android.content.Context; +import android.preference.Preference; +import android.util.AttributeSet; + +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil; +import info.nightscout.androidaps.utils.SP; + +/** + * Created by andy on 10/18/18. + */ + +public class RileyLinkSelectPreference extends Preference { + + public RileyLinkSelectPreference(Context context) { + super(context); + setInitialSummaryValue(); + + MedtronicUtil.setRileyLinkSelectPreference(this); + } + + + public RileyLinkSelectPreference(Context context, AttributeSet attrs) { + super(context, attrs); + setInitialSummaryValue(); + + MedtronicUtil.setRileyLinkSelectPreference(this); + } + + + private void setInitialSummaryValue() { + String value = SP.getString("pref_rileylink_mac_address", null); + + setSummary(value == null ? MainApp.gs(R.string.rileylink_error_address_not_set_short) : value); + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/utils/ByteUtil.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/utils/ByteUtil.java new file mode 100644 index 0000000000..5451689ebe --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/utils/ByteUtil.java @@ -0,0 +1,450 @@ +package info.nightscout.androidaps.plugins.pump.common.utils; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by geoff on 4/28/15. + */ +public class ByteUtil { + + private final static char[] HEX_DIGITS = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + + private final static String HEX_DIGITS_STR = "0123456789ABCDEF"; + + + public static byte highByte(short s) { + return (byte) (s / 256); + } + + + public static byte lowByte(short s) { + return (byte) (s % 256); + } + + + public static int asUINT8(byte b) { + return (b < 0) ? b + 256 : b; + } + + + /* For Reference: static void System.arraycopy(Object src, int srcPos, Object dest, int destPos, int length) */ + + public static byte[] concat(byte[] a, byte[] b) { + + if (b == null) { + return a; + } + + int aLen = a.length; + int bLen = b.length; + byte[] c = new byte[aLen + bLen]; + System.arraycopy(a, 0, c, 0, aLen); + System.arraycopy(b, 0, c, aLen, bLen); + return c; + } + + + public static byte[] concat(byte[] a, byte b) { + int aLen = a.length; + byte[] c = new byte[aLen + 1]; + System.arraycopy(a, 0, c, 0, aLen); + c[aLen] = b; + return c; + } + + + public static byte[] concat(byte a, byte[] b) { + int aLen = b.length; + byte[] c = new byte[aLen + 1]; + c[0] = a; + System.arraycopy(b, 0, c, 1, aLen); + + return c; + } + + + public static byte[] substring(byte[] a, int start, int len) { + byte[] rval = new byte[len]; + System.arraycopy(a, start, rval, 0, len); + return rval; + } + + public static byte[] substring(List a, int start, int len) { + byte[] rval = new byte[len]; + + for (int i = start, j = 0; i < start + len; i++, j++) { + rval[j] = a.get(i); + } + return rval; + } + + + public static byte[] substring(byte[] a, int start) { + int len = a.length - start; + byte[] rval = new byte[len]; + System.arraycopy(a, start, rval, 0, len); + return rval; + } + + + public static String shortHexString(byte[] ra) { + String rval = ""; + if (ra == null) { + return rval; + } + if (ra.length == 0) { + return rval; + } + for (int i = 0; i < ra.length; i++) { + rval = rval + HEX_DIGITS[(ra[i] & 0xF0) >> 4]; + rval = rval + HEX_DIGITS[(ra[i] & 0x0F)]; + if (i < ra.length - 1) { + rval = rval + " "; + } + } + return rval; + } + + public static String shortHexString(List list) { + + byte[] abyte0 = getByteArrayFromList(list); + + return shortHexString(abyte0); + } + + + public static String shortHexString(byte val) { + return getHexCompact(val); + } + + + public static String showPrintable(byte[] ra) { + String s = new String(); + for (int i = 0; i < ra.length; i++) { + char c = (char) ra[i]; + if (((c >= '0') && (c <= '9')) || ((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z'))) { + s = s + c; + } else { + s = s + '.'; + } + } + return s; + } + + + public static byte[] fromHexString(String src) { + String s = src.toUpperCase(); + byte[] rval = new byte[]{}; + if ((s.length() % 2) != 0) { + // invalid hex string! + return null; + } + for (int i = 0; i < s.length(); i += 2) { + int highNibbleOrd = HEX_DIGITS_STR.indexOf(s.charAt(i)); + if (highNibbleOrd < 0) { + // Not a hex digit. + return null; + } + int lowNibbleOrd = HEX_DIGITS_STR.indexOf(s.charAt(i + 1)); + if (lowNibbleOrd < 0) { + // Not a hex digit + return null; + } + rval = concat(rval, (byte) (highNibbleOrd * 16 + lowNibbleOrd)); + } + return rval; + } + + + // public static byte[] fromByteList(List byteArray) { + // byte[] rval = new byte[byteArray.size()]; + // for (int i = 0; i < byteArray.size(); i++) { + // rval[i] = byteArray.get(i); + // } + // return rval; + // } + + // public static List toByteList(byte[] data) { + // ArrayList rval = new ArrayList<>(data.length); + // for (int i = 0; i < data.length; i++) { + // rval.add(i, new Byte(data[i])); + // } + // return rval; + // } + + public static List getListFromByteArray(byte[] array) { + List listOut = new ArrayList(); + + for (byte val : array) { + listOut.add(val); + } + + return listOut; + } + + + public static byte[] getByteArrayFromList(List list) { + byte[] out = new byte[list.size()]; + + for (int i = 0; i < list.size(); i++) { + out[i] = list.get(i); + } + + return out; + } + + + // compares byte strings like strcmp + public static int compare(byte[] s1, byte[] s2) { + int i; + int len1 = s1.length; + int len2 = s2.length; + if (len1 > len2) { + return 1; + } + if (len2 > len1) { + return -1; + } + int acc = 0; + for (i = 0; i < len1; i++) { + acc += s1[i]; + acc -= s2[i]; + if (acc != 0) { + return acc; + } + } + return 0; + } + + + /** + * Converts 4 (or less) ints into int. (Shorts are objects, so you can send null if you have less parameters) + * + * @param b1 short 1 + * @param b2 short 2 + * @param b3 short 3 + * @param b4 short 4 + * @param flag Conversion Flag (Big Endian, Little endian) + * @return int value + */ + public static int toInt(Integer b1, Integer b2, Integer b3, Integer b4, BitConversion flag) { + switch (flag) { + case LITTLE_ENDIAN: { + if (b4 != null) { + return (b4 & 0xff) << 24 | (b3 & 0xff) << 16 | (b2 & 0xff) << 8 | b1 & 0xff; + } else if (b3 != null) { + return (b3 & 0xff) << 16 | (b2 & 0xff) << 8 | b1 & 0xff; + } else if (b2 != null) { + return (b2 & 0xff) << 8 | b1 & 0xff; + } else { + return b1 & 0xff; + } + } + + default: + case BIG_ENDIAN: { + if (b4 != null) { + return (b1 & 0xff) << 24 | (b2 & 0xff) << 16 | (b3 & 0xff) << 8 | b4 & 0xff; + } else if (b3 != null) { + return (b1 & 0xff) << 16 | (b2 & 0xff) << 8 | b3 & 0xff; + } else if (b2 != null) { + return (b1 & 0xff) << 8 | b2 & 0xff; + } else { + return b1 & 0xff; + } + } + } + } + + + public static int toInt(int b1, int b2) { + return toInt(b1, b2, null, null, BitConversion.BIG_ENDIAN); + } + + + public static int toInt(int b1, int b2, int b3) { + return toInt(b1, b2, b3, null, BitConversion.BIG_ENDIAN); + } + + + public static int toInt(int b1, int b2, BitConversion flag) { + return toInt(b1, b2, null, null, flag); + } + + + public static int makeUnsignedShort(int i, int j) { + int k = (i & 0xff) << 8 | j & 0xff; + return k; + } + + + /** + * Gets the correct hex value. + * + * @param inp the inp + * @return the correct hex value + */ + public static String getCorrectHexValue(int inp) { + String hx = Integer.toHexString((char) inp); + + if (hx.length() == 0) + return "00"; + else if (hx.length() == 1) + return "0" + hx; + else if (hx.length() == 2) + return hx; + else if (hx.length() == 4) + return hx.substring(2); + else { + System.out.println("Hex Error: " + inp); + } + + return null; + } + + + public static String getHex(byte[] abyte0) { + return abyte0 != null ? getHex(abyte0, abyte0.length) : null; + } + + + public static String getString(short[] abyte0) { + StringBuilder sb = new StringBuilder(); + + for (short i : abyte0) { + sb.append(i); + sb.append(" "); + } + + return sb.toString(); + } + + + public static String getHex(List list) { + + byte[] abyte0 = getByteArrayFromList(list); + + return abyte0 != null ? getHex(abyte0, abyte0.length) : null; + } + + + public static String getHex(byte[] abyte0, int i) { + StringBuffer stringbuffer = new StringBuffer(); + if (abyte0 != null) { + i = Math.min(i, abyte0.length); + for (int j = 0; j < i; j++) { + stringbuffer.append(shortHexString(abyte0[j])); + if (j < i - 1) { + stringbuffer.append(" "); + } + } + + } + return new String(stringbuffer); + } + + + public static String getHex(byte byte0) { + String s = byte0 != -1 ? "0x" : ""; + return s + getHexCompact(byte0); + } + + + public static String getHexCompact(byte byte0) { + int i = byte0 != -1 ? convertUnsignedByteToInt(byte0) : (int) byte0; + return getHexCompact(i); + } + + + public static int convertUnsignedByteToInt(byte data) { + return data & 0xff; + } + + + // public String getHexCompact(int i) { + // long l = i != -1 ? convertUnsignedIntToLong(i) : i; + // return getHexCompact(l); + // } + + public static String getHexCompact(int l) { + String s = Long.toHexString(l).toUpperCase(); + String s1 = isOdd(s.length()) ? "0" : ""; + return l != -1L ? s1 + s : "-1"; + } + + + public static boolean isEven(int i) { + return i % 2 == 0; + } + + + public static boolean isOdd(int i) { + return !isEven(i); + } + + public enum BitConversion { + LITTLE_ENDIAN, // 20 0 0 0 = reverse + BIG_ENDIAN // 0 0 0 20 = normal - java + } + + + public static String getCompactString(byte[] data) { + if (data == null) + return "null"; + + String vval2 = ByteUtil.getHex(data); + vval2 = vval2.replace(" 0x", ""); + vval2 = vval2.replace("0x", ""); + return vval2; + } + + + // 000300050100C800A0 + public static byte[] createByteArrayFromCompactString(String dataFull) { + return createByteArrayFromCompactString(dataFull, 0, dataFull.length()); + } + + + // 00 03 00 05 01 00 C8 00 A0 + public static byte[] createByteArrayFromString(String dataFull) { + + String data = dataFull.replace(" ", ""); + + return createByteArrayFromCompactString(data, 0, data.length()); + } + + + public static byte[] createByteArrayFromHexString(String dataFull) { + + String data = dataFull.replace(" 0x", ""); + data = data.replace("0x", ""); + + return createByteArrayFromCompactString(data, 0, data.length()); + } + + + public static byte[] createByteArrayFromCompactString(String dataFull, int startIndex) { + return createByteArrayFromCompactString(dataFull, startIndex, dataFull.length()); + } + + + public static byte[] createByteArrayFromCompactString(String dataFull, int startIndex, int length) { + + String data = dataFull.substring(startIndex); + + data = data.substring(0, length); + + int len = data.length(); + byte[] outArray = new byte[len / 2]; + for (int i = 0; i < len; i += 2) { + outArray[i / 2] = (byte) ((Character.digit(data.charAt(i), 16) << 4) + Character.digit(data.charAt(i + 1), + 16)); + } + + return outArray; + } + + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/utils/CRC.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/utils/CRC.java new file mode 100644 index 0000000000..ac90d1edb8 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/utils/CRC.java @@ -0,0 +1,153 @@ +package info.nightscout.androidaps.plugins.pump.common.utils; + +/** + * Created by geoff on 4/27/15. + */ +public class CRC { + + static final int[] crc8lookup = new int[] { + 0, 155, 173, 54, 193, 90, 108, 247, 25, + 130, + 180, + 47, + 216, + 67, + 117, + 238, + 50, + 169, // + 159, 4, 243, 104, 94, 197, 43, 176, 134, 29, 234, 113, 71, 220, 100, 255, 201, + 82, + 165, + 62, + 8, + 147, + 125, + 230, + 208, + 75, // + 188, 39, 17, 138, 86, 205, 251, 96, 151, 12, 58, 161, 79, 212, 226, 121, 142, 21, + 35, + 184, + 200, + 83, + 101, + 254, + 9, + 146, // + 164, 63, 209, 74, 124, 231, 16, 139, 189, 38, 250, 97, 87, 204, 59, 160, 150, 13, 227, + 120, + 78, + 213, + 34, + 185, + 143, + 20, // + 172, 55, 1, 154, 109, 246, 192, 91, 181, 46, 24, 131, 116, 239, 217, 66, 158, 5, 51, 168, 95, + 196, + 242, + 105, + 135, + 28, + 42, // + 177, 70, 221, 235, 112, 11, 144, 166, 61, 202, 81, 103, 252, 18, 137, 191, 36, 211, 72, 126, 229, + 57, + 162, + 148, + 15, + 248, // + 99, 85, 206, 32, 187, 141, 22, 225, 122, 76, 215, 111, 244, 194, 89, 174, 53, 3, 152, 118, 237, 219, 64, + 183, + 44, + 26, + 129, // + 93, 198, 240, 107, 156, 7, 49, 170, 68, 223, 233, 114, 133, 30, 40, 179, 195, 88, 110, 245, 2, 153, 175, 52, + 218, + 65, + 119, // + 236, 27, 128, 182, 45, 241, 106, 92, 199, 48, 171, 157, 6, 232, 115, 69, 222, 41, 178, 132, 31, 167, 60, 10, + 145, 102, + 253, // + 203, 80, 190, 37, 19, 136, 127, 228, 210, 73, 149, 14, 56, 163, 84, 207, 249, 98, 140, 23, 33, 186, 77, 214, + 224, 123 }; + + static final int[] crc16lookup = new int[] { + 0x0000, 0x8005, 0x800f, 0x000a, 0x801b, 0x001e, 0x0014, 0x8011, 0x8033, 0x0036, 0x003c, 0x8039, 0x0028, 0x802d, + 0x8027, 0x0022, 0x8063, 0x0066, 0x006c, 0x8069, 0x0078, 0x807d, 0x8077, 0x0072, 0x0050, 0x8055, 0x805f, 0x005a, + 0x804b, 0x004e, 0x0044, 0x8041, 0x80c3, 0x00c6, 0x00cc, 0x80c9, 0x00d8, 0x80dd, 0x80d7, 0x00d2, 0x00f0, 0x80f5, + 0x80ff, 0x00fa, 0x80eb, 0x00ee, 0x00e4, 0x80e1, 0x00a0, 0x80a5, 0x80af, 0x00aa, 0x80bb, 0x00be, 0x00b4, 0x80b1, + 0x8093, 0x0096, 0x009c, 0x8099, 0x0088, 0x808d, 0x8087, 0x0082, 0x8183, 0x0186, 0x018c, 0x8189, 0x0198, 0x819d, + 0x8197, 0x0192, 0x01b0, 0x81b5, 0x81bf, 0x01ba, 0x81ab, 0x01ae, 0x01a4, 0x81a1, 0x01e0, 0x81e5, 0x81ef, 0x01ea, + 0x81fb, 0x01fe, 0x01f4, 0x81f1, 0x81d3, 0x01d6, 0x01dc, 0x81d9, 0x01c8, 0x81cd, 0x81c7, 0x01c2, 0x0140, 0x8145, + 0x814f, 0x014a, 0x815b, 0x015e, 0x0154, 0x8151, 0x8173, 0x0176, 0x017c, 0x8179, 0x0168, 0x816d, 0x8167, 0x0162, + 0x8123, 0x0126, 0x012c, 0x8129, 0x0138, 0x813d, 0x8137, 0x0132, 0x0110, 0x8115, 0x811f, 0x011a, 0x810b, 0x010e, + 0x0104, 0x8101, 0x8303, 0x0306, 0x030c, 0x8309, 0x0318, 0x831d, 0x8317, 0x0312, 0x0330, 0x8335, 0x833f, 0x033a, + 0x832b, 0x032e, 0x0324, 0x8321, 0x0360, 0x8365, 0x836f, 0x036a, 0x837b, 0x037e, 0x0374, 0x8371, 0x8353, 0x0356, + 0x035c, 0x8359, 0x0348, 0x834d, 0x8347, 0x0342, 0x03c0, 0x83c5, 0x83cf, 0x03ca, 0x83db, 0x03de, 0x03d4, 0x83d1, + 0x83f3, 0x03f6, 0x03fc, 0x83f9, 0x03e8, 0x83ed, 0x83e7, 0x03e2, 0x83a3, 0x03a6, 0x03ac, 0x83a9, 0x03b8, 0x83bd, + 0x83b7, 0x03b2, 0x0390, 0x8395, 0x839f, 0x039a, 0x838b, 0x038e, 0x0384, 0x8381, 0x0280, 0x8285, 0x828f, 0x028a, + 0x829b, 0x029e, 0x0294, 0x8291, 0x82b3, 0x02b6, 0x02bc, 0x82b9, 0x02a8, 0x82ad, 0x82a7, 0x02a2, 0x82e3, 0x02e6, + 0x02ec, 0x82e9, 0x02f8, 0x82fd, 0x82f7, 0x02f2, 0x02d0, 0x82d5, 0x82df, 0x02da, 0x82cb, 0x02ce, 0x02c4, 0x82c1, + 0x8243, 0x0246, 0x024c, 0x8249, 0x0258, 0x825d, 0x8257, 0x0252, 0x0270, 0x8275, 0x827f, 0x027a, 0x826b, 0x026e, + 0x0264, 0x8261, 0x0220, 0x8225, 0x822f, 0x022a, 0x823b, 0x023e, 0x0234, 0x8231, 0x8213, 0x0216, 0x021c, 0x8219, + 0x0208, 0x820d, 0x8207, 0x0202 }; + + + public static byte crc8(byte[] data, int len) { + byte result = 0; + if (data == null) { + return 0; + } + if (len > data.length) { + len = data.length; + } + for (int i = 0; i < len; i++) { + int tmp = result; + int tmp2 = tmp ^ data[i]; + int tmp3 = tmp2 & 0xFF; + int idx = tmp3; + result = (byte)crc8lookup[idx]; + // log(String.format("iter=%d,tmp=0x%02x, tmp2=0x%02x, tmp3=0x%02x, lookup=0x%02x",i,tmp,tmp2,tmp3,result)); + } + // orig python: + // result = klass.lookup[ ( result ^ block[ i ] & 0xFF ) ] + return result; + + } + + + public static byte crc8(byte[] data) { + return crc8(data, data.length); + } + + + public static byte[] calculate16CCITT(byte[] data) { + int crc = 0xFFFF; + int polynomial = 0x1021; + if (data != null) { + if (data.length > 0) { + for (int j = 0; j < data.length; j++) { + byte b = data[j]; + for (int i = 0; i < 8; i++) { + boolean bit = ((b >> (7 - i) & 1) == 1); + boolean c15 = ((crc >> 15 & 1) == 1); + crc <<= 1; + if (c15 ^ bit) + crc ^= polynomial; + } + } + } + } + crc &= 0xffff; + return new byte[] { (byte)((crc & 0xFF00) >> 8), (byte)(crc & 0xFF) }; + } + + + public static int crc16(byte[] bytes) { + int crc = 0x0000; + for (byte b : bytes) { + crc = (crc >>> 8) ^ crc16lookup[(crc ^ b) & 0xff]; + } + return crc; + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/utils/DateTimeUtil.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/utils/DateTimeUtil.java new file mode 100644 index 0000000000..783553f431 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/utils/DateTimeUtil.java @@ -0,0 +1,278 @@ +package info.nightscout.androidaps.plugins.pump.common.utils; + +/** + * Created by andy on 10/25/18. + */ + +import org.joda.time.LocalDateTime; +import org.joda.time.Minutes; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Calendar; +import java.util.GregorianCalendar; + +import info.nightscout.androidaps.logging.L; + +/** + * This is simple version of ATechDate, limited only to one format (yyyymmddHHMIss) + */ +public class DateTimeUtil { + + private static final Logger LOG = LoggerFactory.getLogger(L.PUMPCOMM); + + /** + * DateTime is packed as long: yyyymmddHHMMss + * + * @param atechDateTime + * @return + */ + public static LocalDateTime toLocalDateTime(long atechDateTime) { + int year = (int) (atechDateTime / 10000000000L); + atechDateTime -= year * 10000000000L; + + int month = (int) (atechDateTime / 100000000L); + atechDateTime -= month * 100000000L; + + int dayOfMonth = (int) (atechDateTime / 1000000L); + atechDateTime -= dayOfMonth * 1000000L; + + int hourOfDay = (int) (atechDateTime / 10000L); + atechDateTime -= hourOfDay * 10000L; + + int minute = (int) (atechDateTime / 100L); + atechDateTime -= minute * 100L; + + int second = (int) atechDateTime; + + try { + return new LocalDateTime(year, month, dayOfMonth, hourOfDay, minute, second); + } catch (Exception ex) { + if (L.isEnabled(L.PUMPCOMM)) + LOG.error("Error creating LocalDateTime from values [atechDateTime={}, year={}, month={}, day={}, hour={}, minute={}, second={}]. Exception: {}", atechDateTime, year, month, dayOfMonth, hourOfDay, minute, second, ex.getMessage()); + //return null; + throw ex; + } + } + + + /** + * DateTime is packed as long: yyyymmddHHMMss + * + * @param atechDateTime + * @return + */ + public static GregorianCalendar toGregorianCalendar(long atechDateTime) { + int year = (int) (atechDateTime / 10000000000L); + atechDateTime -= year * 10000000000L; + + int month = (int) (atechDateTime / 100000000L); + atechDateTime -= month * 100000000L; + + int dayOfMonth = (int) (atechDateTime / 1000000L); + atechDateTime -= dayOfMonth * 1000000L; + + int hourOfDay = (int) (atechDateTime / 10000L); + atechDateTime -= hourOfDay * 10000L; + + int minute = (int) (atechDateTime / 100L); + atechDateTime -= minute * 100L; + + int second = (int) atechDateTime; + + try { + return new GregorianCalendar(year, month - 1, dayOfMonth, hourOfDay, minute, second); + } catch (Exception ex) { + if (L.isEnabled(L.PUMPCOMM)) + LOG.error("DateTimeUtil", String.format("Error creating GregorianCalendar from values [atechDateTime=%d, year=%d, month=%d, day=%d, hour=%d, minute=%d, second=%d]", atechDateTime, year, month, dayOfMonth, hourOfDay, minute, second)); + //return null; + throw ex; + } + } + + + public static long toATechDate(LocalDateTime ldt) { + long atechDateTime = 0L; + + atechDateTime += ldt.getYear() * 10000000000L; + atechDateTime += ldt.getMonthOfYear() * 100000000L; + atechDateTime += ldt.getDayOfMonth() * 1000000L; + atechDateTime += ldt.getHourOfDay() * 10000L; + atechDateTime += ldt.getMinuteOfHour() * 100L; + atechDateTime += ldt.getSecondOfMinute(); + + return atechDateTime; + } + + + public static long toATechDate(GregorianCalendar gc) { + long atechDateTime = 0L; + + atechDateTime += gc.get(Calendar.YEAR) * 10000000000L; + atechDateTime += (gc.get(Calendar.MONTH) + 1) * 100000000L; + atechDateTime += gc.get(Calendar.DAY_OF_MONTH) * 1000000L; + atechDateTime += gc.get(Calendar.HOUR_OF_DAY) * 10000L; + atechDateTime += gc.get(Calendar.MINUTE) * 100L; + atechDateTime += gc.get(Calendar.SECOND); + + return atechDateTime; + } + + + public static boolean isSameDay(LocalDateTime ldt1, LocalDateTime ldt2) { + + return (ldt1.getYear() == ldt2.getYear() && // + ldt1.getMonthOfYear() == ldt2.getMonthOfYear() && // + ldt1.getDayOfMonth() == ldt2.getDayOfMonth()); + + } + + + public static boolean isSameDay(long ldt1, long ldt2) { + + long day1 = ldt1 / 10000L; + long day2 = ldt2 / 10000L; + + return day1 == day2; + } + + + public static long toATechDate(int year, int month, int dayOfMonth, int hour, int minutes, int seconds) { + + long atechDateTime = 0L; + + atechDateTime += year * 10000000000L; + atechDateTime += month * 100000000L; + atechDateTime += dayOfMonth * 1000000L; + atechDateTime += hour * 10000L; + atechDateTime += minutes * 100L; + atechDateTime += seconds; + + return atechDateTime; + } + + +// public static long toATechDate(Date date) { +// +// long atechDateTime = 0L; +// +// atechDateTime += (date.getYear() + 1900) * 10000000000L; +// atechDateTime += (date.getMonth() + 1) * 100000000L; +// atechDateTime += date.getDate() * 1000000L; +// atechDateTime += date.getHours() * 10000L; +// atechDateTime += date.getMinutes() * 100L; +// atechDateTime += date.getSeconds(); +// +// return atechDateTime; +// } + + + public static String toString(long atechDateTime) { + int year = (int) (atechDateTime / 10000000000L); + atechDateTime -= year * 10000000000L; + + int month = (int) (atechDateTime / 100000000L); + atechDateTime -= month * 100000000L; + + int dayOfMonth = (int) (atechDateTime / 1000000L); + atechDateTime -= dayOfMonth * 1000000L; + + int hourOfDay = (int) (atechDateTime / 10000L); + atechDateTime -= hourOfDay * 10000L; + + int minute = (int) (atechDateTime / 100L); + atechDateTime -= minute * 100L; + + int second = (int) atechDateTime; + + return getZeroPrefixed(dayOfMonth) + "." + getZeroPrefixed(month) + "." + year + " " + // + getZeroPrefixed(hourOfDay) + ":" + getZeroPrefixed(minute) + ":" + getZeroPrefixed(second); + } + + + public static String toString(GregorianCalendar gc) { + + return getZeroPrefixed(gc.get(Calendar.DAY_OF_MONTH)) + "." + getZeroPrefixed(gc.get(Calendar.MONTH) + 1) + "." + + gc.get(Calendar.YEAR) + " " + + // + getZeroPrefixed(gc.get(Calendar.HOUR_OF_DAY)) + ":" + getZeroPrefixed(gc.get(Calendar.MINUTE)) + ":" + + getZeroPrefixed(gc.get(Calendar.SECOND)); + } + + + public static String toStringFromTimeInMillis(long timeInMillis) { + + GregorianCalendar gc = new GregorianCalendar(); + gc.setTimeInMillis(timeInMillis); + + return toString(gc); + } + + + private static String getZeroPrefixed(int number) { + return (number < 10) ? "0" + number : "" + number; + } + + + public static int getYear(Long atechDateTime) { + + if (atechDateTime == null || atechDateTime == 0) { + return 2000; + } + + int year = (int) (atechDateTime / 10000000000L); + return year; + } + + + public static boolean isSameDayATDAndMillis(long atechDateTime, long timeInMillis) { + + GregorianCalendar dt = new GregorianCalendar(); + dt.setTimeInMillis(timeInMillis); + + long entryDate = toATechDate(dt); + + return (isSameDay(atechDateTime, entryDate)); + } + + + public static long toMillisFromATD(long atechDateTime) { + + GregorianCalendar gc = toGregorianCalendar(atechDateTime); + + return gc.getTimeInMillis(); + } + + + public static int getATechDateDiferenceAsMinutes(Long date1, Long date2) { + + Minutes minutes = Minutes.minutesBetween(toLocalDateTime(date1), toLocalDateTime(date2)); + + return minutes.getMinutes(); + } + + + public static long getMillisFromATDWithAddedMinutes(long atd, int minutesDiff) { + GregorianCalendar oldestEntryTime = DateTimeUtil.toGregorianCalendar(atd); + oldestEntryTime.add(Calendar.MINUTE, minutesDiff); + + return oldestEntryTime.getTimeInMillis(); + } + + + public static long getATDWithAddedMinutes(long atd, int minutesDiff) { + GregorianCalendar oldestEntryTime = DateTimeUtil.toGregorianCalendar(atd); + oldestEntryTime.add(Calendar.MINUTE, minutesDiff); + + return oldestEntryTime.getTimeInMillis(); + } + + + public static long getATDWithAddedMinutes(GregorianCalendar oldestEntryTime, int minutesDiff) { + oldestEntryTime.add(Calendar.MINUTE, minutesDiff); + + return toATechDate(oldestEntryTime); + } + + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/utils/LocationHelper.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/utils/LocationHelper.java new file mode 100644 index 0000000000..98682679a7 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/utils/LocationHelper.java @@ -0,0 +1,80 @@ +package info.nightscout.androidaps.plugins.pump.common.utils; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.location.LocationManager; +import android.os.Build; + +import info.nightscout.androidaps.R; + +/** + * Helper for checking if location services are enabled on the device. + */ +public class LocationHelper { + + /** + * Determine if GPS is currently enabled. + *

+ * On Android 6 (Marshmallow), location needs to be enabled for Bluetooth discovery to work. + * + * @param context The current app context. + * @return true if location is enabled, false otherwise. + */ + public static boolean isLocationEnabled(Context context) { + LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); + + return (locationManager != null && // + (locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) || // + locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER))); + + // return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER); + } + + + /** + * Prompt the user to enable GPS location if it isn't already on. + * + * @param parent The currently visible activity. + */ + public static void requestLocation(final Activity parent) { + if (LocationHelper.isLocationEnabled(parent)) { + return; + } + + // Shamelessly borrowed from http://stackoverflow.com/a/10311877/868533 + + AlertDialog.Builder builder = new AlertDialog.Builder(parent); + builder.setTitle(R.string.location_not_found_title); + builder.setMessage(R.string.location_not_found_message); + builder.setPositiveButton(R.string.location_yes, new DialogInterface.OnClickListener() { + + public void onClick(DialogInterface dialogInterface, int i) { + parent.startActivity(new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS)); + } + }); + builder.setNegativeButton(R.string.location_no, null); + builder.create().show(); + } + + + /** + * Prompt the user to enable GPS location on devices that need it for Bluetooth discovery. + *

+ * Android 6 (Marshmallow) needs GPS enabled for Bluetooth discovery to work. + * + * @param activity The currently visible activity. + */ + public static void requestLocationForBluetooth(Activity activity) { + // Location needs to be enabled for Bluetooth discovery on Marshmallow. + LocationHelper.requestLocation(activity); + } + + // public static Boolean locationPermission(ActivityWithMenu act) { + // return ActivityCompat.checkSelfPermission(act, Manifest.permission.ACCESS_FINE_LOCATION) == + // PackageManager.PERMISSION_GRANTED; + // } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/utils/StringUtil.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/utils/StringUtil.java new file mode 100644 index 0000000000..ed7e4e2634 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/utils/StringUtil.java @@ -0,0 +1,121 @@ +package info.nightscout.androidaps.plugins.pump.common.utils; + +import org.joda.time.LocalDateTime; + +import java.nio.charset.Charset; +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.List; + +/** + * Created by geoff on 4/28/15. + * modified by Andy + */ +public class StringUtil { + + public static DecimalFormat[] DecimalFormaters = { + new DecimalFormat("#0"), new DecimalFormat("#0.0"), new DecimalFormat("#0.00"), new DecimalFormat("#0.000")}; + + + public static String fromBytes(byte[] ra) { + if (ra == null) + return "null array"; + else + return new String(ra, Charset.forName("UTF-8")); + } + + + // these should go in some project-wide string utils package + public static String join(ArrayList ra, String joiner) { + int sz = ra.size(); + String rval = ""; + int n; + for (n = 0; n < sz; n++) { + rval = rval + ra.get(n); + if (n < sz - 1) { + rval = rval + joiner; + } + } + return rval; + } + + + public static String testJoin() { + ArrayList ra = new ArrayList(); + ra.add("one"); + ra.add("two"); + ra.add("three"); + return join(ra, "+"); + } + + + /** + * Append To StringBuilder + * + * @param stringBuilder + * @param stringToAdd + * @param delimiter + * @return + */ + public static void appendToStringBuilder(StringBuilder stringBuilder, String stringToAdd, String delimiter) { + if (stringBuilder.length() > 0) { + stringBuilder.append(delimiter + stringToAdd); + } else { + stringBuilder.append(stringToAdd); + } + } + + + public static String getFormatedValueUS(Number value, int decimals) { + return DecimalFormaters[decimals].format(value).replace(",", "."); + } + + + public static String getLeadingZero(int number, int places) { + String nn = "" + number; + + while (nn.length() < places) { + nn = "0" + nn; + } + + return nn; + } + + + public static String toDateTimeString(LocalDateTime localDateTime) { + return localDateTime.toString("dd.MM.yyyy HH:mm:ss"); + } + + + public static String getStringInLength(String value, int length) { + StringBuilder val = new StringBuilder(value); + + if (val.length() > length) { + return val.substring(0, length); + } + + for (int i = val.length(); i < length; i++) { + val.append(" "); + } + + return val.toString(); + } + + + public static List splitString(String s, int characters) { + + List outString = new ArrayList<>(); + + do { + if (s.length() > characters) { + String token = s.substring(0, characters); + outString.add(token); + s = s.substring(characters); + } + } while (s.length() > characters); + + outString.add(s); + + return outString; + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/utils/ThreadUtil.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/utils/ThreadUtil.java new file mode 100644 index 0000000000..d1781da72a --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/common/utils/ThreadUtil.java @@ -0,0 +1,22 @@ +package info.nightscout.androidaps.plugins.pump.common.utils; + +/** + * Created by geoff on 5/27/16. + */ +public class ThreadUtil { + + public static long getThreadId() { + return Thread.currentThread().getId(); + } + + + public static String getThreadName() { + return Thread.currentThread().getName(); + } + + + public static String sig() { + Thread t = Thread.currentThread(); + return t.getName() + "[" + t.getId() + "]"; + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/AbstractDanaRPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/AbstractDanaRPlugin.java index 749307e1a7..1a40e449e5 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/AbstractDanaRPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/AbstractDanaRPlugin.java @@ -1,6 +1,7 @@ package info.nightscout.androidaps.plugins.pump.danaR; -import android.support.annotation.Nullable; +import androidx.annotation.Nullable; +import androidx.fragment.app.FragmentActivity; import org.json.JSONException; import org.json.JSONObject; @@ -28,6 +29,8 @@ import info.nightscout.androidaps.interfaces.ProfileInterface; import info.nightscout.androidaps.interfaces.PumpDescription; import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.bus.RxBus; +import info.nightscout.androidaps.plugins.common.ManufacturerType; import info.nightscout.androidaps.plugins.general.actions.defs.CustomAction; import info.nightscout.androidaps.plugins.general.actions.defs.CustomActionType; import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification; @@ -76,6 +79,11 @@ public abstract class AbstractDanaRPlugin extends PluginBase implements PumpInte } } + @Override + public void switchAllowed(boolean newState, FragmentActivity activity, PluginType type) { + confirmPumpPluginActivation(newState, activity, type); + } + @Override public boolean isSuspended() { return DanaRPump.getInstance().pumpSuspended; @@ -100,22 +108,22 @@ public abstract class AbstractDanaRPlugin extends PluginBase implements PumpInte if (!isInitialized()) { log.error("setNewBasalProfile not initialized"); Notification notification = new Notification(Notification.PROFILE_NOT_SET_NOT_INITIALIZED, MainApp.gs(R.string.pumpNotInitializedProfileNotSet), Notification.URGENT); - MainApp.bus().post(new EventNewNotification(notification)); + RxBus.INSTANCE.send(new EventNewNotification(notification)); result.comment = MainApp.gs(R.string.pumpNotInitializedProfileNotSet); return result; } else { - MainApp.bus().post(new EventDismissNotification(Notification.PROFILE_NOT_SET_NOT_INITIALIZED)); + RxBus.INSTANCE.send(new EventDismissNotification(Notification.PROFILE_NOT_SET_NOT_INITIALIZED)); } if (!sExecutionService.updateBasalsInPump(profile)) { Notification notification = new Notification(Notification.FAILED_UDPATE_PROFILE, MainApp.gs(R.string.failedupdatebasalprofile), Notification.URGENT); - MainApp.bus().post(new EventNewNotification(notification)); + RxBus.INSTANCE.send(new EventNewNotification(notification)); result.comment = MainApp.gs(R.string.failedupdatebasalprofile); return result; } else { - MainApp.bus().post(new EventDismissNotification(Notification.PROFILE_NOT_SET_NOT_INITIALIZED)); - MainApp.bus().post(new EventDismissNotification(Notification.FAILED_UDPATE_PROFILE)); + RxBus.INSTANCE.send(new EventDismissNotification(Notification.PROFILE_NOT_SET_NOT_INITIALIZED)); + RxBus.INSTANCE.send(new EventDismissNotification(Notification.FAILED_UDPATE_PROFILE)); Notification notification = new Notification(Notification.PROFILE_SET_OK, MainApp.gs(R.string.profile_set_ok), Notification.INFO, 60); - MainApp.bus().post(new EventNewNotification(notification)); + RxBus.INSTANCE.send(new EventNewNotification(notification)); result.success = true; result.enacted = true; result.comment = "OK"; @@ -186,8 +194,8 @@ public abstract class AbstractDanaRPlugin extends PluginBase implements PumpInte if (percent > getPumpDescription().maxTempPercent) percent = getPumpDescription().maxTempPercent; long now = System.currentTimeMillis(); - TemporaryBasal runningTB = TreatmentsPlugin.getPlugin().getRealTempBasalFromHistory(now); - if (runningTB != null && runningTB.percentRate == percent && !enforceNew) { + TemporaryBasal activeTemp = TreatmentsPlugin.getPlugin().getRealTempBasalFromHistory(now); + if (activeTemp != null && activeTemp.percentRate == percent && activeTemp.getPlannedRemainingMinutes() > 4 && !enforceNew) { result.enacted = false; result.success = true; result.isTempCancel = false; @@ -376,7 +384,12 @@ public abstract class AbstractDanaRPlugin extends PluginBase implements PumpInte } @Override - public String deviceID() { + public ManufacturerType manufacturer() { + return ManufacturerType.Sooil; + } + + @Override + public String serialNumber() { return DanaRPump.getInstance().serialNumber; } @@ -469,7 +482,6 @@ public abstract class AbstractDanaRPlugin extends PluginBase implements PumpInte if (!veryShort) { ret += "TDD: " + DecimalFormatter.to0Decimal(pump.dailyTotalUnits) + " / " + pump.maxDailyTotalUnits + " U\n"; } - ret += "IOB: " + pump.iob + "U\n"; ret += "Reserv: " + DecimalFormatter.to0Decimal(pump.reservoirRemainingUnits) + "U\n"; ret += "Batt: " + pump.batteryRemaining + "\n"; return ret; @@ -493,7 +505,10 @@ public abstract class AbstractDanaRPlugin extends PluginBase implements PumpInte return false; } + @Override + public void timeDateOrTimeZoneChanged() { + } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/DanaRFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/DanaRFragment.java deleted file mode 100644 index e044a2db27..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/DanaRFragment.java +++ /dev/null @@ -1,304 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.danaR; - - -import android.app.Activity; -import android.content.Intent; -import android.os.Bundle; -import android.os.Handler; -import android.support.v4.app.FragmentManager; -import android.text.Spanned; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.Button; -import android.widget.LinearLayout; -import android.widget.TextView; - -import com.squareup.otto.Subscribe; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.OnClick; -import info.nightscout.androidaps.MainApp; -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.activities.TDDStatsActivity; -import info.nightscout.androidaps.db.ExtendedBolus; -import info.nightscout.androidaps.db.TemporaryBasal; -import info.nightscout.androidaps.events.EventExtendedBolusChange; -import info.nightscout.androidaps.events.EventPumpStatusChanged; -import info.nightscout.androidaps.events.EventTempBasalChange; -import info.nightscout.androidaps.interfaces.PluginType; -import info.nightscout.androidaps.logging.L; -import info.nightscout.androidaps.plugins.common.SubscriberFragment; -import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; -import info.nightscout.androidaps.plugins.pump.danaR.dialogs.ProfileViewDialog; -import info.nightscout.androidaps.plugins.pump.danaR.activities.DanaRHistoryActivity; -import info.nightscout.androidaps.plugins.pump.danaR.activities.DanaRUserOptionsActivity; -import info.nightscout.androidaps.plugins.pump.danaR.events.EventDanaRNewStatus; -import info.nightscout.androidaps.plugins.pump.danaRKorean.DanaRKoreanPlugin; -import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; -import info.nightscout.androidaps.queue.events.EventQueueChanged; -import info.nightscout.androidaps.utils.DateUtil; -import info.nightscout.androidaps.utils.DecimalFormatter; -import info.nightscout.androidaps.utils.SetWarnColor; - -public class DanaRFragment extends SubscriberFragment { - private static Logger log = LoggerFactory.getLogger(L.PUMP); - - private Handler loopHandler = new Handler(); - private Runnable refreshLoop = new Runnable() { - @Override - public void run() { - updateGUI(); - loopHandler.postDelayed(refreshLoop, 60 * 1000L); - } - }; - - @BindView(R.id.danar_lastconnection) - TextView lastConnectionView; - @BindView(R.id.danar_btconnection) - TextView btConnectionView; - @BindView(R.id.danar_lastbolus) - TextView lastBolusView; - @BindView(R.id.danar_dailyunits) - TextView dailyUnitsView; - @BindView(R.id.danar_basabasalrate) - TextView basaBasalRateView; - @BindView(R.id.danar_tempbasal) - TextView tempBasalView; - @BindView(R.id.danar_extendedbolus) - TextView extendedBolusView; - @BindView(R.id.danar_battery) - TextView batteryView; - @BindView(R.id.danar_reservoir) - TextView reservoirView; - @BindView(R.id.danar_iob) - TextView iobView; - @BindView(R.id.danar_firmware) - TextView firmwareView; - @BindView(R.id.danar_basalstep) - TextView basalStepView; - @BindView(R.id.danar_bolusstep) - TextView bolusStepView; - @BindView(R.id.danar_serialnumber) - TextView serialNumberView; - @BindView(R.id.danar_queue) - TextView queueView; - - @BindView(R.id.overview_pumpstatuslayout) - LinearLayout pumpStatusLayout; - @BindView(R.id.overview_pumpstatus) - TextView pumpStatusView; - @BindView(R.id.danar_user_options) - Button danar_user_options; - - public DanaRFragment() { - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - loopHandler.postDelayed(refreshLoop, 60 * 1000L); - } - - @Override - public void onDestroy() { - super.onDestroy(); - loopHandler.removeCallbacks(refreshLoop); - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.danar_fragment, container, false); - unbinder = ButterKnife.bind(this, view); - - pumpStatusView.setBackgroundColor(MainApp.gc(R.color.colorInitializingBorder)); - - return view; - } - - @OnClick(R.id.danar_history) - void onHistoryClick() { - startActivity(new Intent(getContext(), DanaRHistoryActivity.class)); - } - - @OnClick(R.id.danar_viewprofile) - void onViewProfileClick() { - FragmentManager manager = getFragmentManager(); - ProfileViewDialog profileViewDialog = new ProfileViewDialog(); - profileViewDialog.show(manager, "ProfileViewDialog"); - } - - @OnClick(R.id.danar_stats) - void onStatsClick() { - startActivity(new Intent(getContext(), TDDStatsActivity.class)); - } - - @OnClick(R.id.danar_user_options) - void onUserOptionsClick() { - startActivity(new Intent(getContext(), DanaRUserOptionsActivity.class)); - } - - @OnClick(R.id.danar_btconnection) - void onBtConnectionClick() { - if (L.isEnabled(L.PUMP)) - log.debug("Clicked connect to pump"); - DanaRPump.getInstance().lastConnection = 0; - ConfigBuilderPlugin.getPlugin().getCommandQueue().readStatus("Clicked connect to pump", null); - } - - @Subscribe - public void onStatusEvent(final EventPumpStatusChanged c) { - Activity activity = getActivity(); - final String status = c.textStatus(); - if (activity != null) { - activity.runOnUiThread( - () -> { - synchronized (DanaRFragment.this) { - - if (btConnectionView == null || pumpStatusView == null || pumpStatusLayout == null) - return; - - if (c.sStatus == EventPumpStatusChanged.CONNECTING) - btConnectionView.setText("{fa-bluetooth-b spin} " + c.sSecondsElapsed + "s"); - else if (c.sStatus == EventPumpStatusChanged.CONNECTED) - btConnectionView.setText("{fa-bluetooth}"); - else if (c.sStatus == EventPumpStatusChanged.DISCONNECTED) - btConnectionView.setText("{fa-bluetooth-b}"); - - if (!status.equals("")) { - pumpStatusView.setText(status); - pumpStatusLayout.setVisibility(View.VISIBLE); - } else { - pumpStatusLayout.setVisibility(View.GONE); - } - } - } - ); - } - } - - @Subscribe - public void onStatusEvent(final EventDanaRNewStatus s) { - updateGUI(); - } - - @Subscribe - public void onStatusEvent(final EventTempBasalChange s) { - updateGUI(); - } - - @Subscribe - public void onStatusEvent(final EventExtendedBolusChange s) { - updateGUI(); - } - - @Subscribe - public void onStatusEvent(final EventQueueChanged s) { - updateGUI(); - } - - // GUI functions - @Override - protected void updateGUI() { - Activity activity = getActivity(); - if (activity != null && basaBasalRateView != null) - activity.runOnUiThread(() -> { - synchronized (DanaRFragment.this) { - if (!isBound()) return; - - DanaRPump pump = DanaRPump.getInstance(); - if (pump.lastConnection != 0) { - Long agoMsec = System.currentTimeMillis() - pump.lastConnection; - int agoMin = (int) (agoMsec / 60d / 1000d); - lastConnectionView.setText(DateUtil.timeString(pump.lastConnection) + " (" + String.format(MainApp.gs(R.string.minago), agoMin) + ")"); - SetWarnColor.setColor(lastConnectionView, agoMin, 16d, 31d); - } - if (pump.lastBolusTime != 0) { - Long agoMsec = System.currentTimeMillis() - pump.lastBolusTime; - double agoHours = agoMsec / 60d / 60d / 1000d; - if (agoHours < 6) // max 6h back - lastBolusView.setText(DateUtil.timeString(pump.lastBolusTime) + " " + DateUtil.sinceString(pump.lastBolusTime) + " " + DecimalFormatter.to2Decimal(DanaRPump.getInstance().lastBolusAmount) + " U"); - else lastBolusView.setText(""); - } - - dailyUnitsView.setText(DecimalFormatter.to0Decimal(pump.dailyTotalUnits) + " / " + pump.maxDailyTotalUnits + " U"); - SetWarnColor.setColor(dailyUnitsView, pump.dailyTotalUnits, pump.maxDailyTotalUnits * 0.75d, pump.maxDailyTotalUnits * 0.9d); - basaBasalRateView.setText("( " + (pump.activeProfile + 1) + " ) " + DecimalFormatter.to2Decimal(ConfigBuilderPlugin.getPlugin().getActivePump().getBaseBasalRate()) + " U/h"); - // DanaRPlugin, DanaRKoreanPlugin - if (ConfigBuilderPlugin.getPlugin().getActivePump().isFakingTempsByExtendedBoluses()) { - if (TreatmentsPlugin.getPlugin().isInHistoryRealTempBasalInProgress()) { - tempBasalView.setText(TreatmentsPlugin.getPlugin().getRealTempBasalFromHistory(System.currentTimeMillis()).toStringFull()); - } else { - tempBasalView.setText(""); - } - } else { - // v2 plugin - TemporaryBasal tb = TreatmentsPlugin.getPlugin().getTempBasalFromHistory(System.currentTimeMillis()); - if (tb != null) { - tempBasalView.setText(tb.toStringFull()); - } else { - tempBasalView.setText(""); - } - } - ExtendedBolus activeExtendedBolus = TreatmentsPlugin.getPlugin().getExtendedBolusFromHistory(System.currentTimeMillis()); - if (activeExtendedBolus != null) { - extendedBolusView.setText(activeExtendedBolus.toString()); - } else { - extendedBolusView.setText(""); - } - reservoirView.setText(DecimalFormatter.to0Decimal(pump.reservoirRemainingUnits) + " / 300 U"); - SetWarnColor.setColorInverse(reservoirView, pump.reservoirRemainingUnits, 50d, 20d); - batteryView.setText("{fa-battery-" + (pump.batteryRemaining / 25) + "}"); - SetWarnColor.setColorInverse(batteryView, pump.batteryRemaining, 51d, 26d); - iobView.setText(pump.iob + " U"); - if (pump.model != 0 || pump.protocol != 0 || pump.productCode != 0) { - firmwareView.setText(String.format(MainApp.gs(R.string.danar_model), pump.model, pump.protocol, pump.productCode)); - } else { - firmwareView.setText("OLD"); - } - basalStepView.setText("" + pump.basalStep); - bolusStepView.setText("" + pump.bolusStep); - serialNumberView.setText("" + pump.serialNumber); - if (queueView != null) { - Spanned status = ConfigBuilderPlugin.getPlugin().getCommandQueue().spannedStatus(); - if (status.toString().equals("")) { - queueView.setVisibility(View.GONE); - } else { - queueView.setVisibility(View.VISIBLE); - queueView.setText(status); - } - } - //hide user options button if not an RS pump or old firmware - // also excludes pump with model 03 because of untested error - boolean isKorean = DanaRKoreanPlugin.getPlugin().isEnabled(PluginType.PUMP); - if (isKorean || firmwareView.getText() == "OLD" || pump.model == 3) { - danar_user_options.setVisibility(View.GONE); - } - } - }); - } - - private boolean isBound() { - return lastConnectionView != null - && lastBolusView != null - && dailyUnitsView != null - && basaBasalRateView != null - && tempBasalView != null - && extendedBolusView != null - && reservoirView != null - && batteryView != null - && iobView != null - && firmwareView != null - && basalStepView != null - && bolusStepView != null - && serialNumberView != null - && danar_user_options != null - && queueView != null; - } - -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/DanaRFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/DanaRFragment.kt new file mode 100644 index 0000000000..3f58acf4d2 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/DanaRFragment.kt @@ -0,0 +1,189 @@ +package info.nightscout.androidaps.plugins.pump.danaR + + +import android.content.Intent +import android.os.Bundle +import android.os.Handler +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import info.nightscout.androidaps.MainApp +import info.nightscout.androidaps.R +import info.nightscout.androidaps.activities.TDDStatsActivity +import info.nightscout.androidaps.events.EventExtendedBolusChange +import info.nightscout.androidaps.events.EventPumpStatusChanged +import info.nightscout.androidaps.events.EventTempBasalChange +import info.nightscout.androidaps.interfaces.PluginType +import info.nightscout.androidaps.interfaces.PumpInterface +import info.nightscout.androidaps.logging.L +import info.nightscout.androidaps.plugins.bus.RxBus +import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin +import info.nightscout.androidaps.plugins.pump.danaR.activities.DanaRHistoryActivity +import info.nightscout.androidaps.plugins.pump.danaR.activities.DanaRUserOptionsActivity +import info.nightscout.androidaps.plugins.pump.danaR.events.EventDanaRNewStatus +import info.nightscout.androidaps.plugins.pump.danaRKorean.DanaRKoreanPlugin +import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin +import info.nightscout.androidaps.plugins.treatments.fragments.ProfileViewerDialog +import info.nightscout.androidaps.queue.events.EventQueueChanged +import info.nightscout.androidaps.utils.* +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.CompositeDisposable +import kotlinx.android.synthetic.main.danar_fragment.* +import org.slf4j.LoggerFactory + +class DanaRFragment : Fragment() { + private val log = LoggerFactory.getLogger(L.PUMP) + private var disposable: CompositeDisposable = CompositeDisposable() + + private val loopHandler = Handler() + private lateinit var refreshLoop: Runnable + + init { + refreshLoop = Runnable { + activity?.runOnUiThread { updateGUI() } + loopHandler.postDelayed(refreshLoop, T.mins(1).msecs()) + } + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.danar_fragment, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + dana_pumpstatus.setBackgroundColor(MainApp.gc(R.color.colorInitializingBorder)) + + danar_history.setOnClickListener { startActivity(Intent(context, DanaRHistoryActivity::class.java)) } + danar_viewprofile.setOnClickListener { + fragmentManager?.let { fragmentManager -> + val args = Bundle() + args.putLong("time", DateUtil.now()) + args.putInt("mode", ProfileViewerDialog.Mode.PUMP_PROFILE.ordinal) + val pvd = ProfileViewerDialog() + pvd.arguments = args + pvd.show(fragmentManager, "ProfileViewDialog") + } + } + danar_stats.setOnClickListener { startActivity(Intent(context, TDDStatsActivity::class.java)) } + danar_user_options.setOnClickListener { startActivity(Intent(context, DanaRUserOptionsActivity::class.java)) } + danar_btconnection.setOnClickListener { + if (L.isEnabled(L.PUMP)) + log.debug("Clicked connect to pump") + DanaRPump.getInstance().lastConnection = 0 + ConfigBuilderPlugin.getPlugin().commandQueue.readStatus("Clicked connect to pump", null) + } + } + + @Synchronized + override fun onResume() { + super.onResume() + loopHandler.postDelayed(refreshLoop, T.mins(1).msecs()) + disposable += RxBus + .toObservable(EventDanaRNewStatus::class.java) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ updateGUI() }, { FabricPrivacy.logException(it) }) + disposable += RxBus + .toObservable(EventExtendedBolusChange::class.java) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ updateGUI() }, { FabricPrivacy.logException(it) }) + disposable += RxBus + .toObservable(EventTempBasalChange::class.java) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ updateGUI() }, { FabricPrivacy.logException(it) }) + disposable += RxBus + .toObservable(EventQueueChanged::class.java) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ updateGUI() }, { FabricPrivacy.logException(it) }) + disposable += RxBus + .toObservable(EventPumpStatusChanged::class.java) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ + when { + it.sStatus == EventPumpStatusChanged.Status.CONNECTING -> danar_btconnection?.text = "{fa-bluetooth-b spin} " + it.sSecondsElapsed + "s" + it.sStatus == EventPumpStatusChanged.Status.CONNECTED -> danar_btconnection?.text = "{fa-bluetooth}" + it.sStatus == EventPumpStatusChanged.Status.DISCONNECTED -> danar_btconnection?.text = "{fa-bluetooth-b}" + } + if (it.getStatus() != "") { + dana_pumpstatus?.text = it.getStatus() + dana_pumpstatuslayout?.visibility = View.VISIBLE + } else { + dana_pumpstatuslayout?.visibility = View.GONE + } + }, { FabricPrivacy.logException(it) }) + updateGUI() + } + + @Synchronized + override fun onPause() { + super.onPause() + disposable.clear() + loopHandler.removeCallbacks(refreshLoop) + } + + // GUI functions + @Synchronized + internal fun updateGUI() { + if (danar_dailyunits == null) return + val pump = DanaRPump.getInstance() + val plugin: PumpInterface = ConfigBuilderPlugin.getPlugin().activePump ?: return + if (pump.lastConnection != 0L) { + val agoMsec = System.currentTimeMillis() - pump.lastConnection + val agoMin = (agoMsec.toDouble() / 60.0 / 1000.0).toInt() + danar_lastconnection.text = DateUtil.timeString(pump.lastConnection) + " (" + String.format(MainApp.gs(R.string.minago), agoMin) + ")" + SetWarnColor.setColor(danar_lastconnection, agoMin.toDouble(), 16.0, 31.0) + } + if (pump.lastBolusTime != 0L) { + val agoMsec = System.currentTimeMillis() - pump.lastBolusTime + val agoHours = agoMsec.toDouble() / 60.0 / 60.0 / 1000.0 + if (agoHours < 6) + // max 6h back + danar_lastbolus.text = DateUtil.timeString(pump.lastBolusTime) + " " + DateUtil.sinceString(pump.lastBolusTime) + " " + MainApp.gs(R.string.formatinsulinunits, pump.lastBolusAmount) + else + danar_lastbolus.text = "" + } + + danar_dailyunits.text = MainApp.gs(R.string.reservoirvalue, pump.dailyTotalUnits, pump.maxDailyTotalUnits) + SetWarnColor.setColor(danar_dailyunits, pump.dailyTotalUnits, pump.maxDailyTotalUnits * 0.75, pump.maxDailyTotalUnits * 0.9) + danar_basabasalrate.text = "( " + (pump.activeProfile + 1) + " ) " + MainApp.gs(R.string.pump_basebasalrate, plugin.baseBasalRate) + // DanaRPlugin, DanaRKoreanPlugin + if (ConfigBuilderPlugin.getPlugin().activePump!!.isFakingTempsByExtendedBoluses) { + danar_tempbasal.text = TreatmentsPlugin.getPlugin() + .getRealTempBasalFromHistory(System.currentTimeMillis())?.toStringFull() ?: "" + } else { + // v2 plugin + danar_tempbasal.text = TreatmentsPlugin.getPlugin() + .getTempBasalFromHistory(System.currentTimeMillis())?.toStringFull() ?: "" + } + danar_extendedbolus.text = TreatmentsPlugin.getPlugin() + .getExtendedBolusFromHistory(System.currentTimeMillis())?.toString() ?: "" + danar_reservoir.text = MainApp.gs(R.string.reservoirvalue, pump.reservoirRemainingUnits, 300) + SetWarnColor.setColorInverse(danar_reservoir, pump.reservoirRemainingUnits, 50.0, 20.0) + danar_battery.text = "{fa-battery-" + pump.batteryRemaining / 25 + "}" + SetWarnColor.setColorInverse(danar_battery, pump.batteryRemaining.toDouble(), 51.0, 26.0) + danar_iob.text = MainApp.gs(R.string.formatinsulinunits, pump.iob) + if (pump.model != 0 || pump.protocol != 0 || pump.productCode != 0) { + danar_firmware.text = String.format(MainApp.gs(R.string.danar_model), pump.model, pump.protocol, pump.productCode) + } else { + danar_firmware.text = "OLD" + } + danar_basalstep.text = pump.basalStep.toString() + danar_bolusstep.text = pump.bolusStep.toString() + danar_serialnumber.text = pump.serialNumber + val status = ConfigBuilderPlugin.getPlugin().commandQueue.spannedStatus() + if (status.toString() == "") { + danar_queue.visibility = View.GONE + } else { + danar_queue.visibility = View.VISIBLE + danar_queue.text = status + } + //hide user options button if not an RS pump or old firmware + // also excludes pump with model 03 because of untested error + val isKorean = DanaRKoreanPlugin.getPlugin().isEnabled(PluginType.PUMP) + if (isKorean || danar_firmware.text === "OLD" || pump.model == 3) { + danar_user_options.visibility = View.GONE + } + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/DanaRPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/DanaRPlugin.java index 2f7484cd6f..e9293b39a6 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/DanaRPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/DanaRPlugin.java @@ -5,10 +5,6 @@ import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.IBinder; -import android.support.v4.app.FragmentActivity; -import android.support.v7.app.AlertDialog; - -import com.squareup.otto.Subscribe; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; @@ -22,19 +18,23 @@ import info.nightscout.androidaps.events.EventPreferenceChange; import info.nightscout.androidaps.interfaces.Constraint; import info.nightscout.androidaps.interfaces.PluginType; import info.nightscout.androidaps.logging.L; -import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderFragment; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.pump.common.defs.PumpType; import info.nightscout.androidaps.plugins.pump.danaR.comm.MsgBolusStartWithSpeed; import info.nightscout.androidaps.plugins.pump.danaR.services.DanaRExecutionService; import info.nightscout.androidaps.plugins.treatments.Treatment; import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; +import info.nightscout.androidaps.utils.FabricPrivacy; import info.nightscout.androidaps.utils.Round; import info.nightscout.androidaps.utils.SP; +import io.reactivex.disposables.CompositeDisposable; +import io.reactivex.schedulers.Schedulers; /** * Created by mike on 05.08.2016. */ public class DanaRPlugin extends AbstractDanaRPlugin { + private CompositeDisposable disposable = new CompositeDisposable(); private static DanaRPlugin plugin = null; @@ -50,35 +50,32 @@ public class DanaRPlugin extends AbstractDanaRPlugin { pumpDescription.setPumpDescription(PumpType.DanaR); } - @Override - public void switchAllowed(ConfigBuilderFragment.PluginViewHolder.PluginSwitcher pluginSwitcher, FragmentActivity context) { - boolean allowHardwarePump = SP.getBoolean("allow_hardware_pump", false); - if (allowHardwarePump || context == null) { - pluginSwitcher.invoke(); - } else { - AlertDialog.Builder builder = new AlertDialog.Builder(context); - builder.setMessage(R.string.allow_hardware_pump_text) - .setPositiveButton(R.string.yes, (dialog, id) -> { - pluginSwitcher.invoke(); - SP.putBoolean("allow_hardware_pump", true); - if (L.isEnabled(L.PUMP)) - log.debug("First time HW pump allowed!"); - }) - .setNegativeButton(R.string.cancel, (dialog, id) -> { - pluginSwitcher.cancel(); - if (L.isEnabled(L.PUMP)) - log.debug("User does not allow switching to HW pump!"); - }); - builder.create().show(); - } - } - @Override protected void onStart() { Context context = MainApp.instance().getApplicationContext(); Intent intent = new Intent(context, DanaRExecutionService.class); context.bindService(intent, mConnection, Context.BIND_AUTO_CREATE); - MainApp.bus().register(this); + disposable.add(RxBus.INSTANCE + .toObservable(EventPreferenceChange.class) + .observeOn(Schedulers.io()) + .subscribe(event -> { + if (isEnabled(PluginType.PUMP)) { + boolean previousValue = useExtendedBoluses; + useExtendedBoluses = SP.getBoolean(R.string.key_danar_useextended, false); + + if (useExtendedBoluses != previousValue && TreatmentsPlugin.getPlugin().isInHistoryExtendedBoluslInProgress()) { + sExecutionService.extendedBolusStop(); + } + } + }, FabricPrivacy::logException) + ); + disposable.add(RxBus.INSTANCE + .toObservable(EventAppExit.class) + .observeOn(Schedulers.io()) + .subscribe(event -> { + MainApp.instance().getApplicationContext().unbindService(mConnection); + }, FabricPrivacy::logException) + ); super.onStart(); } @@ -87,7 +84,8 @@ public class DanaRPlugin extends AbstractDanaRPlugin { Context context = MainApp.instance().getApplicationContext(); context.unbindService(mConnection); - MainApp.bus().unregister(this); + disposable.clear(); + super.onStop(); } private ServiceConnection mConnection = new ServiceConnection() { @@ -106,24 +104,6 @@ public class DanaRPlugin extends AbstractDanaRPlugin { } }; - @SuppressWarnings("UnusedParameters") - @Subscribe - public void onStatusEvent(final EventAppExit e) { - MainApp.instance().getApplicationContext().unbindService(mConnection); - } - - @Subscribe - public void onStatusEvent(final EventPreferenceChange s) { - if (isEnabled(PluginType.PUMP)) { - boolean previousValue = useExtendedBoluses; - useExtendedBoluses = SP.getBoolean(R.string.key_danar_useextended, false); - - if (useExtendedBoluses != previousValue && TreatmentsPlugin.getPlugin().isInHistoryExtendedBoluslInProgress()) { - sExecutionService.extendedBolusStop(); - } - } - } - // Plugin base interface @Override public String getName() { @@ -262,7 +242,7 @@ public class DanaRPlugin extends AbstractDanaRPlugin { // Correct basal already set ? if (L.isEnabled(L.PUMP)) log.debug("setTempBasalAbsolute: currently running: " + activeTemp.toString()); - if (activeTemp.percentRate == percentRate) { + if (activeTemp.percentRate == percentRate && activeTemp.getPlannedRemainingMinutes() > 4) { if (enforceNew) { cancelTempBasal(true); } else { @@ -360,6 +340,11 @@ public class DanaRPlugin extends AbstractDanaRPlugin { return result; } + @Override + public PumpType model() { + return PumpType.DanaR; + } + private PumpEnactResult cancelRealTempBasal() { PumpEnactResult result = new PumpEnactResult(); TemporaryBasal runningTB = TreatmentsPlugin.getPlugin().getTempBasalFromHistory(System.currentTimeMillis()); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/Dialogs/ProfileViewDialog.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/Dialogs/ProfileViewDialog.java deleted file mode 100644 index 2b09b180cf..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/Dialogs/ProfileViewDialog.java +++ /dev/null @@ -1,91 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.danaR.dialogs; - -import android.os.Bundle; -import android.support.v4.app.DialogFragment; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.Button; -import android.widget.TextView; - -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.data.Profile; -import info.nightscout.androidaps.data.ProfileStore; -import info.nightscout.androidaps.interfaces.ProfileInterface; -import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; -import info.nightscout.androidaps.plugins.treatments.fragments.ProfileGraph; -import info.nightscout.androidaps.utils.DecimalFormatter; - -/** - * Created by mike on 10.07.2016. - */ -public class ProfileViewDialog extends DialogFragment { - private TextView noProfile; - private TextView units; - private TextView dia; - private TextView activeProfile; - private TextView ic; - private TextView isf; - private TextView basal; - private TextView target; - private ProfileGraph basalGraph; - - - private Button refreshButton; - - public ProfileViewDialog() { - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - View layout = inflater.inflate(R.layout.profileviewer_fragment, container, false); - - noProfile = (TextView) layout.findViewById(R.id.profileview_noprofile); - units = (TextView) layout.findViewById(R.id.profileview_units); - dia = (TextView) layout.findViewById(R.id.profileview_dia); - activeProfile = (TextView) layout.findViewById(R.id.profileview_activeprofile); - ic = (TextView) layout.findViewById(R.id.profileview_ic); - isf = (TextView) layout.findViewById(R.id.profileview_isf); - basal = (TextView) layout.findViewById(R.id.profileview_basal); - target = (TextView) layout.findViewById(R.id.profileview_target); - refreshButton = (Button) layout.findViewById(R.id.profileview_reload); - refreshButton.setVisibility(View.VISIBLE); - refreshButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - ConfigBuilderPlugin.getPlugin().getCommandQueue().readStatus("ProfileViewDialog", null); - dismiss(); - } - }); - basalGraph = (ProfileGraph) layout.findViewById(R.id.basal_graph); - setContent(); - return layout; - } - - @Override - public void onResume() { - super.onResume(); - getDialog().getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); - } - - private void setContent() { - ProfileStore store = ((ProfileInterface)ConfigBuilderPlugin.getPlugin().getActivePump()).getProfile(); - if (store != null) { - noProfile.setVisibility(View.GONE); - Profile profile = store.getDefaultProfile(); - units.setText(profile.getUnits()); - dia.setText(DecimalFormatter.to2Decimal(profile.getDia()) + " h"); - activeProfile.setText(((ProfileInterface) ConfigBuilderPlugin.getPlugin().getActivePump()).getProfileName()); - ic.setText(profile.getIcList()); - isf.setText(profile.getIsfList()); - basal.setText(profile.getBasalList()); - target.setText(profile.getTargetList()); - basalGraph.show(store.getDefaultProfile()); - } else { - noProfile.setVisibility(View.VISIBLE); - } - } - - -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/SerialIOThread.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/SerialIOThread.java index c8141d1265..782ec42eed 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/SerialIOThread.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/SerialIOThread.java @@ -12,7 +12,7 @@ import java.io.OutputStream; import info.nightscout.androidaps.logging.L; import info.nightscout.androidaps.plugins.pump.danaR.comm.MessageBase; -import info.nightscout.androidaps.plugins.pump.danaR.comm.MessageHashTable; +import info.nightscout.androidaps.plugins.pump.danaR.comm.MessageHashTableBase; import info.nightscout.androidaps.plugins.pump.danaR.services.AbstractSerialIOThread; import info.nightscout.androidaps.utils.CRC; @@ -30,9 +30,11 @@ public class SerialIOThread extends AbstractSerialIOThread { private byte[] mReadBuff = new byte[0]; private MessageBase processedMessage; + private MessageHashTableBase hashTable; - public SerialIOThread(BluetoothSocket rfcommSocket) { + public SerialIOThread(BluetoothSocket rfcommSocket, MessageHashTableBase hashTable) { super(); + this.hashTable = hashTable; mRfCommSocket = rfcommSocket; try { @@ -68,11 +70,11 @@ public class SerialIOThread extends AbstractSerialIOThread { message = processedMessage; } else { // get it from hash table - message = MessageHashTable.findMessage(command); + message = hashTable.findMessage(command); } if (L.isEnabled(L.PUMPBTCOMM)) - log.debug("<<<<< " + message.getMessageName() + " " + message.toHexString(extractedBuff)); + log.debug("<<<<< " + message.getMessageName() + " " + MessageBase.toHexString(extractedBuff)); // process the message content message.received = true; @@ -83,14 +85,14 @@ public class SerialIOThread extends AbstractSerialIOThread { } } } catch (Exception e) { - if (e.getMessage().indexOf("bt socket closed") < 0) + if (!e.getMessage().contains("bt socket closed")) log.error("Thread exception: ", e); mKeepRunning = false; } disconnect("EndOfLoop"); } - void appendToBuffer(byte[] newData, int gotBytes) { + private void appendToBuffer(byte[] newData, int gotBytes) { // add newData to mReadBuff byte[] newReadBuff = new byte[mReadBuff.length + gotBytes]; System.arraycopy(mReadBuff, 0, newReadBuff, 0, mReadBuff.length); @@ -98,7 +100,7 @@ public class SerialIOThread extends AbstractSerialIOThread { mReadBuff = newReadBuff; } - byte[] cutMessageFromBuffer() { + private byte[] cutMessageFromBuffer() { if (mReadBuff[0] == (byte) 0x7E && mReadBuff[1] == (byte) 0x7E) { int length = (mReadBuff[2] & 0xFF) + 7; // Check if we have enough data @@ -148,7 +150,7 @@ public class SerialIOThread extends AbstractSerialIOThread { byte[] messageBytes = message.getRawMessageBytes(); if (L.isEnabled(L.PUMPBTCOMM)) - log.debug(">>>>> " + message.getMessageName() + " " + message.toHexString(messageBytes)); + log.debug(">>>>> " + message.getMessageName() + " " + MessageBase.toHexString(messageBytes)); try { mOutputStream.write(messageBytes); @@ -165,8 +167,10 @@ public class SerialIOThread extends AbstractSerialIOThread { } SystemClock.sleep(200); - if (!message.received) { - log.warn("Reply not received " + message.getMessageName()); + if (!message.isReceived()) { + message.handleMessageNotReceived(); + if (L.isEnabled(L.PUMPBTCOMM)) + log.error("Reply not received " + message.getMessageName()); if (message.getCommand() == 0xF0F1) { DanaRPump.getInstance().isNewPump = false; if (L.isEnabled(L.PUMPCOMM)) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/activities/DanaRHistoryActivity.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/activities/DanaRHistoryActivity.java index 95c9f25e37..f20a74b024 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/activities/DanaRHistoryActivity.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/activities/DanaRHistoryActivity.java @@ -1,12 +1,6 @@ package info.nightscout.androidaps.plugins.pump.danaR.activities; -import android.app.Activity; import android.os.Bundle; -import android.os.Handler; -import android.os.HandlerThread; -import android.support.v7.widget.CardView; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -16,7 +10,10 @@ import android.widget.Button; import android.widget.Spinner; import android.widget.TextView; -import com.squareup.otto.Subscribe; +import androidx.annotation.NonNull; +import androidx.cardview.widget.CardView; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -27,11 +24,13 @@ import java.util.List; import info.nightscout.androidaps.Constants; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; +import info.nightscout.androidaps.activities.NoSplashActivity; import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.db.DanaRHistoryRecord; import info.nightscout.androidaps.events.EventPumpStatusChanged; import info.nightscout.androidaps.interfaces.PluginType; import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; import info.nightscout.androidaps.plugins.pump.danaR.comm.RecordTypes; @@ -41,19 +40,20 @@ import info.nightscout.androidaps.plugins.pump.danaRS.DanaRSPlugin; import info.nightscout.androidaps.queue.Callback; import info.nightscout.androidaps.utils.DateUtil; import info.nightscout.androidaps.utils.DecimalFormatter; +import info.nightscout.androidaps.utils.FabricPrivacy; import info.nightscout.androidaps.utils.ToastUtils; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.CompositeDisposable; -public class DanaRHistoryActivity extends Activity { +public class DanaRHistoryActivity extends NoSplashActivity { private static Logger log = LoggerFactory.getLogger(L.PUMP); - - private Handler mHandler; + private CompositeDisposable disposable = new CompositeDisposable(); static Profile profile = null; Spinner historyTypeSpinner; TextView statusView; Button reloadButton; - Button syncButton; RecyclerView recyclerView; LinearLayoutManager llm; @@ -69,6 +69,7 @@ public class DanaRHistoryActivity extends Activity { this.name = name; } + @NonNull @Override public String toString() { return name; @@ -77,34 +78,43 @@ public class DanaRHistoryActivity extends Activity { public DanaRHistoryActivity() { super(); - HandlerThread mHandlerThread = new HandlerThread(DanaRHistoryActivity.class.getSimpleName()); - mHandlerThread.start(); - this.mHandler = new Handler(mHandlerThread.getLooper()); } @Override protected void onResume() { super.onResume(); - MainApp.bus().register(this); + disposable.add(RxBus.INSTANCE + .toObservable(EventPumpStatusChanged.class) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(event -> statusView.setText(event.getStatus()), FabricPrivacy::logException) + ); + disposable.add(RxBus.INSTANCE + .toObservable(EventDanaRSyncStatus.class) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(event -> { + if (L.isEnabled(L.PUMP)) + log.debug("EventDanaRSyncStatus: " + event.getMessage()); + statusView.setText(event.getMessage()); + }, FabricPrivacy::logException) + ); } @Override protected void onPause() { super.onPause(); - MainApp.bus().unregister(this); + disposable.clear(); } @Override - protected void onCreate(Bundle savedInstanceState) { + public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.danar_historyactivity); - historyTypeSpinner = (Spinner) findViewById(R.id.danar_historytype); - statusView = (TextView) findViewById(R.id.danar_historystatus); - reloadButton = (Button) findViewById(R.id.danar_historyreload); - syncButton = (Button) findViewById(R.id.danar_historysync); - recyclerView = (RecyclerView) findViewById(R.id.danar_history_recyclerview); + historyTypeSpinner = findViewById(R.id.danar_historytype); + statusView = findViewById(R.id.danar_historystatus); + reloadButton = findViewById(R.id.danar_historyreload); + recyclerView = findViewById(R.id.danar_history_recyclerview); recyclerView.setHasFixedSize(true); llm = new LinearLayoutManager(this); @@ -144,7 +154,6 @@ public class DanaRHistoryActivity extends Activity { final TypeList selected = (TypeList) historyTypeSpinner.getSelectedItem(); runOnUiThread(() -> { reloadButton.setVisibility(View.GONE); - syncButton.setVisibility(View.GONE); statusView.setVisibility(View.VISIBLE); }); clearCardView(); @@ -154,37 +163,12 @@ public class DanaRHistoryActivity extends Activity { loadDataFromDB(selected.type); runOnUiThread(() -> { reloadButton.setVisibility(View.VISIBLE); - syncButton.setVisibility(View.VISIBLE); statusView.setVisibility(View.GONE); }); } }); }); - syncButton.setOnClickListener(v -> mHandler.post(new Runnable() { - @Override - public void run() { - runOnUiThread(new Runnable() { - @Override - public void run() { - reloadButton.setVisibility(View.GONE); - syncButton.setVisibility(View.GONE); - statusView.setVisibility(View.VISIBLE); - } - }); - DanaRNSHistorySync sync = new DanaRNSHistorySync(historyList); - sync.sync(DanaRNSHistorySync.SYNC_ALL); - runOnUiThread(new Runnable() { - @Override - public void run() { - reloadButton.setVisibility(View.VISIBLE); - syncButton.setVisibility(View.VISIBLE); - statusView.setVisibility(View.GONE); - } - }); - } - })); - historyTypeSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView parent, View view, int position, long id) { @@ -213,14 +197,15 @@ public class DanaRHistoryActivity extends Activity { this.historyList = historyList; } + @NonNull @Override - public HistoryViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) { + public HistoryViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int viewType) { View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.danar_history_item, viewGroup, false); return new HistoryViewHolder(v); } @Override - public void onBindViewHolder(HistoryViewHolder holder, int position) { + public void onBindViewHolder(@NonNull HistoryViewHolder holder, int position) { DanaRHistoryRecord record = historyList.get(position); holder.time.setText(DateUtil.dateAndTimeString(record.recordDate)); holder.value.setText(DecimalFormatter.to2Decimal(record.recordValue)); @@ -305,7 +290,7 @@ public class DanaRHistoryActivity extends Activity { } @Override - public void onAttachedToRecyclerView(RecyclerView recyclerView) { + public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) { super.onAttachedToRecyclerView(recyclerView); } @@ -323,16 +308,16 @@ public class DanaRHistoryActivity extends Activity { HistoryViewHolder(View itemView) { super(itemView); - cv = (CardView) itemView.findViewById(R.id.danar_history_cardview); - time = (TextView) itemView.findViewById(R.id.danar_history_time); - value = (TextView) itemView.findViewById(R.id.danar_history_value); - bolustype = (TextView) itemView.findViewById(R.id.danar_history_bolustype); - stringvalue = (TextView) itemView.findViewById(R.id.danar_history_stringvalue); - duration = (TextView) itemView.findViewById(R.id.danar_history_duration); - dailybasal = (TextView) itemView.findViewById(R.id.danar_history_dailybasal); - dailybolus = (TextView) itemView.findViewById(R.id.danar_history_dailybolus); - dailytotal = (TextView) itemView.findViewById(R.id.danar_history_dailytotal); - alarm = (TextView) itemView.findViewById(R.id.danar_history_alarm); + cv = itemView.findViewById(R.id.danar_history_cardview); + time = itemView.findViewById(R.id.danar_history_time); + value = itemView.findViewById(R.id.danar_history_value); + bolustype = itemView.findViewById(R.id.danar_history_bolustype); + stringvalue = itemView.findViewById(R.id.danar_history_stringvalue); + duration = itemView.findViewById(R.id.danar_history_duration); + dailybasal = itemView.findViewById(R.id.danar_history_dailybasal); + dailybolus = itemView.findViewById(R.id.danar_history_dailybolus); + dailytotal = itemView.findViewById(R.id.danar_history_dailytotal); + alarm = itemView.findViewById(R.id.danar_history_alarm); } } } @@ -347,21 +332,4 @@ public class DanaRHistoryActivity extends Activity { historyList = new ArrayList<>(); runOnUiThread(() -> recyclerView.swapAdapter(new RecyclerViewAdapter(historyList), false)); } - - @Subscribe - public void onStatusEvent(final EventDanaRSyncStatus s) { - if (L.isEnabled(L.PUMP)) - log.debug("EventDanaRSyncStatus: " + s.message); - runOnUiThread( - () -> statusView.setText(s.message)); - } - - @Subscribe - public void onStatusEvent(final EventPumpStatusChanged s) { - runOnUiThread( - () -> statusView.setText(s.textStatus()) - ); - } - - } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/activities/DanaRNSHistorySync.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/activities/DanaRNSHistorySync.java index a15c8d2883..dcb182a0d6 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/activities/DanaRNSHistorySync.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/activities/DanaRNSHistorySync.java @@ -14,6 +14,7 @@ import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.db.CareportalEvent; import info.nightscout.androidaps.db.DanaRHistoryRecord; import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; import info.nightscout.androidaps.plugins.pump.danaR.comm.RecordTypes; import info.nightscout.androidaps.plugins.pump.danaR.events.EventDanaRSyncStatus; @@ -28,13 +29,13 @@ public class DanaRNSHistorySync { private static Logger log = LoggerFactory.getLogger(L.PUMP); private List historyRecords; - public final static int SYNC_BOLUS = 0b00000001; - public final static int SYNC_ERROR = 0b00000010; - public final static int SYNC_REFILL = 0b00000100; - public final static int SYNC_GLUCOSE = 0b00001000; - public final static int SYNC_CARBO = 0b00010000; - public final static int SYNC_ALARM = 0b00100000; - public final static int SYNC_BASALHOURS = 0b01000000; + private final static int SYNC_BOLUS = 0b00000001; + private final static int SYNC_ERROR = 0b00000010; + private final static int SYNC_REFILL = 0b00000100; + private final static int SYNC_GLUCOSE = 0b00001000; + private final static int SYNC_CARBO = 0b00010000; + private final static int SYNC_ALARM = 0b00100000; + private final static int SYNC_BASALHOURS = 0b01000000; public final static int SYNC_ALL = 0b11111111; public final static String DANARSIGNATURE = "DANARMESSAGE"; @@ -52,13 +53,13 @@ public class DanaRNSHistorySync { long uploaded = 0; if (L.isEnabled(L.PUMP)) log.debug("Database contains " + records + " records"); - EventDanaRSyncStatus ev = new EventDanaRSyncStatus(); + EventDanaRSyncStatus ev = new EventDanaRSyncStatus(""); for (DanaRHistoryRecord record : historyRecords) { processing++; if (record._id != null) continue; //log.debug(record.bytes); JSONObject nsrec = new JSONObject(); - ev.message = MainApp.gs(R.string.uploading) + " " + processing + "/" + records + " "; // TODO: translations + ev.setMessage(MainApp.gs(R.string.uploading) + " " + processing + "/" + records + " "); // TODO: translations switch (record.recordCode) { case RecordTypes.RECORD_TYPE_BOLUS: if ((what & SYNC_BOLUS) == 0) break; @@ -73,7 +74,7 @@ public class DanaRNSHistorySync { nsrec.put("enteredBy", "openaps://" + MainApp.gs(R.string.app_name)); NSUpload.uploadCareportalEntryToNS(nsrec); uploaded++; - ev.message += MainApp.gs(R.string.danar_sbolus); + ev.setMessage(ev.getMessage() + MainApp.gs(R.string.danar_sbolus)); break; case "E": if (record.recordDuration > 0) { @@ -92,7 +93,7 @@ public class DanaRNSHistorySync { nsrec.put("enteredBy", "openaps://" + MainApp.gs(R.string.app_name)); NSUpload.uploadCareportalEntryToNS(nsrec); uploaded++; - ev.message += MainApp.gs(R.string.danar_ebolus); + ev.setMessage(ev.getMessage() + MainApp.gs(R.string.danar_ebolus)); } else { if (L.isEnabled(L.PUMP)) log.debug("NOT Syncing extended bolus record " + record.recordValue + "U " + DateUtil.toISOString(record.recordDate) + " zero duration"); @@ -110,7 +111,7 @@ public class DanaRNSHistorySync { nsrec.put("enteredBy", "openaps://" + MainApp.gs(R.string.app_name)); NSUpload.uploadCareportalEntryToNS(nsrec); uploaded++; - ev.message += MainApp.gs(R.string.danar_dsbolus); + ev.setMessage(ev.getMessage() + MainApp.gs(R.string.danar_dsbolus)); break; case "DE": if (L.isEnabled(L.PUMP)) @@ -127,7 +128,7 @@ public class DanaRNSHistorySync { nsrec.put("enteredBy", "openaps://" + MainApp.gs(R.string.app_name)); NSUpload.uploadCareportalEntryToNS(nsrec); uploaded++; - ev.message += MainApp.gs(R.string.danar_debolus); + ev.setMessage(ev.getMessage() + MainApp.gs(R.string.danar_debolus)); break; default: log.error("Unknown bolus record"); @@ -145,7 +146,7 @@ public class DanaRNSHistorySync { nsrec.put("enteredBy", "openaps://" + MainApp.gs(R.string.app_name)); NSUpload.uploadCareportalEntryToNS(nsrec); uploaded++; - ev.message += MainApp.gs(R.string.danar_error); + ev.setMessage(ev.getMessage() + MainApp.gs(R.string.danar_error)); break; case RecordTypes.RECORD_TYPE_REFILL: if ((what & SYNC_REFILL) == 0) break; @@ -158,7 +159,7 @@ public class DanaRNSHistorySync { nsrec.put("enteredBy", "openaps://" + MainApp.gs(R.string.app_name)); NSUpload.uploadCareportalEntryToNS(nsrec); uploaded++; - ev.message += MainApp.gs(R.string.danar_refill); + ev.setMessage(ev.getMessage() + MainApp.gs(R.string.danar_refill)); break; case RecordTypes.RECORD_TYPE_BASALHOUR: if ((what & SYNC_BASALHOURS) == 0) break; @@ -172,7 +173,7 @@ public class DanaRNSHistorySync { nsrec.put("enteredBy", "openaps://" + MainApp.gs(R.string.app_name)); NSUpload.uploadCareportalEntryToNS(nsrec); uploaded++; - ev.message += MainApp.gs(R.string.danar_basalhour); + ev.setMessage(ev.getMessage() + MainApp.gs(R.string.danar_basalhour)); break; case RecordTypes.RECORD_TYPE_TB: //log.debug("Ignoring TB record " + record.bytes + " " + DateUtil.toISOString(record.recordDate)); @@ -189,7 +190,7 @@ public class DanaRNSHistorySync { nsrec.put("enteredBy", "openaps://" + MainApp.gs(R.string.app_name)); NSUpload.uploadCareportalEntryToNS(nsrec); uploaded++; - ev.message += MainApp.gs(R.string.danar_glucose); + ev.setMessage(ev.getMessage() + MainApp.gs(R.string.danar_glucose)); break; case RecordTypes.RECORD_TYPE_CARBO: if ((what & SYNC_CARBO) == 0) break; @@ -202,7 +203,7 @@ public class DanaRNSHistorySync { nsrec.put("enteredBy", "openaps://" + MainApp.gs(R.string.app_name)); NSUpload.uploadCareportalEntryToNS(nsrec); uploaded++; - ev.message += MainApp.gs(R.string.danar_carbohydrate); + ev.setMessage(ev.getMessage() + MainApp.gs(R.string.danar_carbohydrate)); break; case RecordTypes.RECORD_TYPE_ALARM: if ((what & SYNC_ALARM) == 0) break; @@ -215,7 +216,7 @@ public class DanaRNSHistorySync { nsrec.put("enteredBy", "openaps://" + MainApp.gs(R.string.app_name)); NSUpload.uploadCareportalEntryToNS(nsrec); uploaded++; - ev.message += MainApp.gs(R.string.danar_alarm); + ev.setMessage(ev.getMessage() + MainApp.gs(R.string.danar_alarm)); break; case RecordTypes.RECORD_TYPE_SUSPEND: // TODO: this too case RecordTypes.RECORD_TYPE_DAILY: @@ -226,10 +227,10 @@ public class DanaRNSHistorySync { log.error("Unknown record type"); break; } - MainApp.bus().post(ev); + RxBus.INSTANCE.send(ev); } - ev.message = String.format(MainApp.gs(R.string.danar_totaluploaded), uploaded); - MainApp.bus().post(ev); + ev.setMessage(String.format(MainApp.gs(R.string.danar_totaluploaded), uploaded)); + RxBus.INSTANCE.send(ev); } catch (JSONException e) { log.error("Unhandled exception", e); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/activities/DanaRUserOptionsActivity.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/activities/DanaRUserOptionsActivity.java index 0f2ec5f4d2..f1181bb7cb 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/activities/DanaRUserOptionsActivity.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/activities/DanaRUserOptionsActivity.java @@ -1,14 +1,11 @@ package info.nightscout.androidaps.plugins.pump.danaR.activities; -import android.app.Activity; import android.os.Bundle; import android.widget.Button; import android.widget.RadioButton; import android.widget.RadioGroup; import android.widget.Switch; -import com.squareup.otto.Subscribe; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -17,22 +14,28 @@ import java.text.DecimalFormat; import info.nightscout.androidaps.Constants; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; +import info.nightscout.androidaps.activities.NoSplashActivity; import info.nightscout.androidaps.events.EventInitializationChanged; import info.nightscout.androidaps.interfaces.PluginType; import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.pump.danaR.DanaRPlugin; import info.nightscout.androidaps.plugins.pump.danaR.DanaRPump; import info.nightscout.androidaps.plugins.pump.danaRS.DanaRSPlugin; import info.nightscout.androidaps.plugins.pump.danaRv2.DanaRv2Plugin; +import info.nightscout.androidaps.utils.FabricPrivacy; import info.nightscout.androidaps.utils.NumberPicker; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.CompositeDisposable; /** * Created by Rumen Georgiev on 5/31/2018. */ -public class DanaRUserOptionsActivity extends Activity { +public class DanaRUserOptionsActivity extends NoSplashActivity { private static Logger log = LoggerFactory.getLogger(L.PUMP); + private CompositeDisposable disposable = new CompositeDisposable(); Switch timeFormat; Switch buttonScroll; @@ -48,24 +51,28 @@ public class DanaRUserOptionsActivity extends Activity { NumberPicker lowReservoir; Button saveToPumpButton; // This is for Dana pumps only - boolean isRS = MainApp.getSpecificPlugin(DanaRSPlugin.class) != null && MainApp.getSpecificPlugin(DanaRSPlugin.class).isEnabled(PluginType.PUMP); - boolean isDanaR = MainApp.getSpecificPlugin(DanaRPlugin.class) != null && MainApp.getSpecificPlugin(DanaRPlugin.class).isEnabled(PluginType.PUMP); - boolean isDanaRv2 = MainApp.getSpecificPlugin(DanaRv2Plugin.class) != null && MainApp.getSpecificPlugin(DanaRv2Plugin.class).isEnabled(PluginType.PUMP); + boolean isRS = DanaRSPlugin.getPlugin().isEnabled(PluginType.PUMP); + boolean isDanaR = DanaRPlugin.getPlugin().isEnabled(PluginType.PUMP); + boolean isDanaRv2 = DanaRv2Plugin.getPlugin().isEnabled(PluginType.PUMP); @Override - protected void onResume() { + protected synchronized void onResume() { super.onResume(); - MainApp.bus().register(this); + disposable.add(RxBus.INSTANCE + .toObservable(EventInitializationChanged.class) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(event -> setData(), FabricPrivacy::logException) + ); } @Override - protected void onPause() { + protected synchronized void onPause() { + disposable.clear(); super.onPause(); - MainApp.bus().unregister(this); } @Override - protected void onCreate(Bundle savedInstanceState) { + public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.danar_user_options); @@ -97,10 +104,10 @@ public class DanaRUserOptionsActivity extends Activity { + "\npumpUnits:" + pump.units + "\nlowReservoir:" + pump.lowReservoirRate); - screenTimeout.setParams((double) pump.lcdOnTimeSec, 5d, 240d, 5d, new DecimalFormat("1"), false); - backlightTimeout.setParams((double) pump.backlightOnTimeSec, 1d, 60d, 1d, new DecimalFormat("1"), false); - shutdown.setParams((double) pump.shutdownHour, 0d, 24d, 1d, new DecimalFormat("1"), true); - lowReservoir.setParams((double) pump.lowReservoirRate, 10d, 60d, 10d, new DecimalFormat("10"), false); + screenTimeout.setParams((double) pump.lcdOnTimeSec, 5d, 240d, 5d, new DecimalFormat("1"), false, findViewById(R.id.ok)); + backlightTimeout.setParams((double) pump.backlightOnTimeSec, 1d, 60d, 1d, new DecimalFormat("1"), false, findViewById(R.id.ok)); + shutdown.setParams((double) pump.shutdownHour, 0d, 24d, 1d, new DecimalFormat("1"), true, findViewById(R.id.ok)); + lowReservoir.setParams((double) pump.lowReservoirRate, 10d, 60d, 10d, new DecimalFormat("10"), false, findViewById(R.id.ok)); switch (pump.beepAndAlarm) { case 0x01: pumpAlarmSound.setChecked(true); @@ -143,11 +150,6 @@ public class DanaRUserOptionsActivity extends Activity { lowReservoir.setValue((double) pump.lowReservoirRate); } - @Subscribe - public void onEventInitializationChanged(EventInitializationChanged ignored) { - runOnUiThread(this::setData); - } - public void onSaveClick() { if (!isRS && !isDanaR && !isDanaRv2) { //exit if pump is not DanaRS, Dana!, or DanaR with upgraded firmware diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MessageBase.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MessageBase.java index 57244ecc20..bf2ca4b83b 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MessageBase.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MessageBase.java @@ -105,6 +105,9 @@ public class MessageBase { } } + public void handleMessageNotReceived() { + } + public int getCommand() { int command = byteFromRawBuff(buffer, 5) | (byteFromRawBuff(buffer, 4) << 8); return command; @@ -189,4 +192,8 @@ public class MessageBase { return sb.toString(); } + + public boolean isReceived() { + return received; + } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MessageHashTable.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MessageHashTable.java deleted file mode 100644 index d7ec39f6b7..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MessageHashTable.java +++ /dev/null @@ -1,82 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.danaR.comm; - -import java.util.HashMap; - -/** - * Created by mike on 28.05.2016. - */ -public class MessageHashTable { - public static HashMap messages = null; - - static { - if (messages == null) { - messages = new HashMap(); - put(new MsgBolusStop()); // 0x0101 CMD_MEALINS_STOP - put(new MsgBolusStart()); // 0x0102 CMD_MEALINS_START_DATA - put(new MsgBolusStartWithSpeed()); // 0x0104 CMD_MEALINS_START_DATA_SPEED - put(new MsgBolusProgress()); // 0x0202 CMD_PUMP_THIS_REMAINDER_MEAL_INS - put(new MsgStatusProfile()); // 0x0204 CMD_PUMP_CALCULATION_SETTING - put(new MsgStatusTempBasal()); // 0x0205 CMD_PUMP_EXERCISE_MODE - put(new MsgStatusBolusExtended()); // 0x0207 CMD_PUMP_EXPANS_INS_I - put(new MsgStatusBasic()); // 0x020A CMD_PUMP_INITVIEW_I - put(new MsgStatus()); // 0x020B CMD_PUMP_STATUS - put(new MsgInitConnStatusTime()); // 0x0301 CMD_PUMPINIT_TIME_INFO - put(new MsgInitConnStatusBolus()); // 0x0302 CMD_PUMPINIT_BOLUS_INFO - put(new MsgInitConnStatusBasic()); // 0x0303 CMD_PUMPINIT_INIT_INFO - put(new MsgInitConnStatusOption()); // 0x0304 CMD_PUMPINIT_OPTION - put(new MsgSetTempBasalStart()); // 0x0401 CMD_PUMPSET_EXERCISE_S - put(new MsgSetCarbsEntry()); // 0x0402 CMD_PUMPSET_HIS_S - put(new MsgSetTempBasalStop()); // 0x0403 CMD_PUMPSET_EXERCISE_STOP - put(new MsgSetExtendedBolusStop()); // 0x0406 CMD_PUMPSET_EXPANS_INS_STOP - put(new MsgSetExtendedBolusStart()); // 0x0407 CMD_PUMPSET_EXPANS_INS_S - put(new MsgError()); // 0x0601 CMD_PUMPOWAY_SYSTEM_STATUS - put(new MsgPCCommStart()); // 0x3001 CMD_CONNECT - put(new MsgPCCommStop()); // 0x3002 CMD_DISCONNECT - put(new MsgHistoryBolus()); // 0x3101 CMD_HISTORY_MEAL_INS - put(new MsgHistoryDailyInsulin()); // 0x3102 CMD_HISTORY_DAY_INS - put(new MsgHistoryGlucose()); // 0x3104 CMD_HISTORY_GLUCOSE - put(new MsgHistoryAlarm()); // 0x3105 CMD_HISTORY_ALARM - put(new MsgHistoryError()); // 0x3106 CMD_HISTORY_ERROR - put(new MsgHistoryCarbo()); // 0x3107 CMD_HISTORY_CARBOHY - put(new MsgHistoryRefill()); // 0x3108 CMD_HISTORY_REFILL - put(new MsgHistorySuspend()); // 0x3109 CMD_HISTORY_SUSPEND - put(new MsgHistoryBasalHour()); // 0x310A CMD_HISTORY_BASAL_HOUR - put(new MsgHistoryDone()); // 0x31F1 CMD_HISTORY_DONT_USED - put(new MsgSettingBasal()); // 0x3202 CMD_SETTING_V_BASAL_INS_I - put(new MsgSettingMeal()); // 0x3203 CMD_SETTING_V_MEAL_SETTING_I - put(new MsgSettingProfileRatios()); // 0x3204 CMD_SETTING_V_CCC_I - put(new MsgSettingMaxValues()); // 0x3205 CMD_SETTING_V_MAX_VALUE_I - put(new MsgSettingBasalProfileAll()); // 0x3206 CMD_SETTING_V_BASAL_PROFILE_ALL - put(new MsgSettingShippingInfo()); // 0x3207 CMD_SETTING_V_SHIPPING_I - put(new MsgSettingGlucose()); // 0x3209 CMD_SETTING_V_GLUCOSEandEASY - put(new MsgSettingPumpTime()); // 0x320A CMD_SETTING_V_TIME_I - put(new MsgSettingUserOptions()); // 0x320B CMD_SETTING_V_USER_OPTIONS - put(new MsgSettingActiveProfile()); // 0x320C CMD_SETTING_V_PROFILE_NUMBER - put(new MsgSettingProfileRatiosAll()); // 0x320D CMD_SETTING_V_CIR_CF_VALUE - put(new MsgSetSingleBasalProfile()); // 0x3302 CMD_SETTING_BASAL_INS_S - put(new MsgSetBasalProfile()); // 0x3306 CMD_SETTING_BASAL_PROFILE_S - put(new MsgSetUserOptions()); // 0x330B CMD_SETTING_USER_OPTIONS_S - put(new MsgSetActivateBasalProfile()); // 0x330C CMD_SETTING_PROFILE_NUMBER_S - put(new MsgHistoryAllDone()); // 0x41F1 CMD_HISTORY_ALL_DONE - put(new MsgHistoryAll()); // 0x41F2 CMD_HISTORY_ALL - put(new MsgHistoryNewDone()); // 0x42F1 CMD_HISTORY_NEW_DONE - put(new MsgHistoryNew()); // 0x42F2 CMD_HISTORY_NEW - put(new MsgCheckValue()); // 0xF0F1 CMD_PUMP_CHECK_VALUE - } - } - - public static void put(MessageBase message) { - int command = message.getCommand(); - //String name = MessageOriginalNames.getName(command); - messages.put(command, message); - //log.debug(String.format("%04x ", command) + " " + name); - } - - public static MessageBase findMessage(Integer command) { - if (messages.containsKey(command)) { - return messages.get(command); - } else { - return new MessageBase(); - } - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MessageHashTableBase.kt b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MessageHashTableBase.kt new file mode 100644 index 0000000000..9c524e02a6 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MessageHashTableBase.kt @@ -0,0 +1,6 @@ +package info.nightscout.androidaps.plugins.pump.danaR.comm + +interface MessageHashTableBase { + fun put(message: MessageBase) + fun findMessage(command: Int): MessageBase +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MessageHashTableR.kt b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MessageHashTableR.kt new file mode 100644 index 0000000000..6d3129f5ba --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MessageHashTableR.kt @@ -0,0 +1,69 @@ +package info.nightscout.androidaps.plugins.pump.danaR.comm + +import java.util.* + +object MessageHashTableR : MessageHashTableBase { + var messages: HashMap = HashMap() + + init { + put(MsgBolusStop()) // 0x0101 CMD_MEALINS_STOP + put(MsgBolusStart()) // 0x0102 CMD_MEALINS_START_DATA + put(MsgBolusStartWithSpeed()) // 0x0104 CMD_MEALINS_START_DATA_SPEED + put(MsgBolusProgress()) // 0x0202 CMD_PUMP_THIS_REMAINDER_MEAL_INS + put(MsgStatusProfile()) // 0x0204 CMD_PUMP_CALCULATION_SETTING + put(MsgStatusTempBasal()) // 0x0205 CMD_PUMP_EXERCISE_MODE + put(MsgStatusBolusExtended()) // 0x0207 CMD_PUMP_EXPANS_INS_I + put(MsgStatusBasic()) // 0x020A CMD_PUMP_INITVIEW_I + put(MsgStatus()) // 0x020B CMD_PUMP_STATUS + put(MsgInitConnStatusTime()) // 0x0301 CMD_PUMPINIT_TIME_INFO + put(MsgInitConnStatusBolus()) // 0x0302 CMD_PUMPINIT_BOLUS_INFO + put(MsgInitConnStatusBasic()) // 0x0303 CMD_PUMPINIT_INIT_INFO + put(MsgInitConnStatusOption()) // 0x0304 CMD_PUMPINIT_OPTION + put(MsgSetTempBasalStart()) // 0x0401 CMD_PUMPSET_EXERCISE_S + put(MsgSetCarbsEntry()) // 0x0402 CMD_PUMPSET_HIS_S + put(MsgSetTempBasalStop()) // 0x0403 CMD_PUMPSET_EXERCISE_STOP + put(MsgSetExtendedBolusStop()) // 0x0406 CMD_PUMPSET_EXPANS_INS_STOP + put(MsgSetExtendedBolusStart()) // 0x0407 CMD_PUMPSET_EXPANS_INS_S + put(MsgError()) // 0x0601 CMD_PUMPOWAY_SYSTEM_STATUS + put(MsgPCCommStart()) // 0x3001 CMD_CONNECT + put(MsgPCCommStop()) // 0x3002 CMD_DISCONNECT + put(MsgHistoryBolus()) // 0x3101 CMD_HISTORY_MEAL_INS + put(MsgHistoryDailyInsulin()) // 0x3102 CMD_HISTORY_DAY_INS + put(MsgHistoryGlucose()) // 0x3104 CMD_HISTORY_GLUCOSE + put(MsgHistoryAlarm()) // 0x3105 CMD_HISTORY_ALARM + put(MsgHistoryError()) // 0x3106 CMD_HISTORY_ERROR + put(MsgHistoryCarbo()) // 0x3107 CMD_HISTORY_CARBOHY + put(MsgHistoryRefill()) // 0x3108 CMD_HISTORY_REFILL + put(MsgHistorySuspend()) // 0x3109 CMD_HISTORY_SUSPEND + put(MsgHistoryBasalHour()) // 0x310A CMD_HISTORY_BASAL_HOUR + put(MsgHistoryDone()) // 0x31F1 CMD_HISTORY_DONT_USED + put(MsgSettingBasal()) // 0x3202 CMD_SETTING_V_BASAL_INS_I + put(MsgSettingMeal()) // 0x3203 CMD_SETTING_V_MEAL_SETTING_I + put(MsgSettingProfileRatios()) // 0x3204 CMD_SETTING_V_CCC_I + put(MsgSettingMaxValues()) // 0x3205 CMD_SETTING_V_MAX_VALUE_I + put(MsgSettingBasalProfileAll()) // 0x3206 CMD_SETTING_V_BASAL_PROFILE_ALL + put(MsgSettingShippingInfo()) // 0x3207 CMD_SETTING_V_SHIPPING_I + put(MsgSettingGlucose()) // 0x3209 CMD_SETTING_V_GLUCOSEandEASY + put(MsgSettingPumpTime()) // 0x320A CMD_SETTING_V_TIME_I + put(MsgSettingUserOptions()) // 0x320B CMD_SETTING_V_USER_OPTIONS + put(MsgSettingActiveProfile()) // 0x320C CMD_SETTING_V_PROFILE_NUMBER + put(MsgSettingProfileRatiosAll()) // 0x320D CMD_SETTING_V_CIR_CF_VALUE + put(MsgSetSingleBasalProfile()) // 0x3302 CMD_SETTING_BASAL_INS_S + put(MsgSetBasalProfile()) // 0x3306 CMD_SETTING_BASAL_PROFILE_S + put(MsgSetUserOptions()) // 0x330B CMD_SETTING_USER_OPTIONS_S + put(MsgSetActivateBasalProfile()) // 0x330C CMD_SETTING_PROFILE_NUMBER_S + put(MsgHistoryAllDone()) // 0x41F1 CMD_HISTORY_ALL_DONE + put(MsgHistoryAll()) // 0x41F2 CMD_HISTORY_ALL + put(MsgHistoryNewDone()) // 0x42F1 CMD_HISTORY_NEW_DONE + put(MsgHistoryNew()) // 0x42F2 CMD_HISTORY_NEW + put(MsgCheckValue()) // 0xF0F1 CMD_PUMP_CHECK_VALUE + } + + override fun put(message: MessageBase) { + messages[message.command] = message + } + + override fun findMessage(command: Int): MessageBase { + return messages[command] ?: MessageBase() + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MsgBolusProgress.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MsgBolusProgress.java index 6fb1993237..362b72c929 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MsgBolusProgress.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MsgBolusProgress.java @@ -6,6 +6,7 @@ import org.slf4j.LoggerFactory; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.general.overview.events.EventOverviewBolusProgress; import info.nightscout.androidaps.plugins.treatments.Treatment; @@ -37,15 +38,15 @@ public class MsgBolusProgress extends MessageBase { lastReceive = System.currentTimeMillis(); Double done = (amount * 100 - progress) / 100d; t.insulin = done; - EventOverviewBolusProgress bolusingEvent = EventOverviewBolusProgress.getInstance(); - bolusingEvent.status = String.format(MainApp.gs(R.string.bolusdelivering), done); - bolusingEvent.t = t; - bolusingEvent.percent = Math.min((int) (done / amount * 100), 100); + EventOverviewBolusProgress bolusingEvent = EventOverviewBolusProgress.INSTANCE; + bolusingEvent.setStatus(String.format(MainApp.gs(R.string.bolusdelivering), done)); + bolusingEvent.setT(t); + bolusingEvent.setPercent(Math.min((int) (done / amount * 100), 100)); if (L.isEnabled(L.PUMPCOMM)) { log.debug("Bolus remaining: " + progress + " delivered: " + done); } - MainApp.bus().post(bolusingEvent); + RxBus.INSTANCE.send(bolusingEvent); } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MsgBolusStop.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MsgBolusStop.java index 39531b8b10..2ea50297fb 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MsgBolusStop.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MsgBolusStop.java @@ -6,6 +6,7 @@ import org.slf4j.LoggerFactory; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.general.overview.events.EventOverviewBolusProgress; import info.nightscout.androidaps.plugins.treatments.Treatment; @@ -35,15 +36,15 @@ public class MsgBolusStop extends MessageBase { public void handleMessage(byte[] bytes) { if (L.isEnabled(L.PUMPCOMM)) log.debug("Messsage received"); - EventOverviewBolusProgress bolusingEvent = EventOverviewBolusProgress.getInstance(); + EventOverviewBolusProgress bolusingEvent = EventOverviewBolusProgress.INSTANCE; stopped = true; if (!forced) { t.insulin = amount; - bolusingEvent.status = MainApp.gs(R.string.overview_bolusprogress_delivered); - bolusingEvent.percent = 100; + bolusingEvent.setStatus(MainApp.gs(R.string.overview_bolusprogress_delivered)); + bolusingEvent.setPercent(100); } else { - bolusingEvent.status = MainApp.gs(R.string.overview_bolusprogress_stoped); + bolusingEvent.setStatus(MainApp.gs(R.string.overview_bolusprogress_stoped)); } - MainApp.bus().post(bolusingEvent); + RxBus.INSTANCE.send(bolusingEvent); } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MsgCheckValue.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MsgCheckValue.java index d40f8f0cd6..317fe9db81 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MsgCheckValue.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MsgCheckValue.java @@ -32,7 +32,7 @@ public class MsgCheckValue extends MessageBase { pump.protocol = intFromBuff(bytes, 1, 1); pump.productCode = intFromBuff(bytes, 2, 1); if (pump.model != DanaRPump.EXPORT_MODEL) { - MainApp.getSpecificPlugin(DanaRPlugin.class).disconnect("Wrong Model"); + DanaRPlugin.getPlugin().disconnect("Wrong Model"); log.debug("Wrong model selected"); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MsgError.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MsgError.java index f697894778..a6fdaa8d0d 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MsgError.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MsgError.java @@ -6,6 +6,7 @@ import org.slf4j.LoggerFactory; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.general.overview.events.EventOverviewBolusProgress; import info.nightscout.androidaps.plugins.general.nsclient.NSUpload; @@ -44,10 +45,10 @@ public class MsgError extends MessageBase { } if (errorCode < 8) { // bolus delivering stopped - EventOverviewBolusProgress bolusingEvent = EventOverviewBolusProgress.getInstance(); + EventOverviewBolusProgress bolusingEvent = EventOverviewBolusProgress.INSTANCE; MsgBolusStop.stopped = true; - bolusingEvent.status = errorString; - MainApp.bus().post(bolusingEvent); + bolusingEvent.setStatus(errorString); + RxBus.INSTANCE.send(bolusingEvent); failed=true; } if (L.isEnabled(L.PUMPCOMM)) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MsgHistoryAll.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MsgHistoryAll.java index 7de2489c91..955d7fc090 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MsgHistoryAll.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MsgHistoryAll.java @@ -6,6 +6,7 @@ import org.slf4j.LoggerFactory; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.db.DanaRHistoryRecord; import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.pump.danaR.events.EventDanaRSyncStatus; import info.nightscout.androidaps.utils.DateUtil; @@ -33,8 +34,6 @@ public class MsgHistoryAll extends MessageBase { byte paramByte8 = (byte) intFromBuff(bytes, 7, 1); double value = (double) intFromBuff(bytes, 8, 2); - EventDanaRSyncStatus ev = new EventDanaRSyncStatus(); - DanaRHistoryRecord danaRHistoryRecord = new DanaRHistoryRecord(); danaRHistoryRecord.recordCode = recordCode; @@ -145,11 +144,6 @@ public class MsgHistoryAll extends MessageBase { } MainApp.getDbHelper().createOrUpdate(danaRHistoryRecord); - - ev.message = DateUtil.dateAndTimeString(danaRHistoryRecord.recordDate); - ev.message += " " + messageType; - MainApp.bus().post(ev); - - return; + RxBus.INSTANCE.send(new EventDanaRSyncStatus(DateUtil.dateAndTimeString(danaRHistoryRecord.recordDate) + " " + messageType)); } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MsgInitConnStatusBolus.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MsgInitConnStatusBolus.java index 27d5aa5d2d..79fac775a5 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MsgInitConnStatusBolus.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MsgInitConnStatusBolus.java @@ -6,6 +6,7 @@ import org.slf4j.LoggerFactory; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification; import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification; import info.nightscout.androidaps.plugins.general.overview.notifications.Notification; @@ -45,9 +46,9 @@ public class MsgInitConnStatusBolus extends MessageBase { if (!pump.isExtendedBolusEnabled) { Notification notification = new Notification(Notification.EXTENDED_BOLUS_DISABLED, MainApp.gs(R.string.danar_enableextendedbolus), Notification.URGENT); - MainApp.bus().post(new EventNewNotification(notification)); + RxBus.INSTANCE.send(new EventNewNotification(notification)); } else { - MainApp.bus().post(new EventDismissNotification(Notification.EXTENDED_BOLUS_DISABLED)); + RxBus.INSTANCE.send(new EventDismissNotification(Notification.EXTENDED_BOLUS_DISABLED)); } } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MsgInitConnStatusOption.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MsgInitConnStatusOption.java index 220a4d5fc0..f615b4d559 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MsgInitConnStatusOption.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MsgInitConnStatusOption.java @@ -6,6 +6,7 @@ import org.slf4j.LoggerFactory; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification; import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification; @@ -45,13 +46,13 @@ public class MsgInitConnStatusOption extends MessageBase { if (!DanaRPump.getInstance().isPasswordOK()) { Notification notification = new Notification(Notification.WRONG_PUMP_PASSWORD, MainApp.gs(R.string.wrongpumppassword), Notification.URGENT); - MainApp.bus().post(new EventNewNotification(notification)); + RxBus.INSTANCE.send(new EventNewNotification(notification)); } else { - MainApp.bus().post(new EventDismissNotification(Notification.WRONG_PUMP_PASSWORD)); + RxBus.INSTANCE.send(new EventDismissNotification(Notification.WRONG_PUMP_PASSWORD)); } // This is last message of initial sequence - if (ConfigBuilderPlugin.getPlugin().getActivePump() != null ) + if (ConfigBuilderPlugin.getPlugin().getActivePump() != null) ConfigBuilderPlugin.getPlugin().getActivePump().finishHandshaking(); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MsgInitConnStatusTime.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MsgInitConnStatusTime.java index 72e538355a..974e63fe80 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MsgInitConnStatusTime.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MsgInitConnStatusTime.java @@ -5,9 +5,10 @@ import org.slf4j.LoggerFactory; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; -import info.nightscout.androidaps.events.EventRefreshGui; +import info.nightscout.androidaps.events.EventRebuildTabs; import info.nightscout.androidaps.interfaces.PluginType; import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification; import info.nightscout.androidaps.plugins.general.overview.notifications.Notification; @@ -29,24 +30,24 @@ public class MsgInitConnStatusTime extends MessageBase { public void handleMessage(byte[] bytes) { if (bytes.length - 10 > 7) { Notification notification = new Notification(Notification.WRONG_DRIVER, MainApp.gs(R.string.pumpdrivercorrected), Notification.NORMAL); - MainApp.bus().post(new EventNewNotification(notification)); - MainApp.getSpecificPlugin(DanaRPlugin.class).disconnect("Wrong Model"); + RxBus.INSTANCE.send(new EventNewNotification(notification)); + DanaRPlugin.getPlugin().disconnect("Wrong Model"); log.error("Wrong model selected. Switching to Korean DanaR"); - MainApp.getSpecificPlugin(DanaRKoreanPlugin.class).setPluginEnabled(PluginType.PUMP, true); - MainApp.getSpecificPlugin(DanaRKoreanPlugin.class).setFragmentVisible(PluginType.PUMP, true); - MainApp.getSpecificPlugin(DanaRPlugin.class).setPluginEnabled(PluginType.PUMP, false); - MainApp.getSpecificPlugin(DanaRPlugin.class).setFragmentVisible(PluginType.PUMP, false); + DanaRKoreanPlugin.getPlugin().setPluginEnabled(PluginType.PUMP, true); + DanaRKoreanPlugin.getPlugin().setFragmentVisible(PluginType.PUMP, true); + DanaRPlugin.getPlugin().setPluginEnabled(PluginType.PUMP, false); + DanaRPlugin.getPlugin().setFragmentVisible(PluginType.PUMP, false); DanaRPump.reset(); // mark not initialized //If profile coming from pump, switch it as well - if (MainApp.getSpecificPlugin(DanaRPlugin.class).isEnabled(PluginType.PROFILE)) { - (MainApp.getSpecificPlugin(DanaRPlugin.class)).setPluginEnabled(PluginType.PROFILE, false); - (MainApp.getSpecificPlugin(DanaRKoreanPlugin.class)).setPluginEnabled(PluginType.PROFILE, true); + if (DanaRPlugin.getPlugin().isEnabled(PluginType.PROFILE)) { + (DanaRPlugin.getPlugin()).setPluginEnabled(PluginType.PROFILE, false); + (DanaRKoreanPlugin.getPlugin()).setPluginEnabled(PluginType.PROFILE, true); } ConfigBuilderPlugin.getPlugin().storeSettings("ChangingDanaDriver"); - MainApp.bus().post(new EventRefreshGui()); + RxBus.INSTANCE.send(new EventRebuildTabs()); ConfigBuilderPlugin.getPlugin().getCommandQueue().readStatus("PumpDriverChange", null); // force new connection failed = false; return; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MsgSetBasalProfile.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MsgSetBasalProfile.java index 7ea6719e30..89cd1c95f4 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MsgSetBasalProfile.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MsgSetBasalProfile.java @@ -6,6 +6,7 @@ import org.slf4j.LoggerFactory; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification; import info.nightscout.androidaps.plugins.general.overview.notifications.Notification; @@ -37,12 +38,12 @@ public class MsgSetBasalProfile extends MessageBase { if (L.isEnabled(L.PUMPCOMM)) log.debug("Set basal profile result: " + result + " FAILED!!!"); Notification reportFail = new Notification(Notification.PROFILE_SET_FAILED, MainApp.gs(R.string.profile_set_failed), Notification.URGENT); - MainApp.bus().post(new EventNewNotification(reportFail)); + RxBus.INSTANCE.send(new EventNewNotification(reportFail)); } else { if (L.isEnabled(L.PUMPCOMM)) log.debug("Set basal profile result: " + result); Notification reportOK = new Notification(Notification.PROFILE_SET_OK, MainApp.gs(R.string.profile_set_ok), Notification.INFO, 60); - MainApp.bus().post(new EventNewNotification(reportOK)); + RxBus.INSTANCE.send(new EventNewNotification(reportOK)); } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MsgSetSingleBasalProfile.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MsgSetSingleBasalProfile.java index 29c70c9379..8888f17b1c 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MsgSetSingleBasalProfile.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MsgSetSingleBasalProfile.java @@ -6,6 +6,7 @@ import org.slf4j.LoggerFactory; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.general.overview.notifications.Notification; import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification; @@ -34,12 +35,12 @@ public class MsgSetSingleBasalProfile extends MessageBase { if (L.isEnabled(L.PUMPCOMM)) log.debug("Set basal profile result: " + result + " FAILED!!!"); Notification reportFail = new Notification(Notification.PROFILE_SET_FAILED, MainApp.gs(R.string.profile_set_failed), Notification.URGENT); - MainApp.bus().post(new EventNewNotification(reportFail)); + RxBus.INSTANCE.send(new EventNewNotification(reportFail)); } else { if (L.isEnabled(L.PUMPCOMM)) log.debug("Set basal profile result: " + result); Notification reportOK = new Notification(Notification.PROFILE_SET_OK, MainApp.gs(R.string.profile_set_ok), Notification.INFO, 60); - MainApp.bus().post(new EventNewNotification(reportOK)); + RxBus.INSTANCE.send(new EventNewNotification(reportOK)); } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MsgSettingMeal.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MsgSettingMeal.java index b8be76560b..6756d9d504 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MsgSettingMeal.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MsgSettingMeal.java @@ -7,6 +7,7 @@ import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.interfaces.PluginType; import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification; import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification; import info.nightscout.androidaps.plugins.general.overview.notifications.Notification; @@ -51,16 +52,16 @@ public class MsgSettingMeal extends MessageBase { if (pump.basalStep != 0.01d) { Notification notification = new Notification(Notification.WRONGBASALSTEP, MainApp.gs(R.string.danar_setbasalstep001), Notification.URGENT); - MainApp.bus().post(new EventNewNotification(notification)); + RxBus.INSTANCE.send(new EventNewNotification(notification)); } else { - MainApp.bus().post(new EventDismissNotification(Notification.WRONGBASALSTEP)); + RxBus.INSTANCE.send(new EventDismissNotification(Notification.WRONGBASALSTEP)); } if (pump.isConfigUD) { Notification notification = new Notification(Notification.UD_MODE_ENABLED, MainApp.gs(R.string.danar_switchtouhmode), Notification.URGENT); - MainApp.bus().post(new EventNewNotification(notification)); + RxBus.INSTANCE.send(new EventNewNotification(notification)); } else { - MainApp.bus().post(new EventDismissNotification(Notification.UD_MODE_ENABLED)); + RxBus.INSTANCE.send(new EventDismissNotification(Notification.UD_MODE_ENABLED)); } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MsgSettingPumpTime.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MsgSettingPumpTime.java index d22352e69f..23a6ba6f75 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MsgSettingPumpTime.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MsgSettingPumpTime.java @@ -34,4 +34,10 @@ public class MsgSettingPumpTime extends MessageBase { DanaRPump.getInstance().pumpTime = time; } + + @Override + public void handleMessageNotReceived() { + DanaRPump.getInstance().pumpTime = 0; + } + } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MsgStatusBolusExtended.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MsgStatusBolusExtended.java index 9b27561d5a..aa272b546d 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MsgStatusBolusExtended.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MsgStatusBolusExtended.java @@ -1,6 +1,6 @@ package info.nightscout.androidaps.plugins.pump.danaR.comm; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MsgStatusTempBasal.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MsgStatusTempBasal.java index 76fa0e9f43..00b3bf5f69 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MsgStatusTempBasal.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/comm/MsgStatusTempBasal.java @@ -1,6 +1,6 @@ package info.nightscout.androidaps.plugins.pump.danaR.comm; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/events/EventDanaRNewStatus.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/events/EventDanaRNewStatus.java deleted file mode 100644 index 9efdee1057..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/events/EventDanaRNewStatus.java +++ /dev/null @@ -1,9 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.danaR.events; - -import info.nightscout.androidaps.events.Event; - -/** - * Created by mike on 08.07.2016. - */ -public class EventDanaRNewStatus extends Event { -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/events/EventDanaRNewStatus.kt b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/events/EventDanaRNewStatus.kt new file mode 100644 index 0000000000..67b12d954c --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/events/EventDanaRNewStatus.kt @@ -0,0 +1,5 @@ +package info.nightscout.androidaps.plugins.pump.danaR.events + +import info.nightscout.androidaps.events.Event + +class EventDanaRNewStatus : Event() diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/events/EventDanaRSyncStatus.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/events/EventDanaRSyncStatus.java deleted file mode 100644 index 5fc0bd740f..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/events/EventDanaRSyncStatus.java +++ /dev/null @@ -1,14 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.danaR.events; - -import info.nightscout.androidaps.events.Event; - -/** - * Created by mike on 20.07.2016. - */ -public class EventDanaRSyncStatus extends Event { - public String message; - - public EventDanaRSyncStatus() { - } - - } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/events/EventDanaRSyncStatus.kt b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/events/EventDanaRSyncStatus.kt new file mode 100644 index 0000000000..59ad7e2d83 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/events/EventDanaRSyncStatus.kt @@ -0,0 +1,5 @@ +package info.nightscout.androidaps.plugins.pump.danaR.events + +import info.nightscout.androidaps.events.Event + +class EventDanaRSyncStatus(var message: String) : Event() \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/services/AbstractDanaRExecutionService.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/services/AbstractDanaRExecutionService.java index 9e3c1ff3ca..839debe9be 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/services/AbstractDanaRExecutionService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/services/AbstractDanaRExecutionService.java @@ -23,6 +23,7 @@ import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.data.PumpEnactResult; import info.nightscout.androidaps.events.EventPumpStatusChanged; import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.pump.danaR.comm.MessageBase; import info.nightscout.androidaps.plugins.pump.danaR.comm.MsgBolusStop; import info.nightscout.androidaps.plugins.pump.danaR.comm.MsgHistoryAlarm; @@ -105,7 +106,7 @@ public abstract class AbstractDanaRExecutionService extends Service { if (mSerialIOThread != null) { mSerialIOThread.disconnect("BT disconnection broadcast"); } - MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.DISCONNECTED)); + RxBus.INSTANCE.send(new EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTED)); } } } @@ -135,7 +136,7 @@ public abstract class AbstractDanaRExecutionService extends Service { public void finishHandshaking() { mHandshakeInProgress = false; - MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.CONNECTED, 0)); + RxBus.INSTANCE.send(new EventPumpStatusChanged(EventPumpStatusChanged.Status.CONNECTED, 0)); } public void disconnect(String from) { @@ -243,7 +244,7 @@ public abstract class AbstractDanaRExecutionService extends Service { long timeToWholeMinute = (60000 - time % 60000); if (timeToWholeMinute > 59800 || timeToWholeMinute < 3000) break; - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.waitingfortimesynchronization, (int) (timeToWholeMinute / 1000)))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.waitingfortimesynchronization, (int) (timeToWholeMinute / 1000)))); SystemClock.sleep(Math.min(timeToWholeMinute, 100)); } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/services/DanaRExecutionService.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/services/DanaRExecutionService.java index 22d98368a6..0801d8a173 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/services/DanaRExecutionService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/services/DanaRExecutionService.java @@ -5,8 +5,6 @@ import android.content.IntentFilter; import android.os.Binder; import android.os.SystemClock; -import com.squareup.otto.Subscribe; - import java.io.IOException; import java.util.Date; @@ -22,6 +20,7 @@ import info.nightscout.androidaps.events.EventProfileNeedsUpdate; import info.nightscout.androidaps.events.EventPumpStatusChanged; import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; import info.nightscout.androidaps.plugins.general.nsclient.NSUpload; @@ -32,6 +31,7 @@ import info.nightscout.androidaps.plugins.general.overview.notifications.Notific import info.nightscout.androidaps.plugins.pump.danaR.DanaRPump; import info.nightscout.androidaps.plugins.pump.danaR.SerialIOThread; import info.nightscout.androidaps.plugins.pump.danaR.comm.MessageBase; +import info.nightscout.androidaps.plugins.pump.danaR.comm.MessageHashTableR; import info.nightscout.androidaps.plugins.pump.danaR.comm.MsgBolusProgress; import info.nightscout.androidaps.plugins.pump.danaR.comm.MsgBolusStart; import info.nightscout.androidaps.plugins.pump.danaR.comm.MsgBolusStartWithSpeed; @@ -65,15 +65,50 @@ import info.nightscout.androidaps.plugins.treatments.Treatment; import info.nightscout.androidaps.queue.Callback; import info.nightscout.androidaps.queue.commands.Command; import info.nightscout.androidaps.utils.DateUtil; +import info.nightscout.androidaps.utils.FabricPrivacy; import info.nightscout.androidaps.utils.SP; +import io.reactivex.disposables.CompositeDisposable; +import io.reactivex.schedulers.Schedulers; public class DanaRExecutionService extends AbstractDanaRExecutionService { + private CompositeDisposable disposable = new CompositeDisposable(); public DanaRExecutionService() { mBinder = new LocalBinder(); - registerBus(); MainApp.instance().getApplicationContext().registerReceiver(receiver, new IntentFilter(BluetoothDevice.ACTION_ACL_DISCONNECTED)); + } + + @Override + public void onCreate() { + super.onCreate(); + disposable.add(RxBus.INSTANCE + .toObservable(EventPreferenceChange.class) + .observeOn(Schedulers.io()) + .subscribe(event -> { + if (mSerialIOThread != null) + mSerialIOThread.disconnect("EventPreferenceChange"); + }, FabricPrivacy::logException) + ); + disposable.add(RxBus.INSTANCE + .toObservable(EventAppExit.class) + .observeOn(Schedulers.io()) + .subscribe(event -> { + if (L.isEnabled(L.PUMP)) + log.debug("EventAppExit received"); + + if (mSerialIOThread != null) + mSerialIOThread.disconnect("Application exit"); + MainApp.instance().getApplicationContext().unregisterReceiver(receiver); + stopSelf(); + }, FabricPrivacy::logException) + ); + } + + @Override + public void onDestroy() { + disposable.clear(); + super.onDestroy(); } public class LocalBinder extends Binder { @@ -82,21 +117,6 @@ public class DanaRExecutionService extends AbstractDanaRExecutionService { } } - private void registerBus() { - try { - MainApp.bus().unregister(this); - } catch (RuntimeException x) { - // Ignore - } - MainApp.bus().register(this); - } - - @Subscribe - public void onStatusEvent(final EventPreferenceChange pch) { - if (mSerialIOThread != null) - mSerialIOThread.disconnect("EventPreferenceChange"); - } - public void connect() { if (mConnectionInProgress) return; @@ -123,9 +143,9 @@ public class DanaRExecutionService extends AbstractDanaRExecutionService { if (mSerialIOThread != null) { mSerialIOThread.disconnect("Recreate SerialIOThread"); } - mSerialIOThread = new SerialIOThread(mRfcommSocket); + mSerialIOThread = new SerialIOThread(mRfcommSocket, MessageHashTableR.INSTANCE); mHandshakeInProgress = true; - MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.HANDSHAKING, 0)); + RxBus.INSTANCE.send(new EventPumpStatusChanged(EventPumpStatusChanged.Status.HANDSHAKING, 0)); } mConnectionInProgress = false; @@ -135,7 +155,7 @@ public class DanaRExecutionService extends AbstractDanaRExecutionService { public void getPumpStatus() { DanaRPump danaRPump = DanaRPump.getInstance(); try { - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.gettingpumpstatus))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.gettingpumpstatus))); MsgStatus statusMsg = new MsgStatus(); MsgStatusBasic statusBasicMsg = new MsgStatusBasic(); MsgStatusTempBasal tempStatusMsg = new MsgStatusTempBasal(); @@ -151,11 +171,11 @@ public class DanaRExecutionService extends AbstractDanaRExecutionService { mSerialIOThread.sendMessage(statusMsg); mSerialIOThread.sendMessage(statusBasicMsg); - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.gettingtempbasalstatus))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.gettingtempbasalstatus))); mSerialIOThread.sendMessage(tempStatusMsg); - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.gettingextendedbolusstatus))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.gettingextendedbolusstatus))); mSerialIOThread.sendMessage(exStatusMsg); - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.gettingbolusstatus))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.gettingbolusstatus))); long now = System.currentTimeMillis(); danaRPump.lastConnection = now; @@ -163,15 +183,15 @@ public class DanaRExecutionService extends AbstractDanaRExecutionService { Profile profile = ProfileFunctions.getInstance().getProfile(); PumpInterface pump = ConfigBuilderPlugin.getPlugin().getActivePump(); if (profile != null && Math.abs(danaRPump.currentBasal - profile.getBasal()) >= pump.getPumpDescription().basalStep) { - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.gettingpumpsettings))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.gettingpumpsettings))); mSerialIOThread.sendMessage(new MsgSettingBasal()); if (!pump.isThisProfileSet(profile) && !ConfigBuilderPlugin.getPlugin().getCommandQueue().isRunning(Command.CommandType.BASALPROFILE)) { - MainApp.bus().post(new EventProfileNeedsUpdate()); + RxBus.INSTANCE.send(new EventProfileNeedsUpdate()); } } if (danaRPump.lastSettingsRead + 60 * 60 * 1000L < now || !pump.isInitialized()) { - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.gettingpumpsettings))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.gettingpumpsettings))); mSerialIOThread.sendMessage(new MsgSettingShippingInfo()); mSerialIOThread.sendMessage(new MsgSettingActiveProfile()); mSerialIOThread.sendMessage(new MsgSettingMeal()); @@ -183,8 +203,17 @@ public class DanaRExecutionService extends AbstractDanaRExecutionService { mSerialIOThread.sendMessage(new MsgSettingProfileRatios()); mSerialIOThread.sendMessage(new MsgSettingProfileRatiosAll()); mSerialIOThread.sendMessage(new MsgSettingUserOptions()); - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.gettingpumptime))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.gettingpumptime))); mSerialIOThread.sendMessage(new MsgSettingPumpTime()); + if (danaRPump.pumpTime == 0) { + // initial handshake was not successfull + // deinitialize pump + danaRPump.lastConnection = 0; + danaRPump.lastSettingsRead = 0; + RxBus.INSTANCE.send(new EventDanaRNewStatus()); + RxBus.INSTANCE.send(new EventInitializationChanged()); + return; + } long timeDiff = (danaRPump.pumpTime - System.currentTimeMillis()) / 1000L; if (L.isEnabled(L.PUMP)) log.debug("Pump time difference: " + timeDiff + " seconds"); @@ -198,15 +227,15 @@ public class DanaRExecutionService extends AbstractDanaRExecutionService { danaRPump.lastSettingsRead = now; } - MainApp.bus().post(new EventDanaRNewStatus()); - MainApp.bus().post(new EventInitializationChanged()); + RxBus.INSTANCE.send(new EventDanaRNewStatus()); + RxBus.INSTANCE.send(new EventInitializationChanged()); NSUpload.uploadDeviceStatus(); if (danaRPump.dailyTotalUnits > danaRPump.maxDailyTotalUnits * Constants.dailyLimitWarning) { if (L.isEnabled(L.PUMP)) log.debug("Approaching daily limit: " + danaRPump.dailyTotalUnits + "/" + danaRPump.maxDailyTotalUnits); if (System.currentTimeMillis() > lastApproachingDailyLimit + 30 * 60 * 1000) { Notification reportFail = new Notification(Notification.APPROACHING_DAILY_LIMIT, MainApp.gs(R.string.approachingdailylimit), Notification.URGENT); - MainApp.bus().post(new EventNewNotification(reportFail)); + RxBus.INSTANCE.send(new EventNewNotification(reportFail)); NSUpload.uploadError(MainApp.gs(R.string.approachingdailylimit) + ": " + danaRPump.dailyTotalUnits + "/" + danaRPump.maxDailyTotalUnits + "U"); lastApproachingDailyLimit = System.currentTimeMillis(); } @@ -220,41 +249,41 @@ public class DanaRExecutionService extends AbstractDanaRExecutionService { DanaRPump danaRPump = DanaRPump.getInstance(); if (!isConnected()) return false; if (danaRPump.isTempBasalInProgress) { - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.stoppingtempbasal))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.stoppingtempbasal))); mSerialIOThread.sendMessage(new MsgSetTempBasalStop()); SystemClock.sleep(500); } - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.settingtempbasal))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.settingtempbasal))); mSerialIOThread.sendMessage(new MsgSetTempBasalStart(percent, durationInHours)); mSerialIOThread.sendMessage(new MsgStatusTempBasal()); - MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.DISCONNECTING)); + RxBus.INSTANCE.send(new EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTING)); return true; } public boolean tempBasalStop() { if (!isConnected()) return false; - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.stoppingtempbasal))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.stoppingtempbasal))); mSerialIOThread.sendMessage(new MsgSetTempBasalStop()); mSerialIOThread.sendMessage(new MsgStatusTempBasal()); - MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.DISCONNECTING)); + RxBus.INSTANCE.send(new EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTING)); return true; } public boolean extendedBolus(double insulin, int durationInHalfHours) { if (!isConnected()) return false; - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.settingextendedbolus))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.settingextendedbolus))); mSerialIOThread.sendMessage(new MsgSetExtendedBolusStart(insulin, (byte) (durationInHalfHours & 0xFF))); mSerialIOThread.sendMessage(new MsgStatusBolusExtended()); - MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.DISCONNECTING)); + RxBus.INSTANCE.send(new EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTING)); return true; } public boolean extendedBolusStop() { if (!isConnected()) return false; - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.stoppingextendedbolus))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.stoppingextendedbolus))); mSerialIOThread.sendMessage(new MsgSetExtendedBolusStop()); mSerialIOThread.sendMessage(new MsgStatusBolusExtended()); - MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.DISCONNECTING)); + RxBus.INSTANCE.send(new EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTING)); return true; } @@ -302,9 +331,9 @@ public class DanaRExecutionService extends AbstractDanaRExecutionService { } SystemClock.sleep(300); - EventOverviewBolusProgress bolusingEvent = EventOverviewBolusProgress.getInstance(); - bolusingEvent.t = t; - bolusingEvent.percent = 99; + EventOverviewBolusProgress bolusingEvent = EventOverviewBolusProgress.INSTANCE; + bolusingEvent.setT(t); + bolusingEvent.setPercent(99); mBolusingTreatment = null; @@ -328,8 +357,8 @@ public class DanaRExecutionService extends AbstractDanaRExecutionService { while (System.currentTimeMillis() < expectedEnd) { long waitTime = expectedEnd - System.currentTimeMillis(); - bolusingEvent.status = String.format(MainApp.gs(R.string.waitingforestimatedbolusend), waitTime / 1000); - MainApp.bus().post(bolusingEvent); + bolusingEvent.setStatus(String.format(MainApp.gs(R.string.waitingforestimatedbolusend), waitTime / 1000)); + RxBus.INSTANCE.send(bolusingEvent); SystemClock.sleep(1000); } @@ -384,7 +413,7 @@ public class DanaRExecutionService extends AbstractDanaRExecutionService { public boolean updateBasalsInPump(final Profile profile) { DanaRPump danaRPump = DanaRPump.getInstance(); if (!isConnected()) return false; - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.updatingbasalrates))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.updatingbasalrates))); double[] basal = DanaRPump.getInstance().buildDanaRProfileRecord(profile); MsgSetBasalProfile msgSet = new MsgSetBasalProfile((byte) 0, basal); mSerialIOThread.sendMessage(msgSet); @@ -392,23 +421,10 @@ public class DanaRExecutionService extends AbstractDanaRExecutionService { mSerialIOThread.sendMessage(msgActivate); danaRPump.lastSettingsRead = 0; // force read full settings getPumpStatus(); - MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.DISCONNECTING)); + RxBus.INSTANCE.send(new EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTING)); return true; } - @Subscribe - public void onStatusEvent(EventAppExit event) { - if (L.isEnabled(L.PUMP)) - log.debug("EventAppExit received"); - - if (mSerialIOThread != null) - mSerialIOThread.disconnect("Application exit"); - - MainApp.instance().getApplicationContext().unregisterReceiver(receiver); - - stopSelf(); - } - public PumpEnactResult setUserOptions() { if (!isConnected()) return new PumpEnactResult().success(false); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRKorean/DanaRKoreanPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRKorean/DanaRKoreanPlugin.java index 3b4aa0ee4c..2645e25ade 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRKorean/DanaRKoreanPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRKorean/DanaRKoreanPlugin.java @@ -5,10 +5,6 @@ import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.IBinder; -import android.support.v4.app.FragmentActivity; -import android.support.v7.app.AlertDialog; - -import com.squareup.otto.Subscribe; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; @@ -22,7 +18,7 @@ import info.nightscout.androidaps.events.EventPreferenceChange; import info.nightscout.androidaps.interfaces.Constraint; import info.nightscout.androidaps.interfaces.PluginType; import info.nightscout.androidaps.logging.L; -import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderFragment; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.pump.common.defs.PumpType; import info.nightscout.androidaps.plugins.pump.danaR.AbstractDanaRPlugin; import info.nightscout.androidaps.plugins.pump.danaR.DanaRPump; @@ -30,13 +26,17 @@ import info.nightscout.androidaps.plugins.pump.danaR.comm.MsgBolusStart; import info.nightscout.androidaps.plugins.pump.danaRKorean.services.DanaRKoreanExecutionService; import info.nightscout.androidaps.plugins.treatments.Treatment; import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; +import info.nightscout.androidaps.utils.FabricPrivacy; import info.nightscout.androidaps.utils.Round; import info.nightscout.androidaps.utils.SP; +import io.reactivex.disposables.CompositeDisposable; +import io.reactivex.schedulers.Schedulers; /** * Created by mike on 05.08.2016. */ public class DanaRKoreanPlugin extends AbstractDanaRPlugin { + private CompositeDisposable disposable = new CompositeDisposable(); private static DanaRKoreanPlugin plugin = null; @@ -53,36 +53,32 @@ public class DanaRKoreanPlugin extends AbstractDanaRPlugin { pumpDescription.setPumpDescription(PumpType.DanaRKorean); } - @Override - public void switchAllowed(ConfigBuilderFragment.PluginViewHolder.PluginSwitcher pluginSwitcher, FragmentActivity context) { - boolean allowHardwarePump = SP.getBoolean("allow_hardware_pump", false); - if (allowHardwarePump || context == null) { - pluginSwitcher.invoke(); - } else { - AlertDialog.Builder builder = new AlertDialog.Builder(context); - builder.setMessage(R.string.allow_hardware_pump_text) - .setPositiveButton(R.string.yes, (dialog, id) -> { - pluginSwitcher.invoke(); - SP.putBoolean("allow_hardware_pump", true); - if (L.isEnabled(L.PUMP)) - log.debug("First time HW pump allowed!"); - }) - .setNegativeButton(R.string.cancel, (dialog, id) -> { - pluginSwitcher.cancel(); - if (L.isEnabled(L.PUMP)) - log.debug("User does not allow switching to HW pump!"); - }); - builder.create().show(); - } - } - - @Override protected void onStart() { Context context = MainApp.instance().getApplicationContext(); Intent intent = new Intent(context, DanaRKoreanExecutionService.class); context.bindService(intent, mConnection, Context.BIND_AUTO_CREATE); - MainApp.bus().register(this); + disposable.add(RxBus.INSTANCE + .toObservable(EventPreferenceChange.class) + .observeOn(Schedulers.io()) + .subscribe(event -> { + if (isEnabled(PluginType.PUMP)) { + boolean previousValue = useExtendedBoluses; + useExtendedBoluses = SP.getBoolean(R.string.key_danar_useextended, false); + + if (useExtendedBoluses != previousValue && TreatmentsPlugin.getPlugin().isInHistoryExtendedBoluslInProgress()) { + sExecutionService.extendedBolusStop(); + } + } + }, FabricPrivacy::logException) + ); + disposable.add(RxBus.INSTANCE + .toObservable(EventAppExit.class) + .observeOn(Schedulers.io()) + .subscribe(event -> { + MainApp.instance().getApplicationContext().unbindService(mConnection); + }, FabricPrivacy::logException) + ); super.onStart(); } @@ -91,7 +87,8 @@ public class DanaRKoreanPlugin extends AbstractDanaRPlugin { Context context = MainApp.instance().getApplicationContext(); context.unbindService(mConnection); - MainApp.bus().unregister(this); + disposable.clear(); + super.onStop(); } private ServiceConnection mConnection = new ServiceConnection() { @@ -110,24 +107,6 @@ public class DanaRKoreanPlugin extends AbstractDanaRPlugin { } }; - @SuppressWarnings("UnusedParameters") - @Subscribe - public void onStatusEvent(final EventAppExit e) { - MainApp.instance().getApplicationContext().unbindService(mConnection); - } - - @Subscribe - public void onStatusEvent(final EventPreferenceChange s) { - if (isEnabled(PluginType.PUMP)) { - boolean previousValue = useExtendedBoluses; - useExtendedBoluses = SP.getBoolean(R.string.key_danar_useextended, false); - - if (useExtendedBoluses != previousValue && TreatmentsPlugin.getPlugin().isInHistoryExtendedBoluslInProgress()) { - sExecutionService.extendedBolusStop(); - } - } - } - // Plugin base interface @Override public String getName() { @@ -266,7 +245,7 @@ public class DanaRKoreanPlugin extends AbstractDanaRPlugin { // Correct basal already set ? if (L.isEnabled(L.PUMP)) log.debug("setTempBasalAbsolute: currently running: " + activeTemp.toString()); - if (activeTemp.percentRate == percentRate) { + if (activeTemp.percentRate == percentRate && activeTemp.getPlannedRemainingMinutes() > 4) { if (enforceNew) { cancelTempBasal(true); } else { @@ -364,6 +343,11 @@ public class DanaRKoreanPlugin extends AbstractDanaRPlugin { return result; } + @Override + public PumpType model() { + return PumpType.DanaRKorean; + } + private PumpEnactResult cancelRealTempBasal() { PumpEnactResult result = new PumpEnactResult(); TemporaryBasal runningTB = TreatmentsPlugin.getPlugin().getTempBasalFromHistory(System.currentTimeMillis()); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRKorean/SerialIOThread.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRKorean/SerialIOThread.java deleted file mode 100644 index 5c47d7c7d1..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRKorean/SerialIOThread.java +++ /dev/null @@ -1,210 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.danaRKorean; - -import android.bluetooth.BluetoothSocket; -import android.os.SystemClock; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -import info.nightscout.androidaps.logging.L; -import info.nightscout.androidaps.plugins.pump.danaR.DanaRPump; -import info.nightscout.androidaps.plugins.pump.danaR.comm.MessageBase; -import info.nightscout.androidaps.plugins.pump.danaR.services.AbstractSerialIOThread; -import info.nightscout.androidaps.plugins.pump.danaRKorean.comm.MessageHashTable_k; -import info.nightscout.androidaps.utils.CRC; - -/** - * Created by mike on 17.07.2016. - */ -public class SerialIOThread extends AbstractSerialIOThread { - private static Logger log = LoggerFactory.getLogger(L.PUMPBTCOMM); - - private InputStream mInputStream = null; - private OutputStream mOutputStream = null; - private BluetoothSocket mRfCommSocket; - - private boolean mKeepRunning = true; - private byte[] mReadBuff = new byte[0]; - - private MessageBase processedMessage; - - public SerialIOThread(BluetoothSocket rfcommSocket) { - super(); - - mRfCommSocket = rfcommSocket; - try { - mOutputStream = mRfCommSocket.getOutputStream(); - mInputStream = mRfCommSocket.getInputStream(); - } catch (IOException e) { - log.error("Unhandled exception", e); - } - this.start(); - } - - @Override - public final void run() { - try { - while (mKeepRunning) { - int availableBytes = mInputStream.available(); - // Ask for 1024 byte (or more if available) - byte[] newData = new byte[Math.max(1024, availableBytes)]; - int gotBytes = mInputStream.read(newData); - // When we are here there is some new data available - appendToBuffer(newData, gotBytes); - - // process all messages we already got - while (mReadBuff.length > 3) { // 3rd byte is packet size. continue only if we an determine packet size - byte[] extractedBuff = cutMessageFromBuffer(); - if (extractedBuff == null) - break; // message is not complete in buffer (wrong packet calls disconnection) - - int command = (extractedBuff[5] & 0xFF) | ((extractedBuff[4] << 8) & 0xFF00); - - MessageBase message; - if (processedMessage != null && processedMessage.getCommand() == command) { - message = processedMessage; - } else { - // get it from hash table - message = MessageHashTable_k.findMessage(command); - } - - if (L.isEnabled(L.PUMPBTCOMM)) - log.debug("<<<<< " + message.getMessageName() + " " + message.toHexString(extractedBuff)); - - // process the message content - message.received = true; - message.handleMessage(extractedBuff); - synchronized (message) { - message.notify(); - } - } - } - } catch (Exception e) { - if (e.getMessage().indexOf("bt socket closed") < 0) - log.error("Thread exception: ", e); - mKeepRunning = false; - } - disconnect("EndOfLoop"); - } - - void appendToBuffer(byte[] newData, int gotBytes) { - // add newData to mReadBuff - byte[] newReadBuff = new byte[mReadBuff.length + gotBytes]; - System.arraycopy(mReadBuff, 0, newReadBuff, 0, mReadBuff.length); - System.arraycopy(newData, 0, newReadBuff, mReadBuff.length, gotBytes); - mReadBuff = newReadBuff; - } - - byte[] cutMessageFromBuffer() { - if (mReadBuff[0] == (byte) 0x7E && mReadBuff[1] == (byte) 0x7E) { - int length = (mReadBuff[2] & 0xFF) + 7; - // Check if we have enough data - if (mReadBuff.length < length) { - return null; - } - if (mReadBuff[length - 2] != (byte) 0x2E || mReadBuff[length - 1] != (byte) 0x2E) { - log.error("wrong packet lenght=" + length + " data " + MessageBase.toHexString(mReadBuff)); - disconnect("wrong packet"); - return null; - } - - short crc = CRC.getCrc16(mReadBuff, 3, length - 7); - byte crcByte0 = (byte) (crc >> 8 & 0xFF); - byte crcByte1 = (byte) (crc & 0xFF); - - byte crcByte0received = mReadBuff[length - 4]; - byte crcByte1received = mReadBuff[length - 3]; - - if (crcByte0 != crcByte0received || crcByte1 != crcByte1received) { - log.error("CRC Error" + String.format("%02x ", crcByte0) + String.format("%02x ", crcByte1) + String.format("%02x ", crcByte0received) + String.format("%02x ", crcByte1received)); - disconnect("crc error"); - return null; - } - // Packet is verified here. extract data - byte[] extractedBuff = new byte[length]; - System.arraycopy(mReadBuff, 0, extractedBuff, 0, length); - // remove extracted data from read buffer - byte[] unprocessedData = new byte[mReadBuff.length - length]; - System.arraycopy(mReadBuff, length, unprocessedData, 0, unprocessedData.length); - mReadBuff = unprocessedData; - return extractedBuff; - } else { - log.error("Wrong beginning of packet len=" + mReadBuff.length + " " + MessageBase.toHexString(mReadBuff)); - disconnect("Wrong beginning of packet"); - return null; - } - } - - @Override - public synchronized void sendMessage(MessageBase message) { - if (!mRfCommSocket.isConnected()) { - log.error("Socket not connected on sendMessage"); - return; - } - processedMessage = message; - - byte[] messageBytes = message.getRawMessageBytes(); - if (L.isEnabled(L.PUMPBTCOMM)) - log.debug(">>>>> " + message.getMessageName() + " " + message.toHexString(messageBytes)); - - try { - mOutputStream.write(messageBytes); - } catch (Exception e) { - log.error("sendMessage write exception: ", e); - } - - synchronized (message) { - try { - message.wait(5000); - } catch (InterruptedException e) { - log.error("sendMessage InterruptedException", e); - } - } - - SystemClock.sleep(200); - if (!message.received) { - if (L.isEnabled(L.PUMPBTCOMM)) - log.warn("Reply not received " + message.getMessageName()); - if (message.getCommand() == 0xF0F1) { - DanaRPump.getInstance().isNewPump = false; - log.error("Old firmware detected"); - } - } - } - - @Override - public void disconnect(String reason) { - mKeepRunning = false; - try { - mInputStream.close(); - } catch (Exception e) { - if (L.isEnabled(L.PUMPBTCOMM)) - log.debug(e.getMessage()); - } - try { - mOutputStream.close(); - } catch (Exception e) { - if (L.isEnabled(L.PUMPBTCOMM)) - log.debug(e.getMessage()); - } - try { - mRfCommSocket.close(); - } catch (Exception e) { - if (L.isEnabled(L.PUMPBTCOMM)) - log.debug(e.getMessage()); - } - try { - System.runFinalization(); - } catch (Exception e) { - if (L.isEnabled(L.PUMPBTCOMM)) - log.debug(e.getMessage()); - } - if (L.isEnabled(L.PUMPBTCOMM)) - log.debug("Disconnected: " + reason); - } - -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRKorean/comm/MessageHashTableRkorean.kt b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRKorean/comm/MessageHashTableRkorean.kt new file mode 100644 index 0000000000..ffdf9d14c5 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRKorean/comm/MessageHashTableRkorean.kt @@ -0,0 +1,56 @@ +package info.nightscout.androidaps.plugins.pump.danaRKorean.comm + +import info.nightscout.androidaps.plugins.pump.danaR.comm.* +import java.util.* + +object MessageHashTableRkorean : MessageHashTableBase { + var messages: HashMap = HashMap() + + init { + put(MsgBolusStop()) // 0x0101 CMD_MEALINS_STOP + put(MsgBolusStart()) // 0x0102 CMD_MEALINS_START_DATA + put(MsgBolusProgress()) // 0x0202 CMD_PUMP_THIS_REMAINDER_MEAL_INS + put(MsgStatusProfile()) // 0x0204 CMD_PUMP_CALCULATION_SETTING + put(MsgStatusTempBasal()) // 0x0205 CMD_PUMP_EXERCISE_MODE + put(MsgStatusBolusExtended()) // 0x0207 CMD_PUMP_EXPANS_INS_I + put(MsgStatusBasic_k()) // 0x020A CMD_PUMP_INITVIEW_I + put(MsgStatus_k()) // 0x020B CMD_PUMP_STATUS + put(MsgInitConnStatusTime_k()) // 0x0301 CMD_PUMPINIT_TIME_INFO + put(MsgInitConnStatusBolus_k()) // 0x0302 CMD_PUMPINIT_BOLUS_INFO + put(MsgInitConnStatusBasic_k()) // 0x0303 CMD_PUMPINIT_INIT_INFO + put(MsgSetTempBasalStart()) // 0x0401 CMD_PUMPSET_EXERCISE_S + put(MsgSetCarbsEntry()) // 0x0402 CMD_PUMPSET_HIS_S + put(MsgSetTempBasalStop()) // 0x0403 CMD_PUMPSET_EXERCISE_STOP + put(MsgSetExtendedBolusStop()) // 0x0406 CMD_PUMPSET_EXPANS_INS_STOP + put(MsgSetExtendedBolusStart()) // 0x0407 CMD_PUMPSET_EXPANS_INS_S + put(MsgError()) // 0x0601 CMD_PUMPOWAY_SYSTEM_STATUS + put(MsgPCCommStart()) // 0x3001 CMD_CONNECT + put(MsgPCCommStop()) // 0x3002 CMD_DISCONNECT + put(MsgHistoryBolus()) // 0x3101 CMD_HISTORY_MEAL_INS + put(MsgHistoryDailyInsulin()) // 0x3102 CMD_HISTORY_DAY_INS + put(MsgHistoryGlucose()) // 0x3104 CMD_HISTORY_GLUCOSE + put(MsgHistoryAlarm()) // 0x3105 CMD_HISTORY_ALARM + put(MsgHistoryCarbo()) // 0x3107 CMD_HISTORY_CARBOHY + put(MsgSettingBasal_k()) // 0x3202 CMD_SETTING_V_BASAL_INS_I + put(MsgSettingMeal()) // 0x3203 CMD_SETTING_V_MEAL_SETTING_I + put(MsgSettingProfileRatios()) // 0x3204 CMD_SETTING_V_CCC_I + put(MsgSettingMaxValues()) // 0x3205 CMD_SETTING_V_MAX_VALUE_I + put(MsgSettingBasalProfileAll_k()) // 0x3206 CMD_SETTING_V_BASAL_PROFILE_ALL + put(MsgSettingShippingInfo()) // 0x3207 CMD_SETTING_V_SHIPPING_I + put(MsgSettingGlucose()) // 0x3209 CMD_SETTING_V_GLUCOSEandEASY + put(MsgSettingPumpTime()) // 0x320A CMD_SETTING_V_TIME_I + put(MsgSetSingleBasalProfile()) // 0x3302 CMD_SETTING_BASAL_INS_S + put(MsgHistoryAll()) // 0x41F2 CMD_HISTORY_ALL + put(MsgHistoryNewDone()) // 0x42F1 CMD_HISTORY_NEW_DONE + put(MsgHistoryNew()) // 0x42F2 CMD_HISTORY_NEW + put(MsgCheckValue_k()) // 0xF0F1 CMD_PUMP_CHECK_VALUE + } + + override fun put(message: MessageBase) { + messages[message.command] = message + } + + override fun findMessage(command: Int): MessageBase { + return messages[command] ?: MessageBase() + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRKorean/comm/MessageHashTable_k.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRKorean/comm/MessageHashTable_k.java deleted file mode 100644 index 30ac710724..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRKorean/comm/MessageHashTable_k.java +++ /dev/null @@ -1,76 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.danaRKorean.comm; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.HashMap; - -import info.nightscout.androidaps.plugins.pump.danaR.comm.MessageBase; -import info.nightscout.androidaps.plugins.pump.danaR.comm.*; - -/** - * Created by mike on 28.05.2016. - */ -public class MessageHashTable_k { - private static Logger log = LoggerFactory.getLogger(MessageHashTable_k.class); - - public static HashMap messages = null; - - static { - if (messages == null) { - messages = new HashMap(); - put(new MsgBolusStop()); // 0x0101 CMD_MEALINS_STOP - put(new MsgBolusStart()); // 0x0102 CMD_MEALINS_START_DATA - put(new MsgBolusProgress()); // 0x0202 CMD_PUMP_THIS_REMAINDER_MEAL_INS - put(new MsgStatusProfile()); // 0x0204 CMD_PUMP_CALCULATION_SETTING - put(new MsgStatusTempBasal()); // 0x0205 CMD_PUMP_EXERCISE_MODE - put(new MsgStatusBolusExtended()); // 0x0207 CMD_PUMP_EXPANS_INS_I - put(new MsgStatusBasic_k()); // 0x020A CMD_PUMP_INITVIEW_I - put(new MsgStatus_k()); // 0x020B CMD_PUMP_STATUS - put(new MsgInitConnStatusTime_k()); // 0x0301 CMD_PUMPINIT_TIME_INFO - put(new MsgInitConnStatusBolus_k()); // 0x0302 CMD_PUMPINIT_BOLUS_INFO - put(new MsgInitConnStatusBasic_k()); // 0x0303 CMD_PUMPINIT_INIT_INFO - put(new MsgSetTempBasalStart()); // 0x0401 CMD_PUMPSET_EXERCISE_S - put(new MsgSetCarbsEntry()); // 0x0402 CMD_PUMPSET_HIS_S - put(new MsgSetTempBasalStop()); // 0x0403 CMD_PUMPSET_EXERCISE_STOP - put(new MsgSetExtendedBolusStop()); // 0x0406 CMD_PUMPSET_EXPANS_INS_STOP - put(new MsgSetExtendedBolusStart()); // 0x0407 CMD_PUMPSET_EXPANS_INS_S - put(new MsgError()); // 0x0601 CMD_PUMPOWAY_SYSTEM_STATUS - put(new MsgPCCommStart()); // 0x3001 CMD_CONNECT - put(new MsgPCCommStop()); // 0x3002 CMD_DISCONNECT - put(new MsgHistoryBolus()); // 0x3101 CMD_HISTORY_MEAL_INS - put(new MsgHistoryDailyInsulin()); // 0x3102 CMD_HISTORY_DAY_INS - put(new MsgHistoryGlucose()); // 0x3104 CMD_HISTORY_GLUCOSE - put(new MsgHistoryAlarm()); // 0x3105 CMD_HISTORY_ALARM - put(new MsgHistoryCarbo()); // 0x3107 CMD_HISTORY_CARBOHY - put(new MsgSettingBasal_k()); // 0x3202 CMD_SETTING_V_BASAL_INS_I - put(new MsgSettingMeal()); // 0x3203 CMD_SETTING_V_MEAL_SETTING_I - put(new MsgSettingProfileRatios()); // 0x3204 CMD_SETTING_V_CCC_I - put(new MsgSettingMaxValues()); // 0x3205 CMD_SETTING_V_MAX_VALUE_I - put(new MsgSettingBasalProfileAll_k()); // 0x3206 CMD_SETTING_V_BASAL_PROFILE_ALL - put(new MsgSettingShippingInfo()); // 0x3207 CMD_SETTING_V_SHIPPING_I - put(new MsgSettingGlucose()); // 0x3209 CMD_SETTING_V_GLUCOSEandEASY - put(new MsgSettingPumpTime()); // 0x320A CMD_SETTING_V_TIME_I - put(new MsgSetSingleBasalProfile()); // 0x3302 CMD_SETTING_BASAL_INS_S - put(new MsgHistoryAll()); // 0x41F2 CMD_HISTORY_ALL - put(new MsgHistoryNewDone()); // 0x42F1 CMD_HISTORY_NEW_DONE - put(new MsgHistoryNew()); // 0x42F2 CMD_HISTORY_NEW - put(new MsgCheckValue_k()); // 0xF0F1 CMD_PUMP_CHECK_VALUE - } - } - - public static void put(MessageBase message) { - int command = message.getCommand(); - //String name = MessageOriginalNames.getName(command); - messages.put(command, message); - //log.debug(String.format("%04x ", command) + " " + name); - } - - public static MessageBase findMessage(Integer command) { - if (messages.containsKey(command)) { - return messages.get(command); - } else { - return new MessageBase(); - } - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRKorean/comm/MsgInitConnStatusBasic_k.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRKorean/comm/MsgInitConnStatusBasic_k.java index 5c9ac9707b..8f62ccd58f 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRKorean/comm/MsgInitConnStatusBasic_k.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRKorean/comm/MsgInitConnStatusBasic_k.java @@ -6,6 +6,7 @@ import org.slf4j.LoggerFactory; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification; import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification; import info.nightscout.androidaps.plugins.general.overview.notifications.Notification; @@ -42,16 +43,16 @@ public class MsgInitConnStatusBasic_k extends MessageBase { if (pump.isEasyModeEnabled) { Notification notification = new Notification(Notification.EASYMODE_ENABLED, MainApp.gs(R.string.danar_disableeasymode), Notification.URGENT); - MainApp.bus().post(new EventNewNotification(notification)); + RxBus.INSTANCE.send(new EventNewNotification(notification)); } else { - MainApp.bus().post(new EventDismissNotification(Notification.EASYMODE_ENABLED)); + RxBus.INSTANCE.send(new EventDismissNotification(Notification.EASYMODE_ENABLED)); } if (!DanaRPump.getInstance().isPasswordOK()) { Notification notification = new Notification(Notification.WRONG_PUMP_PASSWORD, MainApp.gs(R.string.wrongpumppassword), Notification.URGENT); - MainApp.bus().post(new EventNewNotification(notification)); + RxBus.INSTANCE.send(new EventNewNotification(notification)); } else { - MainApp.bus().post(new EventDismissNotification(Notification.WRONG_PUMP_PASSWORD)); + RxBus.INSTANCE.send(new EventDismissNotification(Notification.WRONG_PUMP_PASSWORD)); } } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRKorean/comm/MsgInitConnStatusBolus_k.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRKorean/comm/MsgInitConnStatusBolus_k.java index b77f8ee61c..0df308369f 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRKorean/comm/MsgInitConnStatusBolus_k.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRKorean/comm/MsgInitConnStatusBolus_k.java @@ -6,6 +6,7 @@ import org.slf4j.LoggerFactory; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification; import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification; @@ -48,9 +49,9 @@ public class MsgInitConnStatusBolus_k extends MessageBase { if (!pump.isExtendedBolusEnabled) { Notification notification = new Notification(Notification.EXTENDED_BOLUS_DISABLED, MainApp.gs(R.string.danar_enableextendedbolus), Notification.URGENT); - MainApp.bus().post(new EventNewNotification(notification)); + RxBus.INSTANCE.send(new EventNewNotification(notification)); } else { - MainApp.bus().post(new EventDismissNotification(Notification.EXTENDED_BOLUS_DISABLED)); + RxBus.INSTANCE.send(new EventDismissNotification(Notification.EXTENDED_BOLUS_DISABLED)); } // This is last message of initial sequence diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRKorean/comm/MsgInitConnStatusTime_k.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRKorean/comm/MsgInitConnStatusTime_k.java index 103c3a2234..8a642a89f8 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRKorean/comm/MsgInitConnStatusTime_k.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRKorean/comm/MsgInitConnStatusTime_k.java @@ -5,9 +5,10 @@ import org.slf4j.LoggerFactory; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; -import info.nightscout.androidaps.events.EventRefreshGui; +import info.nightscout.androidaps.events.EventRebuildTabs; import info.nightscout.androidaps.interfaces.PluginType; import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification; import info.nightscout.androidaps.plugins.general.overview.notifications.Notification; @@ -31,24 +32,24 @@ public class MsgInitConnStatusTime_k extends MessageBase { if (bytes.length - 10 < 10) { Notification notification = new Notification(Notification.WRONG_DRIVER, MainApp.gs(R.string.pumpdrivercorrected), Notification.NORMAL); - MainApp.bus().post(new EventNewNotification(notification)); + RxBus.INSTANCE.send(new EventNewNotification(notification)); DanaRKoreanPlugin.getPlugin().disconnect("Wrong Model"); log.error("Wrong model selected. Switching to export DanaR"); - MainApp.getSpecificPlugin(DanaRKoreanPlugin.class).setPluginEnabled(PluginType.PUMP, false); - MainApp.getSpecificPlugin(DanaRKoreanPlugin.class).setFragmentVisible(PluginType.PUMP, false); - MainApp.getSpecificPlugin(DanaRPlugin.class).setPluginEnabled(PluginType.PUMP, true); - MainApp.getSpecificPlugin(DanaRPlugin.class).setFragmentVisible(PluginType.PUMP, true); + DanaRKoreanPlugin.getPlugin().setPluginEnabled(PluginType.PUMP, false); + DanaRKoreanPlugin.getPlugin().setFragmentVisible(PluginType.PUMP, false); + DanaRPlugin.getPlugin().setPluginEnabled(PluginType.PUMP, true); + DanaRPlugin.getPlugin().setFragmentVisible(PluginType.PUMP, true); DanaRPump.reset(); // mark not initialized //If profile coming from pump, switch it as well - if (MainApp.getSpecificPlugin(DanaRKoreanPlugin.class).isEnabled(PluginType.PROFILE)) { - (MainApp.getSpecificPlugin(DanaRKoreanPlugin.class)).setPluginEnabled(PluginType.PROFILE, false); - (MainApp.getSpecificPlugin(DanaRPlugin.class)).setPluginEnabled(PluginType.PROFILE, true); + if (DanaRKoreanPlugin.getPlugin().isEnabled(PluginType.PROFILE)) { + (DanaRKoreanPlugin.getPlugin()).setPluginEnabled(PluginType.PROFILE, false); + (DanaRPlugin.getPlugin()).setPluginEnabled(PluginType.PROFILE, true); } ConfigBuilderPlugin.getPlugin().storeSettings("ChangingKoreanDanaDriver"); - MainApp.bus().post(new EventRefreshGui()); + RxBus.INSTANCE.send(new EventRebuildTabs()); ConfigBuilderPlugin.getPlugin().getCommandQueue().readStatus("PumpDriverChange", null); // force new connection return; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRKorean/services/DanaRKoreanExecutionService.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRKorean/services/DanaRKoreanExecutionService.java index 749b977c2c..bb1f226730 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRKorean/services/DanaRKoreanExecutionService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRKorean/services/DanaRKoreanExecutionService.java @@ -5,8 +5,6 @@ import android.content.IntentFilter; import android.os.Binder; import android.os.SystemClock; -import com.squareup.otto.Subscribe; - import java.io.IOException; import java.util.Date; @@ -22,6 +20,7 @@ import info.nightscout.androidaps.events.EventProfileNeedsUpdate; import info.nightscout.androidaps.events.EventPumpStatusChanged; import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; import info.nightscout.androidaps.plugins.general.nsclient.NSUpload; @@ -29,6 +28,7 @@ import info.nightscout.androidaps.plugins.general.overview.dialogs.BolusProgress import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification; import info.nightscout.androidaps.plugins.general.overview.notifications.Notification; import info.nightscout.androidaps.plugins.pump.danaR.DanaRPump; +import info.nightscout.androidaps.plugins.pump.danaR.SerialIOThread; import info.nightscout.androidaps.plugins.pump.danaR.comm.MsgBolusProgress; import info.nightscout.androidaps.plugins.pump.danaR.comm.MsgBolusStart; import info.nightscout.androidaps.plugins.pump.danaR.comm.MsgBolusStop; @@ -50,58 +50,65 @@ import info.nightscout.androidaps.plugins.pump.danaR.comm.MsgStatusBolusExtended import info.nightscout.androidaps.plugins.pump.danaR.comm.MsgStatusTempBasal; import info.nightscout.androidaps.plugins.pump.danaR.events.EventDanaRNewStatus; import info.nightscout.androidaps.plugins.pump.danaR.services.AbstractDanaRExecutionService; -import info.nightscout.androidaps.plugins.pump.danaRKorean.SerialIOThread; +import info.nightscout.androidaps.plugins.pump.danaRKorean.comm.MessageHashTableRkorean; import info.nightscout.androidaps.plugins.pump.danaRKorean.comm.MsgCheckValue_k; import info.nightscout.androidaps.plugins.pump.danaRKorean.comm.MsgSettingBasal_k; import info.nightscout.androidaps.plugins.pump.danaRKorean.comm.MsgStatusBasic_k; import info.nightscout.androidaps.plugins.treatments.Treatment; import info.nightscout.androidaps.queue.commands.Command; import info.nightscout.androidaps.utils.DateUtil; +import info.nightscout.androidaps.utils.FabricPrivacy; import info.nightscout.androidaps.utils.T; +import io.reactivex.disposables.CompositeDisposable; +import io.reactivex.schedulers.Schedulers; public class DanaRKoreanExecutionService extends AbstractDanaRExecutionService { + private CompositeDisposable disposable = new CompositeDisposable(); public DanaRKoreanExecutionService() { mBinder = new LocalBinder(); - registerBus(); MainApp.instance().getApplicationContext().registerReceiver(receiver, new IntentFilter(BluetoothDevice.ACTION_ACL_DISCONNECTED)); } + @Override + public void onCreate() { + super.onCreate(); + disposable.add(RxBus.INSTANCE + .toObservable(EventPreferenceChange.class) + .observeOn(Schedulers.io()) + .subscribe(event -> { + if (mSerialIOThread != null) + mSerialIOThread.disconnect("EventPreferenceChange"); + }, FabricPrivacy::logException) + ); + disposable.add(RxBus.INSTANCE + .toObservable(EventAppExit.class) + .observeOn(Schedulers.io()) + .subscribe(event -> { + if (L.isEnabled(L.PUMP)) + log.debug("EventAppExit received"); + + if (mSerialIOThread != null) + mSerialIOThread.disconnect("Application exit"); + MainApp.instance().getApplicationContext().unregisterReceiver(receiver); + stopSelf(); + }, FabricPrivacy::logException) + ); + } + + @Override + public void onDestroy() { + disposable.clear(); + super.onDestroy(); + } + public class LocalBinder extends Binder { public DanaRKoreanExecutionService getServiceInstance() { return DanaRKoreanExecutionService.this; } } - private void registerBus() { - try { - MainApp.bus().unregister(this); - } catch (RuntimeException x) { - // Ignore - } - MainApp.bus().register(this); - } - - @Subscribe - public void onStatusEvent(EventAppExit event) { - if (L.isEnabled(L.PUMP)) - log.debug("EventAppExit received"); - - if (mSerialIOThread != null) - mSerialIOThread.disconnect("Application exit"); - - MainApp.instance().getApplicationContext().unregisterReceiver(receiver); - - stopSelf(); - } - - @Subscribe - public void onStatusEvent(final EventPreferenceChange pch) { - if (mSerialIOThread != null) - mSerialIOThread.disconnect("EventPreferenceChange"); - } - public void connect() { if (mConnectionInProgress) return; @@ -128,9 +135,9 @@ public class DanaRKoreanExecutionService extends AbstractDanaRExecutionService { if (mSerialIOThread != null) { mSerialIOThread.disconnect("Recreate SerialIOThread"); } - mSerialIOThread = new SerialIOThread(mRfcommSocket); + mSerialIOThread = new SerialIOThread(mRfcommSocket, MessageHashTableRkorean.INSTANCE); mHandshakeInProgress = true; - MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.HANDSHAKING, 0)); + RxBus.INSTANCE.send(new EventPumpStatusChanged(EventPumpStatusChanged.Status.HANDSHAKING, 0)); } mConnectionInProgress = false; @@ -140,7 +147,7 @@ public class DanaRKoreanExecutionService extends AbstractDanaRExecutionService { public void getPumpStatus() { DanaRPump danaRPump = DanaRPump.getInstance(); try { - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.gettingpumpstatus))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.gettingpumpstatus))); //MsgStatus_k statusMsg = new MsgStatus_k(); MsgStatusBasic_k statusBasicMsg = new MsgStatusBasic_k(); MsgStatusTempBasal tempStatusMsg = new MsgStatusTempBasal(); @@ -156,11 +163,11 @@ public class DanaRKoreanExecutionService extends AbstractDanaRExecutionService { //mSerialIOThread.sendMessage(statusMsg); mSerialIOThread.sendMessage(statusBasicMsg); - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.gettingtempbasalstatus))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.gettingtempbasalstatus))); mSerialIOThread.sendMessage(tempStatusMsg); - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.gettingextendedbolusstatus))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.gettingextendedbolusstatus))); mSerialIOThread.sendMessage(exStatusMsg); - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.gettingbolusstatus))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.gettingbolusstatus))); long now = System.currentTimeMillis(); danaRPump.lastConnection = now; @@ -168,15 +175,15 @@ public class DanaRKoreanExecutionService extends AbstractDanaRExecutionService { Profile profile = ProfileFunctions.getInstance().getProfile(); PumpInterface pump = ConfigBuilderPlugin.getPlugin().getActivePump(); if (profile != null && Math.abs(danaRPump.currentBasal - profile.getBasal()) >= pump.getPumpDescription().basalStep) { - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.gettingpumpsettings))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.gettingpumpsettings))); mSerialIOThread.sendMessage(new MsgSettingBasal()); if (!pump.isThisProfileSet(profile) && !ConfigBuilderPlugin.getPlugin().getCommandQueue().isRunning(Command.CommandType.BASALPROFILE)) { - MainApp.bus().post(new EventProfileNeedsUpdate()); + RxBus.INSTANCE.send(new EventProfileNeedsUpdate()); } } if (danaRPump.lastSettingsRead + 60 * 60 * 1000L < now || !pump.isInitialized()) { - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.gettingpumpsettings))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.gettingpumpsettings))); mSerialIOThread.sendMessage(new MsgSettingShippingInfo()); mSerialIOThread.sendMessage(new MsgSettingMeal()); mSerialIOThread.sendMessage(new MsgSettingBasal_k()); @@ -184,8 +191,17 @@ public class DanaRKoreanExecutionService extends AbstractDanaRExecutionService { mSerialIOThread.sendMessage(new MsgSettingMaxValues()); mSerialIOThread.sendMessage(new MsgSettingGlucose()); mSerialIOThread.sendMessage(new MsgSettingProfileRatios()); - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.gettingpumptime))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.gettingpumptime))); mSerialIOThread.sendMessage(new MsgSettingPumpTime()); + if (danaRPump.pumpTime == 0) { + // initial handshake was not successfull + // deinitialize pump + danaRPump.lastConnection = 0; + danaRPump.lastSettingsRead = 0; + RxBus.INSTANCE.send(new EventDanaRNewStatus()); + RxBus.INSTANCE.send(new EventInitializationChanged()); + return; + } long timeDiff = (danaRPump.pumpTime - System.currentTimeMillis()) / 1000L; if (L.isEnabled(L.PUMP)) log.debug("Pump time difference: " + timeDiff + " seconds"); @@ -201,15 +217,15 @@ public class DanaRKoreanExecutionService extends AbstractDanaRExecutionService { danaRPump.lastSettingsRead = now; } - MainApp.bus().post(new EventDanaRNewStatus()); - MainApp.bus().post(new EventInitializationChanged()); + RxBus.INSTANCE.send(new EventDanaRNewStatus()); + RxBus.INSTANCE.send(new EventInitializationChanged()); NSUpload.uploadDeviceStatus(); if (danaRPump.dailyTotalUnits > danaRPump.maxDailyTotalUnits * Constants.dailyLimitWarning) { if (L.isEnabled(L.PUMP)) log.debug("Approaching daily limit: " + danaRPump.dailyTotalUnits + "/" + danaRPump.maxDailyTotalUnits); if (System.currentTimeMillis() > lastApproachingDailyLimit + 30 * 60 * 1000) { Notification reportFail = new Notification(Notification.APPROACHING_DAILY_LIMIT, MainApp.gs(R.string.approachingdailylimit), Notification.URGENT); - MainApp.bus().post(new EventNewNotification(reportFail)); + RxBus.INSTANCE.send(new EventNewNotification(reportFail)); NSUpload.uploadError(MainApp.gs(R.string.approachingdailylimit) + ": " + danaRPump.dailyTotalUnits + "/" + danaRPump.maxDailyTotalUnits + "U"); lastApproachingDailyLimit = System.currentTimeMillis(); } @@ -223,41 +239,41 @@ public class DanaRKoreanExecutionService extends AbstractDanaRExecutionService { DanaRPump danaRPump = DanaRPump.getInstance(); if (!isConnected()) return false; if (danaRPump.isTempBasalInProgress) { - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.stoppingtempbasal))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.stoppingtempbasal))); mSerialIOThread.sendMessage(new MsgSetTempBasalStop()); SystemClock.sleep(500); } - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.settingtempbasal))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.settingtempbasal))); mSerialIOThread.sendMessage(new MsgSetTempBasalStart(percent, durationInHours)); mSerialIOThread.sendMessage(new MsgStatusTempBasal()); - MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.DISCONNECTING)); + RxBus.INSTANCE.send(new EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTING)); return true; } public boolean tempBasalStop() { if (!isConnected()) return false; - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.stoppingtempbasal))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.stoppingtempbasal))); mSerialIOThread.sendMessage(new MsgSetTempBasalStop()); mSerialIOThread.sendMessage(new MsgStatusTempBasal()); - MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.DISCONNECTING)); + RxBus.INSTANCE.send(new EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTING)); return true; } public boolean extendedBolus(double insulin, int durationInHalfHours) { if (!isConnected()) return false; - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.settingextendedbolus))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.settingextendedbolus))); mSerialIOThread.sendMessage(new MsgSetExtendedBolusStart(insulin, (byte) (durationInHalfHours & 0xFF))); mSerialIOThread.sendMessage(new MsgStatusBolusExtended()); - MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.DISCONNECTING)); + RxBus.INSTANCE.send(new EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTING)); return true; } public boolean extendedBolusStop() { if (!isConnected()) return false; - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.stoppingextendedbolus))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.stoppingextendedbolus))); mSerialIOThread.sendMessage(new MsgSetExtendedBolusStop()); mSerialIOThread.sendMessage(new MsgStatusBolusExtended()); - MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.DISCONNECTING)); + RxBus.INSTANCE.send(new EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTING)); return true; } @@ -325,13 +341,13 @@ public class DanaRKoreanExecutionService extends AbstractDanaRExecutionService { public boolean updateBasalsInPump(final Profile profile) { DanaRPump danaRPump = DanaRPump.getInstance(); if (!isConnected()) return false; - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.updatingbasalrates))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.updatingbasalrates))); double[] basal = DanaRPump.getInstance().buildDanaRProfileRecord(profile); MsgSetSingleBasalProfile msgSet = new MsgSetSingleBasalProfile(basal); mSerialIOThread.sendMessage(msgSet); danaRPump.lastSettingsRead = 0; // force read full settings getPumpStatus(); - MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.DISCONNECTING)); + RxBus.INSTANCE.send(new EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTING)); return true; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/DanaRSPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/DanaRSPlugin.java index ddbb8547ae..7b2668d45c 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/DanaRSPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/DanaRSPlugin.java @@ -5,11 +5,9 @@ import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.IBinder; -import android.support.annotation.Nullable; -import android.support.v4.app.FragmentActivity; -import android.support.v7.app.AlertDialog; -import com.squareup.otto.Subscribe; +import androidx.annotation.Nullable; +import androidx.fragment.app.FragmentActivity; import org.json.JSONException; import org.json.JSONObject; @@ -38,8 +36,8 @@ import info.nightscout.androidaps.interfaces.ProfileInterface; import info.nightscout.androidaps.interfaces.PumpDescription; import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.logging.L; -import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderFragment; -import info.nightscout.androidaps.plugins.configBuilder.DetailedBolusInfoStorage; +import info.nightscout.androidaps.plugins.bus.RxBus; +import info.nightscout.androidaps.plugins.common.ManufacturerType; import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; import info.nightscout.androidaps.plugins.general.actions.defs.CustomAction; import info.nightscout.androidaps.plugins.general.actions.defs.CustomActionType; @@ -47,6 +45,7 @@ import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNo import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification; import info.nightscout.androidaps.plugins.general.overview.notifications.Notification; import info.nightscout.androidaps.plugins.profile.ns.NSProfilePlugin; +import info.nightscout.androidaps.plugins.pump.common.bolusInfo.DetailedBolusInfoStorage; import info.nightscout.androidaps.plugins.pump.common.defs.PumpType; import info.nightscout.androidaps.plugins.pump.danaR.DanaRFragment; import info.nightscout.androidaps.plugins.pump.danaR.DanaRPump; @@ -58,9 +57,12 @@ import info.nightscout.androidaps.plugins.treatments.Treatment; import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; import info.nightscout.androidaps.utils.DateUtil; import info.nightscout.androidaps.utils.DecimalFormatter; +import info.nightscout.androidaps.utils.FabricPrivacy; import info.nightscout.androidaps.utils.Round; import info.nightscout.androidaps.utils.SP; import info.nightscout.androidaps.utils.T; +import io.reactivex.disposables.CompositeDisposable; +import io.reactivex.schedulers.Schedulers; /** * Created by mike on 03.09.2017. @@ -68,6 +70,8 @@ import info.nightscout.androidaps.utils.T; public class DanaRSPlugin extends PluginBase implements PumpInterface, DanaRInterface, ConstraintsInterface, ProfileInterface { private Logger log = LoggerFactory.getLogger(L.PUMP); + private CompositeDisposable disposable = new CompositeDisposable(); + private static DanaRSPlugin plugin = null; public static DanaRSPlugin getPlugin() { @@ -112,8 +116,21 @@ public class DanaRSPlugin extends PluginBase implements PumpInterface, DanaRInte Intent intent = new Intent(context, DanaRSService.class); context.bindService(intent, mConnection, Context.BIND_AUTO_CREATE); - MainApp.bus().register(this); - onStatusEvent(new EventDanaRSDeviceChange()); // load device name + disposable.add(RxBus.INSTANCE + .toObservable(EventAppExit.class) + .observeOn(Schedulers.io()) + .subscribe(event -> { + MainApp.instance().getApplicationContext().unbindService(mConnection); + }, FabricPrivacy::logException) + ); + disposable.add(RxBus.INSTANCE + .toObservable(EventDanaRSDeviceChange.class) + .observeOn(Schedulers.io()) + .subscribe(event -> { + loadAddress(); + }, FabricPrivacy::logException) + ); + loadAddress(); // load device name super.onStart(); } @@ -122,30 +139,13 @@ public class DanaRSPlugin extends PluginBase implements PumpInterface, DanaRInte Context context = MainApp.instance().getApplicationContext(); context.unbindService(mConnection); - MainApp.bus().unregister(this); + disposable.clear(); + super.onStop(); } @Override - public void switchAllowed(ConfigBuilderFragment.PluginViewHolder.PluginSwitcher pluginSwitcher, FragmentActivity context) { - boolean allowHardwarePump = SP.getBoolean("allow_hardware_pump", false); - if (allowHardwarePump || context == null) { - pluginSwitcher.invoke(); - } else { - AlertDialog.Builder builder = new AlertDialog.Builder(context); - builder.setMessage(R.string.allow_hardware_pump_text) - .setPositiveButton(R.string.yes, (dialog, id) -> { - pluginSwitcher.invoke(); - SP.putBoolean("allow_hardware_pump", true); - if (L.isEnabled(L.PUMP)) - log.debug("First time HW pump allowed!"); - }) - .setNegativeButton(R.string.cancel, (dialog, id) -> { - pluginSwitcher.cancel(); - if (L.isEnabled(L.PUMP)) - log.debug("User does not allow switching to HW pump!"); - }); - builder.create().show(); - } + public void switchAllowed(boolean newState, FragmentActivity activity, PluginType type) { + confirmPumpPluginActivation(newState, activity, type); } private ServiceConnection mConnection = new ServiceConnection() { @@ -164,14 +164,7 @@ public class DanaRSPlugin extends PluginBase implements PumpInterface, DanaRInte } }; - @SuppressWarnings("UnusedParameters") - @Subscribe - public void onStatusEvent(final EventAppExit e) { - MainApp.instance().getApplicationContext().unbindService(mConnection); - } - - @Subscribe - public void onStatusEvent(final EventDanaRSDeviceChange e) { + private void loadAddress() { mDeviceAddress = SP.getString(R.string.key_danars_address, ""); mDeviceName = SP.getString(R.string.key_danars_name, ""); } @@ -322,22 +315,22 @@ public class DanaRSPlugin extends PluginBase implements PumpInterface, DanaRInte if (!isInitialized()) { log.error("setNewBasalProfile not initialized"); Notification notification = new Notification(Notification.PROFILE_NOT_SET_NOT_INITIALIZED, MainApp.gs(R.string.pumpNotInitializedProfileNotSet), Notification.URGENT); - MainApp.bus().post(new EventNewNotification(notification)); + RxBus.INSTANCE.send(new EventNewNotification(notification)); result.comment = MainApp.gs(R.string.pumpNotInitializedProfileNotSet); return result; } else { - MainApp.bus().post(new EventDismissNotification(Notification.PROFILE_NOT_SET_NOT_INITIALIZED)); + RxBus.INSTANCE.send(new EventDismissNotification(Notification.PROFILE_NOT_SET_NOT_INITIALIZED)); } if (!danaRSService.updateBasalsInPump(profile)) { Notification notification = new Notification(Notification.FAILED_UDPATE_PROFILE, MainApp.gs(R.string.failedupdatebasalprofile), Notification.URGENT); - MainApp.bus().post(new EventNewNotification(notification)); + RxBus.INSTANCE.send(new EventNewNotification(notification)); result.comment = MainApp.gs(R.string.failedupdatebasalprofile); return result; } else { - MainApp.bus().post(new EventDismissNotification(Notification.PROFILE_NOT_SET_NOT_INITIALIZED)); - MainApp.bus().post(new EventDismissNotification(Notification.FAILED_UDPATE_PROFILE)); + RxBus.INSTANCE.send(new EventDismissNotification(Notification.PROFILE_NOT_SET_NOT_INITIALIZED)); + RxBus.INSTANCE.send(new EventDismissNotification(Notification.FAILED_UDPATE_PROFILE)); Notification notification = new Notification(Notification.PROFILE_SET_OK, MainApp.gs(R.string.profile_set_ok), Notification.INFO, 60); - MainApp.bus().post(new EventNewNotification(notification)); + RxBus.INSTANCE.send(new EventNewNotification(notification)); result.success = true; result.enacted = true; result.comment = "OK"; @@ -378,10 +371,14 @@ public class DanaRSPlugin extends PluginBase implements PumpInterface, DanaRInte } @Override - public double getReservoirLevel() { return DanaRPump.getInstance().reservoirRemainingUnits; } + public double getReservoirLevel() { + return DanaRPump.getInstance().reservoirRemainingUnits; + } @Override - public int getBatteryLevel() { return DanaRPump.getInstance().batteryRemaining; } + public int getBatteryLevel() { + return DanaRPump.getInstance().batteryRemaining; + } @Override public synchronized PumpEnactResult deliverTreatment(DetailedBolusInfo detailedBolusInfo) { @@ -411,7 +408,7 @@ public class DanaRSPlugin extends PluginBase implements PumpInterface, DanaRInte if (carbTime == 0) carbTime--; // better set 1 min back to prevents clash with insulin detailedBolusInfo.carbTime = 0; - DetailedBolusInfoStorage.add(detailedBolusInfo); // will be picked up on reading history + DetailedBolusInfoStorage.INSTANCE.add(detailedBolusInfo); // will be picked up on reading history Treatment t = new Treatment(); t.isSMB = detailedBolusInfo.isSMB; @@ -512,7 +509,7 @@ public class DanaRSPlugin extends PluginBase implements PumpInterface, DanaRInte if (L.isEnabled(L.PUMP)) log.debug("setTempBasalAbsolute: currently running: " + activeTemp.toString()); // Correct basal already set ? - if (activeTemp.percentRate == percentRate) { + if (activeTemp.percentRate == percentRate && activeTemp.getPlannedRemainingMinutes() > 4) { if (!enforceNew) { result.success = true; result.percent = percentRate; @@ -530,7 +527,7 @@ public class DanaRSPlugin extends PluginBase implements PumpInterface, DanaRInte if (L.isEnabled(L.PUMP)) log.debug("setTempBasalAbsolute: Setting temp basal " + percentRate + "% for " + durationInMinutes + " mins (doLowTemp || doHighTemp)"); if (percentRate == 0 && durationInMinutes > 30) { - result = setTempBasalPercent(percentRate, durationInMinutes, profile, false); + result = setTempBasalPercent(percentRate, durationInMinutes, profile, enforceNew); } else { // use special APS temp basal call ... 100+/15min .... 100-/30min result = setHighTempBasalPercent(percentRate); @@ -566,8 +563,8 @@ public class DanaRSPlugin extends PluginBase implements PumpInterface, DanaRInte if (percent > getPumpDescription().maxTempPercent) percent = getPumpDescription().maxTempPercent; long now = System.currentTimeMillis(); - TemporaryBasal runningTB = TreatmentsPlugin.getPlugin().getTempBasalFromHistory(now); - if (runningTB != null && runningTB.percentRate == percent && !enforceNew) { + TemporaryBasal activeTemp = TreatmentsPlugin.getPlugin().getTempBasalFromHistory(now); + if (activeTemp != null && activeTemp.percentRate == percent && activeTemp.getPlannedRemainingMinutes() > 4 && !enforceNew) { result.enacted = false; result.success = true; result.isTempCancel = false; @@ -769,7 +766,17 @@ public class DanaRSPlugin extends PluginBase implements PumpInterface, DanaRInte } @Override - public String deviceID() { + public ManufacturerType manufacturer() { + return ManufacturerType.Sooil; + } + + @Override + public PumpType model() { + return PumpType.DanaRS; + } + + @Override + public String serialNumber() { return DanaRPump.getInstance().serialNumber; } @@ -801,7 +808,6 @@ public class DanaRSPlugin extends PluginBase implements PumpInterface, DanaRInte if (!veryShort) { ret += "TDD: " + DecimalFormatter.to0Decimal(pump.dailyTotalUnits) + " / " + pump.maxDailyTotalUnits + " U\n"; } - ret += "IOB: " + pump.iob + "U\n"; ret += "Reserv: " + DecimalFormatter.to0Decimal(pump.reservoirRemainingUnits) + "U\n"; ret += "Batt: " + pump.batteryRemaining + "\n"; return ret; @@ -832,4 +838,9 @@ public class DanaRSPlugin extends PluginBase implements PumpInterface, DanaRInte return false; } + @Override + public void timeDateOrTimeZoneChanged() { + + } + } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/activities/BLEScanActivity.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/activities/BLEScanActivity.java index 502c18cbc9..42e910f291 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/activities/BLEScanActivity.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/activities/BLEScanActivity.java @@ -7,7 +7,6 @@ import android.bluetooth.le.ScanCallback; import android.bluetooth.le.ScanResult; import android.os.Bundle; import android.os.Handler; -import android.support.v7.app.AppCompatActivity; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; @@ -18,23 +17,23 @@ import java.util.ArrayList; import java.util.regex.Matcher; import java.util.regex.Pattern; -import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; +import info.nightscout.androidaps.activities.NoSplashAppCompatActivity; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.pump.danaRS.events.EventDanaRSDeviceChange; import info.nightscout.androidaps.utils.SP; -public class BLEScanActivity extends AppCompatActivity { +public class BLEScanActivity extends NoSplashAppCompatActivity { private ListView listView = null; private ListAdapter mListAdapter = null; private ArrayList mDevices = new ArrayList<>(); - ; private BluetoothAdapter mBluetoothAdapter = null; private BluetoothLeScanner mBluetoothLeScanner = null; @Override - protected void onCreate(Bundle savedInstanceState) { + public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.danars_blescanner_activity); @@ -157,7 +156,7 @@ public class BLEScanActivity extends AppCompatActivity { SP.putString(R.string.key_danars_address, item.device.getAddress()); SP.putString(R.string.key_danars_name, mName.getText().toString()); item.device.createBond(); - MainApp.bus().post(new EventDanaRSDeviceChange()); + RxBus.INSTANCE.send(new EventDanaRSDeviceChange()); finish(); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/activities/PairingHelperActivity.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/activities/PairingHelperActivity.java index a98d541963..5be6d7fbd1 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/activities/PairingHelperActivity.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/activities/PairingHelperActivity.java @@ -1,12 +1,13 @@ package info.nightscout.androidaps.plugins.pump.danaRS.activities; -import android.support.v7.app.AppCompatActivity; import android.os.Bundle; -public class PairingHelperActivity extends AppCompatActivity { +import info.nightscout.androidaps.activities.NoSplashAppCompatActivity; + +public class PairingHelperActivity extends NoSplashAppCompatActivity { @Override - protected void onCreate(Bundle savedInstanceState) { + public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); PairingProgressDialog bolusProgressDialog = new PairingProgressDialog(); bolusProgressDialog.setHelperActivity(this); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/activities/PairingProgressDialog.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/activities/PairingProgressDialog.java index 1d9943a5f5..afb5e86387 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/activities/PairingProgressDialog.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/activities/PairingProgressDialog.java @@ -5,7 +5,6 @@ import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; -import android.support.v4.app.DialogFragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -13,14 +12,19 @@ import android.widget.Button; import android.widget.ProgressBar; import android.widget.TextView; -import com.squareup.otto.Subscribe; +import androidx.fragment.app.DialogFragment; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.pump.danaRS.events.EventDanaRSPairingSuccess; +import info.nightscout.androidaps.utils.FabricPrivacy; +import io.reactivex.disposables.CompositeDisposable; +import io.reactivex.schedulers.Schedulers; public class PairingProgressDialog extends DialogFragment implements View.OnClickListener { + private CompositeDisposable disposable = new CompositeDisposable(); TextView statusView; ProgressBar progressBar; @@ -101,7 +105,11 @@ public class PairingProgressDialog extends DialogFragment implements View.OnClic @Override public void onResume() { super.onResume(); - MainApp.bus().register(this); + disposable.add(RxBus.INSTANCE + .toObservable(EventDanaRSPairingSuccess.class) + .observeOn(Schedulers.io()) + .subscribe(event -> pairingEnded = true, FabricPrivacy::logException) + ); running = true; if (pairingEnded) dismiss(); } @@ -117,15 +125,10 @@ public class PairingProgressDialog extends DialogFragment implements View.OnClic @Override public void onPause() { super.onPause(); - MainApp.bus().unregister(this); + disposable.clear(); running = false; } - @Subscribe - public void onStatusEvent(final EventDanaRSPairingSuccess ev) { - pairingEnded = true; - } - public void setHelperActivity(PairingHelperActivity activity) { this.helperActivity = activity; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/comm/DanaRS_Packet.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/comm/DanaRS_Packet.java index 28a081aaa8..3d59b34e16 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/comm/DanaRS_Packet.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/comm/DanaRS_Packet.java @@ -47,8 +47,6 @@ public class DanaRS_Packet { return null; } - ; - // STATIC FUNCTIONS public static int getCommand(byte[] data) { @@ -60,6 +58,9 @@ public class DanaRS_Packet { public void handleMessage(byte[] data) { } + public void handleMessageNotReceived() { + } + public String getFriendlyName() { return "UNKNOWN_PACKET"; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/comm/DanaRS_Packet_APS_History_Events.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/comm/DanaRS_Packet_APS_History_Events.java index f6364290c2..fde34c58e0 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/comm/DanaRS_Packet_APS_History_Events.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/comm/DanaRS_Packet_APS_History_Events.java @@ -17,7 +17,8 @@ import info.nightscout.androidaps.db.Source; import info.nightscout.androidaps.db.TemporaryBasal; import info.nightscout.androidaps.events.EventPumpStatusChanged; import info.nightscout.androidaps.logging.L; -import info.nightscout.androidaps.plugins.configBuilder.DetailedBolusInfoStorage; +import info.nightscout.androidaps.plugins.bus.RxBus; +import info.nightscout.androidaps.plugins.pump.common.bolusInfo.DetailedBolusInfoStorage; import info.nightscout.androidaps.plugins.pump.danaR.DanaRPump; import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; import info.nightscout.androidaps.utils.DateUtil; @@ -129,7 +130,7 @@ public class DanaRS_Packet_APS_History_Events extends DanaRS_Packet { status = "EXTENDEDSTOP " + DateUtil.timeString(datetime); break; case DanaRPump.BOLUS: - DetailedBolusInfo detailedBolusInfo = DetailedBolusInfoStorage.findDetailedBolusInfo(datetime); + DetailedBolusInfo detailedBolusInfo = DetailedBolusInfoStorage.INSTANCE.findDetailedBolusInfo(datetime, param1 / 100d); if (detailedBolusInfo == null) { detailedBolusInfo = new DetailedBolusInfo(); } @@ -144,7 +145,7 @@ public class DanaRS_Packet_APS_History_Events extends DanaRS_Packet { status = "BOLUS " + DateUtil.timeString(datetime); break; case DanaRPump.DUALBOLUS: - detailedBolusInfo = DetailedBolusInfoStorage.findDetailedBolusInfo(datetime); + detailedBolusInfo = DetailedBolusInfoStorage.INSTANCE.findDetailedBolusInfo(datetime, param1 / 100d); if (detailedBolusInfo == null) { detailedBolusInfo = new DetailedBolusInfo(); } @@ -223,7 +224,7 @@ public class DanaRS_Packet_APS_History_Events extends DanaRS_Packet { if (datetime > lastEventTimeLoaded) lastEventTimeLoaded = datetime; - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.processinghistory) + ": " + status)); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.processinghistory) + ": " + status)); } @Override diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/comm/DanaRS_Packet_Basal_Get_Basal_Rate.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/comm/DanaRS_Packet_Basal_Get_Basal_Rate.java index eb89cd5d10..830aca2d02 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/comm/DanaRS_Packet_Basal_Get_Basal_Rate.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/comm/DanaRS_Packet_Basal_Get_Basal_Rate.java @@ -11,6 +11,7 @@ import java.util.Locale; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification; import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification; import info.nightscout.androidaps.plugins.general.overview.notifications.Notification; @@ -57,9 +58,9 @@ public class DanaRS_Packet_Basal_Get_Basal_Rate extends DanaRS_Packet { if (pump.basalStep != 0.01d) { failed = true; Notification notification = new Notification(Notification.WRONGBASALSTEP, MainApp.gs(R.string.danar_setbasalstep001), Notification.URGENT); - MainApp.bus().post(new EventNewNotification(notification)); + RxBus.INSTANCE.send(new EventNewNotification(notification)); } else { - MainApp.bus().post(new EventDismissNotification(Notification.WRONGBASALSTEP)); + RxBus.INSTANCE.send(new EventDismissNotification(Notification.WRONGBASALSTEP)); } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/comm/DanaRS_Packet_Basal_Get_Temporary_Basal_State.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/comm/DanaRS_Packet_Basal_Get_Temporary_Basal_State.java index 5724c5ff80..117baf23ff 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/comm/DanaRS_Packet_Basal_Get_Temporary_Basal_State.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/comm/DanaRS_Packet_Basal_Get_Temporary_Basal_State.java @@ -1,6 +1,6 @@ package info.nightscout.androidaps.plugins.pump.danaRS.comm; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import com.cozmo.danar.util.BleCommandUtil; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/comm/DanaRS_Packet_Bolus_Get_Bolus_Option.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/comm/DanaRS_Packet_Bolus_Get_Bolus_Option.java index 73c7a8d8f0..c656de11cf 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/comm/DanaRS_Packet_Bolus_Get_Bolus_Option.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/comm/DanaRS_Packet_Bolus_Get_Bolus_Option.java @@ -8,6 +8,7 @@ import org.slf4j.LoggerFactory; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification; import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification; import info.nightscout.androidaps.plugins.general.overview.notifications.Notification; @@ -105,10 +106,10 @@ public class DanaRS_Packet_Bolus_Get_Bolus_Option extends DanaRS_Packet { if (!pump.isExtendedBolusEnabled) { Notification notification = new Notification(Notification.EXTENDED_BOLUS_DISABLED, MainApp.gs(R.string.danar_enableextendedbolus), Notification.URGENT); - MainApp.bus().post(new EventNewNotification(notification)); + RxBus.INSTANCE.send(new EventNewNotification(notification)); failed = true; } else { - MainApp.bus().post(new EventDismissNotification(Notification.EXTENDED_BOLUS_DISABLED)); + RxBus.INSTANCE.send(new EventDismissNotification(Notification.EXTENDED_BOLUS_DISABLED)); } if (L.isEnabled(L.PUMPCOMM)) { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/comm/DanaRS_Packet_Bolus_Set_Step_Bolus_Stop.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/comm/DanaRS_Packet_Bolus_Set_Step_Bolus_Stop.java index 5e3fe3fbcd..7cb0a11f9b 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/comm/DanaRS_Packet_Bolus_Set_Step_Bolus_Stop.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/comm/DanaRS_Packet_Bolus_Set_Step_Bolus_Stop.java @@ -9,6 +9,7 @@ import info.nightscout.androidaps.R; import com.cozmo.danar.util.BleCommandUtil; import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.treatments.Treatment; import info.nightscout.androidaps.plugins.general.overview.events.EventOverviewBolusProgress; @@ -47,16 +48,16 @@ public class DanaRS_Packet_Bolus_Set_Step_Bolus_Stop extends DanaRS_Packet { } } - EventOverviewBolusProgress bolusingEvent = EventOverviewBolusProgress.getInstance(); + EventOverviewBolusProgress bolusingEvent = EventOverviewBolusProgress.INSTANCE; stopped = true; if (!forced) { t.insulin = amount; - bolusingEvent.status = MainApp.gs(R.string.overview_bolusprogress_delivered); - bolusingEvent.percent = 100; + bolusingEvent.setStatus(MainApp.gs(R.string.overview_bolusprogress_delivered)); + bolusingEvent.setPercent(100); } else { - bolusingEvent.status = MainApp.gs(R.string.overview_bolusprogress_stoped); + bolusingEvent.setStatus(MainApp.gs(R.string.overview_bolusprogress_stoped)); } - MainApp.bus().post(bolusingEvent); + RxBus.INSTANCE.send(bolusingEvent); } @Override diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/comm/DanaRS_Packet_General_Get_Pump_Check.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/comm/DanaRS_Packet_General_Get_Pump_Check.java index 7c0a0ec2ae..f9bda3dfe5 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/comm/DanaRS_Packet_General_Get_Pump_Check.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/comm/DanaRS_Packet_General_Get_Pump_Check.java @@ -8,6 +8,7 @@ import org.slf4j.LoggerFactory; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification; import info.nightscout.androidaps.plugins.general.overview.notifications.Notification; import info.nightscout.androidaps.plugins.pump.danaR.DanaRPump; @@ -24,7 +25,7 @@ public class DanaRS_Packet_General_Get_Pump_Check extends DanaRS_Packet { @Override public void handleMessage(byte[] data) { - if (data.length <5){ + if (data.length < 5) { failed = true; return; } @@ -49,7 +50,7 @@ public class DanaRS_Packet_General_Get_Pump_Check extends DanaRS_Packet { } if (pump.productCode < 2) { - MainApp.bus().post(new EventNewNotification(new Notification(Notification.UNSUPPORTED_FIRMWARE, MainApp.gs(R.string.unsupportedfirmware), Notification.URGENT))); + RxBus.INSTANCE.send(new EventNewNotification(new Notification(Notification.UNSUPPORTED_FIRMWARE, MainApp.gs(R.string.unsupportedfirmware), Notification.URGENT))); } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/comm/DanaRS_Packet_History_.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/comm/DanaRS_Packet_History_.java index ceaf49908b..07893346d7 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/comm/DanaRS_Packet_History_.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/comm/DanaRS_Packet_History_.java @@ -10,6 +10,7 @@ import java.util.GregorianCalendar; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.db.DanaRHistoryRecord; import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.pump.danaR.comm.RecordTypes; import info.nightscout.androidaps.plugins.pump.danaR.events.EventDanaRSyncStatus; import info.nightscout.androidaps.utils.DateUtil; @@ -109,7 +110,6 @@ public abstract class DanaRS_Packet_History_ extends DanaRS_Packet { log.debug("History packet: " + recordCode + " Date: " + datetimewihtsec.toLocaleString() + " Code: " + historyCode + " Value: " + value); - EventDanaRSyncStatus ev = new EventDanaRSyncStatus(); DanaRHistoryRecord danaRHistoryRecord = new DanaRHistoryRecord(); danaRHistoryRecord.setBytes(data); @@ -224,9 +224,7 @@ public abstract class DanaRS_Packet_History_ extends DanaRS_Packet { MainApp.getDbHelper().createOrUpdate(danaRHistoryRecord); - ev.message = DateUtil.dateAndTimeString(danaRHistoryRecord.recordDate); - ev.message += " " + messageType; - MainApp.bus().post(ev); + RxBus.INSTANCE.send(new EventDanaRSyncStatus(DateUtil.dateAndTimeString(danaRHistoryRecord.recordDate) + " " + messageType)); } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/comm/DanaRS_Packet_Notify_Delivery_Complete.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/comm/DanaRS_Packet_Notify_Delivery_Complete.java index 3c916dc399..00b6483fbb 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/comm/DanaRS_Packet_Notify_Delivery_Complete.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/comm/DanaRS_Packet_Notify_Delivery_Complete.java @@ -8,6 +8,7 @@ import org.slf4j.LoggerFactory; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.general.overview.events.EventOverviewBolusProgress; import info.nightscout.androidaps.plugins.treatments.Treatment; @@ -39,12 +40,12 @@ public class DanaRS_Packet_Notify_Delivery_Complete extends DanaRS_Packet { if (t != null) { t.insulin = deliveredInsulin; - EventOverviewBolusProgress bolusingEvent = EventOverviewBolusProgress.getInstance(); - bolusingEvent.status = String.format(MainApp.gs(R.string.bolusdelivering), deliveredInsulin); - bolusingEvent.t = t; - bolusingEvent.percent = Math.min((int) (deliveredInsulin / amount * 100), 100); + EventOverviewBolusProgress bolusingEvent = EventOverviewBolusProgress.INSTANCE; + bolusingEvent.setStatus(String.format(MainApp.gs(R.string.bolusdelivering), deliveredInsulin)); + bolusingEvent.setT(t); + bolusingEvent.setPercent(Math.min((int) (deliveredInsulin / amount * 100), 100)); done = true; - MainApp.bus().post(bolusingEvent); + RxBus.INSTANCE.send(bolusingEvent); } if (L.isEnabled(L.PUMPCOMM)) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/comm/DanaRS_Packet_Notify_Delivery_Rate_Display.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/comm/DanaRS_Packet_Notify_Delivery_Rate_Display.java index 290e9743ef..1d650cb779 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/comm/DanaRS_Packet_Notify_Delivery_Rate_Display.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/comm/DanaRS_Packet_Notify_Delivery_Rate_Display.java @@ -8,6 +8,7 @@ import org.slf4j.LoggerFactory; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.general.overview.events.EventOverviewBolusProgress; import info.nightscout.androidaps.plugins.treatments.Treatment; @@ -40,12 +41,12 @@ public class DanaRS_Packet_Notify_Delivery_Rate_Display extends DanaRS_Packet { if (t != null) { lastReceive = System.currentTimeMillis(); t.insulin = deliveredInsulin; - EventOverviewBolusProgress bolusingEvent = EventOverviewBolusProgress.getInstance(); - bolusingEvent.status = String.format(MainApp.gs(R.string.bolusdelivering), deliveredInsulin); - bolusingEvent.t = t; - bolusingEvent.percent = Math.min((int) (deliveredInsulin / amount * 100), 100); - failed = bolusingEvent.percent < 100? true: false; - MainApp.bus().post(bolusingEvent); + EventOverviewBolusProgress bolusingEvent = EventOverviewBolusProgress.INSTANCE; + bolusingEvent.setStatus(String.format(MainApp.gs(R.string.bolusdelivering), deliveredInsulin)); + bolusingEvent.setT(t); + bolusingEvent.setPercent(Math.min((int) (deliveredInsulin / amount * 100), 100)); + failed = bolusingEvent.getPercent() < 100? true: false; + RxBus.INSTANCE.send(bolusingEvent); } if (L.isEnabled(L.PUMPCOMM)) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/comm/DanaRS_Packet_Option_Get_Pump_Time.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/comm/DanaRS_Packet_Option_Get_Pump_Time.java index d12ed52c79..500c1707b6 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/comm/DanaRS_Packet_Option_Get_Pump_Time.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/comm/DanaRS_Packet_Option_Get_Pump_Time.java @@ -58,6 +58,11 @@ public class DanaRS_Packet_Option_Get_Pump_Time extends DanaRS_Packet { } } + @Override + public void handleMessageNotReceived() { + DanaRPump.getInstance().pumpTime = 0; + } + @Override public String getFriendlyName() { return "OPTION__GET_PUMP_TIME"; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/events/EventDanaRSDeviceChange.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/events/EventDanaRSDeviceChange.java deleted file mode 100644 index 224f939ad7..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/events/EventDanaRSDeviceChange.java +++ /dev/null @@ -1,10 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.danaRS.events; - -import info.nightscout.androidaps.events.Event; - -/** - * Created by mike on 05.09.2017. - */ - -public class EventDanaRSDeviceChange extends Event { -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/events/EventDanaRSDeviceChange.kt b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/events/EventDanaRSDeviceChange.kt new file mode 100644 index 0000000000..8b87d59ec1 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/events/EventDanaRSDeviceChange.kt @@ -0,0 +1,5 @@ +package info.nightscout.androidaps.plugins.pump.danaRS.events + +import info.nightscout.androidaps.events.Event + +class EventDanaRSDeviceChange : Event() \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/events/EventDanaRSPacket.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/events/EventDanaRSPacket.java deleted file mode 100644 index e0c401a27f..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/events/EventDanaRSPacket.java +++ /dev/null @@ -1,16 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.danaRS.events; - -import info.nightscout.androidaps.events.Event; -import info.nightscout.androidaps.plugins.pump.danaRS.comm.DanaRS_Packet; - -/** - * Created by mike on 01.09.2017. - */ - -public class EventDanaRSPacket extends Event{ - public EventDanaRSPacket(DanaRS_Packet data) { - this.data = data; - } - - public DanaRS_Packet data; -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/events/EventDanaRSPairingSuccess.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/events/EventDanaRSPairingSuccess.java deleted file mode 100644 index e332e9e0b2..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/events/EventDanaRSPairingSuccess.java +++ /dev/null @@ -1,10 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.danaRS.events; - -import info.nightscout.androidaps.events.Event; - -/** - * Created by mike on 01.09.2017. - */ - -public class EventDanaRSPairingSuccess extends Event{ -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/events/EventDanaRSPairingSuccess.kt b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/events/EventDanaRSPairingSuccess.kt new file mode 100644 index 0000000000..6e0379a4d3 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/events/EventDanaRSPairingSuccess.kt @@ -0,0 +1,5 @@ +package info.nightscout.androidaps.plugins.pump.danaRS.events + +import info.nightscout.androidaps.events.Event + +class EventDanaRSPairingSuccess : Event() diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/services/BLEComm.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/services/BLEComm.java index 69ca9f1901..8a43d6e35a 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/services/BLEComm.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/services/BLEComm.java @@ -26,6 +26,8 @@ import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.events.EventPumpStatusChanged; import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.bus.RxBus; +import info.nightscout.androidaps.plugins.general.nsclient.NSUpload; import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification; import info.nightscout.androidaps.plugins.general.overview.notifications.Notification; import info.nightscout.androidaps.plugins.pump.danaR.DanaRPump; @@ -33,9 +35,7 @@ import info.nightscout.androidaps.plugins.pump.danaRS.DanaRSPlugin; import info.nightscout.androidaps.plugins.pump.danaRS.activities.PairingHelperActivity; import info.nightscout.androidaps.plugins.pump.danaRS.comm.DanaRSMessageHashTable; import info.nightscout.androidaps.plugins.pump.danaRS.comm.DanaRS_Packet; -import info.nightscout.androidaps.plugins.pump.danaRS.events.EventDanaRSPacket; import info.nightscout.androidaps.plugins.pump.danaRS.events.EventDanaRSPairingSuccess; -import info.nightscout.androidaps.plugins.general.nsclient.NSUpload; import info.nightscout.androidaps.utils.SP; /** @@ -338,7 +338,7 @@ public class BLEComm { close(); isConnected = false; isConnecting = false; - MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.DISCONNECTED)); + RxBus.INSTANCE.send(new EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTED)); if (L.isEnabled(L.PUMPBTCOMM)) log.debug("Device was disconnected " + gatt.getDevice().getName());//Device was disconnected } @@ -451,24 +451,24 @@ public class BLEComm { if (L.isEnabled(L.PUMPBTCOMM)) log.debug("<<<<< " + "ENCRYPTION__PUMP_CHECK (PUMP)" + " " + DanaRS_Packet.toHexString(inputBuffer)); mSendQueue.clear(); - MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.DISCONNECTED, MainApp.gs(R.string.pumperror))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTED, MainApp.gs(R.string.pumperror))); NSUpload.uploadError(MainApp.gs(R.string.pumperror)); Notification n = new Notification(Notification.PUMPERROR, MainApp.gs(R.string.pumperror), Notification.URGENT); - MainApp.bus().post(new EventNewNotification(n)); + RxBus.INSTANCE.send(new EventNewNotification(n)); } else if (inputBuffer.length == 6 && inputBuffer[2] == 'B' && inputBuffer[3] == 'U' && inputBuffer[4] == 'S' && inputBuffer[5] == 'Y') { if (L.isEnabled(L.PUMPBTCOMM)) log.debug("<<<<< " + "ENCRYPTION__PUMP_CHECK (BUSY)" + " " + DanaRS_Packet.toHexString(inputBuffer)); mSendQueue.clear(); - MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.DISCONNECTED, MainApp.gs(R.string.pumpbusy))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTED, MainApp.gs(R.string.pumpbusy))); } else { // ERROR in response, wrong serial number if (L.isEnabled(L.PUMPBTCOMM)) log.debug("<<<<< " + "ENCRYPTION__PUMP_CHECK (ERROR)" + " " + DanaRS_Packet.toHexString(inputBuffer)); mSendQueue.clear(); - MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.DISCONNECTED, MainApp.gs(R.string.connectionerror))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTED, MainApp.gs(R.string.connectionerror))); SP.remove(MainApp.gs(R.string.key_danars_pairingkey) + DanaRSPlugin.mDeviceName); Notification n = new Notification(Notification.WRONGSERIALNUMBER, MainApp.gs(R.string.wrongpassword), Notification.URGENT); - MainApp.bus().post(new EventNewNotification(n)); + RxBus.INSTANCE.send(new EventNewNotification(n)); } break; // 2nd packet, pairing key @@ -495,7 +495,7 @@ public class BLEComm { if (L.isEnabled(L.PUMPBTCOMM)) log.debug("<<<<< " + "ENCRYPTION__PASSKEY_RETURN " + DanaRS_Packet.toHexString(inputBuffer)); // Paring is successfull, sending time info - MainApp.bus().post(new EventDanaRSPairingSuccess()); + RxBus.INSTANCE.send(new EventDanaRSPairingSuccess()); SendTimeInfo(); byte[] pairingKey = {inputBuffer[2], inputBuffer[3]}; // store pairing key to preferences @@ -514,7 +514,7 @@ public class BLEComm { if (L.isEnabled(L.PUMPBTCOMM)) log.debug("Pump user password: " + Integer.toHexString(pass)); - MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.CONNECTED)); + RxBus.INSTANCE.send(new EventPumpStatusChanged(EventPumpStatusChanged.Status.CONNECTED)); isConnected = true; isConnecting = false; if (L.isEnabled(L.PUMPBTCOMM)) @@ -545,7 +545,6 @@ public class BLEComm { // notify to sendMessage message.notify(); } - MainApp.bus().post(new EventDanaRSPacket(message)); } else { log.error("Unknown message received " + DanaRS_Packet.toHexString(inputBuffer)); } @@ -643,6 +642,7 @@ public class BLEComm { //SystemClock.sleep(200); if (!message.isReceived()) { log.warn("Reply not received " + message.getFriendlyName()); + message.handleMessageNotReceived(); } } @@ -663,9 +663,9 @@ public class BLEComm { private void SendPumpCheck() { // 1st message sent to pump after connect String devicename = getConnectDeviceName(); - if(devicename == null || devicename.equals("")){ + if (devicename == null || devicename.equals("")) { Notification n = new Notification(Notification.DEVICENOTPAIRED, MainApp.gs(R.string.pairfirst), Notification.URGENT); - MainApp.bus().post(new EventNewNotification(n)); + RxBus.INSTANCE.send(new EventNewNotification(n)); return; } byte[] bytes = BleCommandUtil.getInstance().getEncryptedPacket(BleCommandUtil.DANAR_PACKET__OPCODE_ENCRYPTION__PUMP_CHECK, null, devicename); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/services/DanaRSService.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/services/DanaRSService.java index 06a3a651ca..2850f08c8d 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/services/DanaRSService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRS/services/DanaRSService.java @@ -6,8 +6,6 @@ import android.os.Binder; import android.os.IBinder; import android.os.SystemClock; -import com.squareup.otto.Subscribe; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -24,8 +22,10 @@ import info.nightscout.androidaps.events.EventProfileNeedsUpdate; import info.nightscout.androidaps.events.EventPumpStatusChanged; import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; +import info.nightscout.androidaps.plugins.general.nsclient.NSUpload; import info.nightscout.androidaps.plugins.general.overview.dialogs.BolusProgressDialog; import info.nightscout.androidaps.plugins.general.overview.dialogs.ErrorHelperActivity; import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification; @@ -79,12 +79,15 @@ import info.nightscout.androidaps.plugins.treatments.Treatment; import info.nightscout.androidaps.queue.Callback; import info.nightscout.androidaps.queue.commands.Command; import info.nightscout.androidaps.utils.DateUtil; -import info.nightscout.androidaps.plugins.general.nsclient.NSUpload; +import info.nightscout.androidaps.utils.FabricPrivacy; import info.nightscout.androidaps.utils.SP; import info.nightscout.androidaps.utils.T; +import io.reactivex.disposables.CompositeDisposable; +import io.reactivex.schedulers.Schedulers; public class DanaRSService extends Service { private Logger log = LoggerFactory.getLogger(L.PUMPCOMM); + private CompositeDisposable disposable = new CompositeDisposable(); private BLEComm bleComm = BLEComm.getInstance(this); @@ -96,12 +99,25 @@ public class DanaRSService extends Service { private long lastApproachingDailyLimit = 0; public DanaRSService() { - try { - MainApp.bus().unregister(this); - } catch (RuntimeException x) { - // Ignore - } - MainApp.bus().register(this); + } + + @Override + public void onCreate() { + super.onCreate(); + disposable.add(RxBus.INSTANCE + .toObservable(EventAppExit.class) + .observeOn(Schedulers.io()) + .subscribe(event -> { + if (L.isEnabled(L.PUMP)) log.debug("EventAppExit received"); + stopSelf(); + }, FabricPrivacy::logException) + ); + } + + @Override + public void onDestroy() { + disposable.clear(); + super.onDestroy(); } public boolean isConnected() { @@ -131,14 +147,14 @@ public class DanaRSService extends Service { public void getPumpStatus() { DanaRPump danaRPump = DanaRPump.getInstance(); try { - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.gettingpumpstatus))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.gettingpumpstatus))); bleComm.sendMessage(new DanaRS_Packet_General_Initial_Screen_Information()); - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.gettingextendedbolusstatus))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.gettingextendedbolusstatus))); bleComm.sendMessage(new DanaRS_Packet_Bolus_Get_Extended_Bolus_State()); - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.gettingbolusstatus))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.gettingbolusstatus))); bleComm.sendMessage(new DanaRS_Packet_Bolus_Get_Step_Bolus_Information()); // last bolus, bolusStep, maxBolus - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.gettingtempbasalstatus))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.gettingtempbasalstatus))); bleComm.sendMessage(new DanaRS_Packet_Basal_Get_Temporary_Basal_State()); danaRPump.lastConnection = System.currentTimeMillis(); @@ -146,17 +162,25 @@ public class DanaRSService extends Service { Profile profile = ProfileFunctions.getInstance().getProfile(); PumpInterface pump = ConfigBuilderPlugin.getPlugin().getActivePump(); if (profile != null && Math.abs(danaRPump.currentBasal - profile.getBasal()) >= pump.getPumpDescription().basalStep) { - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.gettingpumpsettings))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.gettingpumpsettings))); bleComm.sendMessage(new DanaRS_Packet_Basal_Get_Basal_Rate()); // basal profile, basalStep, maxBasal if (!pump.isThisProfileSet(profile) && !ConfigBuilderPlugin.getPlugin().getCommandQueue().isRunning(Command.CommandType.BASALPROFILE)) { - MainApp.bus().post(new EventProfileNeedsUpdate()); + RxBus.INSTANCE.send(new EventProfileNeedsUpdate()); } } - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.gettingpumptime))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.gettingpumptime))); bleComm.sendMessage(new DanaRS_Packet_Option_Get_Pump_Time()); long timeDiff = (danaRPump.pumpTime - System.currentTimeMillis()) / 1000L; + if (danaRPump.pumpTime == 0) { + // initial handshake was not successfull + // deinitialize pump + danaRPump.lastConnection = 0; + RxBus.INSTANCE.send(new EventDanaRNewStatus()); + RxBus.INSTANCE.send(new EventInitializationChanged()); + return; + } if (L.isEnabled(L.PUMPCOMM)) log.debug("Pump time difference: " + timeDiff + " seconds"); if (Math.abs(timeDiff) > 3) { @@ -173,8 +197,8 @@ public class DanaRSService extends Service { //deinitialize pump danaRPump.lastConnection = 0; - MainApp.bus().post(new EventDanaRNewStatus()); - MainApp.bus().post(new EventInitializationChanged()); + RxBus.INSTANCE.send(new EventDanaRNewStatus()); + RxBus.INSTANCE.send(new EventInitializationChanged()); return; } else { waitForWholeMinute(); // Dana can set only whole minute @@ -189,7 +213,7 @@ public class DanaRSService extends Service { long now = System.currentTimeMillis(); if (danaRPump.lastSettingsRead + 60 * 60 * 1000L < now || !pump.isInitialized()) { - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.gettingpumpsettings))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.gettingpumpsettings))); bleComm.sendMessage(new DanaRS_Packet_General_Get_Shipping_Information()); // serial no bleComm.sendMessage(new DanaRS_Packet_General_Get_Pump_Check()); // firmware bleComm.sendMessage(new DanaRS_Packet_Basal_Get_Profile_Number()); @@ -203,15 +227,15 @@ public class DanaRSService extends Service { loadEvents(); - MainApp.bus().post(new EventDanaRNewStatus()); - MainApp.bus().post(new EventInitializationChanged()); + RxBus.INSTANCE.send(new EventDanaRNewStatus()); + RxBus.INSTANCE.send(new EventInitializationChanged()); NSUpload.uploadDeviceStatus(); if (danaRPump.dailyTotalUnits > danaRPump.maxDailyTotalUnits * Constants.dailyLimitWarning) { if (L.isEnabled(L.PUMPCOMM)) log.debug("Approaching daily limit: " + danaRPump.dailyTotalUnits + "/" + danaRPump.maxDailyTotalUnits); if (System.currentTimeMillis() > lastApproachingDailyLimit + 30 * 60 * 1000) { Notification reportFail = new Notification(Notification.APPROACHING_DAILY_LIMIT, MainApp.gs(R.string.approachingdailylimit), Notification.URGENT); - MainApp.bus().post(new EventNewNotification(reportFail)); + RxBus.INSTANCE.send(new EventNewNotification(reportFail)); NSUpload.uploadError(MainApp.gs(R.string.approachingdailylimit) + ": " + danaRPump.dailyTotalUnits + "/" + danaRPump.maxDailyTotalUnits + "U"); lastApproachingDailyLimit = System.currentTimeMillis(); } @@ -225,7 +249,7 @@ public class DanaRSService extends Service { public PumpEnactResult loadEvents() { - if (!MainApp.getSpecificPlugin(DanaRSPlugin.class).isInitialized()) { + if (!DanaRSPlugin.getPlugin().isInitialized()) { PumpEnactResult result = new PumpEnactResult().success(false); result.comment = "pump not initialized"; return result; @@ -241,7 +265,7 @@ public class DanaRSService extends Service { } else { msg = new DanaRS_Packet_APS_History_Events(lastHistoryFetched); if (L.isEnabled(L.PUMPCOMM)) - log.debug("Loading event history from: " +DateUtil.dateAndTimeFullString(lastHistoryFetched)); + log.debug("Loading event history from: " + DateUtil.dateAndTimeFullString(lastHistoryFetched)); } bleComm.sendMessage(msg); while (!msg.done && bleComm.isConnected()) { @@ -269,7 +293,7 @@ public class DanaRSService extends Service { if (!isConnected()) return false; if (BolusProgressDialog.stopPressed) return false; - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.startingbolus))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.startingbolus))); bolusingTreatment = t; final int preferencesSpeed = SP.getInt(R.string.key_danars_bolusspeed, 0); DanaRS_Packet_Bolus_Set_Step_Bolus_Start start = new DanaRS_Packet_Bolus_Set_Step_Bolus_Start(insulin, preferencesSpeed); @@ -305,9 +329,9 @@ public class DanaRSService extends Service { } } - final EventOverviewBolusProgress bolusingEvent = EventOverviewBolusProgress.getInstance(); - bolusingEvent.t = t; - bolusingEvent.percent = 99; + final EventOverviewBolusProgress bolusingEvent = EventOverviewBolusProgress.INSTANCE; + bolusingEvent.setT(t); + bolusingEvent.setPercent(99); bolusingTreatment = null; int speed = 12; @@ -326,8 +350,8 @@ public class DanaRSService extends Service { long expectedEnd = bolusStart + bolusDurationInMSec + 2000; while (System.currentTimeMillis() < expectedEnd) { long waitTime = expectedEnd - System.currentTimeMillis(); - bolusingEvent.status = String.format(MainApp.gs(R.string.waitingforestimatedbolusend), waitTime / 1000); - MainApp.bus().post(bolusingEvent); + bolusingEvent.setStatus(String.format(MainApp.gs(R.string.waitingforestimatedbolusend), waitTime / 1000)); + RxBus.INSTANCE.send(bolusingEvent); SystemClock.sleep(1000); } // do not call loadEvents() directly, reconnection may be needed @@ -335,10 +359,10 @@ public class DanaRSService extends Service { @Override public void run() { // reread bolus status - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.gettingbolusstatus))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.gettingbolusstatus))); bleComm.sendMessage(new DanaRS_Packet_Bolus_Get_Step_Bolus_Information()); // last bolus - bolusingEvent.percent = 100; - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.disconnecting))); + bolusingEvent.setPercent(100); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.disconnecting))); } }); return !start.failed; @@ -363,30 +387,30 @@ public class DanaRSService extends Service { public boolean tempBasal(Integer percent, int durationInHours) { if (!isConnected()) return false; if (DanaRPump.getInstance().isTempBasalInProgress) { - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.stoppingtempbasal))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.stoppingtempbasal))); bleComm.sendMessage(new DanaRS_Packet_Basal_Set_Cancel_Temporary_Basal()); SystemClock.sleep(500); } - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.settingtempbasal))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.settingtempbasal))); bleComm.sendMessage(new DanaRS_Packet_Basal_Set_Temporary_Basal(percent, durationInHours)); SystemClock.sleep(200); bleComm.sendMessage(new DanaRS_Packet_Basal_Get_Temporary_Basal_State()); loadEvents(); - MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.DISCONNECTING)); + RxBus.INSTANCE.send(new EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTING)); return true; } public boolean highTempBasal(Integer percent) { if (DanaRPump.getInstance().isTempBasalInProgress) { - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.stoppingtempbasal))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.stoppingtempbasal))); bleComm.sendMessage(new DanaRS_Packet_Basal_Set_Cancel_Temporary_Basal()); SystemClock.sleep(500); } - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.settingtempbasal))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.settingtempbasal))); bleComm.sendMessage(new DanaRS_Packet_APS_Basal_Set_Temporary_Basal(percent)); bleComm.sendMessage(new DanaRS_Packet_Basal_Get_Temporary_Basal_State()); loadEvents(); - MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.DISCONNECTING)); + RxBus.INSTANCE.send(new EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTING)); return true; } @@ -397,52 +421,52 @@ public class DanaRSService extends Service { } if (DanaRPump.getInstance().isTempBasalInProgress) { - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.stoppingtempbasal))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.stoppingtempbasal))); bleComm.sendMessage(new DanaRS_Packet_Basal_Set_Cancel_Temporary_Basal()); SystemClock.sleep(500); } - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.settingtempbasal))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.settingtempbasal))); bleComm.sendMessage(new DanaRS_Packet_APS_Basal_Set_Temporary_Basal(percent, durationInMinutes == 15, durationInMinutes == 30)); bleComm.sendMessage(new DanaRS_Packet_Basal_Get_Temporary_Basal_State()); loadEvents(); - MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.DISCONNECTING)); + RxBus.INSTANCE.send(new EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTING)); return true; } public boolean tempBasalStop() { if (!isConnected()) return false; - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.stoppingtempbasal))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.stoppingtempbasal))); bleComm.sendMessage(new DanaRS_Packet_Basal_Set_Cancel_Temporary_Basal()); bleComm.sendMessage(new DanaRS_Packet_Basal_Get_Temporary_Basal_State()); loadEvents(); - MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.DISCONNECTING)); + RxBus.INSTANCE.send(new EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTING)); return true; } public boolean extendedBolus(Double insulin, int durationInHalfHours) { if (!isConnected()) return false; - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.settingextendedbolus))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.settingextendedbolus))); bleComm.sendMessage(new DanaRS_Packet_Bolus_Set_Extended_Bolus(insulin, durationInHalfHours)); SystemClock.sleep(200); bleComm.sendMessage(new DanaRS_Packet_Bolus_Get_Extended_Bolus_State()); loadEvents(); - MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.DISCONNECTING)); + RxBus.INSTANCE.send(new EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTING)); return true; } public boolean extendedBolusStop() { if (!isConnected()) return false; - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.stoppingextendedbolus))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.stoppingextendedbolus))); bleComm.sendMessage(new DanaRS_Packet_Bolus_Set_Extended_Bolus_Cancel()); bleComm.sendMessage(new DanaRS_Packet_Bolus_Get_Extended_Bolus_State()); loadEvents(); - MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.DISCONNECTING)); + RxBus.INSTANCE.send(new EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTING)); return true; } public boolean updateBasalsInPump(Profile profile) { if (!isConnected()) return false; - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.updatingbasalrates))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.updatingbasalrates))); double[] basal = DanaRPump.getInstance().buildDanaRProfileRecord(profile); DanaRS_Packet_Basal_Set_Profile_Basal_Rate msgSet = new DanaRS_Packet_Basal_Set_Profile_Basal_Rate(0, basal); bleComm.sendMessage(msgSet); @@ -450,7 +474,7 @@ public class DanaRSService extends Service { bleComm.sendMessage(msgActivate); DanaRPump.getInstance().lastSettingsRead = 0; // force read full settings getPumpStatus(); - MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.DISCONNECTING)); + RxBus.INSTANCE.send(new EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTING)); return true; } @@ -520,21 +544,13 @@ public class DanaRSService extends Service { return START_STICKY; } - @Subscribe - public void onStatusEvent(EventAppExit event) { - if (L.isEnabled(L.PUMP)) - log.debug("EventAppExit received"); - - stopSelf(); - } - void waitForWholeMinute() { while (true) { long time = DateUtil.now(); long timeToWholeMinute = (60000 - time % 60000); if (timeToWholeMinute > 59800 || timeToWholeMinute < 300) break; - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.waitingfortimesynchronization, (int) (timeToWholeMinute / 1000)))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.waitingfortimesynchronization, (int) (timeToWholeMinute / 1000)))); SystemClock.sleep(Math.min(timeToWholeMinute, 100)); } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRv2/DanaRv2Plugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRv2/DanaRv2Plugin.java index 9e3ca499d0..3437d37656 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRv2/DanaRv2Plugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRv2/DanaRv2Plugin.java @@ -5,10 +5,6 @@ import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.IBinder; -import android.support.v4.app.FragmentActivity; -import android.support.v7.app.AlertDialog; - -import com.squareup.otto.Subscribe; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; @@ -19,11 +15,9 @@ import info.nightscout.androidaps.db.TemporaryBasal; import info.nightscout.androidaps.events.EventAppExit; import info.nightscout.androidaps.interfaces.Constraint; import info.nightscout.androidaps.logging.L; - -import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderFragment; -import info.nightscout.androidaps.plugins.configBuilder.DetailedBolusInfoStorage; +import info.nightscout.androidaps.plugins.bus.RxBus; +import info.nightscout.androidaps.plugins.pump.common.bolusInfo.DetailedBolusInfoStorage; import info.nightscout.androidaps.plugins.pump.common.defs.PumpType; - import info.nightscout.androidaps.plugins.pump.danaR.AbstractDanaRPlugin; import info.nightscout.androidaps.plugins.pump.danaR.DanaRPump; import info.nightscout.androidaps.plugins.pump.danaR.comm.MsgBolusStartWithSpeed; @@ -31,14 +25,18 @@ import info.nightscout.androidaps.plugins.pump.danaRv2.services.DanaRv2Execution import info.nightscout.androidaps.plugins.treatments.Treatment; import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; import info.nightscout.androidaps.utils.DateUtil; +import info.nightscout.androidaps.utils.FabricPrivacy; import info.nightscout.androidaps.utils.Round; import info.nightscout.androidaps.utils.SP; import info.nightscout.androidaps.utils.T; +import io.reactivex.disposables.CompositeDisposable; +import io.reactivex.schedulers.Schedulers; /** * Created by mike on 05.08.2016. */ public class DanaRv2Plugin extends AbstractDanaRPlugin { + private CompositeDisposable disposable = new CompositeDisposable(); private static DanaRv2Plugin plugin = null; @@ -61,7 +59,13 @@ public class DanaRv2Plugin extends AbstractDanaRPlugin { Intent intent = new Intent(context, DanaRv2ExecutionService.class); context.bindService(intent, mConnection, Context.BIND_AUTO_CREATE); - MainApp.bus().register(this); + disposable.add(RxBus.INSTANCE + .toObservable(EventAppExit.class) + .observeOn(Schedulers.io()) + .subscribe(event -> { + MainApp.instance().getApplicationContext().unbindService(mConnection); + }, FabricPrivacy::logException) + ); super.onStart(); } @@ -70,7 +74,8 @@ public class DanaRv2Plugin extends AbstractDanaRPlugin { Context context = MainApp.instance().getApplicationContext(); context.unbindService(mConnection); - MainApp.bus().unregister(this); + disposable.clear(); + super.onStop(); } private ServiceConnection mConnection = new ServiceConnection() { @@ -89,12 +94,6 @@ public class DanaRv2Plugin extends AbstractDanaRPlugin { } }; - @SuppressWarnings("UnusedParameters") - @Subscribe - public void onStatusEvent(final EventAppExit e) { - MainApp.instance().getApplicationContext().unbindService(mConnection); - } - // Plugin base interface @Override public String getName() { @@ -126,29 +125,6 @@ public class DanaRv2Plugin extends AbstractDanaRPlugin { sExecutionService.finishHandshaking(); } - @Override - public void switchAllowed(ConfigBuilderFragment.PluginViewHolder.PluginSwitcher pluginSwitcher, FragmentActivity context) { - boolean allowHardwarePump = SP.getBoolean("allow_hardware_pump", false); - if (allowHardwarePump || context == null) { - pluginSwitcher.invoke(); - } else { - AlertDialog.Builder builder = new AlertDialog.Builder(context); - builder.setMessage(R.string.allow_hardware_pump_text) - .setPositiveButton(R.string.yes, (dialog, id) -> { - pluginSwitcher.invoke(); - SP.putBoolean("allow_hardware_pump", true); - if (L.isEnabled(L.PUMP)) - log.debug("First time HW pump allowed!"); - }) - .setNegativeButton(R.string.cancel, (dialog, id) -> { - pluginSwitcher.cancel(); - if (L.isEnabled(L.PUMP)) - log.debug("User does not allow switching to HW pump!"); - }); - builder.create().show(); - } - } - // Pump interface @Override public PumpEnactResult deliverTreatment(DetailedBolusInfo detailedBolusInfo) { @@ -178,7 +154,7 @@ public class DanaRv2Plugin extends AbstractDanaRPlugin { if (carbTime == 0) carbTime--; // better set 1 man back to prevent clash with insulin detailedBolusInfo.carbTime = 0; - DetailedBolusInfoStorage.add(detailedBolusInfo); // will be picked up on reading history + DetailedBolusInfoStorage.INSTANCE.add(detailedBolusInfo); // will be picked up on reading history Treatment t = new Treatment(); t.isSMB = detailedBolusInfo.isSMB; @@ -261,7 +237,7 @@ public class DanaRv2Plugin extends AbstractDanaRPlugin { TemporaryBasal activeTemp = TreatmentsPlugin.getPlugin().getTempBasalFromHistory(System.currentTimeMillis()); if (activeTemp != null) { // Correct basal already set ? - if (activeTemp.percentRate == percentRate) { + if (activeTemp.percentRate == percentRate && activeTemp.getPlannedRemainingMinutes() > 4) { if (!enforceNew) { result.success = true; result.percent = percentRate; @@ -279,7 +255,7 @@ public class DanaRv2Plugin extends AbstractDanaRPlugin { if (L.isEnabled(L.PUMP)) log.debug("setTempBasalAbsolute: Setting temp basal " + percentRate + "% for " + durationInMinutes + " mins (doLowTemp || doHighTemp)"); if (percentRate == 0 && durationInMinutes > 30) { - result = setTempBasalPercent(percentRate, durationInMinutes, profile, false); + result = setTempBasalPercent(percentRate, durationInMinutes, profile, enforceNew); } else { // use special APS temp basal call ... 100+/15min .... 100-/30min result = setHighTempBasalPercent(percentRate); @@ -315,8 +291,8 @@ public class DanaRv2Plugin extends AbstractDanaRPlugin { if (percent > getPumpDescription().maxTempPercent) percent = getPumpDescription().maxTempPercent; long now = System.currentTimeMillis(); - TemporaryBasal runningTB = TreatmentsPlugin.getPlugin().getRealTempBasalFromHistory(now); - if (runningTB != null && runningTB.percentRate == percent && !enforceNew) { + TemporaryBasal activeTemp = TreatmentsPlugin.getPlugin().getRealTempBasalFromHistory(now); + if (activeTemp != null && activeTemp.percentRate == percent && activeTemp.getPlannedRemainingMinutes() > 4 && !enforceNew) { result.enacted = false; result.success = true; result.isTempCancel = false; @@ -402,6 +378,11 @@ public class DanaRv2Plugin extends AbstractDanaRPlugin { } } + @Override + public PumpType model() { + return PumpType.DanaRv2; + } + @Override public PumpEnactResult loadEvents() { return sExecutionService.loadEvents(); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRv2/SerialIOThread.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRv2/SerialIOThread.java deleted file mode 100644 index ba1fdc5cb2..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRv2/SerialIOThread.java +++ /dev/null @@ -1,209 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.danaRv2; - -import android.bluetooth.BluetoothSocket; -import android.os.SystemClock; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -import info.nightscout.androidaps.logging.L; -import info.nightscout.androidaps.plugins.pump.danaR.DanaRPump; -import info.nightscout.androidaps.plugins.pump.danaR.comm.MessageBase; -import info.nightscout.androidaps.plugins.pump.danaR.services.AbstractSerialIOThread; -import info.nightscout.androidaps.plugins.pump.danaRv2.comm.MessageHashTable_v2; -import info.nightscout.androidaps.utils.CRC; - -/** - * Created by mike on 17.07.2016. - */ -public class SerialIOThread extends AbstractSerialIOThread { - private static Logger log = LoggerFactory.getLogger(L.PUMPBTCOMM); - - private InputStream mInputStream = null; - private OutputStream mOutputStream = null; - private BluetoothSocket mRfCommSocket; - - private boolean mKeepRunning = true; - private byte[] mReadBuff = new byte[0]; - - private MessageBase processedMessage; - - public SerialIOThread(BluetoothSocket rfcommSocket) { - super(); - - mRfCommSocket = rfcommSocket; - try { - mOutputStream = mRfCommSocket.getOutputStream(); - mInputStream = mRfCommSocket.getInputStream(); - } catch (IOException e) { - log.error("Unhandled exception", e); - } - this.start(); - } - - @Override - public final void run() { - try { - while (mKeepRunning) { - int availableBytes = mInputStream.available(); - // Ask for 1024 byte (or more if available) - byte[] newData = new byte[Math.max(1024, availableBytes)]; - int gotBytes = mInputStream.read(newData); - // When we are here there is some new data available - appendToBuffer(newData, gotBytes); - - // process all messages we already got - while (mReadBuff.length > 3) { // 3rd byte is packet size. continue only if we an determine packet size - byte[] extractedBuff = cutMessageFromBuffer(); - if (extractedBuff == null) - break; // message is not complete in buffer (wrong packet calls disconnection) - - int command = (extractedBuff[5] & 0xFF) | ((extractedBuff[4] << 8) & 0xFF00); - - MessageBase message; - if (processedMessage != null && processedMessage.getCommand() == command) { - message = processedMessage; - } else { - // get it from hash table - message = MessageHashTable_v2.findMessage(command); - } - - if (L.isEnabled(L.PUMPBTCOMM)) - log.debug("<<<<< " + message.getMessageName() + " " + message.toHexString(extractedBuff)); - - // process the message content - message.received = true; - message.handleMessage(extractedBuff); - synchronized (message) { - message.notify(); - } - } - } - } catch (Exception e) { - if (e.getMessage().indexOf("bt socket closed") < 0) - log.error("Thread exception: ", e); - mKeepRunning = false; - } - disconnect("EndOfLoop"); - } - - void appendToBuffer(byte[] newData, int gotBytes) { - // add newData to mReadBuff - byte[] newReadBuff = new byte[mReadBuff.length + gotBytes]; - System.arraycopy(mReadBuff, 0, newReadBuff, 0, mReadBuff.length); - System.arraycopy(newData, 0, newReadBuff, mReadBuff.length, gotBytes); - mReadBuff = newReadBuff; - } - - byte[] cutMessageFromBuffer() { - if (mReadBuff[0] == (byte) 0x7E && mReadBuff[1] == (byte) 0x7E) { - int length = (mReadBuff[2] & 0xFF) + 7; - // Check if we have enough data - if (mReadBuff.length < length) { - return null; - } - if (mReadBuff[length - 2] != (byte) 0x2E || mReadBuff[length - 1] != (byte) 0x2E) { - log.error("wrong packet lenght=" + length + " data " + MessageBase.toHexString(mReadBuff)); - disconnect("wrong packet"); - return null; - } - - short crc = CRC.getCrc16(mReadBuff, 3, length - 7); - byte crcByte0 = (byte) (crc >> 8 & 0xFF); - byte crcByte1 = (byte) (crc & 0xFF); - - byte crcByte0received = mReadBuff[length - 4]; - byte crcByte1received = mReadBuff[length - 3]; - - if (crcByte0 != crcByte0received || crcByte1 != crcByte1received) { - log.error("CRC Error" + String.format("%02x ", crcByte0) + String.format("%02x ", crcByte1) + String.format("%02x ", crcByte0received) + String.format("%02x ", crcByte1received)); - disconnect("crc error"); - return null; - } - // Packet is verified here. extract data - byte[] extractedBuff = new byte[length]; - System.arraycopy(mReadBuff, 0, extractedBuff, 0, length); - // remove extracted data from read buffer - byte[] unprocessedData = new byte[mReadBuff.length - length]; - System.arraycopy(mReadBuff, length, unprocessedData, 0, unprocessedData.length); - mReadBuff = unprocessedData; - return extractedBuff; - } else { - log.error("Wrong beginning of packet len=" + mReadBuff.length + " " + MessageBase.toHexString(mReadBuff)); - disconnect("Wrong beginning of packet"); - return null; - } - } - - @Override - public synchronized void sendMessage(MessageBase message) { - if (!mRfCommSocket.isConnected()) { - log.error("Socket not connected on sendMessage"); - return; - } - processedMessage = message; - - byte[] messageBytes = message.getRawMessageBytes(); - if (L.isEnabled(L.PUMPBTCOMM)) - log.debug(">>>>> " + message.getMessageName() + " " + message.toHexString(messageBytes)); - - try { - mOutputStream.write(messageBytes); - } catch (Exception e) { - log.error("sendMessage write exception: ", e); - } - - synchronized (message) { - try { - message.wait(5000); - } catch (InterruptedException e) { - log.error("sendMessage InterruptedException", e); - } - } - - SystemClock.sleep(200); - if (!message.received) { - log.error("Reply not received " + message.getMessageName()); - if (message.getCommand() == 0xF0F1) { - DanaRPump.getInstance().isNewPump = false; - log.error("Old firmware detected"); - } - } - } - - @Override - public void disconnect(String reason) { - mKeepRunning = false; - try { - mInputStream.close(); - } catch (Exception e) { - if (L.isEnabled(L.PUMPBTCOMM)) - log.debug(e.getMessage()); - } - try { - mOutputStream.close(); - } catch (Exception e) { - if (L.isEnabled(L.PUMPBTCOMM)) - log.debug(e.getMessage()); - } - try { - mRfCommSocket.close(); - } catch (Exception e) { - if (L.isEnabled(L.PUMPBTCOMM)) - log.debug(e.getMessage()); - } - try { - System.runFinalization(); - } catch (Exception e) { - if (L.isEnabled(L.PUMPBTCOMM)) - log.debug(e.getMessage()); - } - if (L.isEnabled(L.PUMPBTCOMM)) - log.debug("Disconnected: " + reason); - } - -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRv2/comm/MessageHashTableRv2.kt b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRv2/comm/MessageHashTableRv2.kt new file mode 100644 index 0000000000..f22a3cf3fa --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRv2/comm/MessageHashTableRv2.kt @@ -0,0 +1,77 @@ +package info.nightscout.androidaps.plugins.pump.danaRv2.comm + +import info.nightscout.androidaps.plugins.pump.danaR.comm.* +import java.util.* + + +object MessageHashTableRv2 : MessageHashTableBase { + var messages: HashMap = HashMap() + + init { + put(MsgBolusStop()) // 0x0101 CMD_MEALINS_STOP + put(MsgBolusStart()) // 0x0102 CMD_MEALINS_START_DATA + put(MsgBolusStartWithSpeed()) // 0x0104 CMD_MEALINS_START_DATA_SPEED + put(MsgBolusProgress()) // 0x0202 CMD_PUMP_THIS_REMAINDER_MEAL_INS + put(MsgStatusProfile()) // 0x0204 CMD_PUMP_CALCULATION_SETTING + + put(MsgStatusTempBasal_v2()) // 0x0205 CMD_PUMP_EXERCISE_MODE + put(MsgStatusBolusExtended_v2()) // 0x0207 CMD_PUMP_EXPANS_INS_I + + put(MsgStatusBasic()) // 0x020A CMD_PUMP_INITVIEW_I + put(MsgStatus()) // 0x020B CMD_PUMP_STATUS + put(MsgInitConnStatusTime()) // 0x0301 CMD_PUMPINIT_TIME_INFO + put(MsgInitConnStatusBolus()) // 0x0302 CMD_PUMPINIT_BOLUS_INFO + put(MsgInitConnStatusBasic()) // 0x0303 CMD_PUMPINIT_INIT_INFO + put(MsgInitConnStatusOption()) // 0x0304 CMD_PUMPINIT_OPTION + put(MsgSetTempBasalStart()) // 0x0401 CMD_PUMPSET_EXERCISE_S + put(MsgSetCarbsEntry()) // 0x0402 CMD_PUMPSET_HIS_S + put(MsgSetTempBasalStop()) // 0x0403 CMD_PUMPSET_EXERCISE_STOP + put(MsgSetExtendedBolusStop()) // 0x0406 CMD_PUMPSET_EXPANS_INS_STOP + put(MsgSetExtendedBolusStart()) // 0x0407 CMD_PUMPSET_EXPANS_INS_S + put(MsgError()) // 0x0601 CMD_PUMPOWAY_SYSTEM_STATUS + put(MsgPCCommStart()) // 0x3001 CMD_CONNECT + put(MsgPCCommStop()) // 0x3002 CMD_DISCONNECT + put(MsgHistoryBolus()) // 0x3101 CMD_HISTORY_MEAL_INS + put(MsgHistoryDailyInsulin()) // 0x3102 CMD_HISTORY_DAY_INS + put(MsgHistoryGlucose()) // 0x3104 CMD_HISTORY_GLUCOSE + put(MsgHistoryAlarm()) // 0x3105 CMD_HISTORY_ALARM + put(MsgHistoryError()) // 0x3106 CMD_HISTORY_ERROR + put(MsgHistoryCarbo()) // 0x3107 CMD_HISTORY_CARBOHY + put(MsgHistoryRefill()) // 0x3108 CMD_HISTORY_REFILL + put(MsgHistorySuspend()) // 0x3109 CMD_HISTORY_SUSPEND + put(MsgHistoryBasalHour()) // 0x310A CMD_HISTORY_BASAL_HOUR + put(MsgHistoryDone()) // 0x31F1 CMD_HISTORY_DONT_USED + put(MsgSettingBasal()) // 0x3202 CMD_SETTING_V_BASAL_INS_I + put(MsgSettingMeal()) // 0x3203 CMD_SETTING_V_MEAL_SETTING_I + put(MsgSettingProfileRatios()) // 0x3204 CMD_SETTING_V_CCC_I + put(MsgSettingMaxValues()) // 0x3205 CMD_SETTING_V_MAX_VALUE_I + put(MsgSettingBasalProfileAll()) // 0x3206 CMD_SETTING_V_BASAL_PROFILE_ALL + put(MsgSettingShippingInfo()) // 0x3207 CMD_SETTING_V_SHIPPING_I + put(MsgSettingGlucose()) // 0x3209 CMD_SETTING_V_GLUCOSEandEASY + put(MsgSettingPumpTime()) // 0x320A CMD_SETTING_V_TIME_I + put(MsgSettingUserOptions()) // 0x320B CMD_SETTING_V_USER_OPTIONS + put(MsgSettingActiveProfile()) // 0x320C CMD_SETTING_V_PROFILE_NUMBER + put(MsgSettingProfileRatiosAll()) // 0x320D CMD_SETTING_V_CIR_CF_VALUE + put(MsgSetSingleBasalProfile()) // 0x3302 CMD_SETTING_BASAL_INS_S + put(MsgSetBasalProfile()) // 0x3306 CMD_SETTING_BASAL_PROFILE_S + put(MsgSetUserOptions()) // 0x330B CMD_SETTING_USER_OPTIONS_S + put(MsgSetActivateBasalProfile()) // 0x330C CMD_SETTING_PROFILE_NUMBER_S + put(MsgHistoryAllDone()) // 0x41F1 CMD_HISTORY_ALL_DONE + put(MsgHistoryAll()) // 0x41F2 CMD_HISTORY_ALL + put(MsgHistoryNewDone()) // 0x42F1 CMD_HISTORY_NEW_DONE + put(MsgHistoryNew()) // 0x42F2 CMD_HISTORY_NEW + put(MsgCheckValue_v2()) // 0xF0F1 CMD_PUMP_CHECK_VALUE + put(MsgStatusAPS_v2()) // 0xE001 CMD_PUMPSTATUS_APS + put(MsgSetAPSTempBasalStart_v2()) // 0xE002 CMD_PUMPSET_APSTEMP + put(MsgHistoryEvents_v2()) // 0xE003 CMD_GET_HISTORY + put(MsgSetHistoryEntry_v2()) // 0xE004 CMD_SET_HISTORY_ENTRY + } + + override fun put(message: MessageBase) { + messages[message.command] = message + } + + override fun findMessage(command: Int): MessageBase { + return messages[command] ?: MessageBase() + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRv2/comm/MessageHashTable_v2.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRv2/comm/MessageHashTable_v2.java deleted file mode 100644 index 66376290af..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRv2/comm/MessageHashTable_v2.java +++ /dev/null @@ -1,92 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.danaRv2.comm; - -import java.util.HashMap; - -import info.nightscout.androidaps.plugins.pump.danaR.comm.MessageBase; -import info.nightscout.androidaps.plugins.pump.danaR.comm.*; - - -/** - * Created by mike on 28.05.2016. - */ -public class MessageHashTable_v2 { - public static HashMap messages = null; - - static { - if (messages == null) { - messages = new HashMap(); - put(new MsgBolusStop()); // 0x0101 CMD_MEALINS_STOP - put(new MsgBolusStart()); // 0x0102 CMD_MEALINS_START_DATA - put(new MsgBolusStartWithSpeed()); // 0x0104 CMD_MEALINS_START_DATA_SPEED - put(new MsgBolusProgress()); // 0x0202 CMD_PUMP_THIS_REMAINDER_MEAL_INS - put(new MsgStatusProfile()); // 0x0204 CMD_PUMP_CALCULATION_SETTING - - put(new MsgStatusTempBasal_v2()); // 0x0205 CMD_PUMP_EXERCISE_MODE - put(new MsgStatusBolusExtended_v2()); // 0x0207 CMD_PUMP_EXPANS_INS_I - - put(new MsgStatusBasic()); // 0x020A CMD_PUMP_INITVIEW_I - put(new MsgStatus()); // 0x020B CMD_PUMP_STATUS - put(new MsgInitConnStatusTime()); // 0x0301 CMD_PUMPINIT_TIME_INFO - put(new MsgInitConnStatusBolus()); // 0x0302 CMD_PUMPINIT_BOLUS_INFO - put(new MsgInitConnStatusBasic()); // 0x0303 CMD_PUMPINIT_INIT_INFO - put(new MsgInitConnStatusOption()); // 0x0304 CMD_PUMPINIT_OPTION - put(new MsgSetTempBasalStart()); // 0x0401 CMD_PUMPSET_EXERCISE_S - put(new MsgSetCarbsEntry()); // 0x0402 CMD_PUMPSET_HIS_S - put(new MsgSetTempBasalStop()); // 0x0403 CMD_PUMPSET_EXERCISE_STOP - put(new MsgSetExtendedBolusStop()); // 0x0406 CMD_PUMPSET_EXPANS_INS_STOP - put(new MsgSetExtendedBolusStart()); // 0x0407 CMD_PUMPSET_EXPANS_INS_S - put(new MsgError()); // 0x0601 CMD_PUMPOWAY_SYSTEM_STATUS - put(new MsgPCCommStart()); // 0x3001 CMD_CONNECT - put(new MsgPCCommStop()); // 0x3002 CMD_DISCONNECT - put(new MsgHistoryBolus()); // 0x3101 CMD_HISTORY_MEAL_INS - put(new MsgHistoryDailyInsulin()); // 0x3102 CMD_HISTORY_DAY_INS - put(new MsgHistoryGlucose()); // 0x3104 CMD_HISTORY_GLUCOSE - put(new MsgHistoryAlarm()); // 0x3105 CMD_HISTORY_ALARM - put(new MsgHistoryError()); // 0x3106 CMD_HISTORY_ERROR - put(new MsgHistoryCarbo()); // 0x3107 CMD_HISTORY_CARBOHY - put(new MsgHistoryRefill()); // 0x3108 CMD_HISTORY_REFILL - put(new MsgHistorySuspend()); // 0x3109 CMD_HISTORY_SUSPEND - put(new MsgHistoryBasalHour()); // 0x310A CMD_HISTORY_BASAL_HOUR - put(new MsgHistoryDone()); // 0x31F1 CMD_HISTORY_DONT_USED - put(new MsgSettingBasal()); // 0x3202 CMD_SETTING_V_BASAL_INS_I - put(new MsgSettingMeal()); // 0x3203 CMD_SETTING_V_MEAL_SETTING_I - put(new MsgSettingProfileRatios()); // 0x3204 CMD_SETTING_V_CCC_I - put(new MsgSettingMaxValues()); // 0x3205 CMD_SETTING_V_MAX_VALUE_I - put(new MsgSettingBasalProfileAll()); // 0x3206 CMD_SETTING_V_BASAL_PROFILE_ALL - put(new MsgSettingShippingInfo()); // 0x3207 CMD_SETTING_V_SHIPPING_I - put(new MsgSettingGlucose()); // 0x3209 CMD_SETTING_V_GLUCOSEandEASY - put(new MsgSettingPumpTime()); // 0x320A CMD_SETTING_V_TIME_I - put(new MsgSettingUserOptions()); // 0x320B CMD_SETTING_V_USER_OPTIONS - put(new MsgSettingActiveProfile()); // 0x320C CMD_SETTING_V_PROFILE_NUMBER - put(new MsgSettingProfileRatiosAll()); // 0x320D CMD_SETTING_V_CIR_CF_VALUE - put(new MsgSetSingleBasalProfile()); // 0x3302 CMD_SETTING_BASAL_INS_S - put(new MsgSetBasalProfile()); // 0x3306 CMD_SETTING_BASAL_PROFILE_S - put(new MsgSetUserOptions()); // 0x330B CMD_SETTING_USER_OPTIONS_S - put(new MsgSetActivateBasalProfile()); // 0x330C CMD_SETTING_PROFILE_NUMBER_S - put(new MsgHistoryAllDone()); // 0x41F1 CMD_HISTORY_ALL_DONE - put(new MsgHistoryAll()); // 0x41F2 CMD_HISTORY_ALL - put(new MsgHistoryNewDone()); // 0x42F1 CMD_HISTORY_NEW_DONE - put(new MsgHistoryNew()); // 0x42F2 CMD_HISTORY_NEW - put(new MsgCheckValue_v2()); // 0xF0F1 CMD_PUMP_CHECK_VALUE - put(new MsgStatusAPS_v2()); // 0xE001 CMD_PUMPSTATUS_APS - put(new MsgSetAPSTempBasalStart_v2()); // 0xE002 CMD_PUMPSET_APSTEMP - put(new MsgHistoryEvents_v2()); // 0xE003 CMD_GET_HISTORY - put(new MsgSetHistoryEntry_v2()); // 0xE004 CMD_SET_HISTORY_ENTRY - } - } - - public static void put(MessageBase message) { - int command = message.getCommand(); - //String name = MessageOriginalNames.getName(command); - messages.put(command, message); - //log.debug(String.format("%04x ", command) + " " + name); - } - - public static MessageBase findMessage(Integer command) { - if (messages.containsKey(command)) { - return messages.get(command); - } else { - return new MessageBase(); - } - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRv2/comm/MsgCheckValue_v2.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRv2/comm/MsgCheckValue_v2.java index 5af7705106..3070c52b64 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRv2/comm/MsgCheckValue_v2.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRv2/comm/MsgCheckValue_v2.java @@ -5,9 +5,10 @@ import org.slf4j.LoggerFactory; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; -import info.nightscout.androidaps.events.EventRefreshGui; +import info.nightscout.androidaps.events.EventRebuildTabs; import info.nightscout.androidaps.interfaces.PluginType; import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification; import info.nightscout.androidaps.plugins.general.overview.notifications.Notification; @@ -43,24 +44,24 @@ public class MsgCheckValue_v2 extends MessageBase { if (pump.model != DanaRPump.EXPORT_MODEL) { pump.lastConnection = 0; Notification notification = new Notification(Notification.WRONG_DRIVER, MainApp.gs(R.string.pumpdrivercorrected), Notification.NORMAL); - MainApp.bus().post(new EventNewNotification(notification)); - MainApp.getSpecificPlugin(DanaRPlugin.class).disconnect("Wrong Model"); + RxBus.INSTANCE.send(new EventNewNotification(notification)); + DanaRPlugin.getPlugin().disconnect("Wrong Model"); log.error("Wrong model selected. Switching to Korean DanaR"); - MainApp.getSpecificPlugin(DanaRKoreanPlugin.class).setPluginEnabled(PluginType.PUMP, true); - MainApp.getSpecificPlugin(DanaRKoreanPlugin.class).setFragmentVisible(PluginType.PUMP, true); - MainApp.getSpecificPlugin(DanaRPlugin.class).setPluginEnabled(PluginType.PUMP, false); - MainApp.getSpecificPlugin(DanaRPlugin.class).setFragmentVisible(PluginType.PUMP, false); + DanaRKoreanPlugin.getPlugin().setPluginEnabled(PluginType.PUMP, true); + DanaRKoreanPlugin.getPlugin().setFragmentVisible(PluginType.PUMP, true); + DanaRPlugin.getPlugin().setPluginEnabled(PluginType.PUMP, false); + DanaRPlugin.getPlugin().setFragmentVisible(PluginType.PUMP, false); DanaRPump.reset(); // mark not initialized //If profile coming from pump, switch it as well - if (MainApp.getSpecificPlugin(DanaRPlugin.class).isEnabled(PluginType.PROFILE)) { - (MainApp.getSpecificPlugin(DanaRPlugin.class)).setPluginEnabled(PluginType.PROFILE, false); - (MainApp.getSpecificPlugin(DanaRKoreanPlugin.class)).setPluginEnabled(PluginType.PROFILE, true); + if (DanaRPlugin.getPlugin().isEnabled(PluginType.PROFILE)) { + (DanaRPlugin.getPlugin()).setPluginEnabled(PluginType.PROFILE, false); + (DanaRKoreanPlugin.getPlugin()).setPluginEnabled(PluginType.PROFILE, true); } ConfigBuilderPlugin.getPlugin().storeSettings("ChangingDanaRv2Driver"); - MainApp.bus().post(new EventRefreshGui()); + RxBus.INSTANCE.send(new EventRebuildTabs()); ConfigBuilderPlugin.getPlugin().getCommandQueue().readStatus("PumpDriverChange", null); // force new connection return; } @@ -68,22 +69,22 @@ public class MsgCheckValue_v2 extends MessageBase { if (pump.protocol != 2) { pump.lastConnection = 0; Notification notification = new Notification(Notification.WRONG_DRIVER, MainApp.gs(R.string.pumpdrivercorrected), Notification.NORMAL); - MainApp.bus().post(new EventNewNotification(notification)); + RxBus.INSTANCE.send(new EventNewNotification(notification)); DanaRKoreanPlugin.getPlugin().disconnect("Wrong Model"); log.error("Wrong model selected. Switching to non APS DanaR"); - (MainApp.getSpecificPlugin(DanaRv2Plugin.class)).setPluginEnabled(PluginType.PUMP, false); - (MainApp.getSpecificPlugin(DanaRv2Plugin.class)).setFragmentVisible(PluginType.PUMP, false); - (MainApp.getSpecificPlugin(DanaRPlugin.class)).setPluginEnabled(PluginType.PUMP, true); - (MainApp.getSpecificPlugin(DanaRPlugin.class)).setFragmentVisible(PluginType.PUMP, true); + (DanaRv2Plugin.getPlugin()).setPluginEnabled(PluginType.PUMP, false); + (DanaRv2Plugin.getPlugin()).setFragmentVisible(PluginType.PUMP, false); + (DanaRPlugin.getPlugin()).setPluginEnabled(PluginType.PUMP, true); + (DanaRPlugin.getPlugin()).setFragmentVisible(PluginType.PUMP, true); //If profile coming from pump, switch it as well - if (MainApp.getSpecificPlugin(DanaRv2Plugin.class).isEnabled(PluginType.PROFILE)) { - (MainApp.getSpecificPlugin(DanaRv2Plugin.class)).setPluginEnabled(PluginType.PROFILE, false); - (MainApp.getSpecificPlugin(DanaRPlugin.class)).setPluginEnabled(PluginType.PROFILE, true); + if (DanaRv2Plugin.getPlugin().isEnabled(PluginType.PROFILE)) { + (DanaRv2Plugin.getPlugin()).setPluginEnabled(PluginType.PROFILE, false); + (DanaRPlugin.getPlugin()).setPluginEnabled(PluginType.PROFILE, true); } ConfigBuilderPlugin.getPlugin().storeSettings("ChangingDanaRv2Driver"); - MainApp.bus().post(new EventRefreshGui()); + RxBus.INSTANCE.send(new EventRebuildTabs()); ConfigBuilderPlugin.getPlugin().getCommandQueue().readStatus("PumpDriverChange", null); // force new connection return; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRv2/comm/MsgHistoryEvents_v2.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRv2/comm/MsgHistoryEvents_v2.java index b3c7d00612..3ef43cad86 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRv2/comm/MsgHistoryEvents_v2.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRv2/comm/MsgHistoryEvents_v2.java @@ -13,7 +13,8 @@ import info.nightscout.androidaps.db.Source; import info.nightscout.androidaps.db.TemporaryBasal; import info.nightscout.androidaps.events.EventPumpStatusChanged; import info.nightscout.androidaps.logging.L; -import info.nightscout.androidaps.plugins.configBuilder.DetailedBolusInfoStorage; +import info.nightscout.androidaps.plugins.bus.RxBus; +import info.nightscout.androidaps.plugins.pump.common.bolusInfo.DetailedBolusInfoStorage; import info.nightscout.androidaps.plugins.pump.danaR.DanaRPump; import info.nightscout.androidaps.plugins.pump.danaR.comm.MessageBase; import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; @@ -110,7 +111,7 @@ public class MsgHistoryEvents_v2 extends MessageBase { status = "EXTENDEDSTOP " + DateUtil.timeString(datetime); break; case DanaRPump.BOLUS: - DetailedBolusInfo detailedBolusInfo = DetailedBolusInfoStorage.findDetailedBolusInfo(datetime); + DetailedBolusInfo detailedBolusInfo = DetailedBolusInfoStorage.INSTANCE.findDetailedBolusInfo(datetime, param1 / 100d); if (detailedBolusInfo == null) { detailedBolusInfo = new DetailedBolusInfo(); } @@ -125,7 +126,7 @@ public class MsgHistoryEvents_v2 extends MessageBase { status = "BOLUS " + DateUtil.timeString(datetime); break; case DanaRPump.DUALBOLUS: - detailedBolusInfo = DetailedBolusInfoStorage.findDetailedBolusInfo(datetime); + detailedBolusInfo = DetailedBolusInfoStorage.INSTANCE.findDetailedBolusInfo(datetime, param1 / 100d); if (detailedBolusInfo == null) { detailedBolusInfo = new DetailedBolusInfo(); } @@ -199,6 +200,6 @@ public class MsgHistoryEvents_v2 extends MessageBase { if (datetime > lastEventTimeLoaded) lastEventTimeLoaded = datetime; - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.processinghistory) + ": " + status)); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.processinghistory) + ": " + status)); } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRv2/comm/MsgStatusBolusExtended_v2.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRv2/comm/MsgStatusBolusExtended_v2.java index 578be6d4b6..a829b3afcb 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRv2/comm/MsgStatusBolusExtended_v2.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRv2/comm/MsgStatusBolusExtended_v2.java @@ -1,6 +1,6 @@ package info.nightscout.androidaps.plugins.pump.danaRv2.comm; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRv2/comm/MsgStatusTempBasal_v2.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRv2/comm/MsgStatusTempBasal_v2.java index a92c534d92..19a0a2b6ce 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRv2/comm/MsgStatusTempBasal_v2.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRv2/comm/MsgStatusTempBasal_v2.java @@ -1,6 +1,6 @@ package info.nightscout.androidaps.plugins.pump.danaRv2.comm; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRv2/services/DanaRv2ExecutionService.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRv2/services/DanaRv2ExecutionService.java index 2d2d2526dd..e528a67fae 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRv2/services/DanaRv2ExecutionService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaRv2/services/DanaRv2ExecutionService.java @@ -6,8 +6,6 @@ import android.content.IntentFilter; import android.os.Binder; import android.os.SystemClock; -import com.squareup.otto.Subscribe; - import java.io.IOException; import java.util.Date; @@ -23,6 +21,7 @@ import info.nightscout.androidaps.events.EventProfileNeedsUpdate; import info.nightscout.androidaps.events.EventPumpStatusChanged; import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; import info.nightscout.androidaps.plugins.general.nsclient.NSUpload; @@ -32,6 +31,7 @@ import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotifi import info.nightscout.androidaps.plugins.general.overview.events.EventOverviewBolusProgress; import info.nightscout.androidaps.plugins.general.overview.notifications.Notification; import info.nightscout.androidaps.plugins.pump.danaR.DanaRPump; +import info.nightscout.androidaps.plugins.pump.danaR.SerialIOThread; import info.nightscout.androidaps.plugins.pump.danaR.comm.MessageBase; import info.nightscout.androidaps.plugins.pump.danaR.comm.MsgBolusProgress; import info.nightscout.androidaps.plugins.pump.danaR.comm.MsgBolusStart; @@ -61,7 +61,7 @@ import info.nightscout.androidaps.plugins.pump.danaR.comm.MsgStatusBasic; import info.nightscout.androidaps.plugins.pump.danaR.events.EventDanaRNewStatus; import info.nightscout.androidaps.plugins.pump.danaR.services.AbstractDanaRExecutionService; import info.nightscout.androidaps.plugins.pump.danaRv2.DanaRv2Plugin; -import info.nightscout.androidaps.plugins.pump.danaRv2.SerialIOThread; +import info.nightscout.androidaps.plugins.pump.danaRv2.comm.MessageHashTableRv2; import info.nightscout.androidaps.plugins.pump.danaRv2.comm.MsgCheckValue_v2; import info.nightscout.androidaps.plugins.pump.danaRv2.comm.MsgHistoryEvents_v2; import info.nightscout.androidaps.plugins.pump.danaRv2.comm.MsgSetAPSTempBasalStart_v2; @@ -72,17 +72,20 @@ import info.nightscout.androidaps.plugins.treatments.Treatment; import info.nightscout.androidaps.queue.Callback; import info.nightscout.androidaps.queue.commands.Command; import info.nightscout.androidaps.utils.DateUtil; +import info.nightscout.androidaps.utils.FabricPrivacy; import info.nightscout.androidaps.utils.SP; import info.nightscout.androidaps.utils.T; +import io.reactivex.disposables.CompositeDisposable; +import io.reactivex.schedulers.Schedulers; public class DanaRv2ExecutionService extends AbstractDanaRExecutionService { + private CompositeDisposable disposable = new CompositeDisposable(); private long lastHistoryFetched = 0; public DanaRv2ExecutionService() { mBinder = new LocalBinder(); - registerBus(); MainApp.instance().getApplicationContext().registerReceiver(receiver, new IntentFilter(BluetoothDevice.ACTION_ACL_DISCONNECTED)); } @@ -92,32 +95,36 @@ public class DanaRv2ExecutionService extends AbstractDanaRExecutionService { } } - private void registerBus() { - try { - MainApp.bus().unregister(this); - } catch (RuntimeException x) { - // Ignore - } - MainApp.bus().register(this); + @Override + public void onCreate() { + super.onCreate(); + disposable.add(RxBus.INSTANCE + .toObservable(EventPreferenceChange.class) + .observeOn(Schedulers.io()) + .subscribe(event -> { + if (mSerialIOThread != null) + mSerialIOThread.disconnect("EventPreferenceChange"); + }, FabricPrivacy::logException) + ); + disposable.add(RxBus.INSTANCE + .toObservable(EventAppExit.class) + .observeOn(Schedulers.io()) + .subscribe(event -> { + if (L.isEnabled(L.PUMP)) + log.debug("EventAppExit received"); + + if (mSerialIOThread != null) + mSerialIOThread.disconnect("Application exit"); + MainApp.instance().getApplicationContext().unregisterReceiver(receiver); + stopSelf(); + }, FabricPrivacy::logException) + ); } - @Subscribe - public void onStatusEvent(EventAppExit event) { - if (L.isEnabled(L.PUMP)) - log.debug("EventAppExit received"); - - if (mSerialIOThread != null) - mSerialIOThread.disconnect("Application exit"); - - MainApp.instance().getApplicationContext().unregisterReceiver(receiver); - - stopSelf(); - } - - @Subscribe - public void onStatusEvent(final EventPreferenceChange pch) { - if (mSerialIOThread != null) - mSerialIOThread.disconnect("EventPreferenceChange"); + @Override + public void onDestroy() { + disposable.clear(); + super.onDestroy(); } public void connect() { @@ -146,9 +153,9 @@ public class DanaRv2ExecutionService extends AbstractDanaRExecutionService { if (mSerialIOThread != null) { mSerialIOThread.disconnect("Recreate SerialIOThread"); } - mSerialIOThread = new SerialIOThread(mRfcommSocket); + mSerialIOThread = new SerialIOThread(mRfcommSocket, MessageHashTableRv2.INSTANCE); mHandshakeInProgress = true; - MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.HANDSHAKING, 0)); + RxBus.INSTANCE.send(new EventPumpStatusChanged(EventPumpStatusChanged.Status.HANDSHAKING, 0)); } mConnectionInProgress = false; @@ -158,7 +165,7 @@ public class DanaRv2ExecutionService extends AbstractDanaRExecutionService { public void getPumpStatus() { DanaRPump danaRPump = DanaRPump.getInstance(); try { - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.gettingpumpstatus))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.gettingpumpstatus))); MsgStatus statusMsg = new MsgStatus(); MsgStatusBasic statusBasicMsg = new MsgStatusBasic(); MsgStatusTempBasal_v2 tempStatusMsg = new MsgStatusTempBasal_v2(); @@ -172,12 +179,12 @@ public class DanaRv2ExecutionService extends AbstractDanaRExecutionService { } } - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.gettingbolusstatus))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.gettingbolusstatus))); mSerialIOThread.sendMessage(statusMsg); mSerialIOThread.sendMessage(statusBasicMsg); - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.gettingtempbasalstatus))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.gettingtempbasalstatus))); mSerialIOThread.sendMessage(tempStatusMsg); - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.gettingextendedbolusstatus))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.gettingextendedbolusstatus))); mSerialIOThread.sendMessage(exStatusMsg); danaRPump.lastConnection = System.currentTimeMillis(); @@ -185,15 +192,24 @@ public class DanaRv2ExecutionService extends AbstractDanaRExecutionService { Profile profile = ProfileFunctions.getInstance().getProfile(); PumpInterface pump = ConfigBuilderPlugin.getPlugin().getActivePump(); if (profile != null && Math.abs(danaRPump.currentBasal - profile.getBasal()) >= pump.getPumpDescription().basalStep) { - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.gettingpumpsettings))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.gettingpumpsettings))); mSerialIOThread.sendMessage(new MsgSettingBasal()); if (!pump.isThisProfileSet(profile) && !ConfigBuilderPlugin.getPlugin().getCommandQueue().isRunning(Command.CommandType.BASALPROFILE)) { - MainApp.bus().post(new EventProfileNeedsUpdate()); + RxBus.INSTANCE.send(new EventProfileNeedsUpdate()); } } - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.gettingpumptime))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.gettingpumptime))); mSerialIOThread.sendMessage(new MsgSettingPumpTime()); + if (danaRPump.pumpTime == 0) { + // initial handshake was not successfull + // deinitialize pump + danaRPump.lastConnection = 0; + danaRPump.lastSettingsRead = 0; + RxBus.INSTANCE.send(new EventDanaRNewStatus()); + RxBus.INSTANCE.send(new EventInitializationChanged()); + return; + } long timeDiff = (danaRPump.pumpTime - System.currentTimeMillis()) / 1000L; if (L.isEnabled(L.PUMP)) log.debug("Pump time difference: " + timeDiff + " seconds"); @@ -211,8 +227,8 @@ public class DanaRv2ExecutionService extends AbstractDanaRExecutionService { //deinitialize pump danaRPump.lastConnection = 0; - MainApp.bus().post(new EventDanaRNewStatus()); - MainApp.bus().post(new EventInitializationChanged()); + RxBus.INSTANCE.send(new EventDanaRNewStatus()); + RxBus.INSTANCE.send(new EventInitializationChanged()); return; } else { waitForWholeMinute(); // Dana can set only whole minute @@ -227,7 +243,7 @@ public class DanaRv2ExecutionService extends AbstractDanaRExecutionService { long now = System.currentTimeMillis(); if (danaRPump.lastSettingsRead + 60 * 60 * 1000L < now || !pump.isInitialized()) { - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.gettingpumpsettings))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.gettingpumpsettings))); mSerialIOThread.sendMessage(new MsgSettingShippingInfo()); mSerialIOThread.sendMessage(new MsgSettingActiveProfile()); mSerialIOThread.sendMessage(new MsgSettingMeal()); @@ -244,15 +260,15 @@ public class DanaRv2ExecutionService extends AbstractDanaRExecutionService { loadEvents(); - MainApp.bus().post(new EventDanaRNewStatus()); - MainApp.bus().post(new EventInitializationChanged()); + RxBus.INSTANCE.send(new EventDanaRNewStatus()); + RxBus.INSTANCE.send(new EventInitializationChanged()); NSUpload.uploadDeviceStatus(); if (danaRPump.dailyTotalUnits > danaRPump.maxDailyTotalUnits * Constants.dailyLimitWarning) { if (L.isEnabled(L.PUMP)) log.debug("Approaching daily limit: " + danaRPump.dailyTotalUnits + "/" + danaRPump.maxDailyTotalUnits); if (System.currentTimeMillis() > lastApproachingDailyLimit + 30 * 60 * 1000) { Notification reportFail = new Notification(Notification.APPROACHING_DAILY_LIMIT, MainApp.gs(R.string.approachingdailylimit), Notification.URGENT); - MainApp.bus().post(new EventNewNotification(reportFail)); + RxBus.INSTANCE.send(new EventNewNotification(reportFail)); NSUpload.uploadError(MainApp.gs(R.string.approachingdailylimit) + ": " + danaRPump.dailyTotalUnits + "/" + danaRPump.maxDailyTotalUnits + "U"); lastApproachingDailyLimit = System.currentTimeMillis(); } @@ -266,15 +282,15 @@ public class DanaRv2ExecutionService extends AbstractDanaRExecutionService { DanaRPump danaRPump = DanaRPump.getInstance(); if (!isConnected()) return false; if (danaRPump.isTempBasalInProgress) { - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.stoppingtempbasal))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.stoppingtempbasal))); mSerialIOThread.sendMessage(new MsgSetTempBasalStop()); SystemClock.sleep(500); } - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.settingtempbasal))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.settingtempbasal))); mSerialIOThread.sendMessage(new MsgSetTempBasalStart(percent, durationInHours)); mSerialIOThread.sendMessage(new MsgStatusTempBasal_v2()); loadEvents(); - MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.DISCONNECTING)); + RxBus.INSTANCE.send(new EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTING)); return true; } @@ -282,15 +298,15 @@ public class DanaRv2ExecutionService extends AbstractDanaRExecutionService { DanaRPump danaRPump = DanaRPump.getInstance(); if (!isConnected()) return false; if (danaRPump.isTempBasalInProgress) { - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.stoppingtempbasal))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.stoppingtempbasal))); mSerialIOThread.sendMessage(new MsgSetTempBasalStop()); SystemClock.sleep(500); } - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.settingtempbasal))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.settingtempbasal))); mSerialIOThread.sendMessage(new MsgSetAPSTempBasalStart_v2(percent)); mSerialIOThread.sendMessage(new MsgStatusTempBasal_v2()); loadEvents(); - MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.DISCONNECTING)); + RxBus.INSTANCE.send(new EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTING)); return true; } @@ -303,45 +319,45 @@ public class DanaRv2ExecutionService extends AbstractDanaRExecutionService { if (!isConnected()) return false; if (danaRPump.isTempBasalInProgress) { - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.stoppingtempbasal))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.stoppingtempbasal))); mSerialIOThread.sendMessage(new MsgSetTempBasalStop()); SystemClock.sleep(500); } - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.settingtempbasal))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.settingtempbasal))); mSerialIOThread.sendMessage(new MsgSetAPSTempBasalStart_v2(percent, durationInMinutes == 15, durationInMinutes == 30)); mSerialIOThread.sendMessage(new MsgStatusTempBasal_v2()); loadEvents(); - MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.DISCONNECTING)); + RxBus.INSTANCE.send(new EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTING)); return true; } public boolean tempBasalStop() { if (!isConnected()) return false; - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.stoppingtempbasal))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.stoppingtempbasal))); mSerialIOThread.sendMessage(new MsgSetTempBasalStop()); mSerialIOThread.sendMessage(new MsgStatusTempBasal_v2()); loadEvents(); - MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.DISCONNECTING)); + RxBus.INSTANCE.send(new EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTING)); return true; } public boolean extendedBolus(double insulin, int durationInHalfHours) { if (!isConnected()) return false; - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.settingextendedbolus))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.settingextendedbolus))); mSerialIOThread.sendMessage(new MsgSetExtendedBolusStart(insulin, (byte) (durationInHalfHours & 0xFF))); mSerialIOThread.sendMessage(new MsgStatusBolusExtended_v2()); loadEvents(); - MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.DISCONNECTING)); + RxBus.INSTANCE.send(new EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTING)); return true; } public boolean extendedBolusStop() { if (!isConnected()) return false; - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.stoppingextendedbolus))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.stoppingextendedbolus))); mSerialIOThread.sendMessage(new MsgSetExtendedBolusStop()); mSerialIOThread.sendMessage(new MsgStatusBolusExtended_v2()); loadEvents(); - MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.DISCONNECTING)); + RxBus.INSTANCE.send(new EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTING)); return true; } @@ -349,7 +365,7 @@ public class DanaRv2ExecutionService extends AbstractDanaRExecutionService { if (!isConnected()) return false; if (BolusProgressDialog.stopPressed) return false; - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.startingbolus))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.startingbolus))); mBolusingTreatment = t; final int preferencesSpeed = SP.getInt(R.string.key_danars_bolusspeed, 0); MessageBase start; @@ -387,9 +403,9 @@ public class DanaRv2ExecutionService extends AbstractDanaRExecutionService { } } - final EventOverviewBolusProgress bolusingEvent = EventOverviewBolusProgress.getInstance(); - bolusingEvent.t = t; - bolusingEvent.percent = 99; + final EventOverviewBolusProgress bolusingEvent = EventOverviewBolusProgress.INSTANCE; + bolusingEvent.setT(t); + bolusingEvent.setPercent(99); mBolusingTreatment = null; int speed = 12; @@ -408,8 +424,8 @@ public class DanaRv2ExecutionService extends AbstractDanaRExecutionService { long expectedEnd = bolusStart + bolusDurationInMSec + 2000; while (System.currentTimeMillis() < expectedEnd) { long waitTime = expectedEnd - System.currentTimeMillis(); - bolusingEvent.status = String.format(MainApp.gs(R.string.waitingforestimatedbolusend), waitTime / 1000); - MainApp.bus().post(bolusingEvent); + bolusingEvent.setStatus(String.format(MainApp.gs(R.string.waitingforestimatedbolusend), waitTime / 1000)); + RxBus.INSTANCE.send(bolusingEvent); SystemClock.sleep(1000); } // do not call loadEvents() directly, reconnection may be needed @@ -417,10 +433,10 @@ public class DanaRv2ExecutionService extends AbstractDanaRExecutionService { @Override public void run() { // load last bolus status - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.gettingbolusstatus))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.gettingbolusstatus))); mSerialIOThread.sendMessage(new MsgStatus()); - bolusingEvent.percent = 100; - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.disconnecting))); + bolusingEvent.setPercent(100); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.disconnecting))); } }); return !start.failed; @@ -455,7 +471,7 @@ public class DanaRv2ExecutionService extends AbstractDanaRExecutionService { public PumpEnactResult loadEvents() { DanaRPump danaRPump = DanaRPump.getInstance(); - if (!MainApp.getSpecificPlugin(DanaRv2Plugin.class).isInitialized()) { + if (!DanaRv2Plugin.getPlugin().isInitialized()) { PumpEnactResult result = new PumpEnactResult().success(false); result.comment = "pump not initialized"; return result; @@ -485,7 +501,7 @@ public class DanaRv2ExecutionService extends AbstractDanaRExecutionService { public boolean updateBasalsInPump(final Profile profile) { DanaRPump danaRPump = DanaRPump.getInstance(); if (!isConnected()) return false; - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.updatingbasalrates))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.updatingbasalrates))); double[] basal = DanaRPump.getInstance().buildDanaRProfileRecord(profile); MsgSetBasalProfile msgSet = new MsgSetBasalProfile((byte) 0, basal); mSerialIOThread.sendMessage(msgSet); @@ -493,7 +509,7 @@ public class DanaRv2ExecutionService extends AbstractDanaRExecutionService { mSerialIOThread.sendMessage(msgActivate); danaRPump.lastSettingsRead = 0; // force read full settings getPumpStatus(); - MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.DISCONNECTING)); + RxBus.INSTANCE.send(new EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTING)); return true; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/EventLocalInsightUpdateGUI.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/EventLocalInsightUpdateGUI.java deleted file mode 100644 index 89b001891c..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/EventLocalInsightUpdateGUI.java +++ /dev/null @@ -1,6 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.insight; - -import info.nightscout.androidaps.events.EventUpdateGui; - -public class EventLocalInsightUpdateGUI extends EventUpdateGui { -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/InsightAlertService.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/InsightAlertService.java index d851389aaf..26555898c0 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/InsightAlertService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/InsightAlertService.java @@ -15,7 +15,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Vibrator; -import android.support.annotation.Nullable; +import androidx.annotation.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/LocalInsightFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/LocalInsightFragment.java index 26f5180f47..3464a1803a 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/LocalInsightFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/LocalInsightFragment.java @@ -3,8 +3,6 @@ package info.nightscout.androidaps.plugins.pump.insight; import android.os.Bundle; import android.os.Handler; import android.os.Looper; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -12,14 +10,16 @@ import android.widget.Button; import android.widget.LinearLayout; import android.widget.TextView; -import com.squareup.otto.Subscribe; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; import java.util.ArrayList; import java.util.List; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; -import info.nightscout.androidaps.plugins.common.SubscriberFragment; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.pump.insight.app_layer.parameter_blocks.TBROverNotificationBlock; import info.nightscout.androidaps.plugins.pump.insight.descriptors.ActiveBasalRate; @@ -28,11 +28,16 @@ import info.nightscout.androidaps.plugins.pump.insight.descriptors.ActiveTBR; import info.nightscout.androidaps.plugins.pump.insight.descriptors.CartridgeStatus; import info.nightscout.androidaps.plugins.pump.insight.descriptors.InsightState; import info.nightscout.androidaps.plugins.pump.insight.descriptors.TotalDailyDose; +import info.nightscout.androidaps.plugins.pump.insight.events.EventLocalInsightUpdateGUI; import info.nightscout.androidaps.queue.Callback; import info.nightscout.androidaps.utils.DateUtil; import info.nightscout.androidaps.utils.DecimalFormatter; +import info.nightscout.androidaps.utils.FabricPrivacy; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.CompositeDisposable; -public class LocalInsightFragment extends SubscriberFragment implements View.OnClickListener { +public class LocalInsightFragment extends Fragment implements View.OnClickListener { + private CompositeDisposable disposable = new CompositeDisposable(); private static final boolean ENABLE_OPERATING_MODE_BUTTON = false; @@ -61,6 +66,23 @@ public class LocalInsightFragment extends SubscriberFragment implements View.OnC return view; } + @Override + public synchronized void onResume() { + super.onResume(); + disposable.add(RxBus.INSTANCE + .toObservable(EventLocalInsightUpdateGUI.class) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(event -> updateGUI(), FabricPrivacy::logException) + ); + updateGUI(); + } + + @Override + public synchronized void onPause() { + super.onPause(); + disposable.clear(); + } + @Override public synchronized void onDestroyView() { super.onDestroyView(); @@ -121,12 +143,6 @@ public class LocalInsightFragment extends SubscriberFragment implements View.OnC } } - @Subscribe - public void onUpdateGUIEvent(EventLocalInsightUpdateGUI event) { - updateGUI(); - } - - @Override protected void updateGUI() { if (!viewsCreated) return; statusItemContainer.removeAllViews(); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/LocalInsightPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/LocalInsightPlugin.java index c5b505a3d3..7baf9458c9 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/LocalInsightPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/LocalInsightPlugin.java @@ -8,10 +8,8 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; -import info.nightscout.androidaps.plugins.general.actions.defs.CustomAction; -import info.nightscout.androidaps.plugins.general.actions.defs.CustomActionType; -import info.nightscout.androidaps.plugins.pump.insight.app_layer.parameter_blocks.*; -import info.nightscout.androidaps.plugins.pump.insight.descriptors.*; +import androidx.fragment.app.FragmentActivity; + import org.json.JSONException; import org.json.JSONObject; import org.slf4j.Logger; @@ -45,8 +43,12 @@ import info.nightscout.androidaps.interfaces.PluginType; import info.nightscout.androidaps.interfaces.PumpDescription; import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.bus.RxBus; +import info.nightscout.androidaps.plugins.common.ManufacturerType; import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; +import info.nightscout.androidaps.plugins.general.actions.defs.CustomAction; +import info.nightscout.androidaps.plugins.general.actions.defs.CustomActionType; import info.nightscout.androidaps.plugins.general.nsclient.NSUpload; import info.nightscout.androidaps.plugins.general.nsclient.UploadQueue; import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification; @@ -73,6 +75,14 @@ import info.nightscout.androidaps.plugins.pump.insight.app_layer.history.history import info.nightscout.androidaps.plugins.pump.insight.app_layer.history.history_events.StartOfTBREvent; import info.nightscout.androidaps.plugins.pump.insight.app_layer.history.history_events.TotalDailyDoseEvent; import info.nightscout.androidaps.plugins.pump.insight.app_layer.history.history_events.TubeFilledEvent; +import info.nightscout.androidaps.plugins.pump.insight.app_layer.parameter_blocks.ActiveBRProfileBlock; +import info.nightscout.androidaps.plugins.pump.insight.app_layer.parameter_blocks.BRProfile1Block; +import info.nightscout.androidaps.plugins.pump.insight.app_layer.parameter_blocks.BRProfileBlock; +import info.nightscout.androidaps.plugins.pump.insight.app_layer.parameter_blocks.FactoryMinBasalAmountBlock; +import info.nightscout.androidaps.plugins.pump.insight.app_layer.parameter_blocks.FactoryMinBolusAmountBlock; +import info.nightscout.androidaps.plugins.pump.insight.app_layer.parameter_blocks.MaxBasalAmountBlock; +import info.nightscout.androidaps.plugins.pump.insight.app_layer.parameter_blocks.MaxBolusAmountBlock; +import info.nightscout.androidaps.plugins.pump.insight.app_layer.parameter_blocks.TBROverNotificationBlock; import info.nightscout.androidaps.plugins.pump.insight.app_layer.remote_control.CancelBolusMessage; import info.nightscout.androidaps.plugins.pump.insight.app_layer.remote_control.CancelTBRMessage; import info.nightscout.androidaps.plugins.pump.insight.app_layer.remote_control.ChangeTBRMessage; @@ -96,6 +106,20 @@ import info.nightscout.androidaps.plugins.pump.insight.connection_service.Insigh import info.nightscout.androidaps.plugins.pump.insight.database.InsightBolusID; import info.nightscout.androidaps.plugins.pump.insight.database.InsightHistoryOffset; import info.nightscout.androidaps.plugins.pump.insight.database.InsightPumpID; +import info.nightscout.androidaps.plugins.pump.insight.descriptors.ActiveBasalRate; +import info.nightscout.androidaps.plugins.pump.insight.descriptors.ActiveBolus; +import info.nightscout.androidaps.plugins.pump.insight.descriptors.ActiveTBR; +import info.nightscout.androidaps.plugins.pump.insight.descriptors.AlertType; +import info.nightscout.androidaps.plugins.pump.insight.descriptors.BasalProfile; +import info.nightscout.androidaps.plugins.pump.insight.descriptors.BasalProfileBlock; +import info.nightscout.androidaps.plugins.pump.insight.descriptors.BatteryStatus; +import info.nightscout.androidaps.plugins.pump.insight.descriptors.BolusType; +import info.nightscout.androidaps.plugins.pump.insight.descriptors.CartridgeStatus; +import info.nightscout.androidaps.plugins.pump.insight.descriptors.InsightState; +import info.nightscout.androidaps.plugins.pump.insight.descriptors.OperatingMode; +import info.nightscout.androidaps.plugins.pump.insight.descriptors.PumpTime; +import info.nightscout.androidaps.plugins.pump.insight.descriptors.TotalDailyDose; +import info.nightscout.androidaps.plugins.pump.insight.events.EventLocalInsightUpdateGUI; import info.nightscout.androidaps.plugins.pump.insight.exceptions.InsightException; import info.nightscout.androidaps.plugins.pump.insight.exceptions.app_layer_errors.AppLayerErrorException; import info.nightscout.androidaps.plugins.pump.insight.exceptions.app_layer_errors.NoActiveTBRToCanceLException; @@ -125,8 +149,9 @@ public class LocalInsightPlugin extends PluginBase implements PumpInterface, Con } else if (binder instanceof InsightAlertService.LocalBinder) { alertService = ((InsightAlertService.LocalBinder) binder).getService(); } - if (connectionService != null && alertService != null) - MainApp.bus().post(new EventInitializationChanged()); + if (connectionService != null && alertService != null) { + RxBus.INSTANCE.send(new EventInitializationChanged()); + } } @Override @@ -168,7 +193,8 @@ public class LocalInsightPlugin extends PluginBase implements PumpInterface, Con .mainType(PluginType.PUMP) .description(R.string.description_pump_insight_local) .fragmentClass(LocalInsightFragment.class.getName()) - .preferencesId(R.xml.pref_insight_local)); + .preferencesId(MainApp.instance().getPackageName().equals("info.nightscout.androidaps") + ? R.xml.pref_insight_local_full : R.xml.pref_insight_local_pumpcontrol)); pumpDescription = new PumpDescription(); pumpDescription.setPumpDescription(PumpType.AccuChekInsightBluetooth); @@ -227,6 +253,11 @@ public class LocalInsightPlugin extends PluginBase implements PumpInterface, Con MainApp.instance().unbindService(serviceConnection); } + @Override + public void switchAllowed(boolean newState, FragmentActivity activity, PluginType type) { + confirmPumpPluginActivation(newState, activity, type); + } + @Override public boolean isInitialized() { return connectionService != null && alertService != null && connectionService.isPaired(); @@ -327,7 +358,7 @@ public class LocalInsightPlugin extends PluginBase implements PumpInterface, Con setDateTimeMessage.setPumpTime(pumpTime); connectionService.requestMessage(setDateTimeMessage).await(); Notification notification = new Notification(Notification.INSIGHT_DATE_TIME_UPDATED, MainApp.gs(R.string.pump_time_updated), Notification.INFO, 60); - MainApp.bus().post(new EventNewNotification(notification)); + RxBus.INSTANCE.send(new EventNewNotification(notification)); } } @@ -395,8 +426,8 @@ public class LocalInsightPlugin extends PluginBase implements PumpInterface, Con } lastUpdated = System.currentTimeMillis(); new Handler(Looper.getMainLooper()).post(() -> { - MainApp.bus().post(new EventLocalInsightUpdateGUI()); - MainApp.bus().post(new EventRefreshOverview("LocalInsightPlugin::fetchStatus")); + RxBus.INSTANCE.send(new EventLocalInsightUpdateGUI()); + RxBus.INSTANCE.send(new EventRefreshOverview("LocalInsightPlugin::fetchStatus")); }); } @@ -404,7 +435,7 @@ public class LocalInsightPlugin extends PluginBase implements PumpInterface, Con maximumBolusAmount = ParameterBlockUtil.readParameterBlock(connectionService, Service.CONFIGURATION, MaxBolusAmountBlock.class).getAmountLimitation(); maximumBasalAmount = ParameterBlockUtil.readParameterBlock(connectionService, Service.CONFIGURATION, MaxBasalAmountBlock.class).getAmountLimitation(); minimumBolusAmount = ParameterBlockUtil.readParameterBlock(connectionService, Service.CONFIGURATION, FactoryMinBolusAmountBlock.class).getAmountLimitation(); - minimumBasalAmount = ParameterBlockUtil.readParameterBlock(connectionService, Service.CONFIGURATION, FactoryMinBolusAmountBlock.class).getAmountLimitation(); + minimumBasalAmount = ParameterBlockUtil.readParameterBlock(connectionService, Service.CONFIGURATION, FactoryMinBasalAmountBlock.class).getAmountLimitation(); this.pumpDescription.basalMaximumRate = maximumBasalAmount; this.pumpDescription.basalMinimumRate = minimumBasalAmount; limitsFetched = true; @@ -413,11 +444,11 @@ public class LocalInsightPlugin extends PluginBase implements PumpInterface, Con @Override public PumpEnactResult setNewBasalProfile(Profile profile) { PumpEnactResult result = new PumpEnactResult(); - MainApp.bus().post(new EventDismissNotification(Notification.PROFILE_NOT_SET_NOT_INITIALIZED)); + RxBus.INSTANCE.send(new EventDismissNotification(Notification.PROFILE_NOT_SET_NOT_INITIALIZED)); List profileBlocks = new ArrayList<>(); for (int i = 0; i < profile.getBasalValues().length; i++) { - Profile.BasalValue basalValue = profile.getBasalValues()[i]; - Profile.BasalValue nextValue = null; + Profile.ProfileValue basalValue = profile.getBasalValues()[i]; + Profile.ProfileValue nextValue = null; if (profile.getBasalValues().length > i + 1) nextValue = profile.getBasalValues()[i + 1]; BasalProfileBlock profileBlock = new BasalProfileBlock(); @@ -433,9 +464,9 @@ public class LocalInsightPlugin extends PluginBase implements PumpInterface, Con BRProfileBlock profileBlock = new BRProfile1Block(); profileBlock.setProfileBlocks(profileBlocks); ParameterBlockUtil.writeConfigurationBlock(connectionService, profileBlock); - MainApp.bus().post(new EventDismissNotification(Notification.FAILED_UDPATE_PROFILE)); + RxBus.INSTANCE.send(new EventDismissNotification(Notification.FAILED_UDPATE_PROFILE)); Notification notification = new Notification(Notification.PROFILE_SET_OK, MainApp.gs(R.string.profile_set_ok), Notification.INFO, 60); - MainApp.bus().post(new EventNewNotification(notification)); + RxBus.INSTANCE.send(new EventNewNotification(notification)); result.success = true; result.enacted = true; result.comment = MainApp.gs(R.string.virtualpump_resultok); @@ -447,17 +478,17 @@ public class LocalInsightPlugin extends PluginBase implements PumpInterface, Con } catch (AppLayerErrorException e) { log.info("Exception while setting profile: " + e.getClass().getCanonicalName() + " (" + e.getErrorCode() + ")"); Notification notification = new Notification(Notification.FAILED_UDPATE_PROFILE, MainApp.gs(R.string.failedupdatebasalprofile), Notification.URGENT); - MainApp.bus().post(new EventNewNotification(notification)); + RxBus.INSTANCE.send(new EventNewNotification(notification)); result.comment = ExceptionTranslator.getString(e); } catch (InsightException e) { log.info("Exception while setting profile: " + e.getClass().getCanonicalName()); Notification notification = new Notification(Notification.FAILED_UDPATE_PROFILE, MainApp.gs(R.string.failedupdatebasalprofile), Notification.URGENT); - MainApp.bus().post(new EventNewNotification(notification)); + RxBus.INSTANCE.send(new EventNewNotification(notification)); result.comment = ExceptionTranslator.getString(e); } catch (Exception e) { log.error("Exception while setting profile", e); Notification notification = new Notification(Notification.FAILED_UDPATE_PROFILE, MainApp.gs(R.string.failedupdatebasalprofile), Notification.URGENT); - MainApp.bus().post(new EventNewNotification(notification)); + RxBus.INSTANCE.send(new EventNewNotification(notification)); result.comment = ExceptionTranslator.getString(e); } return result; @@ -470,8 +501,8 @@ public class LocalInsightPlugin extends PluginBase implements PumpInterface, Con if (activeBasalProfile != BasalProfile.PROFILE_1) return false; for (int i = 0; i < profileBlocks.size(); i++) { BasalProfileBlock profileBlock = profileBlocks.get(i); - Profile.BasalValue basalValue = profile.getBasalValues()[i]; - Profile.BasalValue nextValue = null; + Profile.ProfileValue basalValue = profile.getBasalValues()[i]; + Profile.ProfileValue nextValue = null; if (profile.getBasalValues().length > i + 1) nextValue = profile.getBasalValues()[i + 1]; if (profileBlock.getDuration() * 60 != (nextValue != null ? nextValue.timeAsSeconds : 24 * 60 * 60) - basalValue.timeAsSeconds) @@ -526,11 +557,11 @@ public class LocalInsightPlugin extends PluginBase implements PumpInterface, Con result.enacted = true; Treatment t = new Treatment(); t.isSMB = detailedBolusInfo.isSMB; - final EventOverviewBolusProgress bolusingEvent = EventOverviewBolusProgress.getInstance(); - bolusingEvent.t = t; - bolusingEvent.status = MainApp.gs(R.string.insight_delivered, 0d, insulin); - bolusingEvent.percent = 0; - MainApp.bus().post(bolusingEvent); + final EventOverviewBolusProgress bolusingEvent = EventOverviewBolusProgress.INSTANCE; + bolusingEvent.setT(t); + bolusingEvent.setStatus(MainApp.gs(R.string.insight_delivered, 0d, insulin)); + bolusingEvent.setPercent(0); + RxBus.INSTANCE.send(bolusingEvent); int trials = 0; InsightBolusID insightBolusID = new InsightBolusID(); insightBolusID.bolusID = bolusID; @@ -557,18 +588,18 @@ public class LocalInsightPlugin extends PluginBase implements PumpInterface, Con } if (activeBolus != null) { trials = -1; - int percentBefore = bolusingEvent.percent; - bolusingEvent.percent = (int) (100D / activeBolus.getInitialAmount() * (activeBolus.getInitialAmount() - activeBolus.getRemainingAmount())); - bolusingEvent.status = MainApp.gs(R.string.insight_delivered, activeBolus.getInitialAmount() - activeBolus.getRemainingAmount(), activeBolus.getInitialAmount()); - if (percentBefore != bolusingEvent.percent) - MainApp.bus().post(bolusingEvent); + int percentBefore = bolusingEvent.getPercent(); + bolusingEvent.setPercent((int) (100D / activeBolus.getInitialAmount() * (activeBolus.getInitialAmount() - activeBolus.getRemainingAmount()))); + bolusingEvent.setStatus(MainApp.gs(R.string.insight_delivered, activeBolus.getInitialAmount() - activeBolus.getRemainingAmount(), activeBolus.getInitialAmount())); + if (percentBefore != bolusingEvent.getPercent()) + RxBus.INSTANCE.send(bolusingEvent); } else { synchronized ($bolusLock) { if (bolusCancelled || trials == -1 || trials++ >= 5) { if (!bolusCancelled) { - bolusingEvent.status = MainApp.gs(R.string.insight_delivered, insulin, insulin); - bolusingEvent.percent = 100; - MainApp.bus().post(bolusingEvent); + bolusingEvent.setStatus(MainApp.gs(R.string.insight_delivered, insulin, insulin)); + bolusingEvent.setPercent(100); + RxBus.INSTANCE.send(bolusingEvent); } break; } @@ -844,7 +875,6 @@ public class LocalInsightPlugin extends PluginBase implements PumpInterface, Con if (extendedBolus != null) { extendedBolus.durationInMinutes = (int) ((System.currentTimeMillis() - extendedBolus.date) / 60000); if (extendedBolus.durationInMinutes <= 0) { - ; final String _id = extendedBolus._id; if (NSUpload.isIdValid(_id)) NSUpload.removeCareportalEntryFromNS(_id); @@ -944,8 +974,18 @@ public class LocalInsightPlugin extends PluginBase implements PumpInterface, Con } @Override - public String deviceID() { - if (connectionService == null || alertService == null) return null; + public ManufacturerType manufacturer() { + return ManufacturerType.Roche; + } + + @Override + public PumpType model() { + return PumpType.AccuChekInsightBluetooth; + } + + @Override + public String serialNumber() { + if (connectionService == null || alertService == null) return "Unknown"; return connectionService.getPumpSystemIdentification().getSerialNumber(); } @@ -1127,7 +1167,7 @@ public class LocalInsightPlugin extends PluginBase implements PumpInterface, Con } catch (Exception e) { log.error("Exception while reading history", e); } - new Handler(Looper.getMainLooper()).post(() -> MainApp.bus().post(new EventRefreshOverview("LocalInsightPlugin::readHistory"))); + new Handler(Looper.getMainLooper()).post(() -> RxBus.INSTANCE.send(new EventRefreshOverview("LocalInsightPlugin::readHistory"))); } private void processHistoryEvents(String serial, List historyEvents) { @@ -1549,7 +1589,7 @@ public class LocalInsightPlugin extends PluginBase implements PumpInterface, Con public void onStateChanged(InsightState state) { if (state == InsightState.CONNECTED) { statusLoaded = false; - new Handler(Looper.getMainLooper()).post(() -> MainApp.bus().post(new EventDismissNotification(Notification.INSIGHT_TIMEOUT_DURING_HANDSHAKE))); + new Handler(Looper.getMainLooper()).post(() -> RxBus.INSTANCE.send(new EventDismissNotification(Notification.INSIGHT_TIMEOUT_DURING_HANDSHAKE))); } else if (state == InsightState.NOT_PAIRED) { connectionService.withdrawConnectionRequest(this); statusLoaded = false; @@ -1562,9 +1602,9 @@ public class LocalInsightPlugin extends PluginBase implements PumpInterface, Con activeTBR = null; activeBoluses = null; tbrOverNotificationBlock = null; - new Handler(Looper.getMainLooper()).post(() -> MainApp.bus().post(new EventRefreshOverview("LocalInsightPlugin::onStateChanged"))); + new Handler(Looper.getMainLooper()).post(() -> RxBus.INSTANCE.send(new EventRefreshOverview("LocalInsightPlugin::onStateChanged"))); } - new Handler(Looper.getMainLooper()).post(() -> MainApp.bus().post(new EventLocalInsightUpdateGUI())); + new Handler(Looper.getMainLooper()).post(() -> RxBus.INSTANCE.send(new EventLocalInsightUpdateGUI())); } @Override @@ -1575,11 +1615,17 @@ public class LocalInsightPlugin extends PluginBase implements PumpInterface, Con @Override public void onTimeoutDuringHandshake() { Notification notification = new Notification(Notification.INSIGHT_TIMEOUT_DURING_HANDSHAKE, MainApp.gs(R.string.timeout_during_handshake), Notification.URGENT); - new Handler(Looper.getMainLooper()).post(() -> MainApp.bus().post(new EventNewNotification(notification))); + new Handler(Looper.getMainLooper()).post(() -> RxBus.INSTANCE.send(new EventNewNotification(notification))); } @Override public boolean canHandleDST() { return true; } + + @Override + public void timeDateOrTimeZoneChanged() { + + } + } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/activities/InsightAlertActivity.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/activities/InsightAlertActivity.java index c40cb99ed6..77be3ab03a 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/activities/InsightAlertActivity.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/activities/InsightAlertActivity.java @@ -5,8 +5,6 @@ import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.IBinder; -import android.support.v4.content.ContextCompat; -import android.support.v7.app.AppCompatActivity; import android.text.Html; import android.view.View; import android.view.WindowManager; @@ -14,14 +12,17 @@ import android.widget.Button; import android.widget.ImageView; import android.widget.TextView; +import androidx.core.content.ContextCompat; + import java.text.DecimalFormat; import info.nightscout.androidaps.R; +import info.nightscout.androidaps.activities.NoSplashAppCompatActivity; import info.nightscout.androidaps.plugins.pump.insight.InsightAlertService; import info.nightscout.androidaps.plugins.pump.insight.descriptors.Alert; import info.nightscout.androidaps.plugins.pump.insight.descriptors.AlertStatus; -public class InsightAlertActivity extends AppCompatActivity { +public class InsightAlertActivity extends NoSplashAppCompatActivity { private Alert alert; private InsightAlertService alertService; @@ -40,7 +41,7 @@ public class InsightAlertActivity extends AppCompatActivity { alertService.setAlertActivity(InsightAlertActivity.this); alert = alertService.getAlert(); if (alert == null) finish(); - update(alert); + else update(alert); } @Override @@ -50,7 +51,7 @@ public class InsightAlertActivity extends AppCompatActivity { }; @Override - protected void onCreate(Bundle savedInstanceState) { + public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_insight_alert); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/activities/InsightPairingActivity.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/activities/InsightPairingActivity.java index 7ea52ed271..71450aaa0c 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/activities/InsightPairingActivity.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/activities/InsightPairingActivity.java @@ -10,11 +10,6 @@ import android.content.IntentFilter; import android.content.ServiceConnection; import android.os.Bundle; import android.os.IBinder; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v7.app.AppCompatActivity; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -22,15 +17,21 @@ import android.widget.Button; import android.widget.LinearLayout; import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + import java.util.ArrayList; import java.util.List; import info.nightscout.androidaps.R; +import info.nightscout.androidaps.activities.NoSplashAppCompatActivity; import info.nightscout.androidaps.plugins.pump.insight.connection_service.InsightConnectionService; import info.nightscout.androidaps.plugins.pump.insight.descriptors.InsightState; import info.nightscout.androidaps.plugins.pump.insight.utils.ExceptionTranslator; -public class InsightPairingActivity extends AppCompatActivity implements InsightConnectionService.StateCallback, View.OnClickListener, InsightConnectionService.ExceptionCallback { +public class InsightPairingActivity extends NoSplashAppCompatActivity implements InsightConnectionService.StateCallback, View.OnClickListener, InsightConnectionService.ExceptionCallback { private boolean scanning; private LinearLayout deviceSearchSection; @@ -66,7 +67,7 @@ public class InsightPairingActivity extends AppCompatActivity implements Insight }; @Override - protected void onCreate(@Nullable Bundle savedInstanceState) { + public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_insight_pairing); @@ -160,20 +161,25 @@ public class InsightPairingActivity extends AppCompatActivity implements Insight private void startBLScan() { if (!scanning) { BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); - if (!bluetoothAdapter.isEnabled()) bluetoothAdapter.enable(); - IntentFilter intentFilter = new IntentFilter(); - intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED); - intentFilter.addAction(BluetoothDevice.ACTION_FOUND); - registerReceiver(broadcastReceiver, intentFilter); - bluetoothAdapter.startDiscovery(); - scanning = true; + if (bluetoothAdapter != null) { + if (!bluetoothAdapter.isEnabled()) bluetoothAdapter.enable(); + IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED); + intentFilter.addAction(BluetoothDevice.ACTION_FOUND); + registerReceiver(broadcastReceiver, intentFilter); + bluetoothAdapter.startDiscovery(); + scanning = true; + } } } private void stopBLScan() { if (scanning) { unregisterReceiver(broadcastReceiver); - BluetoothAdapter.getDefaultAdapter().cancelDiscovery(); + BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); + if (bluetoothAdapter != null) { + bluetoothAdapter.cancelDiscovery(); + } scanning = false; } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/activities/InsightPairingInformationActivity.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/activities/InsightPairingInformationActivity.java index 8580b30dca..e20fb2c94f 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/activities/InsightPairingInformationActivity.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/activities/InsightPairingInformationActivity.java @@ -5,15 +5,16 @@ import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.IBinder; -import android.support.annotation.Nullable; -import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.TextView; +import androidx.annotation.Nullable; + import info.nightscout.androidaps.R; +import info.nightscout.androidaps.activities.NoSplashAppCompatActivity; import info.nightscout.androidaps.plugins.pump.insight.connection_service.InsightConnectionService; -public class InsightPairingInformationActivity extends AppCompatActivity { +public class InsightPairingInformationActivity extends NoSplashAppCompatActivity { private InsightConnectionService connectionService; @@ -57,7 +58,7 @@ public class InsightPairingInformationActivity extends AppCompatActivity { }; @Override - protected void onCreate(@Nullable Bundle savedInstanceState) { + public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_insight_pairing_information); serialNumber = findViewById(R.id.serial_number); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/app_layer/history/HistoryReadingDirection.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/app_layer/history/HistoryReadingDirection.java index 327304eb21..1bf9a2d165 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/app_layer/history/HistoryReadingDirection.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/app_layer/history/HistoryReadingDirection.java @@ -3,6 +3,6 @@ package info.nightscout.androidaps.plugins.pump.insight.app_layer.history; public enum HistoryReadingDirection { FORWARD, - BACKWARD; + BACKWARD } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/connection_service/InsightConnectionService.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/connection_service/InsightConnectionService.java index e964c9fa20..cecb631776 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/connection_service/InsightConnectionService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/connection_service/InsightConnectionService.java @@ -8,7 +8,7 @@ import android.content.Intent; import android.os.Binder; import android.os.IBinder; import android.os.PowerManager; -import android.support.annotation.Nullable; +import androidx.annotation.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/descriptors/AlertCategory.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/descriptors/AlertCategory.java index c2355f2b6a..95c39b77a2 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/descriptors/AlertCategory.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/descriptors/AlertCategory.java @@ -5,5 +5,5 @@ public enum AlertCategory { REMINDER, MAINTENANCE, WARNING, - ERROR; + ERROR } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/descriptors/AlertStatus.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/descriptors/AlertStatus.java index b3842edfed..3b85528462 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/descriptors/AlertStatus.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/descriptors/AlertStatus.java @@ -3,5 +3,5 @@ package info.nightscout.androidaps.plugins.pump.insight.descriptors; public enum AlertStatus { ACTIVE, - SNOOZED; + SNOOZED } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/descriptors/AlertType.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/descriptors/AlertType.java index b2c8331a49..92a59bd7fc 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/descriptors/AlertType.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/descriptors/AlertType.java @@ -27,5 +27,5 @@ public enum AlertType { MAINTENANCE_30, ERROR_6, ERROR_10, - ERROR_13; + ERROR_13 } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/descriptors/BasalProfile.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/descriptors/BasalProfile.java index 89cc68cbd5..298c2957dc 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/descriptors/BasalProfile.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/descriptors/BasalProfile.java @@ -6,5 +6,5 @@ public enum BasalProfile { PROFILE_2, PROFILE_3, PROFILE_4, - PROFILE_5; + PROFILE_5 } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/descriptors/BatteryType.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/descriptors/BatteryType.java index 0cd5373c93..e0e1e2b69f 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/descriptors/BatteryType.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/descriptors/BatteryType.java @@ -4,5 +4,5 @@ public enum BatteryType { ALKALI, LITHIUM, - NI_MH; + NI_MH } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/descriptors/BolusType.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/descriptors/BolusType.java index 7af48fbfb7..0625e5d5e7 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/descriptors/BolusType.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/descriptors/BolusType.java @@ -4,5 +4,5 @@ public enum BolusType { STANDARD, EXTENDED, - MULTIWAVE; + MULTIWAVE } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/descriptors/CartridgeType.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/descriptors/CartridgeType.java index 8c2f878387..480e4d2a54 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/descriptors/CartridgeType.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/descriptors/CartridgeType.java @@ -3,5 +3,5 @@ package info.nightscout.androidaps.plugins.pump.insight.descriptors; public enum CartridgeType { PREFILLED, - SELF_FILLED; + SELF_FILLED } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/descriptors/MessagePriority.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/descriptors/MessagePriority.java index 10a47283a3..f24846f514 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/descriptors/MessagePriority.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/descriptors/MessagePriority.java @@ -4,5 +4,5 @@ public enum MessagePriority { NORMAL, HIGHER, - HIGHEST; + HIGHEST } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/descriptors/OperatingMode.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/descriptors/OperatingMode.java index d745394715..85500f50df 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/descriptors/OperatingMode.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/descriptors/OperatingMode.java @@ -4,5 +4,5 @@ public enum OperatingMode { STARTED, STOPPED, - PAUSED; + PAUSED } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/descriptors/SymbolStatus.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/descriptors/SymbolStatus.java index 567e6afb13..b68362692b 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/descriptors/SymbolStatus.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/descriptors/SymbolStatus.java @@ -4,5 +4,5 @@ public enum SymbolStatus { FULL, LOW, - EMPTY; + EMPTY } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/events/EventLocalInsightUpdateGUI.kt b/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/events/EventLocalInsightUpdateGUI.kt new file mode 100644 index 0000000000..d89515fe56 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/events/EventLocalInsightUpdateGUI.kt @@ -0,0 +1,5 @@ +package info.nightscout.androidaps.plugins.pump.insight.events + +import info.nightscout.androidaps.events.EventUpdateGui + +class EventLocalInsightUpdateGUI : EventUpdateGui() diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/satl/PairingStatus.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/satl/PairingStatus.java index 52131e2d1e..899425f279 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/satl/PairingStatus.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/satl/PairingStatus.java @@ -4,5 +4,5 @@ public enum PairingStatus { CONFIRMED, REJECTED, - PENDING; + PENDING } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/satl/SatlError.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/satl/SatlError.java index 7e04018dc8..ac0b4ebebe 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/satl/SatlError.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/satl/SatlError.java @@ -14,5 +14,5 @@ public enum SatlError { WRONG_STATE, INVALID_MESSAGE_TYPE, INVALID_PAYLOAD_LENGTH, - NONE; + NONE } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/utils/ByteBuf.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/utils/ByteBuf.java index 107634acf9..0e5f275949 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/utils/ByteBuf.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/utils/ByteBuf.java @@ -1,8 +1,8 @@ package info.nightscout.androidaps.plugins.pump.insight.utils; -import java.io.UnsupportedEncodingException; import java.math.BigDecimal; import java.math.RoundingMode; +import java.nio.charset.StandardCharsets; public class ByteBuf { @@ -45,13 +45,11 @@ public class ByteBuf { } - public void putBytes(byte b, int count) { for (int i = 0; i < count; i++) bytes[size++] = b; } - public byte[] getBytes(int position, int length) { byte[] copy = new byte[length]; System.arraycopy(bytes, position, copy, 0, length); @@ -68,7 +66,7 @@ public class ByteBuf { return copy; } - public byte[] readBytes() { + byte[] readBytes() { return readBytes(size); } @@ -82,15 +80,14 @@ public class ByteBuf { } - - public byte[] getBytesLE(int position, int length) { + private byte[] getBytesLE(int position, int length) { byte[] copy = new byte[length]; for (int i = 0; i < length; i++) copy[i] = bytes[length - 1 - i + position]; return copy; } - public byte[] getBytesLE(int length) { + private byte[] getBytesLE(int length) { return getBytesLE(0, length); } @@ -100,13 +97,13 @@ public class ByteBuf { return copy; } - public void putBytesLE(byte[] bytes, int length) { + private void putBytesLE(byte[] bytes, int length) { for (int i = 0; i < length; i++) this.bytes[size + length - 1 - i] = bytes[i]; size += length; } - public void putBytesLE(byte[] bytes) { + void putBytesLE(byte[] bytes) { putBytesLE(bytes, bytes.length); } @@ -116,12 +113,11 @@ public class ByteBuf { } - - public short getUInt8(int position) { + private short getUInt8(int position) { return (short) (bytes[position] & 0xFF); } - public short getUInt8() { + private short getUInt8() { return getUInt8(0); } @@ -136,13 +132,12 @@ public class ByteBuf { } - public int getUInt16LE(int position) { return (bytes[position++] & 0xFF | - (bytes[position] & 0xFF) << 8); + (bytes[position] & 0xFF) << 8); } - public int getUInt16LE() { + private int getUInt16LE() { return getUInt16LE(0); } @@ -158,14 +153,13 @@ public class ByteBuf { } - - public double getUInt16Decimal(int position) { + private double getUInt16Decimal(int position) { return new BigDecimal(getUInt16LE(position)) .divide(new BigDecimal(100), 2, RoundingMode.HALF_UP) .doubleValue(); } - public double getUInt16Decimal() { + private double getUInt16Decimal() { return getUInt16Decimal(0); } @@ -183,14 +177,13 @@ public class ByteBuf { } - - public double getUInt32Decimal100(int position) { + private double getUInt32Decimal100(int position) { return new BigDecimal(getUInt32LE(position)) .divide(new BigDecimal(100), 2, RoundingMode.HALF_UP) .doubleValue(); } - public double getUInt32Decimal100() { + private double getUInt32Decimal100() { return getUInt32Decimal100(0); } @@ -208,14 +201,13 @@ public class ByteBuf { } - - public double getUInt32Decimal1000(int position) { + private double getUInt32Decimal1000(int position) { return new BigDecimal(getUInt32LE(position)) .divide(new BigDecimal(1000), 3, RoundingMode.HALF_UP) .doubleValue(); } - public double getUInt32Decimal1000() { + private double getUInt32Decimal1000() { return getUInt32Decimal1000(0); } @@ -233,9 +225,8 @@ public class ByteBuf { } - - public short getShort(int position) { - return (short) (bytes[position++] << 8 | + private short getShort(int position) { + return (short) (bytes[position++] << 8 | bytes[position] & 0xFF); } @@ -255,15 +246,14 @@ public class ByteBuf { } - - public long getUInt32LE(int position) { + private long getUInt32LE(int position) { return ((long) bytes[position++] & 0xFF) | ((long) bytes[position++] & 0xFF) << 8 | ((long) bytes[position++] & 0xFF) << 16 | ((long) bytes[position] & 0xFF) << 24; } - public long getUInt32LE() { + private long getUInt32LE() { return getUInt32LE(0); } @@ -281,18 +271,12 @@ public class ByteBuf { } - - public String getUTF16(int position, int stringLength) { - try { - String string = new String(getBytes(position, stringLength * 2 + 2), "UTF-16LE"); - return string.substring(0, string.indexOf(new String(new char[] {0, 0}))); - } catch (UnsupportedEncodingException e) { - e.printStackTrace(); - } - return null; + private String getUTF16(int position, int stringLength) { + String string = new String(getBytes(position, stringLength * 2 + 2), StandardCharsets.UTF_16LE); + return string.substring(0, string.indexOf(new String(new char[]{0, 0}))); } - public String getUTF16(int stringLength) { + private String getUTF16(int stringLength) { return getUTF16(0, stringLength); } @@ -303,27 +287,17 @@ public class ByteBuf { } public void putUTF16(String string, int stringLength) { - try { - putBytes(string.getBytes("UTF-16LE"), stringLength * 2); - putBytes((byte) 0, 2); - } catch (UnsupportedEncodingException e) { - e.printStackTrace(); - } + putBytes(string.getBytes(StandardCharsets.UTF_16LE), stringLength * 2); + putBytes((byte) 0, 2); } - - public String getASCII(int position, int stringLength) { - try { - String string = new String(getBytes(position, stringLength + 1), "US-ASCII"); - return string.substring(0, string.indexOf(0)); - } catch (UnsupportedEncodingException e) { - e.printStackTrace(); - } - return null; + private String getASCII(int position, int stringLength) { + String string = new String(getBytes(position, stringLength + 1), StandardCharsets.US_ASCII); + return string.substring(0, string.indexOf(0)); } - public String getASCII(int stringLength) { + private String getASCII(int stringLength) { return getASCII(0, stringLength); } @@ -334,16 +308,11 @@ public class ByteBuf { } public void putASCII(String string, int stringLength) { - try { - putBytes(string.getBytes("UTF-16LE"), stringLength * 2); - putBytes((byte) 0, 1); - } catch (UnsupportedEncodingException e) { - e.printStackTrace(); - } + putBytes(string.getBytes(StandardCharsets.UTF_16LE), stringLength * 2); + putBytes((byte) 0, 1); } - public boolean getBoolean(int position) { return getUInt16LE(position) == 75; } @@ -363,7 +332,6 @@ public class ByteBuf { } - public static ByteBuf from(byte[] bytes, int length) { ByteBuf byteBuf = new ByteBuf(length); byteBuf.putBytes(bytes, length); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/mdi/MDIPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/mdi/MDIPlugin.java index 89d1184581..21cac73d5d 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/mdi/MDIPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/mdi/MDIPlugin.java @@ -19,10 +19,13 @@ import info.nightscout.androidaps.interfaces.PluginType; import info.nightscout.androidaps.interfaces.PumpDescription; import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.common.ManufacturerType; import info.nightscout.androidaps.plugins.general.actions.defs.CustomAction; import info.nightscout.androidaps.plugins.general.actions.defs.CustomActionType; +import info.nightscout.androidaps.plugins.pump.common.defs.PumpType; import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; import info.nightscout.androidaps.utils.DateUtil; +import info.nightscout.androidaps.utils.InstanceId; /** @@ -236,8 +239,18 @@ public class MDIPlugin extends PluginBase implements PumpInterface { } @Override - public String deviceID() { - return "MDI"; + public ManufacturerType manufacturer() { + return ManufacturerType.AndroidAPS; + } + + @Override + public PumpType model() { + return PumpType.MDI; + } + + @Override + public String serialNumber() { + return InstanceId.INSTANCE.instanceId(); } @Override @@ -247,7 +260,7 @@ public class MDIPlugin extends PluginBase implements PumpInterface { @Override public String shortStatus(boolean veryShort) { - return deviceID(); + return model().getModel(); } @Override @@ -265,4 +278,10 @@ public class MDIPlugin extends PluginBase implements PumpInterface { return true; } + @Override + public void timeDateOrTimeZoneChanged() { + + } + + } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicFragment.kt new file mode 100644 index 0000000000..69e0e0bbe8 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicFragment.kt @@ -0,0 +1,317 @@ +package info.nightscout.androidaps.plugins.pump.medtronic + +import android.content.Intent +import android.graphics.Color +import android.os.Bundle +import android.os.Handler +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import info.nightscout.androidaps.MainApp +import info.nightscout.androidaps.R +import info.nightscout.androidaps.events.EventExtendedBolusChange +import info.nightscout.androidaps.events.EventPumpStatusChanged +import info.nightscout.androidaps.events.EventTempBasalChange +import info.nightscout.androidaps.logging.L +import info.nightscout.androidaps.plugins.bus.RxBus +import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkUtil +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkError +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkServiceState +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkTargetDevice +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.dialog.RileyLinkStatusActivity +import info.nightscout.androidaps.plugins.pump.medtronic.defs.BatteryType +import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicCommandType +import info.nightscout.androidaps.plugins.pump.medtronic.defs.PumpDeviceState +import info.nightscout.androidaps.plugins.pump.medtronic.dialog.MedtronicHistoryActivity +import info.nightscout.androidaps.plugins.pump.medtronic.driver.MedtronicPumpStatus +import info.nightscout.androidaps.plugins.pump.medtronic.events.EventMedtronicDeviceStatusChange +import info.nightscout.androidaps.plugins.pump.medtronic.events.EventMedtronicPumpConfigurationChanged +import info.nightscout.androidaps.plugins.pump.medtronic.events.EventMedtronicPumpValuesChanged +import info.nightscout.androidaps.plugins.pump.medtronic.events.EventRefreshButtonState +import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil +import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin +import info.nightscout.androidaps.queue.Callback +import info.nightscout.androidaps.queue.events.EventQueueChanged +import info.nightscout.androidaps.utils.* +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.CompositeDisposable +import kotlinx.android.synthetic.main.medtronic_fragment.* +import org.slf4j.LoggerFactory + + +class MedtronicFragment : Fragment() { + private val log = LoggerFactory.getLogger(L.PUMP) + private var disposable: CompositeDisposable = CompositeDisposable() + + private val loopHandler = Handler() + private lateinit var refreshLoop: Runnable + + init { + refreshLoop = Runnable { + activity?.runOnUiThread { updateGUI() } + loopHandler.postDelayed(refreshLoop, T.mins(1).msecs()) + } + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.medtronic_fragment, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + medtronic_pumpstatus.setBackgroundColor(MainApp.gc(R.color.colorInitializingBorder)) + + medtronic_rl_status.text = MainApp.gs(RileyLinkServiceState.NotStarted.getResourceId(RileyLinkTargetDevice.MedtronicPump)) + + medtronic_pump_status.setTextColor(Color.WHITE) + medtronic_pump_status.text = "{fa-bed}" + + medtronic_history.setOnClickListener { + if (MedtronicUtil.getPumpStatus().verifyConfiguration()) { + startActivity(Intent(context, MedtronicHistoryActivity::class.java)) + } else { + MedtronicUtil.displayNotConfiguredDialog(context) + } + } + + medtronic_refresh.setOnClickListener { + if (!MedtronicUtil.getPumpStatus().verifyConfiguration()) { + MedtronicUtil.displayNotConfiguredDialog(context) + } else { + medtronic_refresh.isEnabled = false + MedtronicPumpPlugin.getPlugin().resetStatusState() + ConfigBuilderPlugin.getPlugin().commandQueue.readStatus("Clicked refresh", object : Callback() { + override fun run() { + activity?.runOnUiThread { medtronic_refresh?.isEnabled = true } + } + }) + } + } + + medtronic_stats.setOnClickListener { + if (MedtronicUtil.getPumpStatus().verifyConfiguration()) { + startActivity(Intent(context, RileyLinkStatusActivity::class.java)) + } else { + MedtronicUtil.displayNotConfiguredDialog(context) + } + } + } + + @Synchronized + override fun onResume() { + super.onResume() + loopHandler.postDelayed(refreshLoop, T.mins(1).msecs()) + disposable += RxBus + .toObservable(EventRefreshButtonState::class.java) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ medtronic_refresh.isEnabled = it.newState }, { FabricPrivacy.logException(it) }) + disposable += RxBus + .toObservable(EventMedtronicDeviceStatusChange::class.java) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ + if (L.isEnabled(L.PUMP)) + log.info("onStatusEvent(EventMedtronicDeviceStatusChange): {}", it) + setDeviceStatus() + }, { FabricPrivacy.logException(it) }) + disposable += RxBus + .toObservable(EventMedtronicPumpValuesChanged::class.java) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ updateGUI() }, { FabricPrivacy.logException(it) }) + disposable += RxBus + .toObservable(EventExtendedBolusChange::class.java) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ updateGUI() }, { FabricPrivacy.logException(it) }) + disposable += RxBus + .toObservable(EventTempBasalChange::class.java) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ updateGUI() }, { FabricPrivacy.logException(it) }) + disposable += RxBus + .toObservable(EventMedtronicPumpConfigurationChanged::class.java) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ + if (L.isEnabled(L.PUMP)) + log.debug("EventMedtronicPumpConfigurationChanged triggered") + MedtronicUtil.getPumpStatus().verifyConfiguration() + updateGUI() + }, { FabricPrivacy.logException(it) }) + disposable += RxBus + .toObservable(EventPumpStatusChanged::class.java) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ updateGUI() }, { FabricPrivacy.logException(it) }) + disposable += RxBus + .toObservable(EventQueueChanged::class.java) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ updateGUI() }, { FabricPrivacy.logException(it) }) + + updateGUI() + } + + @Synchronized + override fun onPause() { + super.onPause() + disposable.clear() + loopHandler.removeCallbacks(refreshLoop) + } + + @Synchronized + private fun setDeviceStatus() { + val pumpStatus: MedtronicPumpStatus = MedtronicUtil.getPumpStatus() + pumpStatus.rileyLinkServiceState = checkStatusSet(pumpStatus.rileyLinkServiceState, + RileyLinkUtil.getServiceState()) as RileyLinkServiceState? + + val resourceId = pumpStatus.rileyLinkServiceState.getResourceId(RileyLinkTargetDevice.MedtronicPump) + val rileyLinkError = RileyLinkUtil.getError() + medtronic_rl_status.text = + when { + pumpStatus.rileyLinkServiceState == RileyLinkServiceState.NotStarted -> MainApp.gs(resourceId) + pumpStatus.rileyLinkServiceState.isConnecting -> "{fa-bluetooth-b spin} " + MainApp.gs(resourceId) + pumpStatus.rileyLinkServiceState.isError && rileyLinkError == null -> "{fa-bluetooth-b} " + MainApp.gs(resourceId) + pumpStatus.rileyLinkServiceState.isError && rileyLinkError != null -> "{fa-bluetooth-b} " + MainApp.gs(rileyLinkError.getResourceId(RileyLinkTargetDevice.MedtronicPump)) + else -> "{fa-bluetooth-b} " + MainApp.gs(resourceId) + } + medtronic_rl_status.setTextColor(if (rileyLinkError != null) Color.RED else Color.WHITE) + + pumpStatus.rileyLinkError = checkStatusSet(pumpStatus.rileyLinkError, RileyLinkUtil.getError()) as RileyLinkError? + + medtronic_errors.text = + pumpStatus.rileyLinkError?.let { + MainApp.gs(it.getResourceId(RileyLinkTargetDevice.MedtronicPump)) + } ?: "-" + + pumpStatus.pumpDeviceState = checkStatusSet(pumpStatus.pumpDeviceState, + MedtronicUtil.getPumpDeviceState()) as PumpDeviceState? + + when (pumpStatus.pumpDeviceState) { + null, + PumpDeviceState.Sleeping -> medtronic_pump_status.text = "{fa-bed} " // + pumpStatus.pumpDeviceState.name()); + PumpDeviceState.NeverContacted, + PumpDeviceState.WakingUp, + PumpDeviceState.PumpUnreachable, + PumpDeviceState.ErrorWhenCommunicating, + PumpDeviceState.TimeoutWhenCommunicating, + PumpDeviceState.InvalidConfiguration -> medtronic_pump_status.text = " " + MainApp.gs(pumpStatus.pumpDeviceState.resourceId) + PumpDeviceState.Active -> { + val cmd = MedtronicUtil.getCurrentCommand() + if (cmd == null) + medtronic_pump_status.text = " " + MainApp.gs(pumpStatus.pumpDeviceState.resourceId) + else { + log.debug("Command: " + cmd) + val cmdResourceId = cmd.resourceId + if (cmd == MedtronicCommandType.GetHistoryData) { + medtronic_pump_status.text = MedtronicUtil.frameNumber?.let { + MainApp.gs(cmdResourceId, MedtronicUtil.pageNumber, MedtronicUtil.frameNumber) + } + ?: MainApp.gs(R.string.medtronic_cmd_desc_get_history_request, MedtronicUtil.pageNumber) + } else { + medtronic_pump_status.text = " " + (cmdResourceId?.let { MainApp.gs(it) } + ?: cmd.getCommandDescription()) + } + } + } + else -> log.warn("Unknown pump state: " + pumpStatus.pumpDeviceState) + } + + val status = ConfigBuilderPlugin.getPlugin().commandQueue.spannedStatus() + if (status.toString() == "") { + medtronic_queue.visibility = View.GONE + } else { + medtronic_queue.visibility = View.VISIBLE + medtronic_queue.text = status + } + } + + + private fun checkStatusSet(object1: Any?, object2: Any?): Any? { + return if (object1 == null) { + object2 + } else { + if (object1 != object2) { + object2 + } else + object1 + } + } + + // GUI functions + @Synchronized + fun updateGUI() { + if (medtronic_rl_status == null) return + val plugin = MedtronicPumpPlugin.getPlugin() + val pumpStatus = MedtronicUtil.getPumpStatus() + + setDeviceStatus() + + // last connection + if (pumpStatus.lastConnection != 0L) { + val minAgo = DateUtil.minAgo(pumpStatus.lastConnection) + val min = (System.currentTimeMillis() - pumpStatus.lastConnection) / 1000 / 60 + if (pumpStatus.lastConnection + 60 * 1000 > System.currentTimeMillis()) { + medtronic_lastconnection.setText(R.string.combo_pump_connected_now) + medtronic_lastconnection.setTextColor(Color.WHITE) + } else if (pumpStatus.lastConnection + 30 * 60 * 1000 < System.currentTimeMillis()) { + + if (min < 60) { + medtronic_lastconnection.text = MainApp.gs(R.string.minago, min) + } else if (min < 1440) { + val h = (min / 60).toInt() + medtronic_lastconnection.text = (MainApp.gq(R.plurals.objective_hours, h, h) + " " + + MainApp.gs(R.string.ago)) + } else { + val h = (min / 60).toInt() + val d = h / 24 + // h = h - (d * 24); + medtronic_lastconnection.text = (MainApp.gq(R.plurals.objective_days, d, d) + " " + + MainApp.gs(R.string.ago)) + } + medtronic_lastconnection.setTextColor(Color.RED) + } else { + medtronic_lastconnection.text = minAgo + medtronic_lastconnection.setTextColor(Color.WHITE) + } + } + + // last bolus + val bolus = pumpStatus.lastBolusAmount + val bolusTime = pumpStatus.lastBolusTime + if (bolus != null && bolusTime != null) { + val agoMsc = System.currentTimeMillis() - pumpStatus.lastBolusTime.time + val bolusMinAgo = agoMsc.toDouble() / 60.0 / 1000.0 + val unit = MainApp.gs(R.string.insulin_unit_shortname) + val ago: String + if (agoMsc < 60 * 1000) { + ago = MainApp.gs(R.string.combo_pump_connected_now) + } else if (bolusMinAgo < 60) { + ago = DateUtil.minAgo(pumpStatus.lastBolusTime.time) + } else { + ago = DateUtil.hourAgo(pumpStatus.lastBolusTime.time) + } + medtronic_lastbolus.text = MainApp.gs(R.string.combo_last_bolus, bolus, unit, ago) + } else { + medtronic_lastbolus.text = "" + } + + // base basal rate + medtronic_basabasalrate.text = ("(" + pumpStatus.activeProfileName + ") " + + MainApp.gs(R.string.pump_basebasalrate, plugin.baseBasalRate)) + + medtronic_tempbasal.text = TreatmentsPlugin.getPlugin() + .getTempBasalFromHistory(System.currentTimeMillis())?.toStringFull() ?: "" + + // battery + if (MedtronicUtil.getBatteryType() == BatteryType.None || pumpStatus.batteryVoltage == null) { + medtronic_pumpstate_battery.text = "{fa-battery-" + pumpStatus.batteryRemaining / 25 + "} " + } else { + medtronic_pumpstate_battery.text = "{fa-battery-" + pumpStatus.batteryRemaining / 25 + "} " + pumpStatus.batteryRemaining + "%" + String.format(" (%.2f V)", pumpStatus.batteryVoltage) + } + SetWarnColor.setColorInverse(medtronic_pumpstate_battery, pumpStatus.batteryRemaining.toDouble(), 25.0, 10.0) + + // reservoir + medtronic_reservoir.text = MainApp.gs(R.string.reservoirvalue, pumpStatus.reservoirRemainingUnits, pumpStatus.reservoirFullUnits) + SetWarnColor.setColorInverse(medtronic_reservoir, pumpStatus.reservoirRemainingUnits, 50.0, 20.0) + + medtronic_errors.text = pumpStatus.errorInfo + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicPumpPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicPumpPlugin.java new file mode 100644 index 0000000000..ae6d754493 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicPumpPlugin.java @@ -0,0 +1,1622 @@ +package info.nightscout.androidaps.plugins.pump.medtronic; + +import android.content.ComponentName; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.IBinder; +import android.os.SystemClock; + +import androidx.annotation.NonNull; + +import org.joda.time.LocalDateTime; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; + +import info.nightscout.androidaps.BuildConfig; +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.data.DetailedBolusInfo; +import info.nightscout.androidaps.data.Profile; +import info.nightscout.androidaps.data.PumpEnactResult; +import info.nightscout.androidaps.db.Source; +import info.nightscout.androidaps.db.TemporaryBasal; +import info.nightscout.androidaps.events.EventCustomActionsChanged; +import info.nightscout.androidaps.events.EventRefreshOverview; +import info.nightscout.androidaps.interfaces.PluginDescription; +import info.nightscout.androidaps.interfaces.PluginType; +import info.nightscout.androidaps.interfaces.PumpInterface; +import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.bus.RxBus; +import info.nightscout.androidaps.plugins.common.ManufacturerType; +import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; +import info.nightscout.androidaps.plugins.general.actions.defs.CustomAction; +import info.nightscout.androidaps.plugins.general.actions.defs.CustomActionType; +import info.nightscout.androidaps.plugins.general.overview.dialogs.ErrorHelperActivity; +import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification; +import info.nightscout.androidaps.plugins.general.overview.notifications.Notification; +import info.nightscout.androidaps.plugins.pump.common.PumpPluginAbstract; +import info.nightscout.androidaps.plugins.pump.common.defs.PumpDriverState; +import info.nightscout.androidaps.plugins.pump.common.defs.PumpType; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkConst; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkServiceState; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.tasks.ResetRileyLinkConfigurationTask; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.tasks.ServiceTaskExecutor; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.tasks.WakeAndTuneTask; +import info.nightscout.androidaps.plugins.pump.common.utils.DateTimeUtil; +import info.nightscout.androidaps.plugins.pump.medtronic.comm.MedtronicCommunicationManager; +import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.PumpHistoryEntry; +import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.PumpHistoryResult; +import info.nightscout.androidaps.plugins.pump.medtronic.comm.ui.MedtronicUIComm; +import info.nightscout.androidaps.plugins.pump.medtronic.comm.ui.MedtronicUITask; +import info.nightscout.androidaps.plugins.pump.medtronic.data.MedtronicHistoryData; +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.BasalProfile; +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.BasalProfileEntry; +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.ClockDTO; +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.TempBasalPair; +import info.nightscout.androidaps.plugins.pump.medtronic.defs.BasalProfileStatus; +import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicCommandType; +import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicCustomActionType; +import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicNotificationType; +import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicStatusRefreshType; +import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicUIResponseType; +import info.nightscout.androidaps.plugins.pump.medtronic.driver.MedtronicPumpStatus; +import info.nightscout.androidaps.plugins.pump.medtronic.events.EventMedtronicPumpValuesChanged; +import info.nightscout.androidaps.plugins.pump.medtronic.events.EventRefreshButtonState; +import info.nightscout.androidaps.plugins.pump.medtronic.service.RileyLinkMedtronicService; +import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicConst; +import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil; +import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; +import info.nightscout.androidaps.utils.SP; + +import static info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil.sendNotification; + +/** + * Created by andy on 23.04.18. + * + * @author Andy Rozman (andy.rozman@gmail.com) + */ +public class MedtronicPumpPlugin extends PumpPluginAbstract implements PumpInterface { + + private static final Logger LOG = LoggerFactory.getLogger(L.PUMP); + + protected static MedtronicPumpPlugin plugin = null; + private RileyLinkMedtronicService medtronicService; + private MedtronicPumpStatus pumpStatusLocal = null; + private MedtronicUIComm medtronicUIComm = new MedtronicUIComm(); + + // variables for handling statuses and history + private boolean firstRun = true; + private boolean isRefresh = false; + private boolean isBasalProfileInvalid = false; + private boolean basalProfileChanged = false; + private Map statusRefreshMap = new HashMap<>(); + private boolean isInitialized = false; + private MedtronicHistoryData medtronicHistoryData; + private MedtronicCommunicationManager medtronicCommunicationManager; + private PumpHistoryEntry lastPumpHistoryEntry; + + public static boolean isBusy = false; + private List busyTimestamps = new ArrayList<>(); + private boolean sentIdToFirebase = false; + private boolean hasTimeDateOrTimeZoneChanged = false; + + + private MedtronicPumpPlugin() { + + super(new PluginDescription() // + .mainType(PluginType.PUMP) // + .fragmentClass(MedtronicFragment.class.getName()) // + .pluginName(R.string.medtronic_name) // + .shortName(R.string.medtronic_name_short) // + .preferencesId(R.xml.pref_medtronic).description(R.string.description_pump_medtronic), // + PumpType.Medtronic_522_722 // we default to most basic model, correct model from config is loaded later + ); + + displayConnectionMessages = false; + + medtronicHistoryData = new MedtronicHistoryData(); + + serviceConnection = new ServiceConnection() { + + public void onServiceDisconnected(ComponentName name) { + if (isLoggingEnabled()) + LOG.debug("RileyLinkMedtronicService is disconnected"); + medtronicService = null; + } + + public void onServiceConnected(ComponentName name, IBinder service) { + if (isLoggingEnabled()) + LOG.debug("RileyLinkMedtronicService is connected"); + RileyLinkMedtronicService.LocalBinder mLocalBinder = (RileyLinkMedtronicService.LocalBinder) service; + medtronicService = mLocalBinder.getServiceInstance(); + + new Thread(() -> { + + for (int i = 0; i < 20; i++) { + SystemClock.sleep(5000); + + if (MedtronicUtil.getPumpStatus() != null) { + if (isLoggingEnabled()) + LOG.debug("Starting Medtronic-RileyLink service"); + if (MedtronicUtil.getPumpStatus().setNotInPreInit()) { + break; + } + } + } + }).start(); + } + }; + } + + + public static MedtronicPumpPlugin getPlugin() { + if (plugin == null) + plugin = new MedtronicPumpPlugin(); + return plugin; + } + + + private String getLogPrefix() { + return "MedtronicPumpPlugin::"; + } + + + public MedtronicHistoryData getMedtronicHistoryData() { + return this.medtronicHistoryData; + } + + + @Override + public void initPumpStatusData() { + + this.pumpStatusLocal = new MedtronicPumpStatus(pumpDescription); + MedtronicUtil.setPumpStatus(pumpStatusLocal); + + pumpStatusLocal.lastConnection = SP.getLong(RileyLinkConst.Prefs.LastGoodDeviceCommunicationTime, 0L); + pumpStatusLocal.lastDataTime = new LocalDateTime(pumpStatusLocal.lastConnection); + pumpStatusLocal.previousConnection = pumpStatusLocal.lastConnection; + + pumpStatusLocal.refreshConfiguration(); + + if (isLoggingEnabled()) + LOG.debug("initPumpStatusData: {}", this.pumpStatusLocal); + + this.pumpStatus = pumpStatusLocal; + + // this is only thing that can change, by being configured + pumpDescription.maxTempAbsolute = (pumpStatusLocal.maxBasal != null) ? pumpStatusLocal.maxBasal : 35.0d; + + // set first Medtronic Pump Start + if (!SP.contains(MedtronicConst.Statistics.FirstPumpStart)) { + SP.putLong(MedtronicConst.Statistics.FirstPumpStart, System.currentTimeMillis()); + } + + migrateSettings(); + + } + + + private void migrateSettings() { + + if ("US (916 MHz)".equals(SP.getString(MedtronicConst.Prefs.PumpFrequency, null))) { + SP.putString(MedtronicConst.Prefs.PumpFrequency, MainApp.gs(R.string.key_medtronic_pump_frequency_us_ca)); + } + + String encoding = SP.getString(MedtronicConst.Prefs.Encoding, null); + + if ("RileyLink 4b6b Encoding".equals(encoding)) { + SP.putString(MedtronicConst.Prefs.Encoding, MainApp.gs(R.string.key_medtronic_pump_encoding_4b6b_rileylink)); + } + + if ("Local 4b6b Encoding".equals(encoding)) { + SP.putString(MedtronicConst.Prefs.Encoding, MainApp.gs(R.string.key_medtronic_pump_encoding_4b6b_local)); + } + } + + + public void onStartCustomActions() { + + // check status every minute (if any status needs refresh we send readStatus command) + new Thread(() -> { + + do { + SystemClock.sleep(60000); + + if (this.isInitialized) { + + Map statusRefresh = workWithStatusRefresh( + StatusRefreshAction.GetData, null, null); + + if (doWeHaveAnyStatusNeededRefereshing(statusRefresh)) { + if (!ConfigBuilderPlugin.getPlugin().getCommandQueue().statusInQueue()) { + ConfigBuilderPlugin.getPlugin().getCommandQueue() + .readStatus("Scheduled Status Refresh", null); + } + } + + clearBusyQueue(); + } + + } while (serviceRunning); + + }).start(); + } + + + public Class getServiceClass() { + return RileyLinkMedtronicService.class; + } + + + @Override + public String deviceID() { + return "Medtronic"; + } + + + @Override + public boolean isFakingTempsByExtendedBoluses() { + return false; + } + + + @Override + public boolean canHandleDST() { + return false; + } + + + // Pump Plugin + + private boolean isServiceSet() { + return medtronicService != null; + } + + + @Override + public boolean isInitialized() { + if (isLoggingEnabled() && displayConnectionMessages) + LOG.debug("MedtronicPumpPlugin::isInitialized"); + return isServiceSet() && isInitialized; + } + + + @Override + public boolean isBusy() { + if (isLoggingEnabled() && displayConnectionMessages) + LOG.debug("MedtronicPumpPlugin::isBusy"); + + if (isServiceSet()) { + + if (isBusy) + return true; + + if (busyTimestamps.size() > 0) { + + clearBusyQueue(); + + if (busyTimestamps.size() > 0) { + return true; + } + } + } + + return false; + } + + + private synchronized void clearBusyQueue() { + + if (busyTimestamps.size() == 0) { + return; + } + + Set deleteFromQueue = new HashSet<>(); + + for (Long busyTimestamp : busyTimestamps) { + + if (System.currentTimeMillis() > busyTimestamp) { + deleteFromQueue.add(busyTimestamp); + } + } + + if (deleteFromQueue.size() == busyTimestamps.size()) { + busyTimestamps.clear(); + setEnableCustomAction(MedtronicCustomActionType.ClearBolusBlock, false); + } + + if (deleteFromQueue.size() > 0) { + busyTimestamps.removeAll(deleteFromQueue); + } + + } + + + @Override + public boolean isConnected() { + if (isLoggingEnabled() && displayConnectionMessages) + LOG.debug("MedtronicPumpPlugin::isConnected"); + return isServiceSet() && medtronicService.isInitialized(); + } + + + @Override + public boolean isConnecting() { + if (isLoggingEnabled() && displayConnectionMessages) + LOG.debug("MedtronicPumpPlugin::isConnecting"); + return !isServiceSet() || !medtronicService.isInitialized(); + } + + + @Override + public void getPumpStatus() { + + getMDTPumpStatus(); + + if (firstRun) { + initializePump(!isRefresh); + } else { + refreshAnyStatusThatNeedsToBeRefreshed(); + } + + RxBus.INSTANCE.send(new EventMedtronicPumpValuesChanged()); + } + + + void resetStatusState() { + firstRun = true; + isRefresh = true; + } + + + private boolean isPumpNotReachable() { + + RileyLinkServiceState rileyLinkServiceState = MedtronicUtil.getServiceState(); + + if (rileyLinkServiceState==null) { + LOG.error("RileyLink unreachable. RileyLinkServiceState is null."); + return false; + } + + if (rileyLinkServiceState != RileyLinkServiceState.PumpConnectorReady // + && rileyLinkServiceState != RileyLinkServiceState.RileyLinkReady // + && rileyLinkServiceState != RileyLinkServiceState.TuneUpDevice) { + LOG.error("RileyLink unreachable."); + return false; + } + + return (!medtronicCommunicationManager.isDeviceReachable()); + } + + + private void refreshAnyStatusThatNeedsToBeRefreshed() { + + Map statusRefresh = workWithStatusRefresh(StatusRefreshAction.GetData, null, + null); + + if (!doWeHaveAnyStatusNeededRefereshing(statusRefresh)) { + return; + } + + boolean resetTime = false; + + if (isPumpNotReachable()) { + if (isLoggingEnabled()) + LOG.error("Pump unreachable."); + MedtronicUtil.sendNotification(MedtronicNotificationType.PumpUnreachable); + + return; + } + + MedtronicUtil.dismissNotification(MedtronicNotificationType.PumpUnreachable); + + + if (hasTimeDateOrTimeZoneChanged) { + + checkTimeAndOptionallySetTime(); + + // read time if changed, set new time + hasTimeDateOrTimeZoneChanged = false; + } + + + // execute + Set refreshTypesNeededToReschedule = new HashSet<>(); + + for (Map.Entry refreshType : statusRefresh.entrySet()) { + + if (refreshType.getValue() > 0 && System.currentTimeMillis() > refreshType.getValue()) { + + switch (refreshType.getKey()) { + case PumpHistory: { + readPumpHistory(); + } + break; + + case PumpTime: { + checkTimeAndOptionallySetTime(); + refreshTypesNeededToReschedule.add(refreshType.getKey()); + resetTime = true; + } + break; + + case BatteryStatus: + case RemainingInsulin: { + medtronicUIComm.executeCommand(refreshType.getKey().getCommandType()); + refreshTypesNeededToReschedule.add(refreshType.getKey()); + resetTime = true; + } + break; + + case Configuration: { + medtronicUIComm.executeCommand(refreshType.getKey().getCommandType()); + resetTime = true; + } + break; + } + } + + // reschedule + for (MedtronicStatusRefreshType refreshType2 : refreshTypesNeededToReschedule) { + scheduleNextRefresh(refreshType2); + } + + } + + if (resetTime) + pumpStatusLocal.setLastCommunicationToNow(); + + } + + + private boolean doWeHaveAnyStatusNeededRefereshing(Map statusRefresh) { + + for (Map.Entry refreshType : statusRefresh.entrySet()) { + + if (refreshType.getValue() > 0 && System.currentTimeMillis() > refreshType.getValue()) { + return true; + } + } + + return hasTimeDateOrTimeZoneChanged; + } + + + private void setRefreshButtonEnabled(boolean enabled) { + RxBus.INSTANCE.send(new EventRefreshButtonState(enabled)); + } + + + private void initializePump(boolean realInit) { + + if (isLoggingEnabled()) + LOG.info(getLogPrefix() + "initializePump - start"); + + if (medtronicCommunicationManager == null) { + medtronicCommunicationManager = MedtronicCommunicationManager.getInstance(); + medtronicCommunicationManager.setDoWakeUpBeforeCommand(false); + } + + setRefreshButtonEnabled(false); + + getMDTPumpStatus(); + + if (isRefresh) { + if (isPumpNotReachable()) { + if (isLoggingEnabled()) + LOG.error(getLogPrefix() + "initializePump::Pump unreachable."); + MedtronicUtil.sendNotification(MedtronicNotificationType.PumpUnreachable); + + setRefreshButtonEnabled(true); + + return; + } + + MedtronicUtil.dismissNotification(MedtronicNotificationType.PumpUnreachable); + } + + // model (once) + if (MedtronicUtil.getMedtronicPumpModel() == null) { + medtronicUIComm.executeCommand(MedtronicCommandType.PumpModel); + } else { + if (pumpStatusLocal.medtronicDeviceType != MedtronicUtil.getMedtronicPumpModel()) { + if (isLoggingEnabled()) + LOG.warn(getLogPrefix() + "Configured pump is not the same as one detected."); + MedtronicUtil.sendNotification(MedtronicNotificationType.PumpTypeNotSame); + } + } + + this.pumpState = PumpDriverState.Connected; + + // time (1h) + checkTimeAndOptionallySetTime(); + + readPumpHistory(); + + // remaining insulin (>50 = 4h; 50-20 = 1h; 15m) + medtronicUIComm.executeCommand(MedtronicCommandType.GetRemainingInsulin); + scheduleNextRefresh(MedtronicStatusRefreshType.RemainingInsulin, 10); + + // remaining power (1h) + medtronicUIComm.executeCommand(MedtronicCommandType.GetBatteryStatus); + scheduleNextRefresh(MedtronicStatusRefreshType.BatteryStatus, 20); + + // configuration (once and then if history shows config changes) + medtronicUIComm.executeCommand(MedtronicCommandType.getSettings(MedtronicUtil.getMedtronicPumpModel())); + + // read profile (once, later its controlled by isThisProfileSet method) + getBasalProfiles(); + + int errorCount = medtronicUIComm.getInvalidResponsesCount(); + + if (errorCount >= 5) { + if (isLoggingEnabled()) + LOG.error("Number of error counts was 5 or more. Starting tunning."); + setRefreshButtonEnabled(true); + ServiceTaskExecutor.startTask(new WakeAndTuneTask()); + return; + } + + pumpStatusLocal.setLastCommunicationToNow(); + setRefreshButtonEnabled(true); + + if (!isRefresh) { + pumpState = PumpDriverState.Initialized; + } + + if (!sentIdToFirebase) { + Bundle params = new Bundle(); + params.putString("version", BuildConfig.VERSION); + MainApp.getFirebaseAnalytics().logEvent("MedtronicPumpInit", params); + + sentIdToFirebase = true; + } + + isInitialized = true; + // this.pumpState = PumpDriverState.Initialized; + + this.firstRun = false; + } + + private void getBasalProfiles() { + + MedtronicUITask medtronicUITask = medtronicUIComm.executeCommand(MedtronicCommandType.GetBasalProfileSTD); + + if (medtronicUITask.getResponseType() == MedtronicUIResponseType.Error) { + medtronicUIComm.executeCommand(MedtronicCommandType.GetBasalProfileSTD); + } + } + + + @Override + public boolean isThisProfileSet(Profile profile) { + MedtronicPumpStatus mdtPumpStatus = getMDTPumpStatus(); + LOG.debug("isThisProfileSet: basalInitalized={}", mdtPumpStatus.basalProfileStatus); + + if (!isInitialized) + return true; + + if (mdtPumpStatus.basalProfileStatus == BasalProfileStatus.NotInitialized) { + // this shouldn't happen, but if there was problem we try again + getBasalProfiles(); + return isProfileSame(profile); + } else if (mdtPumpStatus.basalProfileStatus == BasalProfileStatus.ProfileChanged) { + return false; + } else { + + } + + + return (getMDTPumpStatus().basalProfileStatus != BasalProfileStatus.ProfileOK) || isProfileSame(profile); + } + + + private boolean isProfileSame(Profile profile) { + + boolean invalid = false; + Double[] basalsByHour = getMDTPumpStatus().basalsByHour; + PumpType pumpType = getMDTPumpStatus().getPumpType(); + + if (isLoggingEnabled()) + LOG.debug("Current Basals (h): " + + (basalsByHour == null ? "null" : BasalProfile.getProfilesByHourToString(basalsByHour))); + + // int index = 0; + + if (basalsByHour == null) + return true; // we don't want to set profile again, unless we are sure + + StringBuilder stringBuilder = new StringBuilder("Requested Basals (h): "); + + for (Profile.ProfileValue basalValue : profile.getBasalValues()) { + + double basalValueValue = pumpType.determineCorrectBasalSize(basalValue.value); + + int hour = basalValue.timeAsSeconds / (60 * 60); + + if (!MedtronicUtil.isSame(basalsByHour[hour], basalValueValue)) { + invalid = true; + } + + stringBuilder.append(String.format(Locale.ENGLISH, "%.3f", basalValueValue)); + stringBuilder.append(" "); + } + + if (isLoggingEnabled()) { + LOG.debug(stringBuilder.toString()); + + if (!invalid) { + LOG.debug("Basal profile is same as AAPS one."); + } else { + LOG.debug("Basal profile on Pump is different than the AAPS one."); + } + } + + return (!invalid); + } + + + @Override + public long lastDataTime() { + getMDTPumpStatus(); + + if (pumpStatusLocal.lastConnection != 0) { + return pumpStatusLocal.lastConnection; + } + + return System.currentTimeMillis(); + } + + + @Override + public double getBaseBasalRate() { + return getMDTPumpStatus().getBasalProfileForHour(); + } + + + @Override + public double getReservoirLevel() { + return getMDTPumpStatus().reservoirRemainingUnits; + } + + + @Override + public int getBatteryLevel() { + return getMDTPumpStatus().batteryRemaining; + } + + + private MedtronicPumpStatus getMDTPumpStatus() { + if (pumpStatusLocal == null) { + // FIXME I don't know why this happens + if (isLoggingEnabled()) + LOG.warn("!!!! Reset Pump Status Local"); + pumpStatusLocal = MedtronicUtil.getPumpStatus(); + } + + return pumpStatusLocal; + } + + + protected void triggerUIChange() { + RxBus.INSTANCE.send(new EventMedtronicPumpValuesChanged()); + } + + private BolusDeliveryType bolusDeliveryType = BolusDeliveryType.Idle; + + private enum BolusDeliveryType { + Idle, // + DeliveryPrepared, // + Delivering, // + CancelDelivery + } + + + private void checkTimeAndOptionallySetTime() { + + if (isLoggingEnabled()) + LOG.info("MedtronicPumpPlugin::checkTimeAndOptionallySetTime - Start"); + + setRefreshButtonEnabled(false); + + if (isPumpNotReachable()) { + LOG.debug("MedtronicPumpPlugin::checkTimeAndOptionallySetTime - Pump Unreachable."); + setRefreshButtonEnabled(true); + return; + } + + MedtronicUtil.dismissNotification(MedtronicNotificationType.PumpUnreachable); + + medtronicUIComm.executeCommand(MedtronicCommandType.GetRealTimeClock); + + ClockDTO clock = MedtronicUtil.getPumpTime(); + + if (clock==null) { // retry + medtronicUIComm.executeCommand(MedtronicCommandType.GetRealTimeClock); + + clock = MedtronicUtil.getPumpTime(); + } + + if (clock==null) + return; + + int timeDiff = Math.abs(clock.timeDifference); + + if (timeDiff > 20) { + + if ((clock.localDeviceTime.getYear() <= 2015) || (timeDiff <= 24 * 60 * 60)) { + + if (isLoggingEnabled()) + LOG.info("MedtronicPumpPlugin::checkTimeAndOptionallySetTime - Time difference is {} s. Set time on pump.", timeDiff); + + medtronicUIComm.executeCommand(MedtronicCommandType.SetRealTimeClock); + + if (clock.timeDifference == 0) { + Notification notification = new Notification(Notification.INSIGHT_DATE_TIME_UPDATED, MainApp.gs(R.string.pump_time_updated), Notification.INFO, 60); + RxBus.INSTANCE.send(new EventNewNotification(notification)); + } + } else { + if ((clock.localDeviceTime.getYear() > 2015)) { + LOG.error("MedtronicPumpPlugin::checkTimeAndOptionallySetTime - Time difference over 24h requested [diff={}]. Doing nothing.", timeDiff); + sendNotification(MedtronicNotificationType.TimeChangeOver24h); + } + } + + } else { + if (isLoggingEnabled()) + LOG.info("MedtronicPumpPlugin::checkTimeAndOptionallySetTime - Time difference is {} s. Do nothing.", timeDiff); + } + + scheduleNextRefresh(MedtronicStatusRefreshType.PumpTime, 0); + } + + + @NonNull + protected PumpEnactResult deliverBolus(final DetailedBolusInfo detailedBolusInfo) { + + LOG.info("MedtronicPumpPlugin::deliverBolus - {}", BolusDeliveryType.DeliveryPrepared); + + setRefreshButtonEnabled(false); + + MedtronicPumpStatus mdtPumpStatus = getMDTPumpStatus(); + + if (detailedBolusInfo.insulin > mdtPumpStatus.reservoirRemainingUnits) { + return new PumpEnactResult() // + .success(false) // + .enacted(false) // + .comment(MainApp.gs(R.string.medtronic_cmd_bolus_could_not_be_delivered_no_insulin, + mdtPumpStatus.reservoirRemainingUnits, + detailedBolusInfo.insulin)); + } + + bolusDeliveryType = BolusDeliveryType.DeliveryPrepared; + + if (isPumpNotReachable()) { + LOG.debug("MedtronicPumpPlugin::deliverBolus - Pump Unreachable."); + return setNotReachable(true, false); + } + + MedtronicUtil.dismissNotification(MedtronicNotificationType.PumpUnreachable); + + if (bolusDeliveryType == BolusDeliveryType.CancelDelivery) { + // LOG.debug("MedtronicPumpPlugin::deliverBolus - Delivery Canceled."); + return setNotReachable(true, true); + } + + // LOG.debug("MedtronicPumpPlugin::deliverBolus - Starting wait period."); + + int sleepTime = SP.getInt(MedtronicConst.Prefs.BolusDelay, 10) * 1000; + + SystemClock.sleep(sleepTime); + + if (bolusDeliveryType == BolusDeliveryType.CancelDelivery) { + // LOG.debug("MedtronicPumpPlugin::deliverBolus - Delivery Canceled, before wait period."); + return setNotReachable(true, true); + } + + // LOG.debug("MedtronicPumpPlugin::deliverBolus - End wait period. Start delivery"); + + try { + + bolusDeliveryType = BolusDeliveryType.Delivering; + + // LOG.debug("MedtronicPumpPlugin::deliverBolus - Start delivery"); + + MedtronicUITask responseTask = medtronicUIComm.executeCommand(MedtronicCommandType.SetBolus, + detailedBolusInfo.insulin); + + Boolean response = (Boolean) responseTask.returnData; + + setRefreshButtonEnabled(true); + + // LOG.debug("MedtronicPumpPlugin::deliverBolus - Response: {}", response); + + if (response) { + + if (bolusDeliveryType == BolusDeliveryType.CancelDelivery) { + // LOG.debug("MedtronicPumpPlugin::deliverBolus - Delivery Canceled after Bolus started."); + + new Thread(() -> { + // Looper.prepare(); + // LOG.debug("MedtronicPumpPlugin::deliverBolus - Show dialog - before"); + SystemClock.sleep(2000); + // LOG.debug("MedtronicPumpPlugin::deliverBolus - Show dialog. Context: " + // + MainApp.instance().getApplicationContext()); + + Intent i = new Intent(MainApp.instance(), ErrorHelperActivity.class); + i.putExtra("soundid", R.raw.boluserror); + i.putExtra("status", MainApp.gs(R.string.medtronic_cmd_cancel_bolus_not_supported)); + i.putExtra("title", MainApp.gs(R.string.combo_warning)); + i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + MainApp.instance().startActivity(i); + + }).start(); + } + + TreatmentsPlugin.getPlugin().addToHistoryTreatment(detailedBolusInfo, true); + + // we subtract insulin, exact amount will be visible with next remainingInsulin update. + getMDTPumpStatus().reservoirRemainingUnits -= detailedBolusInfo.insulin; + + incrementStatistics(detailedBolusInfo.isSMB ? MedtronicConst.Statistics.SMBBoluses + : MedtronicConst.Statistics.StandardBoluses); + + + // calculate time for bolus and set driver to busy for that time + int bolusTime = (int) (detailedBolusInfo.insulin * 42.0d); + long time = System.currentTimeMillis() + (bolusTime * 1000); + + this.busyTimestamps.add(time); + setEnableCustomAction(MedtronicCustomActionType.ClearBolusBlock, true); + + return new PumpEnactResult().success(true) // + .enacted(true) // + .bolusDelivered(detailedBolusInfo.insulin) // + .carbsDelivered(detailedBolusInfo.carbs); + + } else { + return new PumpEnactResult() // + .success(bolusDeliveryType == BolusDeliveryType.CancelDelivery) // + .enacted(false) // + .comment(MainApp.gs(R.string.medtronic_cmd_bolus_could_not_be_delivered)); + } + + } finally { + finishAction("Bolus"); + this.bolusDeliveryType = BolusDeliveryType.Idle; + } + } + + + private PumpEnactResult setNotReachable(boolean isBolus, boolean success) { + setRefreshButtonEnabled(true); + + if (isBolus) { + bolusDeliveryType = BolusDeliveryType.Idle; + } + + if (success) { + return new PumpEnactResult() // + .success(true) // + .enacted(false); + } else { + return new PumpEnactResult() // + .success(false) // + .enacted(false) // + .comment(MainApp.gs(R.string.medtronic_pump_status_pump_unreachable)); + } + } + + + public void stopBolusDelivering() { + + this.bolusDeliveryType = BolusDeliveryType.CancelDelivery; + + // if (isLoggingEnabled()) + // LOG.warn("MedtronicPumpPlugin::deliverBolus - Stop Bolus Delivery."); + } + + + private void incrementStatistics(String statsKey) { + long currentCount = SP.getLong(statsKey, 0L); + currentCount++; + SP.putLong(statsKey, currentCount); + } + + + // if enforceNew===true current temp basal is canceled and new TBR set (duration is prolonged), + // if false and the same rate is requested enacted=false and success=true is returned and TBR is not changed + @Override + public PumpEnactResult setTempBasalAbsolute(Double absoluteRate, Integer durationInMinutes, Profile profile, + boolean enforceNew) { + + setRefreshButtonEnabled(false); + + if (isPumpNotReachable()) { + + setRefreshButtonEnabled(true); + + return new PumpEnactResult() // + .success(false) // + .enacted(false) // + .comment(MainApp.gs(R.string.medtronic_pump_status_pump_unreachable)); + } + + MedtronicUtil.dismissNotification(MedtronicNotificationType.PumpUnreachable); + + getMDTPumpStatus(); + + if (isLoggingEnabled()) + LOG.info(getLogPrefix() + "setTempBasalAbsolute: rate: {}, duration={}", absoluteRate, durationInMinutes); + + // read current TBR + TempBasalPair tbrCurrent = readTBR(); + + if (tbrCurrent == null) { + if (isLoggingEnabled()) + LOG.warn(getLogPrefix() + "setTempBasalAbsolute - Could not read current TBR, canceling operation."); + finishAction("TBR"); + return new PumpEnactResult().success(false).enacted(false) + .comment(MainApp.gs(R.string.medtronic_cmd_cant_read_tbr)); + } else { + if (isLoggingEnabled()) + LOG.info(getLogPrefix() + "setTempBasalAbsolute: Current Basal: duration: {} min, rate={}", + tbrCurrent.getDurationMinutes(), tbrCurrent.getInsulinRate()); + } + + if (!enforceNew) { + + if (MedtronicUtil.isSame(tbrCurrent.getInsulinRate(), absoluteRate)) { + + boolean sameRate = true; + if (MedtronicUtil.isSame(0.0d, absoluteRate) && durationInMinutes > 0) { + // if rate is 0.0 and duration>0 then the rate is not the same + sameRate = false; + } + + if (sameRate) { + if (isLoggingEnabled()) + LOG.info(getLogPrefix() + "setTempBasalAbsolute - No enforceNew and same rate. Exiting."); + finishAction("TBR"); + return new PumpEnactResult().success(true).enacted(false); + } + } + // if not the same rate, we cancel and start new + } + + // if TBR is running we will cancel it. + if (tbrCurrent.getInsulinRate() != 0.0f && tbrCurrent.getDurationMinutes() > 0) { + if (isLoggingEnabled()) + LOG.info(getLogPrefix() + "setTempBasalAbsolute - TBR running - so canceling it."); + + // CANCEL + + MedtronicUITask responseTask2 = medtronicUIComm.executeCommand(MedtronicCommandType.CancelTBR); + + Boolean response = (Boolean) responseTask2.returnData; + + if (response) { + if (isLoggingEnabled()) + LOG.info(getLogPrefix() + "setTempBasalAbsolute - Current TBR cancelled."); + } else { + if (isLoggingEnabled()) + LOG.error(getLogPrefix() + "setTempBasalAbsolute - Cancel TBR failed."); + + finishAction("TBR"); + + return new PumpEnactResult().success(false).enacted(false) + .comment(MainApp.gs(R.string.medtronic_cmd_cant_cancel_tbr_stop_op)); + } + } + + // now start new TBR + MedtronicUITask responseTask = medtronicUIComm.executeCommand(MedtronicCommandType.SetTemporaryBasal, + absoluteRate, durationInMinutes); + + Boolean response = (Boolean) responseTask.returnData; + + if (isLoggingEnabled()) + LOG.info(getLogPrefix() + "setTempBasalAbsolute - setTBR. Response: " + response); + + if (response) { + // FIXME put this into UIPostProcessor + pumpStatusLocal.tempBasalStart = new Date(); + pumpStatusLocal.tempBasalAmount = absoluteRate; + pumpStatusLocal.tempBasalLength = durationInMinutes; + + TemporaryBasal tempStart = new TemporaryBasal() // + .date(System.currentTimeMillis()) // + .duration(durationInMinutes) // + .absolute(absoluteRate) // + .source(Source.USER); + + TreatmentsPlugin.getPlugin().addToHistoryTempBasal(tempStart); + + incrementStatistics(MedtronicConst.Statistics.TBRsSet); + + finishAction("TBR"); + + return new PumpEnactResult().success(true).enacted(true) // + .absolute(absoluteRate).duration(durationInMinutes); + + } else { + finishAction("TBR"); + + return new PumpEnactResult().success(false).enacted(false) // + .comment(MainApp.gs(R.string.medtronic_cmd_tbr_could_not_be_delivered)); + } + + } + + + @Override + public PumpEnactResult setTempBasalPercent(Integer percent, Integer durationInMinutes, Profile profile, + boolean enforceNew) { + if (percent==0) { + return setTempBasalAbsolute(0.0d, durationInMinutes, profile, enforceNew); + } else { + double absoluteValue = profile.getBasal() * (percent /100.0d); + getMDTPumpStatus(); + absoluteValue = pumpStatusLocal.pumpType.determineCorrectBasalSize(absoluteValue); + LOG.warn("setTempBasalPercent [MedtronicPumpPlugin] - You are trying to use setTempBasalPercent with percent other then 0% (%d). This will start setTempBasalAbsolute, with calculated value (%.3f). Result might not be 100% correct.", percent, absoluteValue); + return setTempBasalAbsolute(absoluteValue, durationInMinutes, profile, enforceNew); + } + } + + + private void finishAction(String overviewKey) { + + if (overviewKey != null) + RxBus.INSTANCE.send(new EventRefreshOverview(overviewKey)); + + triggerUIChange(); + + setRefreshButtonEnabled(true); + } + + + private void readPumpHistory() { + +// if (isLoggingEnabled()) +// LOG.error(getLogPrefix() + "readPumpHistory WIP."); + + readPumpHistoryLogic(); + + scheduleNextRefresh(MedtronicStatusRefreshType.PumpHistory); + + if (medtronicHistoryData.hasRelevantConfigurationChanged()) { + scheduleNextRefresh(MedtronicStatusRefreshType.Configuration, -1); + } + + if (medtronicHistoryData.hasPumpTimeChanged()) { + scheduleNextRefresh(MedtronicStatusRefreshType.PumpTime, -1); + } + + if (this.getMDTPumpStatus().basalProfileStatus != BasalProfileStatus.NotInitialized + && medtronicHistoryData.hasBasalProfileChanged()) { + medtronicHistoryData.processLastBasalProfileChange(getMDTPumpStatus()); + } + + PumpDriverState previousState = this.pumpState; + + if (medtronicHistoryData.isPumpSuspended()) { + this.pumpState = PumpDriverState.Suspended; + if (isLoggingEnabled()) + LOG.debug(getLogPrefix() + "isPumpSuspended: true"); + } else { + if (previousState == PumpDriverState.Suspended) { + this.pumpState = PumpDriverState.Ready; + } + if (isLoggingEnabled()) + LOG.debug(getLogPrefix() + "isPumpSuspended: false"); + } + + medtronicHistoryData.processNewHistoryData(); + + this.medtronicHistoryData.finalizeNewHistoryRecords(); + // this.medtronicHistoryData.setLastHistoryRecordTime(this.lastPumpHistoryEntry.atechDateTime); + + } + + + private void readPumpHistoryLogic() { + + LocalDateTime targetDate = null; + + if (lastPumpHistoryEntry == null) { + + if (isLoggingEnabled()) + LOG.debug(getLogPrefix() + "readPumpHistoryLogic(): lastPumpHistoryEntry: null"); + + Long lastPumpHistoryEntryTime = getLastPumpEntryTime(); + + LocalDateTime timeMinus36h = new LocalDateTime(); + timeMinus36h = timeMinus36h.minusHours(36); + medtronicHistoryData.setIsInInit(true); + + if (lastPumpHistoryEntryTime == 0L) { + if (isLoggingEnabled()) + LOG.debug(getLogPrefix() + "readPumpHistoryLogic(): lastPumpHistoryEntryTime: 0L - targetDate: " + + targetDate); + targetDate = timeMinus36h; + } else { + // LocalDateTime lastHistoryRecordTime = DateTimeUtil.toLocalDateTime(lastPumpHistoryEntryTime); + + if (isLoggingEnabled()) + LOG.debug(getLogPrefix() + "readPumpHistoryLogic(): lastPumpHistoryEntryTime: {} - targetDate: {}", + lastPumpHistoryEntryTime, targetDate); + + medtronicHistoryData.setLastHistoryRecordTime(lastPumpHistoryEntryTime); + + LocalDateTime lastHistoryRecordTime = DateTimeUtil.toLocalDateTime(lastPumpHistoryEntryTime); + + lastHistoryRecordTime = lastHistoryRecordTime.minusHours(12); // we get last 12 hours of history to + // determine pump state + // (we don't process that data), we process only + + if (timeMinus36h.isAfter(lastHistoryRecordTime)) { + targetDate = timeMinus36h; + } + + targetDate = (timeMinus36h.isAfter(lastHistoryRecordTime) ? timeMinus36h : lastHistoryRecordTime); + + if (isLoggingEnabled()) + LOG.debug(getLogPrefix() + "readPumpHistoryLogic(): targetDate: " + targetDate); + } + } else { + if (isLoggingEnabled()) + LOG.debug(getLogPrefix() + "readPumpHistoryLogic(): lastPumpHistoryEntry: not null - {}", + MedtronicUtil.gsonInstance.toJson(lastPumpHistoryEntry)); + medtronicHistoryData.setIsInInit(false); + // medtronicHistoryData.setLastHistoryRecordTime(lastPumpHistoryEntry.atechDateTime); + + // targetDate = lastPumpHistoryEntry.atechDateTime; + } + + LOG.debug("HST: Target Date: {}", targetDate); + + MedtronicUITask responseTask2 = medtronicUIComm.executeCommand(MedtronicCommandType.GetHistoryData, + lastPumpHistoryEntry, targetDate); + + LOG.debug("HST: After task"); + + PumpHistoryResult historyResult = (PumpHistoryResult) responseTask2.returnData; + + LOG.debug("HST: History Result: {}", historyResult.toString()); + + PumpHistoryEntry latestEntry = historyResult.getLatestEntry(); + + if (isLoggingEnabled()) + LOG.debug(getLogPrefix() + "Last entry: " + latestEntry); + + if (latestEntry == null) // no new history to read + return; + + this.lastPumpHistoryEntry = latestEntry; + SP.putLong(MedtronicConst.Statistics.LastPumpHistoryEntry, latestEntry.atechDateTime); + + LOG.debug("HST: History: valid={}, unprocessed={}", historyResult.validEntries.size(), + historyResult.unprocessedEntries.size()); + + this.medtronicHistoryData.addNewHistory(historyResult); + this.medtronicHistoryData.filterNewEntries(); + + // determine if first run, if yes detrmine how much of update do we need + // first run: + // get last hiostory entry, if not there download 1.5 days of data + // - there: check if last entry is older than 1.5 days + // - yes: download 1.5 days + // - no: download with last entry + // - not there: download 1.5 days + // + // upload all new entries to NightScout (TBR, Bolus) + // determine pump status + // + // save last entry + // + // not first run: + // update to last entry + // - save + // - determine pump status + + // + + } + + private Long getLastPumpEntryTime() { + Long lastPumpEntryTime = SP.getLong(MedtronicConst.Statistics.LastPumpHistoryEntry, 0L); + + try { + LocalDateTime localDateTime = DateTimeUtil.toLocalDateTime(lastPumpEntryTime); + + if (localDateTime.getYear() != (new GregorianCalendar().get(Calendar.YEAR))) { + LOG.warn("Saved LastPumpHistoryEntry was invalid. Year was not the same."); + return 0L; + } + + return lastPumpEntryTime; + + } catch (Exception ex) { + LOG.warn("Saved LastPumpHistoryEntry was invalid."); + return 0L; + } + + } + + + private void scheduleNextRefresh(MedtronicStatusRefreshType refreshType) { + scheduleNextRefresh(refreshType, 0); + } + + + private void scheduleNextRefresh(MedtronicStatusRefreshType refreshType, int additionalTimeInMinutes) { + switch (refreshType) { + + case RemainingInsulin: { + double remaining = pumpStatusLocal.reservoirRemainingUnits; + int min; + if (remaining > 50) + min = 4 * 60; + else if (remaining > 20) + min = 60; + else + min = 15; + + workWithStatusRefresh(StatusRefreshAction.Add, refreshType, getTimeInFutureFromMinutes(min)); + } + break; + + case PumpTime: + case Configuration: + case BatteryStatus: + case PumpHistory: { + workWithStatusRefresh(StatusRefreshAction.Add, refreshType, + getTimeInFutureFromMinutes(refreshType.getRefreshTime() + additionalTimeInMinutes)); + } + break; + } + } + + private enum StatusRefreshAction { + Add, // + GetData + } + + + private synchronized Map workWithStatusRefresh(StatusRefreshAction action, // + MedtronicStatusRefreshType statusRefreshType, // + Long time) { + + switch (action) { + + case Add: { + statusRefreshMap.put(statusRefreshType, time); + return null; + } + + case GetData: { + return new HashMap<>(statusRefreshMap); + } + + default: + return null; + + } + + } + + + private long getTimeInFutureFromMinutes(int minutes) { + return System.currentTimeMillis() + getTimeInMs(minutes); + } + + + private long getTimeInMs(int minutes) { + return minutes * 60 * 1000L; + } + + + private TempBasalPair readTBR() { + MedtronicUITask responseTask = medtronicUIComm.executeCommand(MedtronicCommandType.ReadTemporaryBasal); + + if (responseTask.hasData()) { + TempBasalPair tbr = (TempBasalPair) responseTask.returnData; + + // we sometimes get rate returned even if TBR is no longer running + if (tbr.getDurationMinutes() == 0) { + tbr.setInsulinRate(0.0d); + } + + return tbr; + } else { + return null; + } + } + + + @Override + public PumpEnactResult cancelTempBasal(boolean enforceNew) { + + if (isLoggingEnabled()) + LOG.info(getLogPrefix() + "cancelTempBasal - started"); + + if (isPumpNotReachable()) { + + setRefreshButtonEnabled(true); + + return new PumpEnactResult() // + .success(false) // + .enacted(false) // + .comment(MainApp.gs(R.string.medtronic_pump_status_pump_unreachable)); + } + + MedtronicUtil.dismissNotification(MedtronicNotificationType.PumpUnreachable); + setRefreshButtonEnabled(false); + + TempBasalPair tbrCurrent = readTBR(); + + if (tbrCurrent != null) { + if (tbrCurrent.getInsulinRate() == 0.0f && tbrCurrent.getDurationMinutes() == 0) { + if (isLoggingEnabled()) + LOG.info(getLogPrefix() + "cancelTempBasal - TBR already canceled."); + finishAction("TBR"); + return new PumpEnactResult().success(true).enacted(false); + } + } else { + if (isLoggingEnabled()) + LOG.warn(getLogPrefix() + "cancelTempBasal - Could not read currect TBR, canceling operation."); + finishAction("TBR"); + return new PumpEnactResult().success(false).enacted(false) + .comment(MainApp.gs(R.string.medtronic_cmd_cant_read_tbr)); + } + + MedtronicUITask responseTask2 = medtronicUIComm.executeCommand(MedtronicCommandType.CancelTBR); + + Boolean response = (Boolean) responseTask2.returnData; + + finishAction("TBR"); + + if (response) { + if (isLoggingEnabled()) + LOG.info(getLogPrefix() + "cancelTempBasal - Cancel TBR successful."); + + TemporaryBasal tempBasal = new TemporaryBasal() // + .date(System.currentTimeMillis()) // + .duration(0) // + .source(Source.USER); + + TreatmentsPlugin.getPlugin().addToHistoryTempBasal(tempBasal); + + return new PumpEnactResult().success(true).enacted(true) // + .isTempCancel(true); + } else { + if (isLoggingEnabled()) + LOG.info(getLogPrefix() + "cancelTempBasal - Cancel TBR failed."); + + return new PumpEnactResult().success(response).enacted(response) // + .comment(MainApp.gs(R.string.medtronic_cmd_cant_cancel_tbr)); + } + } + + @Override + public ManufacturerType manufacturer() { + return getMDTPumpStatus().pumpType.getManufacturer(); + } + + @Override + public PumpType model() { + return getMDTPumpStatus().pumpType; + } + + @Override + public String serialNumber() { + return getMDTPumpStatus().serialNumber; + } + + @Override + public PumpEnactResult setNewBasalProfile(Profile profile) { + if (isLoggingEnabled()) + LOG.info(getLogPrefix() + "setNewBasalProfile"); + + // this shouldn't be needed, but let's do check if profile setting we are setting is same as current one + if (isProfileSame(profile)) { + return new PumpEnactResult() // + .success(true) // + .enacted(false) // + .comment(MainApp.gs(R.string.medtronic_cmd_basal_profile_not_set_is_same)); + } + + setRefreshButtonEnabled(false); + + if (isPumpNotReachable()) { + + setRefreshButtonEnabled(true); + + return new PumpEnactResult() // + .success(false) // + .enacted(false) // + .comment(MainApp.gs(R.string.medtronic_pump_status_pump_unreachable)); + } + + MedtronicUtil.dismissNotification(MedtronicNotificationType.PumpUnreachable); + + BasalProfile basalProfile = convertProfileToMedtronicProfile(profile); + + String profileInvalid = isProfileValid(basalProfile); + + if (profileInvalid != null) { + return new PumpEnactResult() // + .success(false) // + .enacted(false) // + .comment(MainApp.gs(R.string.medtronic_cmd_set_profile_pattern_overflow, profileInvalid)); + } + + MedtronicUITask responseTask = medtronicUIComm.executeCommand(MedtronicCommandType.SetBasalProfileSTD, + basalProfile); + + Boolean response = (Boolean) responseTask.returnData; + + if (isLoggingEnabled()) + LOG.info(getLogPrefix() + "Basal Profile was set: " + response); + + if (response) { + return new PumpEnactResult().success(true).enacted(true); + } else { + return new PumpEnactResult().success(response).enacted(response) // + .comment(MainApp.gs(R.string.medtronic_cmd_basal_profile_could_not_be_set)); + } + } + + + private String isProfileValid(BasalProfile basalProfile) { + + StringBuilder stringBuilder = new StringBuilder(); + + MedtronicPumpStatus pumpStatus = getMDTPumpStatus(); + + if (pumpStatus.maxBasal == null) + return null; + + for (BasalProfileEntry profileEntry : basalProfile.getEntries()) { + + if (profileEntry.rate > pumpStatus.maxBasal) { + stringBuilder.append(profileEntry.startTime.toString("HH:mm")); + stringBuilder.append("="); + stringBuilder.append(profileEntry.rate); + } + } + + return stringBuilder.length() == 0 ? null : stringBuilder.toString(); + } + + + @NonNull + private BasalProfile convertProfileToMedtronicProfile(Profile profile) { + + MedtronicPumpStatus pumpStatus = getMDTPumpStatus(); + + PumpType pumpType = pumpStatus.pumpType; + + BasalProfile basalProfile = new BasalProfile(); + + for (int i = 0; i < 24; i++) { + double rate = profile.getBasalTimeFromMidnight(i * 60 * 60); + + double v = pumpType.determineCorrectBasalSize(rate); + + BasalProfileEntry basalEntry = new BasalProfileEntry(v, i, 0); + basalProfile.addEntry(basalEntry); + + } + + basalProfile.generateRawDataFromEntries(); + + return basalProfile; + } + + // OPERATIONS not supported by Pump or Plugin + + private List customActions = null; + + private CustomAction customActionWakeUpAndTune = new CustomAction(R.string.medtronic_custom_action_wake_and_tune, + MedtronicCustomActionType.WakeUpAndTune); + + private CustomAction customActionClearBolusBlock = new CustomAction( + R.string.medtronic_custom_action_clear_bolus_block, MedtronicCustomActionType.ClearBolusBlock, false); + + private CustomAction customActionResetRLConfig = new CustomAction( + R.string.medtronic_custom_action_reset_rileylink, MedtronicCustomActionType.ResetRileyLinkConfiguration, true); + + + @Override + public List getCustomActions() { + + if (customActions == null) { + this.customActions = Arrays.asList(customActionWakeUpAndTune, // + customActionClearBolusBlock, // + customActionResetRLConfig); + } + + return this.customActions; + } + + + @Override + public void executeCustomAction(CustomActionType customActionType) { + + MedtronicCustomActionType mcat = (MedtronicCustomActionType) customActionType; + + switch (mcat) { + + case WakeUpAndTune: { + if (MedtronicUtil.getPumpStatus().verifyConfiguration()) { + ServiceTaskExecutor.startTask(new WakeAndTuneTask()); + } else { + Intent i = new Intent(MainApp.instance(), ErrorHelperActivity.class); + i.putExtra("soundid", R.raw.boluserror); + i.putExtra("status", MainApp.gs(R.string.medtronic_error_operation_not_possible_no_configuration)); + i.putExtra("title", MainApp.gs(R.string.combo_warning)); + i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + MainApp.instance().startActivity(i); + } + } + break; + + case ClearBolusBlock: { + this.busyTimestamps.clear(); + this.customActionClearBolusBlock.setEnabled(false); + refreshCustomActionsList(); + } + break; + + case ResetRileyLinkConfiguration: { + ServiceTaskExecutor.startTask(new ResetRileyLinkConfigurationTask()); + } + break; + + default: + break; + } + + } + + @Override + public void timeDateOrTimeZoneChanged() { + + if (isLoggingEnabled()) + LOG.warn(getLogPrefix() + "Time, Date and/or TimeZone changed. "); + + this.hasTimeDateOrTimeZoneChanged = true; + } + + private void refreshCustomActionsList() { + RxBus.INSTANCE.send(new EventCustomActionsChanged()); + } + + + public void setEnableCustomAction(MedtronicCustomActionType customAction, boolean isEnabled) { + + if (customAction == MedtronicCustomActionType.ClearBolusBlock) { + this.customActionClearBolusBlock.setEnabled(isEnabled); + } else if (customAction == MedtronicCustomActionType.ResetRileyLinkConfiguration) { + this.customActionResetRLConfig.setEnabled(isEnabled); + } + + refreshCustomActionsList(); + } + + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/MedtronicCommunicationManager.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/MedtronicCommunicationManager.java new file mode 100644 index 0000000000..e054539e8f --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/MedtronicCommunicationManager.java @@ -0,0 +1,966 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.comm; + +import android.content.Context; +import android.os.SystemClock; + +import org.joda.time.LocalDateTime; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Calendar; +import java.util.GregorianCalendar; +import java.util.List; +import java.util.Map; + +import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkCommunicationManager; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkConst; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.RFSpy; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.RileyLinkCommunicationException; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data.RFSpyResponse; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data.RLMessage; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data.RadioPacket; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data.RadioResponse; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.RLMessageType; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.tasks.ServiceTaskExecutor; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.tasks.WakeAndTuneTask; +import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil; +import info.nightscout.androidaps.plugins.pump.common.utils.DateTimeUtil; +import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.RawHistoryPage; +import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.MedtronicPumpHistoryDecoder; +import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.PumpHistoryEntry; +import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.PumpHistoryResult; +import info.nightscout.androidaps.plugins.pump.medtronic.comm.message.CarelinkLongMessageBody; +import info.nightscout.androidaps.plugins.pump.medtronic.comm.message.CarelinkShortMessageBody; +import info.nightscout.androidaps.plugins.pump.medtronic.comm.message.GetHistoryPageCarelinkMessageBody; +import info.nightscout.androidaps.plugins.pump.medtronic.comm.message.MessageBody; +import info.nightscout.androidaps.plugins.pump.medtronic.comm.message.PacketType; +import info.nightscout.androidaps.plugins.pump.medtronic.comm.message.PumpAckMessageBody; +import info.nightscout.androidaps.plugins.pump.medtronic.comm.message.PumpMessage; +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.BasalProfile; +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.BatteryStatusDTO; +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.ClockDTO; +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.PumpSettingDTO; +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.TempBasalPair; +import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicCommandType; +import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicDeviceType; +import info.nightscout.androidaps.plugins.pump.medtronic.defs.PumpDeviceState; +import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil; +import info.nightscout.androidaps.utils.SP; + +/** + * Original file created by geoff on 5/30/16. + *

+ * Split into 2 implementations, so that we can split it by target device. - Andy + * This was mostly rewritten from Original version, and lots of commands and + * functionality added. + */ +public class MedtronicCommunicationManager extends RileyLinkCommunicationManager { + + private static final Logger LOG = LoggerFactory.getLogger(L.PUMPCOMM); + private static final int MAX_COMMAND_TRIES = 3; + private static final int DEFAULT_TIMEOUT = 2000; + private static final long RILEYLINK_TIMEOUT = 15 * 60 * 1000; // 15 min + + static MedtronicCommunicationManager medtronicCommunicationManager; + String errorMessage; + private MedtronicConverter medtronicConverter; + private boolean debugSetCommands = false; + + private MedtronicPumpHistoryDecoder pumpHistoryDecoder; + private boolean doWakeUpBeforeCommand = true; + + + public MedtronicCommunicationManager(Context context, RFSpy rfspy) { + super(context, rfspy); + medtronicCommunicationManager = this; + this.medtronicConverter = new MedtronicConverter(); + this.pumpHistoryDecoder = new MedtronicPumpHistoryDecoder(); + MedtronicUtil.getPumpStatus().previousConnection = SP.getLong( + RileyLinkConst.Prefs.LastGoodDeviceCommunicationTime, 0L); + } + + + public static MedtronicCommunicationManager getInstance() { + return medtronicCommunicationManager; + } + + + @Override + protected void configurePumpSpecificSettings() { + pumpStatus = MedtronicUtil.getPumpStatus(); + } + + + @Override + public E createResponseMessage(byte[] payload, Class clazz) { + PumpMessage pumpMessage = new PumpMessage(payload); + return (E) pumpMessage; + } + + + public void setDoWakeUpBeforeCommand(boolean doWakeUp) { + this.doWakeUpBeforeCommand = doWakeUp; + } + + + public boolean isDeviceReachable() { + return isDeviceReachable(false); + } + + + /** + * We do actual wakeUp and compare PumpModel with currently selected one. If returned model is + * not Unknown, pump is reachable. + * + * @return + */ + public boolean isDeviceReachable(boolean canPreventTuneUp) { + + PumpDeviceState state = MedtronicUtil.getPumpDeviceState(); + + if (state != PumpDeviceState.PumpUnreachable) + MedtronicUtil.setPumpDeviceState(PumpDeviceState.WakingUp); + + for (int retry = 0; retry < 5; retry++) { + + if (isLogEnabled()) + LOG.debug("isDeviceReachable. Waking pump... " + (retry != 0 ? " (retry " + retry + ")" : "")); + + boolean connected = connectToDevice(); + + if (connected) + return true; + + SystemClock.sleep(1000); + + } + + if (state != PumpDeviceState.PumpUnreachable) + MedtronicUtil.setPumpDeviceState(PumpDeviceState.PumpUnreachable); + + if (!canPreventTuneUp) { + + long diff = System.currentTimeMillis() - MedtronicUtil.getPumpStatus().lastConnection; + + if (diff > RILEYLINK_TIMEOUT) { + ServiceTaskExecutor.startTask(new WakeAndTuneTask()); + } + } + + return false; + } + + + private boolean connectToDevice() { + + PumpDeviceState state = MedtronicUtil.getPumpDeviceState(); + + byte[] pumpMsgContent = createPumpMessageContent(RLMessageType.ReadSimpleData); // simple + RFSpyResponse rfSpyResponse = rfspy.transmitThenReceive(new RadioPacket(pumpMsgContent), (byte) 0, (byte) 200, + (byte) 0, (byte) 0, 25000, (byte) 0); + if (isLogEnabled()) + LOG.info("wakeup: raw response is " + ByteUtil.shortHexString(rfSpyResponse.getRaw())); + + if (rfSpyResponse.wasTimeout()) { + LOG.error("isDeviceReachable. Failed to find pump (timeout)."); + } else if (rfSpyResponse.looksLikeRadioPacket()) { + RadioResponse radioResponse = new RadioResponse(); + + try { + + radioResponse.init(rfSpyResponse.getRaw()); + + if (radioResponse.isValid()) { + + PumpMessage pumpResponse = createResponseMessage(radioResponse.getPayload(), PumpMessage.class); + + if (!pumpResponse.isValid()) { + LOG.warn("Response is invalid ! [interrupted={}, timeout={}]", rfSpyResponse.wasInterrupted(), + rfSpyResponse.wasTimeout()); + } else { + + // radioResponse.rssi; + Object dataResponse = medtronicConverter.convertResponse(MedtronicCommandType.PumpModel, + pumpResponse.getRawContent()); + + MedtronicDeviceType pumpModel = (MedtronicDeviceType) dataResponse; + boolean valid = (pumpModel != MedtronicDeviceType.Unknown_Device); + + if (MedtronicUtil.getMedtronicPumpModel() == null && valid) { + MedtronicUtil.setMedtronicPumpModel(pumpModel); + } + + if (isLogEnabled()) + LOG.debug("isDeviceReachable. PumpModel is {} - Valid: {} (rssi={})", pumpModel.name(), valid, + radioResponse.rssi); + + if (valid) { + if (state == PumpDeviceState.PumpUnreachable) + MedtronicUtil.setPumpDeviceState(PumpDeviceState.WakingUp); + else + MedtronicUtil.setPumpDeviceState(PumpDeviceState.Sleeping); + + rememberLastGoodDeviceCommunicationTime(); + + return true; + + } else { + if (state != PumpDeviceState.PumpUnreachable) + MedtronicUtil.setPumpDeviceState(PumpDeviceState.PumpUnreachable); + } + + } + + } else { + LOG.warn("isDeviceReachable. Failed to parse radio response: " + + ByteUtil.shortHexString(rfSpyResponse.getRaw())); + } + + } catch (RileyLinkCommunicationException e) { + LOG.warn("isDeviceReachable. Failed to decode radio response: " + + ByteUtil.shortHexString(rfSpyResponse.getRaw())); + } + + } else { + LOG.warn("isDeviceReachable. Unknown response: " + ByteUtil.shortHexString(rfSpyResponse.getRaw())); + } + + return false; + } + + + @Override + public boolean tryToConnectToDevice() { + return isDeviceReachable(true); + } + + + private PumpMessage runCommandWithArgs(PumpMessage msg) throws RileyLinkCommunicationException { + + if (debugSetCommands) + LOG.debug("Run command with Args: "); + + PumpMessage rval; + PumpMessage shortMessage = makePumpMessage(msg.commandType, new CarelinkShortMessageBody(new byte[]{0})); + // look for ack from short message + PumpMessage shortResponse = sendAndListen(shortMessage); + if (shortResponse.commandType == MedtronicCommandType.CommandACK) { + if (debugSetCommands) + LOG.debug("Run command with Args: Got ACK response"); + + rval = sendAndListen(msg); + if (debugSetCommands) + LOG.debug("2nd Response: {}", rval); + + return rval; + } else { + if (isLogEnabled()) + LOG.error("runCommandWithArgs: Pump did not ack Attention packet"); + return new PumpMessage("No ACK after Attention packet."); + } + } + + + private PumpMessage runCommandWithFrames(MedtronicCommandType commandType, List> frames) + throws RileyLinkCommunicationException { + + if (isLogEnabled()) + LOG.debug("Run command with Frames: {}", commandType.name()); + + PumpMessage rval = null; + PumpMessage shortMessage = makePumpMessage(commandType, new CarelinkShortMessageBody(new byte[]{0})); + // look for ack from short message + PumpMessage shortResponse = sendAndListen(shortMessage); + + if (shortResponse.commandType != MedtronicCommandType.CommandACK) { + if (isLogEnabled()) + LOG.error("runCommandWithFrames: Pump did not ack Attention packet"); + + return new PumpMessage("No ACK after start message."); + } else { + if (isLogEnabled()) + LOG.debug("Run command with Frames: Got ACK response for Attention packet"); + } + + int frameNr = 1; + + for (List frame : frames) { + + byte[] frameData = MedtronicUtil.createByteArray(frame); + + // LOG.debug("Frame {} data:\n{}", frameNr, ByteUtil.getCompactString(frameData)); + + PumpMessage msg = makePumpMessage(commandType, new CarelinkLongMessageBody(frameData)); + + rval = sendAndListen(msg); + + // LOG.debug("PumpResponse: " + rval); + + if (rval.commandType != MedtronicCommandType.CommandACK) { + LOG.error("runCommandWithFrames: Pump did not ACK frame #{}", frameNr); + + LOG.error("Run command with Frames FAILED (command={}, response={})", commandType.name(), + rval.toString()); + + return new PumpMessage("No ACK after frame #" + frameNr); + } else { + if (isLogEnabled()) + LOG.debug("Run command with Frames: Got ACK response for frame #{}", (frameNr)); + } + + frameNr++; + } + + return rval; + + } + + + public PumpHistoryResult getPumpHistory(PumpHistoryEntry lastEntry, LocalDateTime targetDate) { + + PumpHistoryResult pumpTotalResult = new PumpHistoryResult(lastEntry, targetDate == null ? null + : DateTimeUtil.toATechDate(targetDate)); + + if (doWakeUpBeforeCommand) + wakeUp(receiverDeviceAwakeForMinutes, false); + + if (isLogEnabled()) + LOG.debug("Current command: " + MedtronicUtil.getCurrentCommand()); + + MedtronicUtil.setPumpDeviceState(PumpDeviceState.Active); + boolean doneWithError = false; + + for (int pageNumber = 0; pageNumber < 5; pageNumber++) { + + RawHistoryPage rawHistoryPage = new RawHistoryPage(); + // wakeUp(receiverDeviceAwakeForMinutes, false); + PumpMessage getHistoryMsg = makePumpMessage(MedtronicCommandType.GetHistoryData, + new GetHistoryPageCarelinkMessageBody(pageNumber)); + + if (isLogEnabled()) + LOG.info("getPumpHistory: Page {}", pageNumber); + // LOG.info("getPumpHistoryPage("+pageNumber+"): "+ByteUtil.shortHexString(getHistoryMsg.getTxData())); + // Ask the pump to transfer history (we get first frame?) + + PumpMessage firstResponse = null; + boolean failed = false; + + MedtronicUtil.setCurrentCommand(MedtronicCommandType.GetHistoryData, pageNumber, null); + + for (int retries = 0; retries < MAX_COMMAND_TRIES; retries++) { + + try { + firstResponse = runCommandWithArgs(getHistoryMsg); + failed = false; + break; + } catch (RileyLinkCommunicationException e) { + if (isLogEnabled()) + LOG.error("First call for PumpHistory failed (retry={})", retries); + failed = true; + } + } + + if (failed) { + MedtronicUtil.setPumpDeviceState(PumpDeviceState.Sleeping); + return pumpTotalResult; + } + + // LOG.info("getPumpHistoryPage("+pageNumber+"): " + ByteUtil.shortHexString(firstResponse.getContents())); + + PumpMessage ackMsg = makePumpMessage(MedtronicCommandType.CommandACK, new PumpAckMessageBody()); + GetHistoryPageCarelinkMessageBody currentResponse = new GetHistoryPageCarelinkMessageBody(firstResponse + .getMessageBody().getTxData()); + int expectedFrameNum = 1; + boolean done = false; + // while (expectedFrameNum == currentResponse.getFrameNumber()) { + + int failures = 0; + while (!done) { + // examine current response for problems. + byte[] frameData = currentResponse.getFrameData(); + if ((frameData != null) && (frameData.length > 0) + && currentResponse.getFrameNumber() == expectedFrameNum) { + // success! got a frame. + if (frameData.length != 64) { + if (isLogEnabled()) + LOG.warn("Expected frame of length 64, got frame of length " + frameData.length); + // but append it anyway? + } + // handle successful frame data + rawHistoryPage.appendData(currentResponse.getFrameData()); + // RileyLinkMedtronicService.getInstance().announceProgress(((100 / 16) * + // currentResponse.getFrameNumber() + 1)); + MedtronicUtil.setCurrentCommand(MedtronicCommandType.GetHistoryData, pageNumber, + currentResponse.getFrameNumber()); + + if (isLogEnabled()) + LOG.info("getPumpHistory: Got frame {} of Page {}", currentResponse.getFrameNumber(), pageNumber); + // Do we need to ask for the next frame? + if (expectedFrameNum < 16) { // This number may not be correct for pumps other than 522/722 + expectedFrameNum++; + } else { + done = true; // successful completion + } + } else { + if (frameData == null) { + if (isLogEnabled()) + LOG.error("null frame data, retrying"); + } else if (currentResponse.getFrameNumber() != expectedFrameNum) { + if (isLogEnabled()) + LOG.warn("Expected frame number {}, received {} (retrying)", expectedFrameNum, + currentResponse.getFrameNumber()); + } else if (frameData.length == 0) { + if (isLogEnabled()) + LOG.warn("Frame has zero length, retrying"); + } + failures++; + if (failures == 6) { + if (isLogEnabled()) + LOG.error( + "getPumpHistory: 6 failures in attempting to download frame {} of page {}, giving up.", + expectedFrameNum, pageNumber); + done = true; // failure completion. + doneWithError = true; + } + } + + if (!done) { + // ask for next frame + PumpMessage nextMsg = null; + + for (int retries = 0; retries < MAX_COMMAND_TRIES; retries++) { + + try { + nextMsg = sendAndListen(ackMsg); + break; + } catch (RileyLinkCommunicationException e) { + if (isLogEnabled()) + LOG.error("Problem acknowledging frame response. (retry={})", retries); + } + } + + if (nextMsg != null) + currentResponse = new GetHistoryPageCarelinkMessageBody(nextMsg.getMessageBody().getTxData()); + else { + if (isLogEnabled()) + LOG.error("We couldn't acknowledge frame from pump, aborting operation."); + } + } + } + + if (rawHistoryPage.getLength() != 1024) { + if (isLogEnabled()) + LOG.warn("getPumpHistory: short page. Expected length of 1024, found length of " + + rawHistoryPage.getLength()); + doneWithError = true; + } + + if (!rawHistoryPage.isChecksumOK()) { + if (isLogEnabled()) + LOG.error("getPumpHistory: checksum is wrong"); + doneWithError = true; + } + + if (doneWithError) { + MedtronicUtil.setPumpDeviceState(PumpDeviceState.Sleeping); + return pumpTotalResult; + } + + rawHistoryPage.dumpToDebug(); + + List medtronicHistoryEntries = pumpHistoryDecoder + .processPageAndCreateRecords(rawHistoryPage); + + if (isLogEnabled()) + LOG.debug("getPumpHistory: Found {} history entries.", medtronicHistoryEntries.size()); + + pumpTotalResult.addHistoryEntries(medtronicHistoryEntries, pageNumber); + + if (isLogEnabled()) + LOG.debug("getPumpHistory: Search status: Search finished: {}", pumpTotalResult.isSearchFinished()); + + if (pumpTotalResult.isSearchFinished()) { + MedtronicUtil.setPumpDeviceState(PumpDeviceState.Sleeping); + + return pumpTotalResult; + } + } + + MedtronicUtil.setPumpDeviceState(PumpDeviceState.Sleeping); + + return pumpTotalResult; + + } + + + public String getErrorResponse() { + return this.errorMessage; + } + + + @Override + public byte[] createPumpMessageContent(RLMessageType type) { + switch (type) { + case PowerOn: + return MedtronicUtil.buildCommandPayload(MedtronicCommandType.RFPowerOn, // + new byte[]{2, 1, (byte) receiverDeviceAwakeForMinutes}); // maybe this is better FIXME + + case ReadSimpleData: + return MedtronicUtil.buildCommandPayload(MedtronicCommandType.PumpModel, null); + } + return new byte[0]; + } + + + private PumpMessage makePumpMessage(MedtronicCommandType messageType, byte[] body) { + return makePumpMessage(messageType, body == null ? new CarelinkShortMessageBody() + : new CarelinkShortMessageBody(body)); + } + + + private PumpMessage makePumpMessage(MedtronicCommandType messageType) { + return makePumpMessage(messageType, (byte[]) null); + } + + + private PumpMessage makePumpMessage(MedtronicCommandType messageType, MessageBody messageBody) { + PumpMessage msg = new PumpMessage(); + msg.init(PacketType.Carelink, rileyLinkServiceData.pumpIDBytes, messageType, messageBody); + return msg; + } + + + private PumpMessage sendAndGetResponse(MedtronicCommandType commandType) throws RileyLinkCommunicationException { + + return sendAndGetResponse(commandType, null, DEFAULT_TIMEOUT); + } + + + /** + * Main wrapper method for sending data - (for getting responses) + * + * @param commandType + * @param bodyData + * @param timeoutMs + * @return + */ + private PumpMessage sendAndGetResponse(MedtronicCommandType commandType, byte[] bodyData, int timeoutMs) + throws RileyLinkCommunicationException { + // wakeUp + if (doWakeUpBeforeCommand) + wakeUp(receiverDeviceAwakeForMinutes, false); + + MedtronicUtil.setPumpDeviceState(PumpDeviceState.Active); + + // create message + PumpMessage msg; + + if (bodyData == null) + msg = makePumpMessage(commandType); + else + msg = makePumpMessage(commandType, bodyData); + + // send and wait for response + PumpMessage response = sendAndListen(msg, timeoutMs); + + MedtronicUtil.setPumpDeviceState(PumpDeviceState.Sleeping); + + return response; + } + + + private PumpMessage sendAndListen(RLMessage msg) throws RileyLinkCommunicationException { + return sendAndListen(msg, 4000); // 2000 + } + + + // All pump communications go through this function. + private PumpMessage sendAndListen(RLMessage msg, int timeout_ms) throws RileyLinkCommunicationException { + return sendAndListen(msg, timeout_ms, PumpMessage.class); + } + + + private Object sendAndGetResponseWithCheck(MedtronicCommandType commandType) { + + return sendAndGetResponseWithCheck(commandType, null); + } + + + private Object sendAndGetResponseWithCheck(MedtronicCommandType commandType, byte[] bodyData) { + + if (isLogEnabled()) + LOG.debug("getDataFromPump: {}", commandType); + + for (int retries = 0; retries < MAX_COMMAND_TRIES; retries++) { + + try { + PumpMessage response = null; + + response = sendAndGetResponse(commandType, bodyData, DEFAULT_TIMEOUT + (DEFAULT_TIMEOUT * retries)); + + String check = checkResponseContent(response, commandType.commandDescription, + commandType.expectedLength); + + if (check == null) { + + Object dataResponse = medtronicConverter.convertResponse(commandType, response.getRawContent()); + + if (dataResponse != null) { + this.errorMessage = null; + if (isLogEnabled()) + LOG.debug("Converted response for {} is {}.", commandType.name(), dataResponse); + + return dataResponse; + } else { + this.errorMessage = "Error decoding response."; + } + } else { + this.errorMessage = check; + // return null; + } + + } catch (RileyLinkCommunicationException e) { + if (isLogEnabled()) + LOG.warn("Error getting response from RileyLink (error={}, retry={})", e.getMessage(), retries + 1); + } + + } + + return null; + } + + + private String checkResponseContent(PumpMessage response, String method, int expectedLength) { + + if (!response.isValid()) { + String responseData = String.format("%s: Invalid response.", method); + if (isLogEnabled()) + LOG.warn(responseData); + return responseData; + } + + byte[] contents = response.getRawContent(); + + if (contents != null) { + if (contents.length >= expectedLength) { + LOG.trace("{}: Content: {}", method, ByteUtil.shortHexString(contents)); + return null; + + } else { + String responseData = String.format( + "%s: Cannot return data. Data is too short [expected=%s, received=%s].", method, "" + + expectedLength, "" + contents.length); + + if (isLogEnabled()) + LOG.warn(responseData); + return responseData; + } + } else { + String responseData = String.format("%s: Cannot return data. Null response.", method); + LOG.warn(responseData); + return responseData; + } + } + + + // PUMP SPECIFIC COMMANDS + + public Float getRemainingInsulin() { + + Object responseObject = sendAndGetResponseWithCheck(MedtronicCommandType.GetRemainingInsulin); + + return responseObject == null ? null : (Float) responseObject; + } + + + public MedtronicDeviceType getPumpModel() { + + Object responseObject = sendAndGetResponseWithCheck(MedtronicCommandType.PumpModel); + + return responseObject == null ? null : (MedtronicDeviceType) responseObject; + } + + + public BasalProfile getBasalProfile() { + + // wakeUp + if (doWakeUpBeforeCommand) + wakeUp(receiverDeviceAwakeForMinutes, false); + + MedtronicCommandType commandType = MedtronicCommandType.GetBasalProfileSTD; + + if (isLogEnabled()) + LOG.debug("getDataFromPump: {}", commandType); + + MedtronicUtil.setCurrentCommand(commandType); + + MedtronicUtil.setPumpDeviceState(PumpDeviceState.Active); + + for (int retries = 0; retries <= MAX_COMMAND_TRIES; retries++) { + + try { + // create message + PumpMessage msg; + + msg = makePumpMessage(commandType); + + // send and wait for response + PumpMessage response = null; + + response = sendAndListen(msg, DEFAULT_TIMEOUT + (DEFAULT_TIMEOUT * retries)); + +// LOG.debug("1st Response: " + HexDump.toHexStringDisplayable(response.getRawContent())); +// LOG.debug("1st Response: " + HexDump.toHexStringDisplayable(response.getMessageBody().getTxData())); + + String check = checkResponseContent(response, commandType.commandDescription, 1); + + byte[] data = null; + + if (check == null) { + + data = response.getRawContentOfFrame(); + + PumpMessage ackMsg = makePumpMessage(MedtronicCommandType.CommandACK, new PumpAckMessageBody()); + + while (checkIfWeHaveMoreData(commandType, response, data)) { + + response = sendAndListen(ackMsg, DEFAULT_TIMEOUT + (DEFAULT_TIMEOUT * retries)); + +// LOG.debug("{} Response: {}", runs, HexDump.toHexStringDisplayable(response2.getRawContent())); +// LOG.debug("{} Response: {}", runs, +// HexDump.toHexStringDisplayable(response2.getMessageBody().getTxData())); + + String check2 = checkResponseContent(response, commandType.commandDescription, 1); + + if (check2 == null) { + + data = ByteUtil.concat(data, response.getRawContentOfFrame()); + + } else { + this.errorMessage = check2; + LOG.error("Error with response got GetProfile: " + check2); + } + } + + } else { + errorMessage = check; + } + + BasalProfile basalProfile = (BasalProfile) medtronicConverter.convertResponse(commandType, data); + + if (basalProfile != null) { + if (isLogEnabled()) + LOG.debug("Converted response for {} is {}.", commandType.name(), basalProfile); + + MedtronicUtil.setCurrentCommand(null); + MedtronicUtil.setPumpDeviceState(PumpDeviceState.Sleeping); + + return basalProfile; + } + + } catch (RileyLinkCommunicationException e) { + LOG.error("Error getting response from RileyLink (error={}, retry={})", e.getMessage(), retries + 1); + } + } + + LOG.warn("Error reading profile in max retries."); + MedtronicUtil.setCurrentCommand(null); + MedtronicUtil.setPumpDeviceState(PumpDeviceState.Sleeping); + + return null; + + } + + + private boolean checkIfWeHaveMoreData(MedtronicCommandType commandType, PumpMessage response, byte[] data) { + + if (commandType == MedtronicCommandType.GetBasalProfileSTD || // + commandType == MedtronicCommandType.GetBasalProfileA || // + commandType == MedtronicCommandType.GetBasalProfileB) { + byte[] responseRaw = response.getRawContentOfFrame(); + + int last = responseRaw.length - 1; + + LOG.debug("Length: " + data.length); + + if (data.length >= BasalProfile.MAX_RAW_DATA_SIZE) { + return false; + } + + if (responseRaw.length < 2) { + return false; + } + + return !(responseRaw[last] == 0x00 && responseRaw[last - 1] == 0x00 && responseRaw[last - 2] == 0x00); + } + + return false; + } + + + public ClockDTO getPumpTime() { + + ClockDTO clockDTO = new ClockDTO(); + clockDTO.localDeviceTime = new LocalDateTime(); + + Object responseObject = sendAndGetResponseWithCheck(MedtronicCommandType.GetRealTimeClock); + + if (responseObject != null) { + clockDTO.pumpTime = (LocalDateTime) responseObject; + return clockDTO; + } + + return null; + } + + + public TempBasalPair getTemporaryBasal() { + + Object responseObject = sendAndGetResponseWithCheck(MedtronicCommandType.ReadTemporaryBasal); + + return responseObject == null ? null : (TempBasalPair) responseObject; + } + + + public Map getPumpSettings() { + + Object responseObject = sendAndGetResponseWithCheck(MedtronicCommandType.getSettings(MedtronicUtil + .getMedtronicPumpModel())); + + return responseObject == null ? null : (Map) responseObject; + } + + + public Boolean setBolus(double units) { + + if (isLogEnabled()) + LOG.info("setBolus: " + units); + + return setCommand(MedtronicCommandType.SetBolus, MedtronicUtil.getBolusStrokes(units)); + + } + + + public boolean setTBR(TempBasalPair tbr) { + + if (isLogEnabled()) + LOG.info("setTBR: " + tbr.getDescription()); + + return setCommand(MedtronicCommandType.SetTemporaryBasal, tbr.getAsRawData()); + } + + + public Boolean setPumpTime() { + + GregorianCalendar gc = new GregorianCalendar(); + gc.add(Calendar.SECOND, 5); + + if (isLogEnabled()) + LOG.info("setPumpTime: " + DateTimeUtil.toString(gc)); + + int i = 1; + byte[] data = new byte[8]; + data[0] = 7; + data[i] = (byte) gc.get(Calendar.HOUR_OF_DAY); + data[i + 1] = (byte) gc.get(Calendar.MINUTE); + data[i + 2] = (byte) gc.get(Calendar.SECOND); + + byte[] yearByte = MedtronicUtil.getByteArrayFromUnsignedShort(gc.get(Calendar.YEAR), true); + + data[i + 3] = yearByte[0]; + data[i + 4] = yearByte[1]; + + data[i + 5] = (byte) (gc.get(Calendar.MONTH) + 1); + data[i + 6] = (byte) gc.get(Calendar.DAY_OF_MONTH); + + //LOG.info("setPumpTime: Body: " + ByteUtil.getHex(data)); + + return setCommand(MedtronicCommandType.SetRealTimeClock, data); + + } + + + private boolean setCommand(MedtronicCommandType commandType, byte[] body) { + + for (int retries = 0; retries <= MAX_COMMAND_TRIES; retries++) { + + try { + if (this.doWakeUpBeforeCommand) + wakeUp(false); + + if (debugSetCommands) + LOG.debug("{}: Body - {}", commandType.getCommandDescription(), + ByteUtil.getHex(body)); + + PumpMessage msg = makePumpMessage(commandType, new CarelinkLongMessageBody(body)); + + PumpMessage pumpMessage = runCommandWithArgs(msg); + + if (debugSetCommands) + LOG.debug("{}: {}", commandType.getCommandDescription(), pumpMessage.getResponseContent()); + + if (pumpMessage.commandType == MedtronicCommandType.CommandACK) { + return true; + } else { + LOG.warn("We received non-ACK response from pump: {}", pumpMessage.getResponseContent()); + } + + } catch (RileyLinkCommunicationException e) { + if (isLogEnabled()) + LOG.warn("Error getting response from RileyLink (error={}, retry={})", e.getMessage(), retries + 1); + } + } + + return false; + } + + + public boolean cancelTBR() { + return setTBR(new TempBasalPair(0.0d, false, 0)); + } + + + public BatteryStatusDTO getRemainingBattery() { + + Object responseObject = sendAndGetResponseWithCheck(MedtronicCommandType.GetBatteryStatus); + + return responseObject == null ? null : (BatteryStatusDTO) responseObject; + } + + + public Boolean setBasalProfile(BasalProfile basalProfile) { + + List> basalProfileFrames = MedtronicUtil.getBasalProfileFrames(basalProfile.getRawData()); + + for (int retries = 0; retries <= MAX_COMMAND_TRIES; retries++) { + + PumpMessage responseMessage = null; + try { + responseMessage = runCommandWithFrames(MedtronicCommandType.SetBasalProfileSTD, + basalProfileFrames); + + if (responseMessage.commandType == MedtronicCommandType.CommandACK) + return true; + + } catch (RileyLinkCommunicationException e) { + LOG.warn("Error getting response from RileyLink (error={}, retry={})", e.getMessage(), retries + 1); + } + + if (responseMessage != null) + LOG.warn("Set Basal Profile: Invalid response: commandType={},rawData={}", responseMessage.commandType, ByteUtil.shortHexString(responseMessage.getRawContent())); + else + LOG.warn("Set Basal Profile: Null response."); + } + + return false; + + } + + + private boolean isLogEnabled() { + return L.isEnabled(L.PUMPCOMM); + } + + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/MedtronicConverter.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/MedtronicConverter.java new file mode 100644 index 0000000000..dc0a35f2e8 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/MedtronicConverter.java @@ -0,0 +1,430 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.comm; + +import org.joda.time.IllegalFieldValueException; +import org.joda.time.LocalDateTime; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.HashMap; +import java.util.Map; + +import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil; +import info.nightscout.androidaps.plugins.pump.common.utils.StringUtil; +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.BasalProfile; +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.BatteryStatusDTO; +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.PumpSettingDTO; +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.TempBasalPair; +import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicCommandType; +import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicDeviceType; +import info.nightscout.androidaps.plugins.pump.medtronic.defs.PumpConfigurationGroup; +import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil; + +/** + * Created by andy on 5/9/18. + * High level decoder for data returned through MedtroniUIComm + */ + +public class MedtronicConverter { + + private static final Logger LOG = LoggerFactory.getLogger(L.PUMPCOMM); + + MedtronicDeviceType pumpModel; + + + public Object convertResponse(MedtronicCommandType commandType, byte[] rawContent) { + + if ((rawContent == null || rawContent.length < 1) && commandType != MedtronicCommandType.PumpModel) { + LOG.warn("Content is empty or too short, no data to convert (type={},isNull={},length={})", + commandType.name(), rawContent == null, rawContent == null ? "-" : rawContent.length); + return null; + } + + if (isLogEnabled()) + LOG.debug("Raw response before convert: " + ByteUtil.shortHexString(rawContent)); + + this.pumpModel = MedtronicUtil.getMedtronicPumpModel(); + + switch (commandType) { + + case PumpModel: { + return decodeModel(rawContent); + } + + case GetRealTimeClock: { + return decodeTime(rawContent); + } + + case GetRemainingInsulin: { + return decodeRemainingInsulin(rawContent); + } + + case GetBatteryStatus: { + return decodeBatteryStatus(rawContent); // 1 + } + + case GetBasalProfileSTD: + case GetBasalProfileA: + case GetBasalProfileB: { + return decodeBasalProfile(rawContent); + + } + + case ReadTemporaryBasal: { + return new TempBasalPair(rawContent); // 5 + } + + case Settings_512: { + return decodeSettingsLoop(rawContent); + } + + case Settings: { + return decodeSettingsLoop(rawContent); + } + + case SetBolus: { + return rawContent; // 1 + } + + default: { + throw new RuntimeException("Unsupported command Type: " + commandType); + } + + } + + } + + + private BasalProfile decodeBasalProfile(byte[] rawContent) { + + BasalProfile basalProfile = new BasalProfile(rawContent); + + return basalProfile.verify() ? basalProfile : null; + } + + + private MedtronicDeviceType decodeModel(byte[] rawContent) { + + if ((rawContent == null || rawContent.length < 4)) { + LOG.warn("Error reading PumpModel, returning Unknown_Device"); + return MedtronicDeviceType.Unknown_Device; + } + + String rawModel = StringUtil.fromBytes(ByteUtil.substring(rawContent, 1, 3)); + MedtronicDeviceType pumpModel = MedtronicDeviceType.getByDescription(rawModel); + if (isLogEnabled()) + LOG.debug("PumpModel: [raw={}, resolved={}]", rawModel, pumpModel.name()); + + if (pumpModel != MedtronicDeviceType.Unknown_Device) { + if (!MedtronicUtil.isModelSet()) { + MedtronicUtil.setMedtronicPumpModel(pumpModel); + } + } + + return pumpModel; + } + + + private BatteryStatusDTO decodeBatteryStatus(byte[] rawData) { + // 00 7C 00 00 + + BatteryStatusDTO batteryStatus = new BatteryStatusDTO(); + + int status = rawData[0]; + + if (status == 0) { + batteryStatus.batteryStatusType = BatteryStatusDTO.BatteryStatusType.Normal; + } else if (status == 1) { + batteryStatus.batteryStatusType = BatteryStatusDTO.BatteryStatusType.Low; + } else if (status == 2) { + batteryStatus.batteryStatusType = BatteryStatusDTO.BatteryStatusType.Unknown; + } + + if (rawData.length > 1) { + + // if response in 3 bytes then we add additional information + double d = (ByteUtil.toInt(rawData[1], rawData[2]) * 1.0d) / 100.0d; + + batteryStatus.voltage = d; + batteryStatus.extendedDataReceived = true; + } + + return batteryStatus; + } + + + protected Float decodeRemainingInsulin(byte[] rawData) { + int startIdx = 0; + + this.pumpModel = MedtronicUtil.getMedtronicPumpModel(); + + int strokes = pumpModel == null ? 10 : pumpModel.getBolusStrokes(); + + if (strokes == 40) { + startIdx = 2; + } + + float value = ByteUtil.toInt(rawData[startIdx], rawData[startIdx + 1]) / (1.0f * strokes); + + if (isLogEnabled()) + LOG.debug("Remaining insulin: " + value); + return value; + } + + + private LocalDateTime decodeTime(byte[] rawContent) { + + int hours = ByteUtil.asUINT8(rawContent[0]); + int minutes = ByteUtil.asUINT8(rawContent[1]); + int seconds = ByteUtil.asUINT8(rawContent[2]); + int year = (ByteUtil.asUINT8(rawContent[4]) & 0x3f) + 1984; + int month = ByteUtil.asUINT8(rawContent[5]); + int day = ByteUtil.asUINT8(rawContent[6]); + try { + LocalDateTime pumpTime = new LocalDateTime(year, month, day, hours, minutes, seconds); + return pumpTime; + } catch (IllegalFieldValueException e) { + LOG.error( + "decodeTime: Failed to parse pump time value: year=%d, month=%d, hours=%d, minutes=%d, seconds=%d", + year, month, day, hours, minutes, seconds); + return null; + } + + } + + + public Map decodeSettingsLoop(byte[] rd) { + + Map map = new HashMap<>(); + + addSettingToMap("PCFG_MAX_BOLUS", "" + decodeMaxBolus(rd), PumpConfigurationGroup.Bolus, map); + addSettingToMap( + "PCFG_MAX_BASAL", + "" + + decodeBasalInsulin(ByteUtil.makeUnsignedShort(rd[getSettingIndexMaxBasal()], + rd[getSettingIndexMaxBasal() + 1])), PumpConfigurationGroup.Basal, map); + addSettingToMap("CFG_BASE_CLOCK_MODE", rd[getSettingIndexTimeDisplayFormat()] == 0 ? "12h" : "24h", + PumpConfigurationGroup.General, map); + + addSettingToMap("PCFG_BASAL_PROFILES_ENABLED", parseResultEnable(rd[10]), PumpConfigurationGroup.Basal, map); + + if (rd[10] == 1) { + String patt; + switch (rd[11]) { + case 0: + patt = "STD"; + break; + + case 1: + patt = "A"; + break; + + case 2: + patt = "B"; + break; + + default: + patt = "???"; + break; + } + + addSettingToMap("PCFG_ACTIVE_BASAL_PROFILE", patt, PumpConfigurationGroup.Basal, map); + + } else { + addSettingToMap("PCFG_ACTIVE_BASAL_PROFILE", "STD", PumpConfigurationGroup.Basal, map); + } + + addSettingToMap("PCFG_TEMP_BASAL_TYPE", rd[14] != 0 ? "Percent" : "Units", PumpConfigurationGroup.Basal, map); + + return map; + } + + + private Map decodeSettings512(byte[] rd) { + + Map map = new HashMap<>(); + + addSettingToMap("PCFG_AUTOOFF_TIMEOUT", "" + rd[0], PumpConfigurationGroup.General, map); + + if (rd[1] == 4) { + addSettingToMap("PCFG_ALARM_MODE", "Silent", PumpConfigurationGroup.Sound, map); + } else { + addSettingToMap("PCFG_ALARM_MODE", "Normal", PumpConfigurationGroup.Sound, map); + addSettingToMap("PCFG_ALARM_BEEP_VOLUME", "" + rd[1], PumpConfigurationGroup.Sound, map); + } + + addSettingToMap("PCFG_AUDIO_BOLUS_ENABLED", parseResultEnable(rd[2]), PumpConfigurationGroup.Bolus, map); + + if (rd[2] == 1) { + addSettingToMap("PCFG_AUDIO_BOLUS_STEP_SIZE", "" + decodeBolusInsulin(ByteUtil.asUINT8(rd[3])), + PumpConfigurationGroup.Bolus, map); + } + + addSettingToMap("PCFG_VARIABLE_BOLUS_ENABLED", parseResultEnable(rd[4]), PumpConfigurationGroup.Bolus, map); + addSettingToMap("PCFG_MAX_BOLUS", "" + decodeMaxBolus(rd), PumpConfigurationGroup.Bolus, map); + addSettingToMap( + "PCFG_MAX_BASAL", + "" + + decodeBasalInsulin(ByteUtil.makeUnsignedShort(rd[getSettingIndexMaxBasal()], + rd[getSettingIndexMaxBasal() + 1])), PumpConfigurationGroup.Basal, map); + addSettingToMap("CFG_BASE_CLOCK_MODE", rd[getSettingIndexTimeDisplayFormat()] == 0 ? "12h" : "24h", + PumpConfigurationGroup.General, map); + + if (MedtronicDeviceType.isSameDevice(pumpModel, MedtronicDeviceType.Medtronic_523andHigher)) { + addSettingToMap("PCFG_INSULIN_CONCENTRATION", "" + (rd[9] == 0 ? 50 : 100), PumpConfigurationGroup.Insulin, + map); +// LOG.debug("Insulin concentration: " + rd[9]); + } else { + addSettingToMap("PCFG_INSULIN_CONCENTRATION", "" + (rd[9] != 0 ? 50 : 100), PumpConfigurationGroup.Insulin, + map); +// LOG.debug("Insulin concentration: " + rd[9]); + } + addSettingToMap("PCFG_BASAL_PROFILES_ENABLED", parseResultEnable(rd[10]), PumpConfigurationGroup.Basal, map); + + if (rd[10] == 1) { + String patt; + switch (rd[11]) { + case 0: + patt = "STD"; + break; + + case 1: + patt = "A"; + break; + + case 2: + patt = "B"; + break; + + default: + patt = "???"; + break; + } + + addSettingToMap("PCFG_ACTIVE_BASAL_PROFILE", patt, PumpConfigurationGroup.Basal, map); + + } + + addSettingToMap("CFG_MM_RF_ENABLED", parseResultEnable(rd[12]), PumpConfigurationGroup.General, map); + addSettingToMap("CFG_MM_BLOCK_ENABLED", parseResultEnable(rd[13]), PumpConfigurationGroup.General, map); + + addSettingToMap("PCFG_TEMP_BASAL_TYPE", rd[14] != 0 ? "Percent" : "Units", PumpConfigurationGroup.Basal, map); + + if (rd[14] == 1) { + addSettingToMap("PCFG_TEMP_BASAL_PERCENT", "" + rd[15], PumpConfigurationGroup.Basal, map); + } + + addSettingToMap("CFG_PARADIGM_LINK_ENABLE", parseResultEnable(rd[16]), PumpConfigurationGroup.General, map); + + decodeInsulinActionSetting(rd, map); + + return map; + } + + + public void addSettingToMap(String key, String value, PumpConfigurationGroup group, Map map) { + map.put(key, new PumpSettingDTO(key, value, group)); + } + + + public Map decodeSettings(byte[] rd) { + Map map = decodeSettings512(rd); + + addSettingToMap("PCFG_MM_RESERVOIR_WARNING_TYPE_TIME", rd[18] != 0 ? "PCFG_MM_RESERVOIR_WARNING_TYPE_TIME" + : "PCFG_MM_RESERVOIR_WARNING_TYPE_UNITS", PumpConfigurationGroup.Other, map); + + addSettingToMap("PCFG_MM_SRESERVOIR_WARNING_POINT", "" + ByteUtil.asUINT8(rd[19]), + PumpConfigurationGroup.Other, map); + + addSettingToMap("CFG_MM_KEYPAD_LOCKED", parseResultEnable(rd[20]), PumpConfigurationGroup.Other, map); + + if (MedtronicDeviceType.isSameDevice(pumpModel, MedtronicDeviceType.Medtronic_523andHigher)) { + + addSettingToMap("PCFG_BOLUS_SCROLL_STEP_SIZE", "" + rd[21], PumpConfigurationGroup.Bolus, map); + addSettingToMap("PCFG_CAPTURE_EVENT_ENABLE", parseResultEnable(rd[22]), PumpConfigurationGroup.Other, map); + addSettingToMap("PCFG_OTHER_DEVICE_ENABLE", parseResultEnable(rd[23]), PumpConfigurationGroup.Other, map); + addSettingToMap("PCFG_OTHER_DEVICE_PAIRED_STATE", parseResultEnable(rd[24]), PumpConfigurationGroup.Other, + map); + } + + return map; + } + + + protected String parseResultEnable(int i) { + switch (i) { + case 0: + return "No"; + case 1: + return "Yes"; + default: + return "???"; + } + } + + + public float getStrokesPerUnit(boolean isBasal) { + return isBasal ? 40.0f : 10; // pumpModel.getBolusStrokes(); + } + + + // 512 + public void decodeInsulinActionSetting(byte[] ai, Map map) { + if (MedtronicDeviceType.isSameDevice(pumpModel, MedtronicDeviceType.Medtronic_512_712)) { + addSettingToMap("PCFG_INSULIN_ACTION_TYPE", (ai[17] != 0 ? "Regular" : "Fast"), + PumpConfigurationGroup.Insulin, map); + } else { + int i = ai[17]; + String s = ""; + + if ((i == 0) || (i == 1)) { + s = ai[17] != 0 ? "Regular" : "Fast"; + } else { + if (i == 15) + s = "Unset"; + else + s = "Curve: " + i; + } + + addSettingToMap("PCFG_INSULIN_ACTION_TYPE", s, PumpConfigurationGroup.Insulin, map); + } + } + + + public double decodeBasalInsulin(int i) { + return (double) i / (double) getStrokesPerUnit(true); + } + + + public double decodeBolusInsulin(int i) { + + return (double) i / (double) getStrokesPerUnit(false); + } + + + private int getSettingIndexMaxBasal() { + return is523orHigher() ? 7 : 6; + } + + + private int getSettingIndexTimeDisplayFormat() { + return is523orHigher() ? 9 : 8; + } + + + public double decodeMaxBolus(byte[] ai) { + return is523orHigher() ? decodeBolusInsulin(ByteUtil.toInt(ai[5], ai[6])) : decodeBolusInsulin(ByteUtil + .asUINT8(ai[5])); + } + + + private boolean is523orHigher() { + return (MedtronicDeviceType.isSameDevice(pumpModel, MedtronicDeviceType.Medtronic_523andHigher)); + } + + + private boolean isLogEnabled() { + return L.isEnabled(L.PUMPCOMM); + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/MedtronicHistoryDecoder.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/MedtronicHistoryDecoder.java new file mode 100644 index 0000000000..fa0e58a15a --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/MedtronicHistoryDecoder.java @@ -0,0 +1,191 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.comm.history; + +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil; +import info.nightscout.androidaps.plugins.pump.common.utils.StringUtil; +import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicDeviceType; +import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil; + + +/** + * This file was taken from GGC - GNU Gluco Control (ggc.sourceforge.net), application for diabetes + * management and modified/extended for AAPS. + * + * Author: Andy {andy.rozman@gmail.com} + */ + +public abstract class MedtronicHistoryDecoder implements MedtronicHistoryDecoderInterface { + + private static final Logger LOG = LoggerFactory.getLogger(L.PUMPCOMM); + + protected ByteUtil bitUtils; + + // STATISTICS (remove at later time or not) + protected boolean statisticsEnabled = true; + protected Map unknownOpCodes; + protected Map> mapStatistics; + protected MedtronicDeviceType deviceType; + + + public MedtronicHistoryDecoder() { + } + + + // public abstract Class getHistoryEntryClass(); + + // public abstract RecordDecodeStatus decodeRecord(T record); + + public abstract void postProcess(); + + + protected abstract void runPostDecodeTasks(); + + + // TODO_ extend this to also use bigger pages (for now we support only 1024 pages) + private List checkPage(RawHistoryPage page, boolean partial) throws RuntimeException { + List byteList = new ArrayList(); + + // if (!partial && page.getData().length != 1024 /* page.commandType.getRecordLength() */) { + // LOG.error("Page size is not correct. Size should be {}, but it was {} instead.", 1024, + // page.getData().length); + // // throw exception perhaps + // return byteList; + // } + + if (MedtronicUtil.getMedtronicPumpModel() == null) { + LOG.error("Device Type is not defined."); + return byteList; + } + + if (page.getData().length != 1024) { + return ByteUtil.getListFromByteArray(page.getData()); + } else if (page.isChecksumOK()) { + return ByteUtil.getListFromByteArray(page.getOnlyData()); + } else { + return null; + } + } + + + public List processPageAndCreateRecords(RawHistoryPage rawHistoryPage) { + return processPageAndCreateRecords(rawHistoryPage, false); + } + + + protected void prepareStatistics() { + if (!statisticsEnabled) + return; + + unknownOpCodes = new HashMap(); + mapStatistics = new HashMap>(); + + for (RecordDecodeStatus stat : RecordDecodeStatus.values()) { + mapStatistics.put(stat, new HashMap()); + } + } + + + protected void addToStatistics(MedtronicHistoryEntryInterface pumpHistoryEntry, RecordDecodeStatus status, + Integer opCode) { + if (!statisticsEnabled) + return; + + if (opCode != null) { + if (!unknownOpCodes.containsKey(opCode)) { + unknownOpCodes.put(opCode, opCode); + } + return; + } + + if (!mapStatistics.get(status).containsKey(pumpHistoryEntry.getEntryTypeName())) { + mapStatistics.get(status).put(pumpHistoryEntry.getEntryTypeName(), ""); + } + } + + + protected void showStatistics() { + StringBuilder sb = new StringBuilder(); + + for (Map.Entry unknownEntry : unknownOpCodes.entrySet()) { + StringUtil.appendToStringBuilder(sb, "" + unknownEntry.getKey(), ", "); + } + + if (isLogEnabled()) + LOG.debug("STATISTICS OF PUMP DECODE"); + + if (unknownOpCodes.size() > 0) { + LOG.warn("Unknown Op Codes: {}", sb.toString()); + } + + for (Map.Entry> entry : mapStatistics.entrySet()) { + sb = new StringBuilder(); + + if (entry.getKey() != RecordDecodeStatus.OK) { + if (entry.getValue().size() == 0) + continue; + + for (Map.Entry entrysub : entry.getValue().entrySet()) { + StringUtil.appendToStringBuilder(sb, entrysub.getKey(), ", "); + } + + String spaces = StringUtils.repeat(" ", 14 - entry.getKey().name().length()); + + if (isLogEnabled()) + LOG.debug(" {}{} - {}. Elements: {}", entry.getKey().name(), spaces, entry.getValue().size(), + sb.toString()); + } else { + if (isLogEnabled()) + LOG.debug(" {} - {}", entry.getKey().name(), entry.getValue().size()); + } + } + } + + + private int getUnsignedByte(byte value) { + if (value < 0) + return value + 256; + else + return value; + } + + + protected int getUnsignedInt(int value) { + if (value < 0) + return value + 256; + else + return value; + } + + + public String getFormattedFloat(float value, int decimals) { + return StringUtil.getFormatedValueUS(value, decimals); + } + + + private List processPageAndCreateRecords(RawHistoryPage rawHistoryPage, boolean partial) { + List dataClear = checkPage(rawHistoryPage, partial); + List records = createRecords(dataClear); + + for (T record : records) { + decodeRecord(record); + } + + runPostDecodeTasks(); + + return records; + } + + protected boolean isLogEnabled() { + return L.isEnabled(L.PUMPCOMM); + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/MedtronicHistoryDecoderInterface.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/MedtronicHistoryDecoderInterface.java new file mode 100644 index 0000000000..b98b2d7d33 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/MedtronicHistoryDecoderInterface.java @@ -0,0 +1,15 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.comm.history; + +import java.util.List; + +/** + * Created by andy on 3/10/19. + */ + +public interface MedtronicHistoryDecoderInterface { + + RecordDecodeStatus decodeRecord(T record); + + List createRecords(List dataClear); + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/MedtronicHistoryEntry.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/MedtronicHistoryEntry.java new file mode 100644 index 0000000000..3b9dfbb48d --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/MedtronicHistoryEntry.java @@ -0,0 +1,316 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.comm.history; + +import com.google.gson.annotations.Expose; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil; +import info.nightscout.androidaps.plugins.pump.common.utils.DateTimeUtil; +import info.nightscout.androidaps.plugins.pump.common.utils.StringUtil; + +/** + * This file was taken from GGC - GNU Gluco Control (ggc.sourceforge.net), application for diabetes + * management and modified/extended for AAPS. + *

+ * Author: Andy {andy.rozman@gmail.com} + */ + +public abstract class MedtronicHistoryEntry implements MedtronicHistoryEntryInterface { + + protected List rawData; + + public static final Logger LOG = LoggerFactory.getLogger(MedtronicHistoryEntry.class); + + protected int[] sizes = new int[3]; + + protected byte[] head; + protected byte[] datetime; + protected byte[] body; + + // protected LocalDateTime dateTime; + + public long id; + + @Expose + public String DT; + + @Expose + public Long atechDateTime; + + @Expose + protected Map decodedData; + + public long phoneDateTime; // time on phone + + /** + * Pump id that will be used with AAPS object (time * 1000 + historyType (max is FF = 255) + */ + protected Long pumpId; + + /** + * if history object is already linked to AAPS object (either Treatment, TempBasal or TDD (tdd's + * are not actually + * linked)) + */ + public boolean linked = false; + + /** + * Linked object, see linked + */ + public Object linkedObject = null; + + + public void setLinkedObject(Object linkedObject) { + this.linked = true; + this.linkedObject = linkedObject; + } + + + public void setData(List listRawData, boolean doNotProcess) { + this.rawData = listRawData; + + // System.out.println("Head: " + sizes[0] + ", dates: " + sizes[1] + + // ", body=" + sizes[2]); + + if (doNotProcess) + return; + + head = new byte[getHeadLength() - 1]; + for (int i = 1; i < (getHeadLength()); i++) { + head[i - 1] = listRawData.get(i); + } + + if (getDateTimeLength() > 0) { + datetime = new byte[getDateTimeLength()]; + + for (int i = getHeadLength(), j = 0; j < getDateTimeLength(); i++, j++) { + datetime[j] = listRawData.get(i); + } + } + + if (getBodyLength() > 0) { + body = new byte[getBodyLength()]; + + for (int i = (getHeadLength() + getDateTimeLength()), j = 0; j < getBodyLength(); i++, j++) { + body[j] = listRawData.get(i); + } + + } + + } + + + public String getDateTimeString() { + return this.DT == null ? "Unknown" : this.DT; + } + + + public String getDecodedDataAsString() { + if (decodedData == null) + if (isNoDataEntry()) + return "No data"; + else + return ""; + else + return decodedData.toString(); + } + + + public boolean hasData() { + return (decodedData != null) || (isNoDataEntry()) || getEntryTypeName().equals("UnabsorbedInsulin"); + } + + + public boolean isNoDataEntry() { + return (sizes[0] == 2 && sizes[1] == 5 && sizes[2] == 0); + } + + + public Map getDecodedData() { + return this.decodedData; + } + + + public Object getDecodedDataEntry(String key) { + return this.decodedData != null ? this.decodedData.get(key) : null; + } + + + public boolean hasDecodedDataEntry(String key) { + return this.decodedData.containsKey(key); + } + + + public boolean showRaw() { + return getEntryTypeName().equals("EndResultTotals"); + } + + + public int getHeadLength() { + return sizes[0]; + } + + + public int getDateTimeLength() { + return sizes[1]; + } + + + public int getBodyLength() { + return sizes[2]; + } + + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + + if (this.DT == null) { + LOG.error("DT is null. RawData={}", ByteUtil.getHex(this.rawData)); + } + + sb.append(getToStringStart()); + sb.append(", DT: " + (this.DT == null ? "null" : StringUtil.getStringInLength(this.DT, 19))); + sb.append(", length="); + sb.append(getHeadLength()); + sb.append(","); + sb.append(getDateTimeLength()); + sb.append(","); + sb.append(getBodyLength()); + sb.append("("); + sb.append((getHeadLength() + getDateTimeLength() + getBodyLength())); + sb.append(")"); + + boolean hasData = hasData(); + + if (hasData) { + sb.append(", data=" + getDecodedDataAsString()); + } + + if (hasData && !showRaw()) { + sb.append("]"); + return sb.toString(); + } + + if (head != null) { + sb.append(", head="); + sb.append(ByteUtil.shortHexString(this.head)); + } + + if (datetime != null) { + sb.append(", datetime="); + sb.append(ByteUtil.shortHexString(this.datetime)); + } + + if (body != null) { + sb.append(", body="); + sb.append(ByteUtil.shortHexString(this.body)); + } + + sb.append(", rawData="); + sb.append(ByteUtil.shortHexString(this.rawData)); + sb.append("]"); + + // sb.append(" DT: "); + // sb.append(this.dateTime == null ? " - " : this.dateTime.toString("dd.MM.yyyy HH:mm:ss")); + + // sb.append(" Ext: "); + + return sb.toString(); + } + + + public abstract int getOpCode(); + + + public abstract String getToStringStart(); + + + public List getRawData() { + return rawData; + } + + + public byte getRawDataByIndex(int index) { + return rawData.get(index); + } + + + public int getUnsignedRawDataByIndex(int index) { + return ByteUtil.convertUnsignedByteToInt(rawData.get(index)); + } + + + public void setRawData(List rawData) { + this.rawData = rawData; + } + + + public byte[] getHead() { + return head; + } + + + public void setHead(byte[] head) { + this.head = head; + } + + + public byte[] getDatetime() { + return datetime; + } + + + public void setDatetime(byte[] datetime) { + this.datetime = datetime; + } + + + public byte[] getBody() { + return body; + } + + + public void setBody(byte[] body) { + this.body = body; + } + + + public void setAtechDateTime(long dt) { + this.atechDateTime = dt; + this.DT = DateTimeUtil.toString(this.atechDateTime); + } + + + public void addDecodedData(String key, Object value) { + if (decodedData == null) + decodedData = new HashMap<>(); + + decodedData.put(key, value); + } + + + public String toShortString() { + if (head == null) { + return "Unidentified record. "; + } else { + return "HistoryRecord: head=[" + ByteUtil.shortHexString(this.head) + "]"; + } + } + + public boolean containsDecodedData(String key) { + if (decodedData == null) + return false; + + return decodedData.containsKey(key); + } + + // if we extend to CGMS this need to be changed back + // public abstract PumpHistoryEntryType getEntryType(); + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/MedtronicHistoryEntryInterface.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/MedtronicHistoryEntryInterface.java new file mode 100644 index 0000000000..38b7e1dbeb --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/MedtronicHistoryEntryInterface.java @@ -0,0 +1,16 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.comm.history; + +import java.util.List; + +/** + * Created by andy on 7/24/18. + */ +public interface MedtronicHistoryEntryInterface { + + String getEntryTypeName(); + + void setData(List listRawData, boolean doNotProcess); + + int getDateLength(); + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/RawHistoryPage.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/RawHistoryPage.java new file mode 100644 index 0000000000..77ae298235 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/RawHistoryPage.java @@ -0,0 +1,88 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.comm.history; + +import java.util.Arrays; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil; +import info.nightscout.androidaps.plugins.pump.common.utils.CRC; +import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil; + +/** + * Created by geoff on 6/4/16. + */ +public class RawHistoryPage { + + private static final Logger LOG = LoggerFactory.getLogger(L.PUMPBTCOMM); + + private byte[] data = new byte[0]; + + + public RawHistoryPage() { + } + + + public void appendData(byte[] newdata) { + data = ByteUtil.concat(data, newdata); + } + + + public byte[] getData() { + return data; + } + + + public byte[] getOnlyData() { + return Arrays.copyOfRange(data, 0, 1022); + } + + + public int getLength() { + return data.length; + } + + + public boolean isChecksumOK() { + if (getLength() != 1024) { + return false; + } + byte[] computedCRC = CRC.calculate16CCITT(ByteUtil.substring(data, 0, 1022)); + + int crcCalculated = ByteUtil.toInt(computedCRC[0], computedCRC[1]); + int crcStored = ByteUtil.toInt(data[1022], data[1023]); + + if (crcCalculated != crcStored) { + LOG.error("Stored CRC ({}) is different than calculated ({}), but ignored for now.", crcStored, + crcCalculated); + } else { + if (MedtronicUtil.isLowLevelDebug()) + LOG.debug("CRC ok."); + } + + return crcCalculated == crcStored; + } + + + public void dumpToDebug() { + int linesize = 80; + int offset = 0; + + StringBuffer sb = new StringBuffer(); + + while (offset < data.length) { + int bytesToLog = linesize; + if (offset + linesize > data.length) { + bytesToLog = data.length - offset; + } + sb.append(ByteUtil.shortHexString(ByteUtil.substring(data, offset, bytesToLog)) + " "); + // sb.append("\n"); + + offset += linesize; + } + + LOG.debug("History Page Data:\n{}", sb.toString()); + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/RecordDecodeStatus.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/RecordDecodeStatus.java new file mode 100644 index 0000000000..18cb02cfad --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/RecordDecodeStatus.java @@ -0,0 +1,30 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.comm.history; + +/** + * This file was taken from GGC - GNU Gluco Control (ggc.sourceforge.net), application for diabetes + * management and modified/extended for AAPS. + * + * Author: Andy {andy.rozman@gmail.com} + */ + +public enum RecordDecodeStatus { + OK("OK "), // + Ignored("IGNORE "), // + NotSupported("N/A YET"), // + Error("ERROR "), // + WIP("WIP "), // + Unknown("UNK "); + + String description; + + + RecordDecodeStatus(String description) { + this.description = description; + + } + + + public String getDescription() { + return description; + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/cgms/CGMSHistoryEntry.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/cgms/CGMSHistoryEntry.java new file mode 100644 index 0000000000..4d35426fb8 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/cgms/CGMSHistoryEntry.java @@ -0,0 +1,92 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.comm.history.cgms; + +import org.apache.commons.lang3.StringUtils; +import org.joda.time.LocalDateTime; + +import java.util.List; + +import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil; +import info.nightscout.androidaps.plugins.pump.common.utils.DateTimeUtil; +import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.MedtronicHistoryEntry; + +/** + * This file was taken from GGC - GNU Gluco Control (ggc.sourceforge.net), application for diabetes + * management and modified/extended for AAPS. + * + * Author: Andy {andy.rozman@gmail.com} + */ + +public class CGMSHistoryEntry extends MedtronicHistoryEntry { + + private CGMSHistoryEntryType entryType; + private Integer opCode; // this is set only when we have unknown entry... + + + public CGMSHistoryEntryType getEntryType() { + return entryType; + } + + + public void setEntryType(CGMSHistoryEntryType entryType) { + this.entryType = entryType; + + this.sizes[0] = entryType.getHeadLength(); + this.sizes[1] = entryType.getDateLength(); + this.sizes[2] = entryType.getBodyLength(); + } + + + @Override + public String getEntryTypeName() { + return this.entryType.name(); + } + + + public void setData(List listRawData, boolean doNotProcess) { + if (this.entryType.schemaSet) { + super.setData(listRawData, doNotProcess); + } else { + this.rawData = listRawData; + } + } + + + @Override + public int getDateLength() { + return this.entryType.getDateLength(); + } + + + @Override + public int getOpCode() { + if (opCode == null) + return entryType.getOpCode(); + else + return opCode; + } + + + public void setOpCode(Integer opCode) { + this.opCode = opCode; + } + + + public boolean hasTimeStamp() { + return (this.entryType.hasDate()); + } + + + @Override + public String getToStringStart() { + + return "CGMSHistoryEntry [type=" + StringUtils.rightPad(entryType.name(), 18) + " [" + + StringUtils.leftPad("" + getOpCode(), 3) + ", 0x" + ByteUtil.getCorrectHexValue(getOpCode()) + "]"; + } + + + public void setDateTime(LocalDateTime timeStamp, int getIndex) { + + setAtechDateTime(DateTimeUtil.toATechDate(timeStamp.plusMinutes(getIndex * 5))); + + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/cgms/CGMSHistoryEntryType.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/cgms/CGMSHistoryEntryType.java new file mode 100644 index 0000000000..ef24ef136d --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/cgms/CGMSHistoryEntryType.java @@ -0,0 +1,135 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.comm.history.cgms; + +import java.util.HashMap; +import java.util.Map; + +/** + * This file was taken from GGC - GNU Gluco Control (ggc.sourceforge.net), application for diabetes + * management and modified/extended for AAPS. + * + * Author: Andy {andy.rozman@gmail.com} + */ + +public enum CGMSHistoryEntryType { + + None(0, "None", 1, 0, 0, DateType.None), // + + DataEnd(0x01, "DataEnd", 1, 0, 0, DateType.PreviousTimeStamp), // + SensorWeakSignal(0x02, "SensorWeakSignal", 1, 0, 0, DateType.PreviousTimeStamp), // + SensorCal(0x03, "SensorCal", 1, 0, 1, DateType.PreviousTimeStamp), // + SensorPacket(0x04, "SensorPacket", 1, 0, 1, DateType.PreviousTimeStamp), + SensorError(0x05, "SensorError", 1, 0, 1, DateType.PreviousTimeStamp), + SensorDataLow(0x06, "SensorDataLow", 1, 0, 1, DateType.PreviousTimeStamp), + SensorDataHigh(0x07, "SensorDataHigh", 1, 0, 1, DateType.PreviousTimeStamp), + SensorTimestamp(0x08, "SensorTimestamp", 1, 4, 0, DateType.MinuteSpecific), // + BatteryChange(0x0a, "BatteryChange", 1, 4, 0, DateType.MinuteSpecific), // + SensorStatus(0x0b, "SensorStatus", 1, 4, 0, DateType.MinuteSpecific), // + DateTimeChange(0x0c, "DateTimeChange", 1, 4, 0, DateType.SecondSpecific), // + SensorSync(0x0d, "SensorSync',packet_size=4", 1, 4, 0, DateType.MinuteSpecific), // + CalBGForGH(0x0e, "CalBGForGH',packet_size=5", 1, 4, 1, DateType.MinuteSpecific), // + SensorCalFactor(0x0f, "SensorCalFactor", 1, 4, 2, DateType.MinuteSpecific), // + Something10(0x10, "10-Something", 1, 4, 0, DateType.MinuteSpecific), // + Something19(0x13, "19-Something", 1, 0, 0, DateType.PreviousTimeStamp), + GlucoseSensorData(0xFF, "GlucoseSensorData", 1, 0, 0, DateType.PreviousTimeStamp); + + private static Map opCodeMap = new HashMap<>(); + + static { + for (CGMSHistoryEntryType type : values()) { + opCodeMap.put(type.opCode, type); + } + } + + public boolean schemaSet; + private int opCode; + private String description; + private int headLength; + private int dateLength; + private int bodyLength; + private int totalLength; + private DateType dateType; + + + CGMSHistoryEntryType(int opCode, String name, int head, int date, int body, DateType dateType) { + this.opCode = opCode; + this.description = name; + this.headLength = head; + this.dateLength = date; + this.bodyLength = body; + this.totalLength = (head + date + body); + this.schemaSet = true; + this.dateType = dateType; + } + + + // private CGMSHistoryEntryType(int opCode, String name, int length) + // { + // this.opCode = opCode; + // this.description = name; + // this.headLength = 0; + // this.dateLength = 0; + // this.bodyLength = 0; + // this.totalLength = length + 1; // opCode + // } + + public static CGMSHistoryEntryType getByCode(int opCode) { + if (opCodeMap.containsKey(opCode)) { + return opCodeMap.get(opCode); + } else + return CGMSHistoryEntryType.None; + } + + + public int getCode() { + return this.opCode; + } + + + public int getTotalLength() { + return totalLength; + } + + + public int getOpCode() { + return opCode; + } + + + public String getDescription() { + return description; + } + + + public int getHeadLength() { + return headLength; + } + + + public int getDateLength() { + return dateLength; + } + + + public int getBodyLength() { + return bodyLength; + } + + + public DateType getDateType() { + return dateType; + } + + + public boolean hasDate() { + return (this.dateType == DateType.MinuteSpecific) || (this.dateType == DateType.SecondSpecific); + } + + public enum DateType { + None, // + MinuteSpecific, // + SecondSpecific, // + PreviousTimeStamp // + + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/cgms/MedtronicCGMSHistoryDecoder.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/cgms/MedtronicCGMSHistoryDecoder.java new file mode 100644 index 0000000000..5a0f1e7983 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/cgms/MedtronicCGMSHistoryDecoder.java @@ -0,0 +1,470 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.comm.history.cgms; + +import org.joda.time.LocalDateTime; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.pump.common.utils.DateTimeUtil; +import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.MedtronicHistoryDecoder; +import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.RecordDecodeStatus; + +/** + * This file was taken from GGC - GNU Gluco Control (ggc.sourceforge.net), application for diabetes + * management and modified/extended for AAPS. + * + * Author: Andy {andy.rozman@gmail.com} + */ + +public class MedtronicCGMSHistoryDecoder extends MedtronicHistoryDecoder { + + private static final Logger LOG = LoggerFactory.getLogger(L.PUMPCOMM); + + + // CGMSValuesWriter cgmsValuesWriter = null; + + public MedtronicCGMSHistoryDecoder() { + } + + + public RecordDecodeStatus decodeRecord(CGMSHistoryEntry record) { + try { + return decodeRecord(record, false); + } catch (Exception ex) { + LOG.error(" Error decoding: type={}, ex={}", record.getEntryType().name(), ex.getMessage(), ex); + return RecordDecodeStatus.Error; + } + } + + + public RecordDecodeStatus decodeRecord(CGMSHistoryEntry entry, boolean x) { + + if (entry.getDateTimeLength() > 0) { + parseDate(entry); + } + + switch (entry.getEntryType()) { + + case SensorPacket: + decodeSensorPacket(entry); + break; + + case SensorError: + decodeSensorError(entry); + break; + + case SensorDataLow: + decodeDataHighLow(entry, 40); + break; + + case SensorDataHigh: + decodeDataHighLow(entry, 400); + break; + + case SensorTimestamp: + decodeSensorTimestamp(entry); + break; + + case SensorCal: + decodeSensorCal(entry); + break; + + case SensorCalFactor: + decodeSensorCalFactor(entry); + break; + + case SensorSync: + decodeSensorSync(entry); + break; + + case SensorStatus: + decodeSensorStatus(entry); + break; + + case CalBGForGH: + decodeCalBGForGH(entry); + break; + + case GlucoseSensorData: + decodeGlucoseSensorData(entry); + break; + + // just timestamp + case BatteryChange: + case Something10: + case DateTimeChange: + break; + + // just relative timestamp + case Something19: + case DataEnd: + case SensorWeakSignal: + break; + + case None: + break; + + } + + return RecordDecodeStatus.NotSupported; + } + + + @Override + public void postProcess() { + + } + + + public List createRecords(List dataClearInput) { + + List dataClear = reverseList(dataClearInput, Byte.class); + + prepareStatistics(); + + int counter = 0; + + List outList = new ArrayList(); + + // create CGMS entries (without dates) + do { + int opCode = getUnsignedInt(dataClear.get(counter)); + counter++; + + CGMSHistoryEntryType entryType; + + if (opCode == 0) { + // continue; + } else if ((opCode > 0) && (opCode < 20)) { + entryType = CGMSHistoryEntryType.getByCode(opCode); + + if (entryType == CGMSHistoryEntryType.None) { + this.unknownOpCodes.put(opCode, opCode); + LOG.warn("GlucoseHistoryEntry with unknown code: " + opCode); + + CGMSHistoryEntry pe = new CGMSHistoryEntry(); + pe.setEntryType(CGMSHistoryEntryType.None); + pe.setOpCode(opCode); + + pe.setData(Arrays.asList((byte) opCode), false); + + outList.add(pe); + } else { + // System.out.println("OpCode: " + opCode); + + List listRawData = new ArrayList(); + listRawData.add((byte) opCode); + + for (int j = 0; j < (entryType.getTotalLength() - 1); j++) { + listRawData.add(dataClear.get(counter)); + counter++; + } + + CGMSHistoryEntry pe = new CGMSHistoryEntry(); + pe.setEntryType(entryType); + + pe.setOpCode(opCode); + pe.setData(listRawData, false); + + // System.out.println("Record: " + pe); + + outList.add(pe); + } + } else { + CGMSHistoryEntry pe = new CGMSHistoryEntry(); + pe.setEntryType(CGMSHistoryEntryType.GlucoseSensorData); + + pe.setData(Arrays.asList((byte) opCode), false); + + outList.add(pe); + } + + } while (counter < dataClear.size()); + + List reversedOutList = reverseList(outList, CGMSHistoryEntry.class); + + Long timeStamp = null; + LocalDateTime dateTime = null; + int getIndex = 0; + + for (CGMSHistoryEntry entry : reversedOutList) { + + decodeRecord(entry); + + if (entry.hasTimeStamp()) { + timeStamp = entry.atechDateTime; + dateTime = DateTimeUtil.toLocalDateTime(timeStamp); + getIndex = 0; + } else if (entry.getEntryType() == CGMSHistoryEntryType.GlucoseSensorData) { + getIndex++; + if (dateTime != null) + entry.setDateTime(dateTime, getIndex); + } else { + if (dateTime != null) + entry.setDateTime(dateTime, getIndex); + } + + if (isLogEnabled()) + LOG.debug("Record: {}", entry); + } + + return reversedOutList; + + } + + + private List reverseList(List dataClearInput, Class clazz) { + + List outList = new ArrayList(); + + for (int i = dataClearInput.size() - 1; i > 0; i--) { + outList.add(dataClearInput.get(i)); + } + + return outList; + } + + + private int parseMinutes(int one) { + return (one & Integer.parseInt("0111111", 2)); + } + + + private int parseHours(int one) { + return (one & 0x1F); + } + + + private int parseDay(int one) { + return one & 0x1F; + } + + + private int parseMonths(int first_byte, int second_byte) { + + int first_two_bits = first_byte >> 6; + int second_two_bits = second_byte >> 6; + + return (first_two_bits << 2) + second_two_bits; + } + + + private int parseYear(int year) { + return (year & 0x0F) + 2000; + } + + + private Long parseDate(CGMSHistoryEntry entry) { + + if (!entry.getEntryType().hasDate()) + return null; + + byte[] data = entry.getDatetime(); + + if (entry.getEntryType().getDateType() == CGMSHistoryEntryType.DateType.MinuteSpecific) { + + Long atechDateTime = DateTimeUtil.toATechDate(parseYear(data[3]), parseMonths(data[0], data[1]), + parseDay(data[2]), parseHours(data[0]), parseMinutes(data[1]), 0); + + entry.setAtechDateTime(atechDateTime); + + return atechDateTime; + + } else if (entry.getEntryType().getDateType() == CGMSHistoryEntryType.DateType.SecondSpecific) { + LOG.warn("parseDate for SecondSpecific type is not implemented."); + throw new RuntimeException(); + // return null; + } else + return null; + + } + + + private void decodeGlucoseSensorData(CGMSHistoryEntry entry) { + int sgv = entry.getUnsignedRawDataByIndex(0) * 2; + entry.addDecodedData("sgv", sgv); + } + + + private void decodeCalBGForGH(CGMSHistoryEntry entry) { + + int amount = ((entry.getRawDataByIndex(3) & 0b00100000) << 3) | entry.getRawDataByIndex(5); + // + String originType; + + switch (entry.getRawDataByIndex(3) >> 5 & 0b00000011) { + case 0x00: + originType = "rf"; + break; + + default: + originType = "unknown"; + + } + + entry.addDecodedData("amount", amount); + entry.addDecodedData("originType", originType); + + } + + + private void decodeSensorSync(CGMSHistoryEntry entry) { + + String syncType; + + switch (entry.getRawDataByIndex(3) >> 5 & 0b00000011) { + case 0x01: + syncType = "new"; + break; + + case 0x02: + syncType = "old"; + break; + + default: + syncType = "find"; + break; + + } + + entry.addDecodedData("syncType", syncType); + } + + + private void decodeSensorStatus(CGMSHistoryEntry entry) { + + String statusType; + + switch (entry.getRawDataByIndex(3) >> 5 & 0b00000011) { + case 0x00: + statusType = "off"; + break; + + case 0x01: + statusType = "on"; + break; + + case 0x02: + statusType = "lost"; + break; + + default: + statusType = "unknown"; + } + + entry.addDecodedData("statusType", statusType); + + } + + + private void decodeSensorCalFactor(CGMSHistoryEntry entry) { + + double factor = (entry.getRawDataByIndex(5) << 8 | entry.getRawDataByIndex(6)) / 1000.0d; + + entry.addDecodedData("factor", factor); + } + + + private void decodeSensorCal(CGMSHistoryEntry entry) { + + String calibrationType; + + switch (entry.getRawDataByIndex(1)) { + case 0x00: + calibrationType = "meter_bg_now"; + break; + + case 0x01: + calibrationType = "waiting"; + break; + + case 0x02: + calibrationType = "cal_error"; + break; + + default: + calibrationType = "unknown"; + } + + entry.addDecodedData("calibrationType", calibrationType); + + } + + + private void decodeSensorTimestamp(CGMSHistoryEntry entry) { + + String sensorTimestampType; + + switch (entry.getRawDataByIndex(3) >> 5 & 0b00000011) { + + case 0x00: + sensorTimestampType = "LastRf"; + break; + + case 0x01: + sensorTimestampType = "PageEnd"; + break; + + case 0x02: + sensorTimestampType = "Gap"; + break; + + default: + sensorTimestampType = "Unknown"; + break; + + } + + entry.addDecodedData("sensorTimestampType", sensorTimestampType); + } + + + private void decodeSensorPacket(CGMSHistoryEntry entry) { + + String packetType; + + switch (entry.getRawDataByIndex(1)) { + case 0x02: + packetType = "init"; + break; + + default: + packetType = "unknown"; + } + + entry.addDecodedData("packetType", packetType); + } + + + private void decodeSensorError(CGMSHistoryEntry entry) { + + String errorType; + + switch (entry.getRawDataByIndex(1)) { + case 0x01: + errorType = "end"; + break; + + default: + errorType = "unknown"; + } + + entry.addDecodedData("errorType", errorType); + } + + + private void decodeDataHighLow(CGMSHistoryEntry entry, int sgv) { + entry.addDecodedData("sgv", sgv); + } + + + @Override + protected void runPostDecodeTasks() { + this.showStatistics(); + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/MedtronicPumpHistoryDecoder.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/MedtronicPumpHistoryDecoder.java new file mode 100644 index 0000000000..32ca320de2 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/MedtronicPumpHistoryDecoder.java @@ -0,0 +1,720 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump; + +import android.util.Log; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil; +import info.nightscout.androidaps.plugins.pump.common.utils.DateTimeUtil; +import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.MedtronicHistoryDecoder; +import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.RecordDecodeStatus; +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.BasalProfile; +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.BolusDTO; +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.BolusWizardDTO; +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.DailyTotalsDTO; +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.TempBasalPair; +import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicDeviceType; +import info.nightscout.androidaps.plugins.pump.medtronic.defs.PumpBolusType; +import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil; + +/** + * This file was taken from GGC - GNU Gluco Control (ggc.sourceforge.net), application for diabetes + * management and modified/extended for AAPS. + * + * Author: Andy {andy.rozman@gmail.com} + */ + +public class MedtronicPumpHistoryDecoder extends MedtronicHistoryDecoder { + + private static final Logger LOG = LoggerFactory.getLogger(L.PUMPCOMM); + + private PumpHistoryEntry tbrPreviousRecord; + private PumpHistoryEntry changeTimeRecord; + private MedtronicDeviceType deviceType; + private static final String TAG = "MdtPumpHistoryDecoder"; + + + public MedtronicPumpHistoryDecoder() { + } + + + public List createRecords(List dataClear) { + prepareStatistics(); + + int counter = 0; + int record = 0; + boolean incompletePacket = false; + deviceType = MedtronicUtil.getMedtronicPumpModel(); + + List outList = new ArrayList(); + String skipped = null; + int elementStart = 0; + + if (dataClear.size() == 0) { + Log.e(TAG, "Empty page."); + return outList; + } + + do { + int opCode = dataClear.get(counter); + boolean special = false; + incompletePacket = false; + + if (opCode == 0) { + counter++; + if (skipped == null) + skipped = "0x00"; + else + skipped += " 0x00"; + continue; + } else { + if (skipped != null) { + Log.w(TAG, " ... Skipped " + skipped); + skipped = null; + } + } + + PumpHistoryEntryType entryType = PumpHistoryEntryType.getByCode(opCode); + + PumpHistoryEntry pe = new PumpHistoryEntry(); + pe.setEntryType(entryType); + pe.setOffset(counter); + + counter++; + + if (counter >= 1022) { + break; + } + + List listRawData = new ArrayList(); + listRawData.add((byte) opCode); + + if (entryType == PumpHistoryEntryType.UnabsorbedInsulin + || entryType == PumpHistoryEntryType.UnabsorbedInsulin512) { + int elements = dataClear.get(counter); + listRawData.add((byte) elements); + counter++; + + int els = getUnsignedInt(elements); + + for (int k = 0; k < (els - 2); k++) { + listRawData.add((byte) dataClear.get(counter)); + counter++; + } + + special = true; + } else { + + for (int j = 0; j < (entryType.getTotalLength() - 1); j++) { + + try { + listRawData.add(dataClear.get(counter)); + counter++; + } catch (Exception ex) { + LOG.error("OpCode: " + ByteUtil.shortHexString((byte) opCode) + ", Invalid package: " + + ByteUtil.getHex(listRawData)); + // throw ex; + incompletePacket = true; + break; + } + + } + + if (incompletePacket) + break; + + } + + if (entryType == PumpHistoryEntryType.None) { + LOG.error("Error in code. We should have not come into this branch."); + } else { + + if (pe.getEntryType() == PumpHistoryEntryType.UnknownBasePacket) { + pe.setOpCode(opCode); + } + + if (entryType.getHeadLength() == 0) + special = true; + + pe.setData(listRawData, special); + + RecordDecodeStatus decoded = decodeRecord(pe); + + if ((decoded == RecordDecodeStatus.OK) || (decoded == RecordDecodeStatus.Ignored)) { + //Log.i(TAG, "#" + record + " " + decoded.getDescription() + " " + pe); + } else { + Log.w(TAG, "#" + record + " " + decoded.getDescription() + " " + pe); + } + + addToStatistics(pe, decoded, null); + + record++; + + if (decoded == RecordDecodeStatus.OK) // we add only OK records, all others are ignored + { + outList.add(pe); + } + } + + } while (counter < dataClear.size()); + + return outList; + } + + + public RecordDecodeStatus decodeRecord(PumpHistoryEntry record) { + try { + return decodeRecord(record, false); + } catch (Exception ex) { + LOG.error(" Error decoding: type={}, ex={}", record.getEntryType().name(), ex.getMessage(), ex); + return RecordDecodeStatus.Error; + } + } + + + public RecordDecodeStatus decodeRecord(PumpHistoryEntry entry, boolean x) { + + if (entry.getDateTimeLength() > 0) { + decodeDateTime(entry); + } + + switch (entry.getEntryType()) { + + // Valid entries, but not processed + case ChangeBasalPattern: + case CalBGForPH: + case ChangeRemoteId: + case ClearAlarm: + case ChangeAlarmNotifyMode: // ChangeUtility: + case EnableDisableRemote: + case BGReceived: // Ian3F: CGMS + case SensorAlert: // Ian08 CGMS + case ChangeTimeFormat: + case ChangeReservoirWarningTime: + case ChangeBolusReminderEnable: + case ChangeBolusReminderTime: + case ChangeChildBlockEnable: + case BolusWizardEnabled: + case ChangeBGReminderOffset: + case ChangeAlarmClockTime: + case ChangeMeterId: + case ChangeParadigmID: + case JournalEntryMealMarker: + case JournalEntryExerciseMarker: + case DeleteBolusReminderTime: + case SetAutoOff: + case SelfTest: + case JournalEntryInsulinMarker: + case JournalEntryOtherMarker: + case ChangeBolusWizardSetup512: + case ChangeSensorSetup2: + case ChangeSensorAlarmSilenceConfig: + case ChangeSensorRateOfChangeAlertSetup: + case ChangeBolusScrollStepSize: + case ChangeBolusWizardSetup: + case ChangeVariableBolus: + case ChangeAudioBolus: + case ChangeBGReminderEnable: + case ChangeAlarmClockEnable: + case BolusReminder: + case DeleteAlarmClockTime: + case ChangeCarbUnits: + case ChangeWatchdogEnable: + case ChangeOtherDeviceID: + case ReadOtherDevicesIDs: + case BGReceived512: + case SensorStatus: + case ReadCaptureEventEnabled: + case ChangeCaptureEventEnable: + case ReadOtherDevicesStatus: + return RecordDecodeStatus.OK; + + case Sensor_0x54: + case Sensor_0x55: + case Sensor_0x51: + case Sensor_0x52: + case EventUnknown_MM522_0x45: + case EventUnknown_MM522_0x46: + case EventUnknown_MM522_0x47: + case EventUnknown_MM522_0x48: + case EventUnknown_MM522_0x49: + case EventUnknown_MM522_0x4a: + case EventUnknown_MM522_0x4b: + case EventUnknown_MM522_0x4c: + case EventUnknown_MM512_0x10: + case EventUnknown_MM512_0x2e: + case EventUnknown_MM512_0x37: + case EventUnknown_MM512_0x38: + case EventUnknown_MM512_0x4e: + case EventUnknown_MM522_0x70: + case EventUnknown_MM512_0x88: + case EventUnknown_MM512_0x94: + case EventUnknown_MM522_0xE8: + case EventUnknown_0x4d: + case EventUnknown_MM522_0x25: + case EventUnknown_MM522_0x05: + LOG.debug(" -- ignored Unknown Pump Entry: " + entry); + return RecordDecodeStatus.Ignored; + + case UnabsorbedInsulin: + case UnabsorbedInsulin512: + return RecordDecodeStatus.Ignored; + + // **** Implemented records **** + + case DailyTotals522: + case DailyTotals523: + case DailyTotals515: + case EndResultTotals: + return decodeDailyTotals(entry); + + case ChangeBasalProfile_OldProfile: + case ChangeBasalProfile_NewProfile: + return decodeBasalProfile(entry); + + case BasalProfileStart: + return decodeBasalProfileStart(entry); + + case ChangeTime: + changeTimeRecord = entry; + return RecordDecodeStatus.OK; + + case NewTimeSet: + decodeChangeTime(entry); + return RecordDecodeStatus.OK; + + case TempBasalDuration: + // decodeTempBasal(entry); + return RecordDecodeStatus.OK; + + case TempBasalRate: + // decodeTempBasal(entry); + return RecordDecodeStatus.OK; + + case Bolus: + decodeBolus(entry); + return RecordDecodeStatus.OK; + + case BatteryChange: + decodeBatteryActivity(entry); + return RecordDecodeStatus.OK; + + case LowReservoir: + decodeLowReservoir(entry); + return RecordDecodeStatus.OK; + + case LowBattery: + case Suspend: + case Resume: + case Rewind: + case NoDeliveryAlarm: + case ChangeTempBasalType: + case ChangeMaxBolus: + case ChangeMaxBasal: + case ClearSettings: + case SaveSettings: + return RecordDecodeStatus.OK; + + case BolusWizard: + return decodeBolusWizard(entry); + + case BolusWizard512: + return decodeBolusWizard512(entry); + + case Prime: + decodePrime(entry); + return RecordDecodeStatus.OK; + + case TempBasalCombined: + return RecordDecodeStatus.Ignored; + + case None: + case UnknownBasePacket: + return RecordDecodeStatus.Error; + + default: { + LOG.debug("Not supported: " + entry.getEntryType()); + return RecordDecodeStatus.NotSupported; + } + + } + + // return RecordDecodeStatus.Error; + + } + + + private RecordDecodeStatus decodeDailyTotals(PumpHistoryEntry entry) { + + entry.addDecodedData("Raw Data", ByteUtil.getHex(entry.getRawData())); + + DailyTotalsDTO totals = new DailyTotalsDTO(entry); + + entry.addDecodedData("Object", totals); + + return RecordDecodeStatus.OK; + } + + + private RecordDecodeStatus decodeBasalProfile(PumpHistoryEntry entry) { + + // LOG.debug("decodeBasalProfile: {}", entry); + + BasalProfile basalProfile = new BasalProfile(); + basalProfile.setRawDataFromHistory(entry.getBody()); + + // LOG.debug("decodeBasalProfile BasalProfile: {}", basalProfile); + + entry.addDecodedData("Object", basalProfile); + + return RecordDecodeStatus.OK; + } + + + private void decodeChangeTime(PumpHistoryEntry entry) { + if (changeTimeRecord == null) + return; + + entry.setDisplayableValue(entry.getDateTimeString()); + + this.changeTimeRecord = null; + } + + + private void decodeBatteryActivity(PumpHistoryEntry entry) { + // this.writeData(PumpBaseType.Event, entry.getHead()[0] == 0 ? PumpEventType.BatteryRemoved : + // PumpEventType.BatteryReplaced, entry.getATechDate()); + + entry.setDisplayableValue(entry.getHead()[0] == 0 ? "Battery Removed" : "Battery Replaced"); + } + + + public static String getFormattedValue(float value, int decimals) { + return String.format(Locale.ENGLISH, "%." + decimals + "f", value); + } + + + private RecordDecodeStatus decodeBasalProfileStart(PumpHistoryEntry entry) { + byte[] body = entry.getBody(); + // int bodyOffset = headerSize + timestampSize; + int offset = body[0] * 1000 * 30 * 60; + Float rate = null; + int index = entry.getHead()[0]; + + if (MedtronicDeviceType.isSameDevice(MedtronicUtil.getMedtronicPumpModel(), + MedtronicDeviceType.Medtronic_523andHigher)) { + rate = body[1] * 0.025f; + } + + LOG.info("Basal Profile Start: offset={}, rate={}, index={}, body_raw={}", offset, rate, index, + body); + + if (rate == null) { + LOG.warn("Basal Profile Start (ERROR): offset={}, rate={}, index={}, body_raw={}", offset, rate, index, + body); + return RecordDecodeStatus.Error; + } else { + // writeData(PumpBaseType.Basal, PumpBasalType.ValueChange, getFormattedFloat(rate, 3), + // entry.getATechDate()); + entry.addDecodedData("Value", getFormattedFloat(rate, 3)); + entry.setDisplayableValue(getFormattedFloat(rate, 3)); + return RecordDecodeStatus.OK; + } + +// profileIndex = asUINT8(data[1]); +// offset = asUINT8(data[7]) * 30 * 1000 * 60; +// rate = (double)(asUINT8(data[8])) / 40.0; + + } + + + private RecordDecodeStatus decodeBolusWizard(PumpHistoryEntry entry) { + byte[] body = entry.getBody(); + + BolusWizardDTO dto = new BolusWizardDTO(); + + float bolusStrokes = 10.0f; + + if (MedtronicDeviceType.isSameDevice(MedtronicUtil.getMedtronicPumpModel(), + MedtronicDeviceType.Medtronic_523andHigher)) { + // https://github.com/ps2/minimed_rf/blob/master/lib/minimed_rf/log_entries/bolus_wizard.rb#L102 + bolusStrokes = 40.0f; + + dto.carbs = ((body[1] & 0x0c) << 6) + body[0]; + + dto.bloodGlucose = ((body[1] & 0x03) << 8) + entry.getHead()[0]; + dto.carbRatio = body[1] / 10.0f; + // carb_ratio (?) = (((self.body[2] & 0x07) << 8) + self.body[3]) / + // 10.0s + dto.insulinSensitivity = new Float(body[4]); + dto.bgTargetLow = (int)body[5]; + dto.bgTargetHigh = (int)body[14]; + dto.correctionEstimate = (((body[9] & 0x38) << 5) + body[6]) / bolusStrokes; + dto.foodEstimate = ((body[7] << 8) + body[8]) / bolusStrokes; + dto.unabsorbedInsulin = ((body[10] << 8) + body[11]) / bolusStrokes; + dto.bolusTotal = ((body[12] << 8) + body[13]) / bolusStrokes; + } else { + dto.bloodGlucose = (((body[1] & 0x0F) << 8) | entry.getHead()[0]); + dto.carbs = (int)body[0]; + dto.carbRatio = Float.valueOf(body[2]); + dto.insulinSensitivity = new Float(body[3]); + dto.bgTargetLow = (int)body[4]; + dto.bgTargetHigh = (int)body[12]; + dto.bolusTotal = body[11] / bolusStrokes; + dto.foodEstimate = body[6] / bolusStrokes; + dto.unabsorbedInsulin = body[9] / bolusStrokes; + dto.bolusTotal = body[11] / bolusStrokes; + dto.correctionEstimate = (body[7] + (body[5] & 0x0F)) / bolusStrokes; + } + + if (dto.bloodGlucose != null && dto.bloodGlucose < 0) { + dto.bloodGlucose = ByteUtil.convertUnsignedByteToInt(dto.bloodGlucose.byteValue()); + } + + dto.atechDateTime = entry.atechDateTime; + entry.addDecodedData("Object", dto); + entry.setDisplayableValue(dto.getDisplayableValue()); + + return RecordDecodeStatus.OK; + } + + + private RecordDecodeStatus decodeBolusWizard512(PumpHistoryEntry entry) { + byte[] body = entry.getBody(); + + BolusWizardDTO dto = new BolusWizardDTO(); + + float bolusStrokes = 10.0f; + + dto.bloodGlucose = ((body[1] & 0x03 << 8) | entry.getHead()[0]); + dto.carbs = (body[1] & 0xC) << 6 | body[0]; // (int)body[0]; + dto.carbRatio = Float.valueOf(body[2]); + dto.insulinSensitivity = new Float(body[3]); + dto.bgTargetLow = (int)body[4]; + dto.foodEstimate = body[6] / 10.0f; + dto.correctionEstimate = (body[7] + (body[5] & 0x0F)) / bolusStrokes; + dto.unabsorbedInsulin = body[9] / bolusStrokes; + dto.bolusTotal = body[11] / bolusStrokes; + dto.bgTargetHigh = dto.bgTargetLow; + + if (dto.bloodGlucose != null && dto.bloodGlucose < 0) { + dto.bloodGlucose = ByteUtil.convertUnsignedByteToInt(dto.bloodGlucose.byteValue()); + } + + dto.atechDateTime = entry.atechDateTime; + entry.addDecodedData("Object", dto); + entry.setDisplayableValue(dto.getDisplayableValue()); + + return RecordDecodeStatus.OK; + } + + + private void decodeLowReservoir(PumpHistoryEntry entry) { + float amount = (getUnsignedInt(entry.getHead()[0]) * 1.0f / 10.0f) * 2; + + entry.setDisplayableValue(getFormattedValue(amount, 1)); + } + + + private void decodePrime(PumpHistoryEntry entry) { + float amount = bitUtils.toInt(entry.getHead()[2], entry.getHead()[3]) / 10.0f; + float fixed = bitUtils.toInt(entry.getHead()[0], entry.getHead()[1]) / 10.0f; + +// amount = (double)(asUINT8(data[4]) << 2) / 40.0; +// programmedAmount = (double)(asUINT8(data[2]) << 2) / 40.0; +// primeType = programmedAmount == 0 ? "manual" : "fixed"; + + entry.addDecodedData("Amount", amount); + entry.addDecodedData("FixedAmount", fixed); + + entry.setDisplayableValue("Amount=" + getFormattedValue(amount, 2) + ", Fixed Amount=" + + getFormattedValue(fixed, 2)); + } + + + private void decodeChangeTempBasalType(PumpHistoryEntry entry) { + entry.addDecodedData("isPercent", ByteUtil.asUINT8(entry.getRawDataByIndex(0)) == 1); // index moved from 1 -> 0 + } + + + private void decodeBgReceived(PumpHistoryEntry entry) { + entry.addDecodedData("amount", (ByteUtil.asUINT8(entry.getRawDataByIndex(0)) << 3) + (ByteUtil.asUINT8(entry.getRawDataByIndex(3)) >> 5)); + entry.addDecodedData("meter", ByteUtil.substring(entry.getRawData(), 6, 3)); // index moved from 1 -> 0 + } + + + private void decodeCalBGForPH(PumpHistoryEntry entry) { + entry.addDecodedData("amount", ((ByteUtil.asUINT8(entry.getRawDataByIndex(5)) & 0x80) << 1) + ByteUtil.asUINT8(entry.getRawDataByIndex(0))); // index moved from 1 -> 0 + } + + + private void decodeNoDeliveryAlarm(PumpHistoryEntry entry) { + //rawtype = asUINT8(data[1]); + // not sure if this is actually NoDelivery Alarm? + } + + + @Override + public void postProcess() { + } + + + @Override + protected void runPostDecodeTasks() { + this.showStatistics(); + } + + + private void decodeBolus(PumpHistoryEntry entry) { + BolusDTO bolus = new BolusDTO(); + + byte[] data = entry.getHead(); + + if (MedtronicDeviceType.isSameDevice(MedtronicUtil.getMedtronicPumpModel(), + MedtronicDeviceType.Medtronic_523andHigher)) { + bolus.setRequestedAmount(ByteUtil.toInt(data[0], data[1]) / 40.0d); + bolus.setDeliveredAmount(ByteUtil.toInt(data[2], data[3]) / 40.0d); + bolus.setInsulinOnBoard(ByteUtil.toInt(data[4], data[5]) / 40.0d); + bolus.setDuration(data[6] * 30); + } else { + bolus.setRequestedAmount(ByteUtil.asUINT8(data[0]) / 10.0d); + bolus.setDeliveredAmount(ByteUtil.asUINT8(data[1]) / 10.0d); + bolus.setDuration(ByteUtil.asUINT8(data[2]) * 30); + } + + bolus.setBolusType((bolus.getDuration() != null && (bolus.getDuration() > 0)) ? PumpBolusType.Extended + : PumpBolusType.Normal); + bolus.setAtechDateTime(entry.atechDateTime); + + entry.addDecodedData("Object", bolus); + entry.setDisplayableValue(bolus.getDisplayableValue()); + + } + + + private void decodeTempBasal(PumpHistoryEntry entry) { + + if (this.tbrPreviousRecord == null) { + // LOG.debug(this.tbrPreviousRecord.toString()); + this.tbrPreviousRecord = entry; + return; + } + + decodeTempBasal(this.tbrPreviousRecord, entry); + + tbrPreviousRecord = null; + } + + + public static void decodeTempBasal(PumpHistoryEntry tbrPreviousRecord, PumpHistoryEntry entry) { + + PumpHistoryEntry tbrRate = null, tbrDuration = null; + + if (entry.getEntryType() == PumpHistoryEntryType.TempBasalRate) { + tbrRate = entry; + } else { + tbrDuration = entry; + } + + if (tbrRate != null) { + tbrDuration = tbrPreviousRecord; + } else { + tbrRate = tbrPreviousRecord; + } + + TempBasalPair tbr = new TempBasalPair(tbrRate.getHead()[0], tbrDuration.getHead()[0], (ByteUtil.asUINT8(tbrRate + .getDatetime()[4]) >> 3) == 0); + + // System.out.println("TBR: amount=" + tbr.getInsulinRate() + ", duration=" + tbr.getDurationMinutes() + // // + " min. Packed: " + tbr.getValue() + // ); + + entry.addDecodedData("Object", tbr); + entry.setDisplayableValue(tbr.getDescription()); + + } + + + private void decodeDateTime(PumpHistoryEntry entry) { + byte[] dt = entry.getDatetime(); + + if (dt == null) { + LOG.warn("DateTime not set."); + } + + if (entry.getDateTimeLength() == 5) { + + int seconds = dt[0] & 0x3F; + int minutes = dt[1] & 0x3F; + int hour = dt[2] & 0x1F; + + int month = ((dt[0] >> 4) & 0x0c) + ((dt[1] >> 6) & 0x03); + // ((dt[0] & 0xC0) >> 6) | ((dt[1] & 0xC0) >> 4); + + int dayOfMonth = dt[3] & 0x1F; + int year = fix2DigitYear(dt[4] & 0x3F); // Assuming this is correct, need to verify. Otherwise this will be + // a problem in 2016. + + entry.setAtechDateTime(DateTimeUtil.toATechDate(year, month, dayOfMonth, hour, minutes, seconds)); + + } else if (entry.getDateTimeLength() == 2) { + int low = ByteUtil.asUINT8(dt[0]) & 0x1F; + int mhigh = (ByteUtil.asUINT8(dt[0]) & 0xE0) >> 4; + int mlow = (ByteUtil.asUINT8(dt[1]) & 0x80) >> 7; + int month = mhigh + mlow; + // int dayOfMonth = low + 1; + int dayOfMonth = dt[0] & 0x1F; + int year = 2000 + (ByteUtil.asUINT8(dt[1]) & 0x7F); + + int hour = 0; + int minutes = 0; + int seconds = 0; + + //LOG.debug("DT: {} {} {}", year, month, dayOfMonth); + + if (dayOfMonth == 32) { + LOG.warn("Entry: Day 32 {} = [{}] {}", entry.getEntryType().name(), + ByteUtil.getHex(entry.getRawData()), entry); + } + + if (isEndResults(entry.getEntryType())) { + hour = 23; + minutes = 59; + seconds = 59; + } + + entry.setAtechDateTime(DateTimeUtil.toATechDate(year, month, dayOfMonth, hour, minutes, seconds)); + + } else { + LOG.warn("Unknown datetime format: " + entry.getDateTimeLength()); + } + + } + + + private boolean isEndResults(PumpHistoryEntryType entryType) { + + return (entryType == PumpHistoryEntryType.EndResultTotals || + entryType == PumpHistoryEntryType.DailyTotals515 || + entryType == PumpHistoryEntryType.DailyTotals522 || + entryType == PumpHistoryEntryType.DailyTotals523); + } + + + private int fix2DigitYear(int year) { + if (year > 90) { + year += 1900; + } else { + year += 2000; + } + + return year; + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/PumpHistoryEntry.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/PumpHistoryEntry.java new file mode 100644 index 0000000000..5a290c6a02 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/PumpHistoryEntry.java @@ -0,0 +1,177 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump; + +import com.google.gson.annotations.Expose; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Objects; + +import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil; +import info.nightscout.androidaps.plugins.pump.common.utils.StringUtil; +import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.MedtronicHistoryEntry; + +/** + * This file was taken from GGC - GNU Gluco Control (ggc.sourceforge.net), application for diabetes + * management and modified/extended for AAPS. + *

+ * Author: Andy {andy.rozman@gmail.com} + */ + +public class PumpHistoryEntry extends MedtronicHistoryEntry { + + private static Logger LOG = LoggerFactory.getLogger(PumpHistoryEntry.class); + + @Expose + private PumpHistoryEntryType entryType; + private Integer opCode; // this is set only when we have unknown entry... + private int offset; + private String displayableValue = ""; + + + public PumpHistoryEntryType getEntryType() { + return entryType; + } + + + public void setEntryType(PumpHistoryEntryType entryType) { + this.entryType = entryType; + + this.sizes[0] = entryType.getHeadLength(); + this.sizes[1] = entryType.getDateLength(); + this.sizes[2] = entryType.getBodyLength(); + + if (this.entryType != null && this.atechDateTime != null) + setPumpId(); + } + + + private void setPumpId() { + this.pumpId = this.entryType.getCode() + (this.atechDateTime * 1000L); + } + + + @Override + public int getOpCode() { + if (opCode == null) + return entryType.getOpCode(); + else + return opCode; + } + + + public void setOpCode(Integer opCode) { + this.opCode = opCode; + } + + + @Override + public String getToStringStart() { + return "PumpHistoryEntry [type=" + StringUtil.getStringInLength(entryType.name(), 20) + " [" + + StringUtil.getStringInLength("" + getOpCode(), 3) + ", 0x" + + ByteUtil.shortHexString((byte) getOpCode()) + "]"; + } + + + public String toString() { + Object object = this.getDecodedDataEntry("Object"); + + if (object == null) { + return super.toString(); + } else { + return "PumpHistoryEntry [DT: " + DT + ", Object=" + object.toString() + "]"; + } + } + + + public int getOffset() { + return offset; + } + + + public void setOffset(int offset) { + this.offset = offset; + } + + + @Override + public String getEntryTypeName() { + return this.entryType.name(); + } + + + @Override + public int getDateLength() { + return this.entryType.getDateLength(); + } + + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + + if (!(o instanceof PumpHistoryEntry)) + return false; + + PumpHistoryEntry that = (PumpHistoryEntry) o; + + return entryType == that.entryType && // + this.atechDateTime == that.atechDateTime; // && // + // Objects.equals(this.decodedData, that.decodedData); + } + + + @Override + public int hashCode() { + return Objects.hash(entryType, opCode, offset); + } + + + // public boolean isAfter(LocalDateTime dateTimeIn) { + // // LOG.debug("Entry: " + this.dateTime); + // // LOG.debug("Datetime: " + dateTimeIn); + // // LOG.debug("Item after: " + this.dateTime.isAfter(dateTimeIn)); + // return this.dateTime.isAfter(dateTimeIn); + // } + + public boolean isAfter(long atechDateTime) { + if (this.atechDateTime == null) { + LOG.error("Date is null. Show object: " + toString()); + return false; // FIXME shouldn't happen + } + + return atechDateTime < this.atechDateTime; + } + + + public void setDisplayableValue(String displayableValue) { + this.displayableValue = displayableValue; + } + + + public String getDisplayableValue() { + return displayableValue; + } + + public static class Comparator implements java.util.Comparator { + + @Override + public int compare(PumpHistoryEntry o1, PumpHistoryEntry o2) { + int data = (int) (o2.atechDateTime - o1.atechDateTime); + + if (data != 0) + return data; + + return o2.getEntryType().getCode() - o1.getEntryType().getCode(); + } + } + + + public Long getPumpId() { + setPumpId(); + + return pumpId; + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/PumpHistoryEntryGroup.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/PumpHistoryEntryGroup.java new file mode 100644 index 0000000000..33a7c89870 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/PumpHistoryEntryGroup.java @@ -0,0 +1,73 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump; + +import java.util.ArrayList; +import java.util.List; + +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.R; + +/** + * This file was taken from GGC - GNU Gluco Control (ggc.sourceforge.net), application for diabetes + * management and modified/extended for AAPS. + * + * Author: Andy {andy.rozman@gmail.com} + */ + +public enum PumpHistoryEntryGroup { + + All(R.string.medtronic_history_group_all), // + Bolus(R.string.danar_history_bolus), // + Basal(R.string.medtronic_history_group_basal), // + Prime(R.string.danar_history_prime), // + Configuration(R.string.medtronic_history_group_configuration), // + Alarm(R.string.danar_history_alarm), // + Glucose(R.string.danar_history_glucose), // + Notification(R.string.medtronic_history_group_notification), // + Statistic(R.string.medtronic_history_group_statistic), + Unknown(R.string.medtronic_history_group_unknown), // + ; + + private int resourceId; + private String translated; + + public static boolean doNotTranslate = false; + + private static List list; + + static { + list = new ArrayList<>(); + + for (PumpHistoryEntryGroup pumpHistoryEntryGroup : values()) { + //if (doNotTranslate) { + pumpHistoryEntryGroup.translated = MainApp.gs(pumpHistoryEntryGroup.resourceId); + //} + list.add(pumpHistoryEntryGroup); + } + } + + + PumpHistoryEntryGroup(int resourceId) { + this.resourceId = resourceId; + // this.translated = MainApp.gs(resourceId); + } + + + public static List getList() { + return list; + } + + + public int getResourceId() { + return resourceId; + } + + + public String getTranslated() { + return translated; + } + + + public String toString() { + return this.translated; + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/PumpHistoryEntryType.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/PumpHistoryEntryType.java new file mode 100644 index 0000000000..dec0cb80f6 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/PumpHistoryEntryType.java @@ -0,0 +1,432 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicDeviceType; +import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil; + +/** + * This file was taken from GGC - GNU Gluco Control (ggc.sourceforge.net), application for diabetes + * management and modified/extended for AAPS. + * + * Author: Andy {andy.rozman@gmail.com} + */ + +public enum PumpHistoryEntryType // implements CodeEnum +{ + + None(0, "None", PumpHistoryEntryGroup.Unknown, 1, 0, 0), + + Bolus(0x01, "Bolus", PumpHistoryEntryGroup.Bolus, 4, 5, 0), // 523+[H=8] 9/13 + + Prime(0x03, "Prime", PumpHistoryEntryGroup.Prime, 5, 5, 0), // + + /**/EventUnknown_MM522_0x05((byte) 0x05, "Unknown Event 0x05", PumpHistoryEntryGroup.Unknown, 2, 5, 28), // + NoDeliveryAlarm(0x06, "No Delivery", PumpHistoryEntryGroup.Alarm, 4, 5, 0), // + EndResultTotals(0x07, "End Result Totals", PumpHistoryEntryGroup.Statistic, 5, 2, 0), + ChangeBasalProfile_OldProfile(0x08, "Change Basal Profile (Old)", PumpHistoryEntryGroup.Basal, 2, 5, 145), + ChangeBasalProfile_NewProfile(0x09, "Change Basal Profile (New)", PumpHistoryEntryGroup.Basal, 2, 5, 145), // + /**/EventUnknown_MM512_0x10(0x10, "Unknown Event 0x10", PumpHistoryEntryGroup.Unknown), // 29, 5, 0 + CalBGForPH(0x0a, "BG Capture", PumpHistoryEntryGroup.Glucose), // + SensorAlert(0x0b, "Sensor Alert", PumpHistoryEntryGroup.Alarm, 3, 5, 0), // Ian08 + ClearAlarm(0x0c, "Clear Alarm", PumpHistoryEntryGroup.Alarm, 2, 5, 0), // 2,5,4 + + // Andy0d(0x0d, "Unknown", 2, 5, 0), + + ChangeBasalPattern(0x14, "Change Basal Pattern", PumpHistoryEntryGroup.Basal), // + TempBasalDuration(0x16, "TBR Duration", PumpHistoryEntryGroup.Basal), // + ChangeTime(0x17, "Change Time", PumpHistoryEntryGroup.Configuration), // + NewTimeSet(0x18, "New Time Set", PumpHistoryEntryGroup.Notification), // + LowBattery(0x19, "LowBattery", PumpHistoryEntryGroup.Notification), // + BatteryChange(0x1a, "Battery Change", PumpHistoryEntryGroup.Notification), // + SetAutoOff(0x1b, "Set Auto Off", PumpHistoryEntryGroup.Configuration), // + Suspend(0x1e, "Suspend", PumpHistoryEntryGroup.Basal), // + Resume(0x1f, "Resume", PumpHistoryEntryGroup.Basal), // + SelfTest(0x20, "Self Test", PumpHistoryEntryGroup.Statistic), // + Rewind(0x21, "Rewind", PumpHistoryEntryGroup.Prime), // + ClearSettings(0x22, "Clear Settings", PumpHistoryEntryGroup.Configuration), // + ChangeChildBlockEnable(0x23, "Change Child Block Enable", PumpHistoryEntryGroup.Configuration), // + ChangeMaxBolus(0x24, "Change Max Bolus", PumpHistoryEntryGroup.Configuration), // + /**/EventUnknown_MM522_0x25(0x25, "Unknown Event 0x25", PumpHistoryEntryGroup.Unknown), // 8? + EnableDisableRemote(0x26, "Enable/Disable Remote", PumpHistoryEntryGroup.Configuration, 2, 5, 14), // 2, 5, 14 V6:2,5,14 + ChangeRemoteId(0x27, "Change Remote ID", PumpHistoryEntryGroup.Configuration), // ?? + + ChangeMaxBasal(0x2c, "Change Max Basal", PumpHistoryEntryGroup.Configuration), // + BolusWizardEnabled(0x2d, "Bolus Wizard Enabled", PumpHistoryEntryGroup.Configuration), // V3 ? + /**/EventUnknown_MM512_0x2e(0x2e, "Unknown Event 0x2e", PumpHistoryEntryGroup.Unknown), // + BolusWizard512(0x2f, "Bolus Wizard (512)", PumpHistoryEntryGroup.Bolus, 2, 5, 12), // + UnabsorbedInsulin512(0x30, "Unabsorbed Insulin (512)", PumpHistoryEntryGroup.Statistic, 5, 0, 0), // FIXME + ChangeBGReminderOffset(0x31, "Change BG Reminder Offset", PumpHistoryEntryGroup.Configuration), // + ChangeAlarmClockTime(0x32, "Change Alarm Clock Time", PumpHistoryEntryGroup.Configuration), // + TempBasalRate(0x33, "TBR Rate", PumpHistoryEntryGroup.Basal, 2, 5, 1), // + LowReservoir(0x34, "Low Reservoir", PumpHistoryEntryGroup.Notification), // + ChangeAlarmClock(0x35, "Change Alarm Clock", PumpHistoryEntryGroup.Configuration), // + ChangeMeterId(0x36, "Change Meter ID", PumpHistoryEntryGroup.Configuration), // + /**/EventUnknown_MM512_0x37(0x37, "Unknown Event 0x37", PumpHistoryEntryGroup.Unknown), // V:MM512 + /**/EventUnknown_MM512_0x38(0x38, "Unknown Event 0x38", PumpHistoryEntryGroup.Unknown), // + BGReceived512(0x39, "BG Received (512)", PumpHistoryEntryGroup.Glucose), // + /**/EventUnknown_MM512_0x3a(0x3a, "Unknown Event 0x3a", PumpHistoryEntryGroup.Unknown), // + SensorStatus(0x3b, "Sensor Status", PumpHistoryEntryGroup.Glucose), // + ChangeParadigmID(0x3c, "Change Paradigm ID", PumpHistoryEntryGroup.Configuration, 2, 5, 14), // V3 ? V6: 2,5,14 ?? is it this length or just 7 + EventUnknown_MM512_0x3D(0x3d, "Unknown Event 0x3D", PumpHistoryEntryGroup.Unknown), // + EventUnknown_MM512_0x3E(0x3e, "Unknown Event 0x3E", PumpHistoryEntryGroup.Unknown), // + BGReceived(0x3f, "BG Received", PumpHistoryEntryGroup.Glucose, 2, 5, 3), // Ian3F + JournalEntryMealMarker(0x40, "Meal Marker", PumpHistoryEntryGroup.Bolus, 2, 5, 2), // is size just 7??? V6 + JournalEntryExerciseMarker(0x41, "Exercise Marker", PumpHistoryEntryGroup.Bolus, 2, 5, 1), // ?? + JournalEntryInsulinMarker(0x42, "Insulin Marker", PumpHistoryEntryGroup.Bolus, 2, 5, 0), // V6 = body(0)/was=1 + JournalEntryOtherMarker(0x43, "Other Marker", PumpHistoryEntryGroup.Bolus, 2, 5, 1), // V6 = body(1)/was=0 + EnableSensorAutoCal(0x44, "Enable Sensor AutoCal", PumpHistoryEntryGroup.Glucose), // + /**/EventUnknown_MM522_0x45(0x45, "Unknown Event 0x45", PumpHistoryEntryGroup.Unknown, 2, 5, 1), // + /**/EventUnknown_MM522_0x46(0x46, "Unknown Event 0x46", PumpHistoryEntryGroup.Unknown, 2, 5, 1), // + /**/EventUnknown_MM522_0x47(0x47, "Unknown Event 0x47", PumpHistoryEntryGroup.Unknown, 2, 5, 1), // + /**/EventUnknown_MM522_0x48(0x48, "Unknown Event 0x48", PumpHistoryEntryGroup.Unknown, 2, 5, 1), // + /**/EventUnknown_MM522_0x49(0x49, "Unknown Event 0x49", PumpHistoryEntryGroup.Unknown, 2, 5, 1), // + /**/EventUnknown_MM522_0x4a(0x4a, "Unknown Event 0x4a", PumpHistoryEntryGroup.Unknown, 2, 5, 1), // + /**/EventUnknown_MM522_0x4b(0x4b, "Unknown Event 0x4b", PumpHistoryEntryGroup.Unknown, 2, 5, 1), // + /**/EventUnknown_MM522_0x4c(0x4c, "Unknown Event 0x4c", PumpHistoryEntryGroup.Unknown, 2, 5, 1), // + /**/EventUnknown_0x4d(0x4d, "Unknown Event 0x4d", PumpHistoryEntryGroup.Unknown), // V5: 512: 7, 522: 8 ????NS + /**/EventUnknown_MM512_0x4e(0x4e, "Unknown Event 0x4e", PumpHistoryEntryGroup.Unknown), // /**/ + ChangeBolusWizardSetup512(0x4f, "Bolus Wizard Setup (512)", PumpHistoryEntryGroup.Configuration, 2, 5, 32), // + ChangeSensorSetup2(0x50, "Sensor Setup2", PumpHistoryEntryGroup.Configuration, 2, 5, 30), // Ian50 + /**/Sensor_0x51(0x51, "Unknown Event 0x51", PumpHistoryEntryGroup.Unknown), // + /**/Sensor_0x52(0x52, "Unknown Event 0x52", PumpHistoryEntryGroup.Unknown), // + ChangeSensorAlarmSilenceConfig(0x53, "Sensor Alarm Silence Config", PumpHistoryEntryGroup.Configuration, 2, 5, 1), // 8 + + /**/Sensor_0x54(0x54, "Unknown Event 0x54", PumpHistoryEntryGroup.Unknown), // Ian54 + /**/Sensor_0x55(0x55, "Unknown Event 0x55", PumpHistoryEntryGroup.Unknown), // + ChangeSensorRateOfChangeAlertSetup(0x56, "Sensor Rate Of Change Alert Setup", PumpHistoryEntryGroup.Configuration, 2, 5, 5), // 12 + ChangeBolusScrollStepSize(0x57, "Change Bolus Scroll Step Size", PumpHistoryEntryGroup.Configuration), // + + // V4 + // Andy58(0x58, "Unknown", 13, 5, 0), // TO DO is this one really there ??? + + ChangeBolusWizardSetup(0x5a, "Bolus Wizard Setup (512)", PumpHistoryEntryGroup.Configuration, 2, 5, 137), // V2: 522+[B=143] // V6 124 -> 144 + BolusWizard(0x5b, "Bolus Wizard Estimate", PumpHistoryEntryGroup.Configuration, 2, 5, 13), // 15 // + UnabsorbedInsulin(0x5c, "Unabsorbed Insulin", PumpHistoryEntryGroup.Statistic, 5, 0, 0), // head[1] -> body + SaveSettings(0x5d, "Save Settings", PumpHistoryEntryGroup.Configuration), // + ChangeVariableBolus(0x5e, "Change Variable Bolus", PumpHistoryEntryGroup.Configuration), // + ChangeAudioBolus(0x5f, "Easy Bolus Enabled", PumpHistoryEntryGroup.Configuration), // V3 ? + ChangeBGReminderEnable(0x60, "BG Reminder Enable", PumpHistoryEntryGroup.Configuration), // questionable60 + ChangeAlarmClockEnable(0x61, "Alarm Clock Enable", PumpHistoryEntryGroup.Configuration), // + ChangeTempBasalType((byte) 0x62, "Change Basal Type", PumpHistoryEntryGroup.Configuration), // ChangeTempBasalTypePumpEvent + ChangeAlarmNotifyMode(0x63, "Change Alarm Notify Mode", PumpHistoryEntryGroup.Configuration), // + ChangeTimeFormat(0x64, "Change Time Format", PumpHistoryEntryGroup.Configuration), // + ChangeReservoirWarningTime((byte) 0x65, "Change Reservoir Warning Time", PumpHistoryEntryGroup.Configuration), // + ChangeBolusReminderEnable(0x66, "Change Bolus Reminder Enable", PumpHistoryEntryGroup.Configuration, 2, 5, 2), // 9 + ChangeBolusReminderTime((byte) 0x67, "Change Bolus Reminder Time", PumpHistoryEntryGroup.Configuration, 2, 5, 2), // 9 + DeleteBolusReminderTime((byte) 0x68, "Delete Bolus Reminder Time", PumpHistoryEntryGroup.Configuration, 2, 5, 2), // 9 + BolusReminder(0x69, "Bolus Reminder", PumpHistoryEntryGroup.Configuration, 2, 5, 0), // Ian69 + DeleteAlarmClockTime(0x6a, "Delete Alarm Clock Time", PumpHistoryEntryGroup.Configuration, 2, 5, 7), // 14 + + DailyTotals515(0x6c, "Daily Totals (515)", PumpHistoryEntryGroup.Statistic, 1, 2, 35), // v4: 0,0,36. v5: 1,2,33 + DailyTotals522(0x6d, "Daily Totals (522)", PumpHistoryEntryGroup.Statistic, 1, 2, 41), // + DailyTotals523(0x6e, "Daily Totals (523)", PumpHistoryEntryGroup.Statistic, 1, 2, 49), // 1102014-03-17T00:00:00 + ChangeCarbUnits((byte) 0x6f, "Change Carb Units", PumpHistoryEntryGroup.Configuration), // + /**/EventUnknown_MM522_0x70((byte) 0x70, "Unknown Event 0x70", PumpHistoryEntryGroup.Unknown, 2, 5, 1), // + + BasalProfileStart(0x7b, "Basal Profile Start", PumpHistoryEntryGroup.Basal, 2, 5, 3), // // 722 + ChangeWatchdogEnable((byte) 0x7c, "Change Watchdog Enable", PumpHistoryEntryGroup.Configuration), // + ChangeOtherDeviceID((byte) 0x7d, "Change Other Device ID", PumpHistoryEntryGroup.Configuration, 2, 5, 30), // + + ChangeWatchdogMarriageProfile(0x81, "Change Watchdog Marriage Profile", PumpHistoryEntryGroup.Configuration, 2, 5, 5), // 12 + DeleteOtherDeviceID(0x82, "Delete Other Device ID", PumpHistoryEntryGroup.Configuration, 2, 5, 5), // + ChangeCaptureEventEnable(0x83, "Change Capture Event Enable", PumpHistoryEntryGroup.Configuration), // + + /**/EventUnknown_MM512_0x88(0x88, "Unknown Event 0x88", PumpHistoryEntryGroup.Unknown), // + + /**/EventUnknown_MM512_0x94(0x94, "Unknown Event 0x94", PumpHistoryEntryGroup.Unknown), // + // IanA8(0xA8, "xx", 10, 5, 0), // + + // Andy90(0x90, "Unknown", 7, 5, 0), + + // AndyB4(0xb4, "Unknown", 7, 5, 0), + // Andy4A(0x4a, "Unknown", 5, 5, 0), + + // head[1], + // body[49] op[0x6e] + + /**/EventUnknown_MM522_0xE8(0xe8, "Unknown Event 0xE8", PumpHistoryEntryGroup.Unknown, 2, 5, 25), // + + ReadOtherDevicesIDs(0xf0, "Read Other Devices IDs", PumpHistoryEntryGroup.Configuration), // ? + ReadCaptureEventEnabled(0xf1, "Read Capture Event Enabled", PumpHistoryEntryGroup.Configuration), // ? + ChangeCaptureEventEnable2(0xf2, "Change Capture Event Enable2", PumpHistoryEntryGroup.Configuration), // ? + ReadOtherDevicesStatus(0xf3, "Read Other Devices Status", PumpHistoryEntryGroup.Configuration), // ? + + TempBasalCombined(0xfe, "TBR", PumpHistoryEntryGroup.Basal), // + UnknownBasePacket(0xff, "Unknown Base Packet", PumpHistoryEntryGroup.Unknown); + + private static Map opCodeMap = new HashMap(); + private static PumpHistoryEntryType tddType; + + static { + for (PumpHistoryEntryType type : values()) { + opCodeMap.put(type.opCode, type); + } + + setSpecialRulesForEntryTypes(); + } + + private int opCode; + private String description; + private int headLength = 0; + private int dateLength; + // private MinimedDeviceType deviceType; + private int bodyLength; + private int totalLength; + // special rules need to be put in list from highest to lowest (e.g.: + // 523andHigher=12, 515andHigher=10 and default (set in cnstr) would be 8) + private List specialRulesHead; + private List specialRulesBody; + private boolean hasSpecialRules = false; + private PumpHistoryEntryGroup group = PumpHistoryEntryGroup.Unknown; + private static Object TDDType; + + + PumpHistoryEntryType(int opCode, String name, PumpHistoryEntryGroup group) { + this(opCode, name, group, 2, 5, 0); + } + + + PumpHistoryEntryType(int opCode, PumpHistoryEntryGroup group) { + this(opCode, null, group, 2, 5, 0); + } + + + PumpHistoryEntryType(int opCode, PumpHistoryEntryGroup group, int head, int date, int body) { + this(opCode, null, group, head, date, body); + } + + + PumpHistoryEntryType(int opCode, String name, PumpHistoryEntryGroup group, int head, int date, int body) { + this.opCode = (byte) opCode; + this.description = name; + this.headLength = head; + this.dateLength = date; + this.bodyLength = body; + this.totalLength = (head + date + body); + this.group = group; + } + + + static void setSpecialRulesForEntryTypes() { + EndResultTotals.addSpecialRuleBody(new SpecialRule(MedtronicDeviceType.Medtronic_523andHigher, 3)); + Bolus.addSpecialRuleHead(new SpecialRule(MedtronicDeviceType.Medtronic_523andHigher, 8)); + // BolusWizardChange.addSpecialRuleBody(new SpecialRule(MedtronicDeviceType.Medtronic_522andHigher, 143)); + //ChangeBolusWizardSetup.addSpecialRuleBody(new SpecialRule(MedtronicDeviceType.Medtronic_523andHigher, 137)); // V5: + // 522 + // has + // old + // form + BolusWizard.addSpecialRuleBody(new SpecialRule(MedtronicDeviceType.Medtronic_523andHigher, 15)); + BolusReminder.addSpecialRuleBody(new SpecialRule(MedtronicDeviceType.Medtronic_523andHigher, 2)); + } + + + public static PumpHistoryEntryType getByCode(int opCode) { + if (opCodeMap.containsKey(opCode)) { + return opCodeMap.get(opCode); + } else { + return PumpHistoryEntryType.UnknownBasePacket; + } + } + + + // + // private PumpHistoryEntryType(int opCode, String name, int head, int date, + // int body) + // { + // this.opCode = (byte) opCode; + // this.description = name; + // this.headLength = head; + // this.dateLength = date; + // this.bodyLength = body; + // this.totalLength = (head + date + body); + // } + // + + public static boolean isAAPSRelevantEntry(PumpHistoryEntryType entryType) { + return (entryType == PumpHistoryEntryType.Bolus || // Treatments + entryType == PumpHistoryEntryType.TempBasalRate || // + entryType == PumpHistoryEntryType.TempBasalDuration || // + + entryType == PumpHistoryEntryType.Prime || // Pump Status Change + entryType == PumpHistoryEntryType.Suspend || // + entryType == PumpHistoryEntryType.Resume || // + entryType == PumpHistoryEntryType.Rewind || // + entryType == PumpHistoryEntryType.NoDeliveryAlarm || // no delivery + entryType == PumpHistoryEntryType.BasalProfileStart || // + + entryType == PumpHistoryEntryType.ChangeTime || // Time Change + entryType == PumpHistoryEntryType.NewTimeSet || // + + entryType == PumpHistoryEntryType.ChangeBasalPattern || // Configuration + entryType == PumpHistoryEntryType.ClearSettings || // + entryType == PumpHistoryEntryType.SaveSettings || // + entryType == PumpHistoryEntryType.ChangeMaxBolus || // + entryType == PumpHistoryEntryType.ChangeMaxBasal || // + entryType == PumpHistoryEntryType.ChangeTempBasalType || // + + entryType == PumpHistoryEntryType.ChangeBasalProfile_NewProfile || // Basal profile + + entryType == PumpHistoryEntryType.DailyTotals515 || // Daily Totals + entryType == PumpHistoryEntryType.DailyTotals522 || // + entryType == PumpHistoryEntryType.DailyTotals523 || // + entryType == PumpHistoryEntryType.EndResultTotals); + } + + + public static boolean isRelevantEntry() { + return true; + } + + + public int getCode() { + return this.opCode; + } + + + public int getTotalLength() { + if (hasSpecialRules()) { + return getHeadLength() + getBodyLength() + getDateLength(); + } else { + return totalLength; + } + } + + + private boolean hasSpecialRules() { + return hasSpecialRules; + } + + + void addSpecialRuleHead(SpecialRule rule) { + if (isEmpty(specialRulesHead)) { + specialRulesHead = new ArrayList(); + } + + specialRulesHead.add(rule); + hasSpecialRules = true; + } + + + void addSpecialRuleBody(SpecialRule rule) { + if (isEmpty(specialRulesBody)) { + specialRulesBody = new ArrayList(); + } + + specialRulesBody.add(rule); + hasSpecialRules = true; + } + + + public int getOpCode() { + return opCode; + } + + + public String getDescription() { + return this.description == null ? name() : this.description; + } + + + public int getHeadLength() { + if (hasSpecialRules) { + if (isNotEmpty(specialRulesHead)) { + return determineSizeByRule(headLength, specialRulesHead); + } else { + return headLength; + } + } else { + return headLength; + } + } + + + public int getDateLength() { + return dateLength; + } + + + public int getBodyLength() { + if (hasSpecialRules) { + if (isNotEmpty(specialRulesBody)) { + return determineSizeByRule(bodyLength, specialRulesBody); + } else { + return bodyLength; + } + } else { + return bodyLength; + } + } + + + private boolean isNotEmpty(List list) { + return list != null && !list.isEmpty(); + } + + + private boolean isEmpty(List list) { + return list == null || list.isEmpty(); + } + + + // byte[] dh = { 2, 3 }; + + private int determineSizeByRule(int defaultValue, List rules) { + int size = defaultValue; + + for (SpecialRule rule : rules) { + if (MedtronicDeviceType.isSameDevice(MedtronicUtil.getMedtronicPumpModel(), rule.deviceType)) { + size = rule.size; + break; + } + } + + return size; + } + + + public PumpHistoryEntryGroup getGroup() { + + return group; + } + + enum DateFormat { + None(0), // + LongDate(5), // + ShortDate(2); + + private int length; + + + DateFormat(int length) { + this.length = length; + } + + + public int getLength() { + return length; + } + + + public void setLength(int length) { + this.length = length; + } + } + + public static class SpecialRule { + + MedtronicDeviceType deviceType; + int size; + + + public SpecialRule(MedtronicDeviceType deviceType, int size) { + this.deviceType = deviceType; + this.size = size; + } + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/PumpHistoryResult.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/PumpHistoryResult.java new file mode 100644 index 0000000000..5017d50059 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/history/pump/PumpHistoryResult.java @@ -0,0 +1,200 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.pump.common.utils.DateTimeUtil; + +/** + * Created by andy on 9/23/18. + */ + +/** + * History page contains data, sorted from newest to oldest (0=newest..n=oldest) + */ +public class PumpHistoryResult { + + private static final Logger LOG = LoggerFactory.getLogger(L.PUMPCOMM); + + private boolean searchFinished = false; + private PumpHistoryEntry searchEntry = null; + private Long searchDate = null; + private SearchType searchType = SearchType.None; + public List unprocessedEntries; + public List validEntries; + + + // private Object validValues; + + public PumpHistoryResult(PumpHistoryEntry searchEntry, Long targetDate) { + if (searchEntry != null) { + /* + * this.searchEntry = searchEntry; + * this.searchType = SearchType.LastEntry; + * LOG.debug("PumpHistoryResult. Search parameters: Last Entry: " + searchEntry.atechDateTime + " type=" + * + searchEntry.getEntryType().name()); + */ + this.searchDate = searchEntry.atechDateTime; + this.searchType = SearchType.Date; + if (isLogEnabled()) + LOG.debug("PumpHistoryResult. Search parameters: Date(with searchEntry): " + targetDate); + } else if (targetDate != null) { + this.searchDate = targetDate; + this.searchType = SearchType.Date; + if (isLogEnabled()) + LOG.debug("PumpHistoryResult. Search parameters: Date: " + targetDate); + } + + // this.unprocessedEntries = new ArrayList<>(); + this.validEntries = new ArrayList<>(); + } + + + public void addHistoryEntries(List entries, int page) { + this.unprocessedEntries = entries; + //LOG.debug("PumpHistoryResult. Unprocessed entries: {}", MedtronicUtil.getGsonInstance().toJson(entries)); + processEntries(); + } + + // TODO Bug #145 need to check if we had timeChange that went -1, that situation needs to be evaluated separately + public void processEntries() { + int olderEntries = 0; + + Collections.reverse(this.unprocessedEntries); + + switch (searchType) { + case None: + //LOG.debug("PE. None search"); + this.validEntries.addAll(this.unprocessedEntries); + break; + + case LastEntry: { + LOG.debug("PE. Last entry search"); + + //Collections.sort(this.unprocessedEntries, new PumpHistoryEntry.Comparator()); + + LOG.debug("PE. PumpHistoryResult. Search entry date: " + searchEntry.atechDateTime); + + Long date = searchEntry.atechDateTime; + + for (PumpHistoryEntry unprocessedEntry : unprocessedEntries) { + + if (unprocessedEntry.equals(searchEntry)) { + //LOG.debug("PE. Item found {}.", unprocessedEntry); + searchFinished = true; + break; + } + + //LOG.debug("PE. Entry {} added.", unprocessedEntry); + this.validEntries.add(unprocessedEntry); + } + } + break; + case Date: { + LOG.debug("PE. Date search: Search date: {}", this.searchDate); + + + for (PumpHistoryEntry unprocessedEntry : unprocessedEntries) { + + if (unprocessedEntry.atechDateTime == null || unprocessedEntry.atechDateTime == 0) { + LOG.debug("PE. PumpHistoryResult. Search entry date: Entry with no date: {}", unprocessedEntry); + continue; + } + + if (unprocessedEntry.isAfter(this.searchDate)) { + this.validEntries.add(unprocessedEntry); + } else { + LOG.debug("PE. PumpHistoryResult. Not after.. Unprocessed Entry [year={},entry={}]", + DateTimeUtil.getYear(unprocessedEntry.atechDateTime), unprocessedEntry); + + if (DateTimeUtil.getYear(unprocessedEntry.atechDateTime) > 2015) + olderEntries++; + } + } + + if (olderEntries > 0) { + //Collections.sort(this.validEntries, new PumpHistoryEntry.Comparator()); + + searchFinished = true; + } + } + break; + + } // switch + + //LOG.debug("PE. Valid Entries: {}", validEntries); + } + + + private void clearOrPrepareList() { + if (this.validEntries == null) + this.validEntries = new ArrayList<>(); + else + this.validEntries.clear(); + } + + + public String toString() { + return "PumpHistoryResult [unprocessed=" + (unprocessedEntries != null ? "" + unprocessedEntries.size() : "0") + // + ", valid=" + (validEntries != null ? "" + validEntries.size() : "0") + // + ", searchEntry=" + searchEntry + // + ", searchDate=" + searchDate + // + ", searchType=" + searchType + // + ", searchFinished=" + searchFinished + // + "]"; + + } + + + /** + * Return latest entry (entry with highest date time) + * + * @return + */ + public PumpHistoryEntry getLatestEntry() { + if (this.validEntries == null || this.validEntries.size() == 0) + return null; + else { + return this.validEntries.get(0); + // PumpHistoryEntry pumpHistoryEntry = this.validEntries.get(0); + // + // if (pumpHistoryEntry.getEntryType() == PumpHistoryEntryType.EndResultTotals) + // return pumpHistoryEntry; + // else + // return this.validEntries.get(1); + } + } + + + public boolean isSearchRequired() { + return searchType != SearchType.None; + } + + + public boolean isSearchFinished() { + return searchFinished; + } + + + public List getValidEntries() { + return validEntries; + } + + enum SearchType { + None, // + LastEntry, // + Date + } + + + private boolean isLogEnabled() { + return L.isEnabled(L.PUMPCOMM); + } + + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/CarelinkLongMessageBody.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/CarelinkLongMessageBody.java new file mode 100644 index 0000000000..67b773999e --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/CarelinkLongMessageBody.java @@ -0,0 +1,59 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.comm.message; + +import java.util.List; + +import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil; + +/** + * Created by geoff on 6/2/16. + */ +public class CarelinkLongMessageBody extends MessageBody { + + public static final int LONG_MESSAGE_BODY_LENGTH = 65; + protected byte[] data; + + + public CarelinkLongMessageBody() { + init(new byte[0]); + } + + + public CarelinkLongMessageBody(byte[] payload) { + init(payload); + } + + + public CarelinkLongMessageBody(List payload) { + init(MedtronicUtil.createByteArray(payload)); + } + + + @Override + public void init(byte[] rxData) { + + if (rxData != null && rxData.length == LONG_MESSAGE_BODY_LENGTH) { + data = rxData; + } else { + data = new byte[LONG_MESSAGE_BODY_LENGTH]; + if (rxData != null) { + int size = rxData.length < LONG_MESSAGE_BODY_LENGTH ? rxData.length : LONG_MESSAGE_BODY_LENGTH; + for (int i = 0; i < size; i++) { + data[i] = rxData[i]; + } + } + } + } + + + @Override + public int getLength() { + return LONG_MESSAGE_BODY_LENGTH; + } + + + @Override + public byte[] getTxData() { + return data; + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/CarelinkShortMessageBody.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/CarelinkShortMessageBody.java new file mode 100644 index 0000000000..e2ebbff085 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/CarelinkShortMessageBody.java @@ -0,0 +1,53 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.comm.message; + +/** + * Created by geoff on 5/29/16. + */ +// Andy: See comments in message body +public class CarelinkShortMessageBody extends MessageBody { + + byte[] body; + + + public CarelinkShortMessageBody() { + init(new byte[] { 0 }); + } + + + public CarelinkShortMessageBody(byte[] data) { + init(data); + } + + + @Override + public int getLength() { + return body.length; + } + + + @Override + public void init(byte[] rxData) { + body = rxData; + } + + + public byte[] getRxData() { + return body; + } + + + public void setRxData(byte[] rxData) { + init(rxData); + } + + + @Override + public byte[] getTxData() { + return body; + } + + + public void setTxData(byte[] txData) { + init(txData); + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/GetHistoryPageCarelinkMessageBody.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/GetHistoryPageCarelinkMessageBody.java new file mode 100644 index 0000000000..25e8aa087c --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/GetHistoryPageCarelinkMessageBody.java @@ -0,0 +1,59 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.comm.message; + +import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil; + +/** + * Created by geoff on 6/2/16. + */ +public class GetHistoryPageCarelinkMessageBody extends CarelinkLongMessageBody { + + // public boolean wasLastFrame = false; + // public int frameNumber = 0; + // public byte[] frame = new byte[] {}; + + public GetHistoryPageCarelinkMessageBody(byte[] frameData) { + init(frameData); + } + + + public GetHistoryPageCarelinkMessageBody(int pageNum) { + init(pageNum); + } + + + @Override + public int getLength() { + return data.length; + } + + + @Override + public void init(byte[] rxData) { + super.init(rxData); + } + + + public void init(int pageNum) { + byte numArgs = 1; + super.init(new byte[] { numArgs, (byte)pageNum }); + } + + + public int getFrameNumber() { + if (data.length > 0) { + return data[0] & 0x7f; + } + return 255; + } + + + public boolean wasLastFrame() { + return (data[0] & 0x80) != 0; + } + + + public byte[] getFrameData() { + return ByteUtil.substring(data, 1, data.length - 1); + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/MessageBody.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/MessageBody.java new file mode 100644 index 0000000000..fa4a3817be --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/MessageBody.java @@ -0,0 +1,34 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.comm.message; + +import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil; + +/** + * Created by geoff on 5/29/16. + */ +public class MessageBody { + + public int getLength() { + return 0; + } + + + public void init(byte[] rxData) { + } + + + public byte[] getTxData() { + return new byte[]{}; + } + + + public String toString() { + StringBuilder sb = new StringBuilder(getClass().getSimpleName()); + + sb.append(" [txData="); + sb.append(ByteUtil.shortHexString(getTxData())); + sb.append("]"); + + return sb.toString(); + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/PacketType.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/PacketType.java new file mode 100644 index 0000000000..8d02063d66 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/PacketType.java @@ -0,0 +1,47 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.comm.message; + +import java.util.HashMap; +import java.util.Map; + +/** + * Created by geoff on 5/29/16. + * refactored into enum + */ +public enum PacketType { + Invalid(0x00), // + MySentry(0xa2), // + Meter(0xa5), // + Carelink(0xa7), // + Sensor(0xa8) // + ; + + public static Map mapByValue; + + static { + mapByValue = new HashMap<>(); + + for (PacketType packetType : values()) { + mapByValue.put(packetType.value, packetType); + } + } + + private byte value = 0; + + + PacketType(int value) { + this.value = (byte)value; + } + + + public static PacketType getByValue(short value) { + if (mapByValue.containsKey(value)) + return mapByValue.get(value); + else + return PacketType.Invalid; + } + + + public byte getValue() { + return value; + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/PumpAckMessageBody.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/PumpAckMessageBody.java new file mode 100644 index 0000000000..4cfda08975 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/PumpAckMessageBody.java @@ -0,0 +1,16 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.comm.message; + +/** + * Created by geoff on 5/29/16. + */ +public class PumpAckMessageBody extends CarelinkShortMessageBody { + + public PumpAckMessageBody() { + init(new byte[] { 0 }); + } + + + public PumpAckMessageBody(byte[] bodyData) { + init(bodyData); + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/PumpMessage.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/PumpMessage.java new file mode 100644 index 0000000000..4d81d81bfe --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/PumpMessage.java @@ -0,0 +1,219 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.comm.message; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data.RLMessage; +import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil; +import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicCommandType; + +/** + * Created by geoff on 5/29/16. + */ +public class PumpMessage implements RLMessage { + + private static final Logger LOG = LoggerFactory.getLogger(L.PUMPCOMM); + + public PacketType packetType = PacketType.Carelink; + public byte[] address = new byte[]{0, 0, 0}; + public MedtronicCommandType commandType; + public Byte invalidCommandType; + public MessageBody messageBody = new MessageBody(); + public String error = null; + + public static final int FRAME_DATA_LENGTH = 64; + + + public PumpMessage(String error) { + this.error = error; + } + + + public PumpMessage(byte[] rxData) { + init(rxData); + } + + + public PumpMessage() { + + } + + + public boolean isErrorResponse() { + return (this.error != null); + } + + + public void init(PacketType packetType, byte[] address, MedtronicCommandType commandType, MessageBody messageBody) { + this.packetType = packetType; + this.address = address; + this.commandType = commandType; + this.messageBody = messageBody; + } + + + public void init(byte[] rxData) { + if (rxData == null) { + return; + } + if (rxData.length > 0) { + this.packetType = PacketType.getByValue(rxData[0]); + } + if (rxData.length > 3) { + this.address = ByteUtil.substring(rxData, 1, 3); + } + if (rxData.length > 4) { + this.commandType = MedtronicCommandType.getByCode(rxData[4]); + if (this.commandType == MedtronicCommandType.InvalidCommand) { + if (isLogEnabled()) + LOG.error("PumpMessage - Unknown commandType " + rxData[4]); + } + } + if (rxData.length > 5) { + this.messageBody = MedtronicCommandType.constructMessageBody(commandType, + ByteUtil.substring(rxData, 5, rxData.length - 5)); + } + } + + + @Override + public byte[] getTxData() { + byte[] rval = ByteUtil.concat(new byte[]{(byte) packetType.getValue()}, address); + rval = ByteUtil.concat(rval, commandType.getCommandCode()); + rval = ByteUtil.concat(rval, messageBody.getTxData()); + return rval; + } + + + public byte[] getContents() { + return ByteUtil.concat(new byte[]{commandType.getCommandCode()}, messageBody.getTxData()); + } + + + // rawContent = just response without code (contents-2, messageBody.txData-1); + public byte[] getRawContent() { + + if ((messageBody == null) || (messageBody.getTxData() == null) || (messageBody.getTxData().length == 0)) + return null; + + byte[] data = messageBody.getTxData(); + + int length = ByteUtil.asUINT8(data[0]); // length is not always correct so, we check whole array if we have + // data, after length + int originalLength = length; + + // check if displayed length is invalid + if (length > data.length - 1) { + return data; + } + + // check Old Way + boolean oldWay = false; + for (int i = (length + 1); i < data.length; i++) { + if (data[i] != 0x00) { + oldWay = true; + } + } + + if (oldWay) { + length = data.length - 1; + } + + byte[] arrayOut = new byte[length]; + + System.arraycopy(messageBody.getTxData(), 1, arrayOut, 0, length); + +// if (isLogEnabled()) +// LOG.debug("PumpMessage - Length: " + length + ", Original Length: " + originalLength + ", CommandType: " +// + commandType); + + return arrayOut; + } + + + public byte[] getRawContentOfFrame() { + byte[] raw = messageBody.getTxData(); + return ByteUtil.substring(raw, 1, Math.min(FRAME_DATA_LENGTH, raw.length - 1)); + } + + + public boolean isValid() { + if (packetType == null) + return false; + if (address == null) + return false; + if (commandType == null) + return false; + if (messageBody == null) + return false; + return true; + } + + + public MessageBody getMessageBody() { + return messageBody; + } + + + public String getResponseContent() { + StringBuilder sb = new StringBuilder("PumpMessage [response="); + boolean showData = true; + + if (commandType != null) { + if (commandType == MedtronicCommandType.CommandACK) { + sb.append("Acknowledged"); + showData = false; + } else if (commandType == MedtronicCommandType.CommandNAK) { + sb.append("NOT Acknowledged"); + showData = false; + } else { + sb.append(commandType.name()); + } + } else { + sb.append("Unknown_Type"); + sb.append(" (" + invalidCommandType + ")"); + } + + if (showData) { + sb.append(", rawResponse="); + sb.append(ByteUtil.shortHexString(getRawContent())); + } + + sb.append("]"); + + return sb.toString(); + } + + + public String toString() { + StringBuilder sb = new StringBuilder("PumpMessage ["); + + sb.append("packetType="); + sb.append(packetType == null ? "null" : packetType.name()); + + sb.append(", address=("); + sb.append(ByteUtil.shortHexString(this.address)); + + sb.append("), commandType="); + sb.append(commandType == null ? "null" : commandType.name()); + + if (invalidCommandType != null) { + sb.append(", invalidCommandType="); + sb.append(invalidCommandType); + } + + sb.append(", messageBody=("); + sb.append(this.messageBody == null ? "null" : this.messageBody); + + sb.append(")]"); + + return sb.toString(); + } + + + private boolean isLogEnabled() { + return L.isEnabled(L.PUMPCOMM); + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/UnknownMessageBody.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/UnknownMessageBody.java new file mode 100644 index 0000000000..675a794bd8 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/message/UnknownMessageBody.java @@ -0,0 +1,46 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.comm.message; + +/** + * Created by geoff on 5/29/16. + */ +public class UnknownMessageBody extends MessageBody { + + public byte[] rxData; + + + public UnknownMessageBody(byte[] data) { + this.rxData = data; + } + + + @Override + public int getLength() { + return 0; + } + + + @Override + public void init(byte[] rxData) { + } + + + public byte[] getRxData() { + return rxData; + } + + + public void setRxData(byte[] rxData) { + this.rxData = rxData; + } + + + @Override + public byte[] getTxData() { + return rxData; + } + + + public void setTxData(byte[] txData) { + this.rxData = txData; + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/ui/MedtronicUIComm.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/ui/MedtronicUIComm.java new file mode 100644 index 0000000000..35fa184147 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/ui/MedtronicUIComm.java @@ -0,0 +1,109 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.comm.ui; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkConst; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkUtil; +import info.nightscout.androidaps.plugins.pump.medtronic.comm.MedtronicCommunicationManager; +import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicCommandType; +import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil; + +/** + * Created by andy on 6/14/18. + */ +public class MedtronicUIComm { + + private static final Logger LOG = LoggerFactory.getLogger(L.PUMP); + + MedtronicCommunicationManager mcmInstance = null; + MedtronicUIPostprocessor uiPostprocessor = new MedtronicUIPostprocessor(); + + + private MedtronicCommunicationManager getCommunicationManager() { + if (mcmInstance == null) { + mcmInstance = MedtronicCommunicationManager.getInstance(); + } + + return mcmInstance; + } + + + public synchronized MedtronicUITask executeCommand(MedtronicCommandType commandType, Object... parameters) { + + if (isLogEnabled()) + LOG.warn("Execute Command: " + commandType.name()); + + MedtronicUITask task = new MedtronicUITask(commandType, parameters); + + MedtronicUtil.setCurrentCommand(commandType); + + // new Thread(() -> { + // LOG.warn("@@@ Start Thread"); + // + // task.execute(getCommunicationManager()); + // + // LOG.warn("@@@ End Thread"); + // }); + + task.execute(getCommunicationManager()); + + // for (int i = 0; i < getMaxWaitTime(commandType); i++) { + // synchronized (task) { + // // try { + // // + // // //task.wait(1000); + // // } catch (InterruptedException e) { + // // LOG.error("executeCommand InterruptedException", e); + // // } + // + // + // SystemClock.sleep(1000); + // } + // + // if (task.isReceived()) { + // break; + // } + // } + + if (!task.isReceived() && isLogEnabled()) { + LOG.warn("Reply not received for " + commandType); + } + + task.postProcess(uiPostprocessor); + + return task; + + } + + + /** + * We return 25s as waitTime (17 for wakeUp, and addtional 8 for data retrieval) for normal commands and + * 120s for History. Real time for returning data would be arround 5s, but lets be sure. + * + * @param commandType + * @return + */ + private int getMaxWaitTime(MedtronicCommandType commandType) { + if (commandType == MedtronicCommandType.GetHistoryData) + return 120; + else + return 25; + } + + + public int getInvalidResponsesCount() { + return getCommunicationManager().getNotConnectedCount(); + } + + + public void startTunning() { + RileyLinkUtil.sendBroadcastMessage(RileyLinkConst.IPC.MSG_PUMP_tunePump); + } + + private boolean isLogEnabled() { + return L.isEnabled(L.PUMP); + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/ui/MedtronicUIPostprocessor.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/ui/MedtronicUIPostprocessor.java new file mode 100644 index 0000000000..e79531d4fc --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/ui/MedtronicUIPostprocessor.java @@ -0,0 +1,253 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.comm.ui; + +import org.joda.time.DateTimeZone; +import org.joda.time.Duration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Date; +import java.util.Map; + +import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.BasalProfile; +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.BatteryStatusDTO; +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.ClockDTO; +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.PumpSettingDTO; +import info.nightscout.androidaps.plugins.pump.medtronic.defs.BasalProfileStatus; +import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicNotificationType; +import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicUIResponseType; +import info.nightscout.androidaps.plugins.pump.medtronic.driver.MedtronicPumpStatus; +import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil; + +import static info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil.sendNotification; + +/** + * Created by andy on 6/15/18. + */ + +public class MedtronicUIPostprocessor { + + private static final Logger LOG = LoggerFactory.getLogger(L.PUMP); + + MedtronicPumpStatus pumpStatus; + + + public MedtronicUIPostprocessor() { + pumpStatus = MedtronicUtil.getPumpStatus(); + } + + + // this is mostly intended for command that return certain statuses (Remaining Insulin, ...), and + // where responses won't be directly used + public void postProcessData(MedtronicUITask uiTask) { + + switch (uiTask.commandType) { + + case SetBasalProfileSTD: { + Boolean response = (Boolean) uiTask.returnData; + + if (response) { + BasalProfile basalProfile = (BasalProfile) uiTask.getParameter(0); + + pumpStatus.basalsByHour = basalProfile.getProfilesByHour(); + } + } + break; + + case GetBasalProfileSTD: { + BasalProfile basalProfile = (BasalProfile) uiTask.returnData; + + try { + Double[] profilesByHour = basalProfile.getProfilesByHour(); + + if (profilesByHour != null) { + pumpStatus.basalsByHour = profilesByHour; + pumpStatus.basalProfileStatus = BasalProfileStatus.ProfileOK; + } else { + uiTask.responseType = MedtronicUIResponseType.Error; + uiTask.errorDescription = "No profile found."; + LOG.error("Basal Profile was NOT valid. [{}]", basalProfile.basalProfileToStringError()); + } + } catch (Exception ex) { + LOG.error("Basal Profile was returned, but was invalid. [{}]", basalProfile.basalProfileToStringError()); + uiTask.responseType = MedtronicUIResponseType.Error; + uiTask.errorDescription = "No profile found."; + } + } + break; + + case SetBolus: { + pumpStatus.lastBolusAmount = uiTask.getDoubleFromParameters(0); + pumpStatus.lastBolusTime = new Date(); + } + break; + + case GetRemainingInsulin: { + pumpStatus.reservoirRemainingUnits = (Float) uiTask.returnData; + } + break; + + case CancelTBR: { + pumpStatus.tempBasalStart = null; + pumpStatus.tempBasalAmount = null; + pumpStatus.tempBasalLength = null; + } + break; + + case GetRealTimeClock: { + processTime(uiTask); + } + break; + + case SetRealTimeClock: { + boolean response = (Boolean) uiTask.returnData; + + if (isLogEnabled()) + LOG.debug("New time was {} set.", response ? "" : "NOT"); + + if (response) { + MedtronicUtil.getPumpTime().timeDifference = 0; + } + } + break; + + + case GetBatteryStatus: { + BatteryStatusDTO batteryStatusDTO = (BatteryStatusDTO) uiTask.returnData; + + pumpStatus.batteryRemaining = batteryStatusDTO.getCalculatedPercent(pumpStatus.batteryType); + + if (batteryStatusDTO.voltage != null) { + pumpStatus.batteryVoltage = batteryStatusDTO.voltage; + } + + if (isLogEnabled()) + LOG.info("BatteryStatus: {}", batteryStatusDTO.toString()); + + } + break; + + case PumpModel: { + if (pumpStatus.medtronicDeviceType != MedtronicUtil.getMedtronicPumpModel()) { + if (isLogEnabled()) + LOG.warn("Configured pump is different then pump detected !"); + sendNotification(MedtronicNotificationType.PumpTypeNotSame); + } + } + break; + + case Settings_512: + case Settings: { + postProcessSettings(uiTask); + } + break; + + // no postprocessing + + default: + if (isLogEnabled()) + LOG.trace("Post-processing not implemented for {}.", uiTask.commandType.name()); + + } + + } + + + private void processTime(MedtronicUITask uiTask) { + + ClockDTO clockDTO = (ClockDTO) uiTask.returnData; + + Duration dur = new Duration(clockDTO.pumpTime.toDateTime(DateTimeZone.UTC), + clockDTO.localDeviceTime.toDateTime(DateTimeZone.UTC)); + + clockDTO.timeDifference = (int) dur.getStandardSeconds(); + + MedtronicUtil.setPumpTime(clockDTO); + + if (isLogEnabled()) + LOG.debug("Pump Time: " + clockDTO.localDeviceTime + ", DeviceTime=" + clockDTO.pumpTime + // + ", diff: " + dur.getStandardSeconds() + " s"); + +// if (dur.getStandardMinutes() >= 10) { +// if (isLogEnabled()) +// LOG.warn("Pump clock needs update, pump time: " + clockDTO.pumpTime.toString("HH:mm:ss") + " (difference: " +// + dur.getStandardSeconds() + " s)"); +// sendNotification(MedtronicNotificationType.PumpWrongTimeUrgent); +// } else if (dur.getStandardMinutes() >= 4) { +// if (isLogEnabled()) +// LOG.warn("Pump clock needs update, pump time: " + clockDTO.pumpTime.toString("HH:mm:ss") + " (difference: " +// + dur.getStandardSeconds() + " s)"); +// sendNotification(MedtronicNotificationType.PumpWrongTimeNormal); +// } + + } + + + private void postProcessSettings(MedtronicUITask uiTask) { + + Map settings = (Map) uiTask.returnData; + + MedtronicUtil.setSettings(settings); + + PumpSettingDTO checkValue = null; + + if (pumpStatus == null) { + if (isLogEnabled()) + LOG.debug("Pump Status: was null"); + pumpStatus = MedtronicUtil.getPumpStatus(); + if (isLogEnabled()) + LOG.debug("Pump Status: " + this.pumpStatus); + } + + this.pumpStatus.verifyConfiguration(); + + // check profile + if (!"Yes".equals(settings.get("PCFG_BASAL_PROFILES_ENABLED").value)) { + if (isLogEnabled()) + LOG.error("Basal profiles are not enabled on pump."); + sendNotification(MedtronicNotificationType.PumpBasalProfilesNotEnabled); + + } else { + checkValue = settings.get("PCFG_ACTIVE_BASAL_PROFILE"); + + if (!"STD".equals(checkValue.value)) { + if (isLogEnabled()) + LOG.error("Basal profile set on pump is incorrect (must be STD)."); + sendNotification(MedtronicNotificationType.PumpIncorrectBasalProfileSelected); + } + } + + // TBR + + checkValue = settings.get("PCFG_TEMP_BASAL_TYPE"); + + if (!"Units".equals(checkValue.value)) { + if (isLogEnabled()) + LOG.error("Wrong TBR type set on pump (must be Absolute)."); + sendNotification(MedtronicNotificationType.PumpWrongTBRTypeSet); + } + + // MAXes + + checkValue = settings.get("PCFG_MAX_BOLUS"); + + if (!MedtronicUtil.isSame(Double.parseDouble(checkValue.value), pumpStatus.maxBolus)) { + LOG.error("Wrong Max Bolus set on Pump (current={}, required={}).", checkValue.value, pumpStatus.maxBolus); + sendNotification(MedtronicNotificationType.PumpWrongMaxBolusSet, pumpStatus.maxBolus); + } + + checkValue = settings.get("PCFG_MAX_BASAL"); + + if (!MedtronicUtil.isSame(Double.parseDouble(checkValue.value), pumpStatus.maxBasal)) { + if (isLogEnabled()) + LOG.error("Wrong Max Basal set on Pump (current={}, required={}).", checkValue.value, pumpStatus.maxBasal); + sendNotification(MedtronicNotificationType.PumpWrongMaxBasalSet, pumpStatus.maxBasal); + } + + } + + private boolean isLogEnabled() { + return L.isEnabled(L.PUMP); + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/ui/MedtronicUITask.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/ui/MedtronicUITask.java new file mode 100644 index 0000000000..d6df56bb57 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/ui/MedtronicUITask.java @@ -0,0 +1,228 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.comm.ui; + +import org.joda.time.LocalDateTime; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.bus.RxBus; +import info.nightscout.androidaps.plugins.pump.medtronic.comm.MedtronicCommunicationManager; +import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.PumpHistoryEntry; +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.BasalProfile; +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.TempBasalPair; +import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicCommandType; +import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicUIResponseType; +import info.nightscout.androidaps.plugins.pump.medtronic.defs.PumpDeviceState; +import info.nightscout.androidaps.plugins.pump.medtronic.events.EventMedtronicDeviceStatusChange; +import info.nightscout.androidaps.plugins.pump.medtronic.events.EventMedtronicPumpValuesChanged; +import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil; + +/** + * Created by andy on 6/14/18. + */ + +public class MedtronicUITask { + + private static final Logger LOG = LoggerFactory.getLogger(L.PUMP); + + public MedtronicCommandType commandType; + public Object returnData; + String errorDescription; + // boolean invalid = false; + private Object[] parameters; + // private boolean received; + MedtronicUIResponseType responseType; + + + public MedtronicUITask(MedtronicCommandType commandType) { + this.commandType = commandType; + } + + + public MedtronicUITask(MedtronicCommandType commandType, Object... parameters) { + this.commandType = commandType; + this.parameters = parameters; + } + + + public void execute(MedtronicCommunicationManager communicationManager) { + + if (isLogEnabled()) + LOG.debug("MedtronicUITask: @@@ In execute. {}", commandType); + + switch (commandType) { + case PumpModel: { + returnData = communicationManager.getPumpModel(); + } + break; + + case GetBasalProfileSTD: { + returnData = communicationManager.getBasalProfile(); + } + break; + + case GetRemainingInsulin: { + returnData = communicationManager.getRemainingInsulin(); + } + break; + + case GetRealTimeClock: { + returnData = communicationManager.getPumpTime(); + MedtronicUtil.setPumpTime(null); + } + break; + + case SetRealTimeClock: { + returnData = communicationManager.setPumpTime(); + } + break; + + case GetBatteryStatus: { + returnData = communicationManager.getRemainingBattery(); + } + break; + + case SetTemporaryBasal: { + TempBasalPair tbr = getTBRSettings(); + if (tbr != null) { + returnData = communicationManager.setTBR(tbr); + } + } + break; + + case ReadTemporaryBasal: { + returnData = communicationManager.getTemporaryBasal(); + } + break; + + + case Settings: + case Settings_512: { + returnData = communicationManager.getPumpSettings(); + } + break; + + case SetBolus: { + Double amount = getDoubleFromParameters(0); + + if (amount != null) + returnData = communicationManager.setBolus(amount); + } + break; + + case CancelTBR: { + returnData = communicationManager.cancelTBR(); + } + break; + + case SetBasalProfileSTD: + case SetBasalProfileA: { + BasalProfile profile = (BasalProfile) parameters[0]; + + returnData = communicationManager.setBasalProfile(profile); + } + break; + + case GetHistoryData: { + returnData = communicationManager.getPumpHistory((PumpHistoryEntry) parameters[0], + (LocalDateTime) parameters[1]); + } + break; + + default: { + LOG.warn("This commandType is not supported (yet) - {}.", commandType); + // invalid = true; + responseType = MedtronicUIResponseType.Invalid; + } + + } + + if (responseType == null) { + if (returnData == null) { + errorDescription = communicationManager.getErrorResponse(); + this.responseType = MedtronicUIResponseType.Error; + } else { + this.responseType = MedtronicUIResponseType.Data; + } + } + + } + + + private TempBasalPair getTBRSettings() { + return new TempBasalPair(getDoubleFromParameters(0), // + false, // + getIntegerFromParameters(1)); + } + + + private Float getFloatFromParameters(int index) { + return (Float) parameters[index]; + } + + + public Double getDoubleFromParameters(int index) { + return (Double) parameters[index]; + } + + + public Integer getIntegerFromParameters(int index) { + return (Integer) parameters[index]; + } + + + public Object getResult() { + return returnData; + } + + + public boolean isReceived() { + return (returnData != null || errorDescription != null); + } + + + void postProcess(MedtronicUIPostprocessor postprocessor) { + + if (isLogEnabled()) + LOG.debug("MedtronicUITask: @@@ In execute. {}", commandType); + + if (responseType == MedtronicUIResponseType.Data) { + postprocessor.postProcessData(this); + } + + if (responseType == MedtronicUIResponseType.Invalid) { + RxBus.INSTANCE.send(new EventMedtronicDeviceStatusChange(PumpDeviceState.ErrorWhenCommunicating, + "Unsupported command in MedtronicUITask")); + } else if (responseType == MedtronicUIResponseType.Error) { + RxBus.INSTANCE.send(new EventMedtronicDeviceStatusChange(PumpDeviceState.ErrorWhenCommunicating, + errorDescription)); + } else { + RxBus.INSTANCE.send(new EventMedtronicPumpValuesChanged()); + MedtronicUtil.getPumpStatus().setLastCommunicationToNow(); + } + + MedtronicUtil.setCurrentCommand(null); + } + + + public boolean hasData() { + return (responseType == MedtronicUIResponseType.Data); + } + + + public Object getParameter(int index) { + return parameters[index]; + } + + + private boolean isLogEnabled() { + return L.isEnabled(L.PUMP); + } + + + public MedtronicUIResponseType getResponseType() { + return this.responseType; + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/MedtronicHistoryData.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/MedtronicHistoryData.java new file mode 100644 index 0000000000..1eed440cdc --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/MedtronicHistoryData.java @@ -0,0 +1,1543 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.data; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import org.apache.commons.lang3.StringUtils; +import org.joda.time.LocalDateTime; +import org.joda.time.Minutes; +import org.json.JSONException; +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collections; +import java.util.GregorianCalendar; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.data.DetailedBolusInfo; +import info.nightscout.androidaps.db.CareportalEvent; +import info.nightscout.androidaps.db.DatabaseHelper; +import info.nightscout.androidaps.db.DbObjectBase; +import info.nightscout.androidaps.db.ExtendedBolus; +import info.nightscout.androidaps.db.Source; +import info.nightscout.androidaps.db.TDD; +import info.nightscout.androidaps.db.TemporaryBasal; +import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.general.nsclient.NSUpload; +import info.nightscout.androidaps.plugins.pump.common.bolusInfo.DetailedBolusInfoStorage; +import info.nightscout.androidaps.plugins.pump.common.utils.DateTimeUtil; +import info.nightscout.androidaps.plugins.pump.common.utils.StringUtil; +import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.MedtronicPumpHistoryDecoder; +import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.PumpHistoryEntry; +import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.PumpHistoryEntryType; +import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.PumpHistoryResult; +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.BasalProfile; +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.BolusDTO; +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.BolusWizardDTO; +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.ClockDTO; +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.DailyTotalsDTO; +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.TempBasalPair; +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.TempBasalProcessDTO; +import info.nightscout.androidaps.plugins.pump.medtronic.driver.MedtronicPumpStatus; +import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicConst; +import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil; +import info.nightscout.androidaps.plugins.treatments.Treatment; +import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; +import info.nightscout.androidaps.utils.DateUtil; +import info.nightscout.androidaps.utils.SP; + + +/** + * Created by andy on 10/12/18. + */ + +// TODO: After release we need to refactor how data is retrieved from pump, each entry in history needs to be marked, and sorting +// needs to happen according those markings, not on time stamp (since AAPS can change time anytime it drifts away). This +// needs to include not returning any records if TZ goes into -x area. To fully support this AAPS would need to take note of +// all times that time changed (TZ, DST, etc.). Data needs to be returned in batches (time_changed batches, so that we can +// handle it. It would help to assign sort_ids to items (from oldest (1) to newest (x) + +// All things marked with "TODO: Fix db code" needs to be updated in new 2.5 database code + +public class MedtronicHistoryData { + + private static final Logger LOG = LoggerFactory.getLogger(L.PUMP); + + private List allHistory = null; + private List newHistory = null; + + private Long lastHistoryRecordTime; + private boolean isInit = false; + + private Gson gson; + + private DatabaseHelper databaseHelper = MainApp.getDbHelper(); + private ClockDTO pumpTime; + + private long lastIdUsed = 0; + + /** + * Double bolus debug. We seem to have small problem with double Boluses (or sometimes also missing boluses + * from history. This flag turns on debugging for that (default is off=false)... Debuging is pretty detailed, + * so log files will get bigger. + */ + public static boolean doubleBolusDebug = false; + + + public MedtronicHistoryData() { + this.allHistory = new ArrayList<>(); + this.gson = MedtronicUtil.gsonInstance; + + if (this.gson == null) { + this.gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create(); + } + } + + + /** + * Add New History entries + * + * @param result PumpHistoryResult instance + */ + public void addNewHistory(PumpHistoryResult result) { + + List validEntries = result.getValidEntries(); + + List newEntries = new ArrayList<>(); + + for (PumpHistoryEntry validEntry : validEntries) { + + if (!this.allHistory.contains(validEntry)) { + newEntries.add(validEntry); + } + } + + this.newHistory = newEntries; + + showLogs("List of history (before filtering): [" + this.newHistory.size() + "]", gson.toJson(this.newHistory)); + } + + + private static void showLogs(String header, String data) { + + if (!isLogEnabled()) + return; + + if (header != null) { + LOG.debug(header); + } + + if (StringUtils.isNotBlank(data)) { + for (final String token : StringUtil.splitString(data, 3500)) { + LOG.debug("{}", token); + } + } else { + LOG.debug("No data."); + } + } + + + public List getAllHistory() { + return this.allHistory; + } + + + public void filterNewEntries() { + + List newHistory2 = new ArrayList<>(); + List TBRs = new ArrayList<>(); + List bolusEstimates = new ArrayList<>(); + long atechDate = DateTimeUtil.toATechDate(new GregorianCalendar()); + + //LOG.debug("Filter new entries: Before {}", newHistory); + + if (!isCollectionEmpty(newHistory)) { + + for (PumpHistoryEntry pumpHistoryEntry : newHistory) { + + if (!this.allHistory.contains(pumpHistoryEntry)) { + + PumpHistoryEntryType type = pumpHistoryEntry.getEntryType(); + + if (type == PumpHistoryEntryType.TempBasalRate || type == PumpHistoryEntryType.TempBasalDuration) { + TBRs.add(pumpHistoryEntry); + } else if (type == PumpHistoryEntryType.BolusWizard || type == PumpHistoryEntryType.BolusWizard512) { + bolusEstimates.add(pumpHistoryEntry); + newHistory2.add(pumpHistoryEntry); + } else { + + if (type == PumpHistoryEntryType.EndResultTotals) { + if (!DateTimeUtil.isSameDay(atechDate, pumpHistoryEntry.atechDateTime)) { + newHistory2.add(pumpHistoryEntry); + } + } else { + newHistory2.add(pumpHistoryEntry); + } + } + } + } + + TBRs = preProcessTBRs(TBRs); + + if (bolusEstimates.size() > 0) { + extendBolusRecords(bolusEstimates, newHistory2); + } + + newHistory2.addAll(TBRs); + + this.newHistory = newHistory2; + + sort(this.newHistory); + } + + if (isLogEnabled()) + LOG.debug("New History entries found: {}", this.newHistory.size()); + + showLogs("List of history (after filtering): [" + this.newHistory.size() + "]", gson.toJson(this.newHistory)); + + } + + private void extendBolusRecords(List bolusEstimates, List newHistory2) { + + List boluses = getFilteredItems(newHistory2, PumpHistoryEntryType.Bolus); + + for (PumpHistoryEntry bolusEstimate : bolusEstimates) { + for (PumpHistoryEntry bolus : boluses) { + if (bolusEstimate.atechDateTime.equals(bolus.atechDateTime)) { + bolus.addDecodedData("Estimate", bolusEstimate.getDecodedData().get("Object")); + } + } + } + } + + + public void finalizeNewHistoryRecords() { + + if ((newHistory == null) || (newHistory.size() == 0)) + return; + + PumpHistoryEntry pheLast = newHistory.get(0); + + // find last entry + for (PumpHistoryEntry pumpHistoryEntry : newHistory) { + if (pumpHistoryEntry.atechDateTime != null && pumpHistoryEntry.isAfter(pheLast.atechDateTime)) { + pheLast = pumpHistoryEntry; + } + } + + // add new entries + Collections.reverse(newHistory); + + for (PumpHistoryEntry pumpHistoryEntry : newHistory) { + + if (!this.allHistory.contains(pumpHistoryEntry)) { + lastIdUsed++; + pumpHistoryEntry.id = lastIdUsed; + this.allHistory.add(pumpHistoryEntry); + } + + } + + + if (pheLast == null) // if we don't have any valid record we don't do the filtering and setting + return; + + this.setLastHistoryRecordTime(pheLast.atechDateTime); + SP.putLong(MedtronicConst.Statistics.LastPumpHistoryEntry, pheLast.atechDateTime); + + LocalDateTime dt = null; + + try { + dt = DateTimeUtil.toLocalDateTime(pheLast.atechDateTime); + } catch (Exception ex) { + LOG.error("Problem decoding date from last record: {}" + pheLast); + } + + if (dt != null) { + + dt = dt.minusDays(1); // we keep 24 hours + + long dtRemove = DateTimeUtil.toATechDate(dt); + + List removeList = new ArrayList<>(); + + for (PumpHistoryEntry pumpHistoryEntry : allHistory) { + + if (!pumpHistoryEntry.isAfter(dtRemove)) { + removeList.add(pumpHistoryEntry); + } + } + + this.allHistory.removeAll(removeList); + + this.sort(this.allHistory); + + if (isLogEnabled()) + LOG.debug("All History records [afterFilterCount={}, removedItemsCount={}, newItemsCount={}]", + allHistory.size(), removeList.size(), newHistory.size()); + } else { + LOG.error("Since we couldn't determine date, we don't clean full history. This is just workaround."); + } + + this.newHistory.clear(); + } + + + public boolean hasRelevantConfigurationChanged() { + return getStateFromFilteredList( // + PumpHistoryEntryType.ChangeBasalPattern, // + PumpHistoryEntryType.ClearSettings, // + PumpHistoryEntryType.SaveSettings, // + PumpHistoryEntryType.ChangeMaxBolus, // + PumpHistoryEntryType.ChangeMaxBasal, // + PumpHistoryEntryType.ChangeTempBasalType); + } + + + private boolean isCollectionEmpty(List col) { + return (col == null || col.isEmpty()); + } + + private boolean isCollectionNotEmpty(List col) { + return (col != null && !col.isEmpty()); + } + + + public boolean isPumpSuspended() { + + List items = getDataForPumpSuspends(); + + showLogs("isPumpSuspended: ", MedtronicUtil.gsonInstance.toJson(items)); + + if (isCollectionNotEmpty(items)) { + + PumpHistoryEntryType pumpHistoryEntryType = items.get(0).getEntryType(); + + boolean isSuspended = !(pumpHistoryEntryType == PumpHistoryEntryType.TempBasalCombined || // + pumpHistoryEntryType == PumpHistoryEntryType.BasalProfileStart || // + pumpHistoryEntryType == PumpHistoryEntryType.Bolus || // + pumpHistoryEntryType == PumpHistoryEntryType.Resume || // + pumpHistoryEntryType == PumpHistoryEntryType.BatteryChange || // + pumpHistoryEntryType == PumpHistoryEntryType.Prime); + + if (isLogEnabled()) + LOG.debug("isPumpSuspended. Last entry type={}, isSuspended={}", pumpHistoryEntryType, isSuspended); + + return isSuspended; + } else + return false; + + } + + + private List getDataForPumpSuspends() { + + List newAndAll = new ArrayList<>(); + + if (isCollectionNotEmpty(this.allHistory)) { + newAndAll.addAll(this.allHistory); + } + + if (isCollectionNotEmpty(this.newHistory)) { + + for (PumpHistoryEntry pumpHistoryEntry : newHistory) { + if (!newAndAll.contains(pumpHistoryEntry)) { + newAndAll.add(pumpHistoryEntry); + } + } + } + + if (newAndAll.isEmpty()) + return newAndAll; + + this.sort(newAndAll); + + List newAndAll2 = getFilteredItems(newAndAll, // + PumpHistoryEntryType.Bolus, // + PumpHistoryEntryType.TempBasalCombined, // + PumpHistoryEntryType.Prime, // + PumpHistoryEntryType.Suspend, // + PumpHistoryEntryType.Resume, // + PumpHistoryEntryType.Rewind, // + PumpHistoryEntryType.NoDeliveryAlarm, // + PumpHistoryEntryType.BatteryChange, // + PumpHistoryEntryType.BasalProfileStart); + + newAndAll2 = filterPumpSuspend(newAndAll2, 10); + + return newAndAll2; + } + + + private List filterPumpSuspend(List newAndAll, int filterCount) { + + if (newAndAll.size() <= filterCount) { + return newAndAll; + } + + List newAndAllOut = new ArrayList<>(); + + for (int i = 0; i < filterCount; i++) { + newAndAllOut.add(newAndAll.get(i)); + } + + return newAndAllOut; + } + + + /** + * Process History Data: Boluses(Treatments), TDD, TBRs, Suspend-Resume (or other pump stops: battery, prime) + */ + public void processNewHistoryData() { + + // TODO: Fix db code + // Prime (for reseting autosense) + List primeRecords = getFilteredItems(PumpHistoryEntryType.Prime); + + if (isLogEnabled()) + LOG.debug("ProcessHistoryData: Prime [count={}, items={}]", primeRecords.size(), gson.toJson(primeRecords)); + + if (isCollectionNotEmpty(primeRecords)) { + try { + processPrime(primeRecords); + } catch (Exception ex) { + LOG.error("ProcessHistoryData: Error processing Prime entries: " + ex.getMessage(), ex); + throw ex; + } + } + + // TDD + List tdds = getFilteredItems(PumpHistoryEntryType.EndResultTotals, getTDDType()); + + if (isLogEnabled()) + LOG.debug("ProcessHistoryData: TDD [count={}, items={}]", tdds.size(), gson.toJson(tdds)); + + if (isCollectionNotEmpty(tdds)) { + try { + processTDDs(tdds); + } catch (Exception ex) { + LOG.error("ProcessHistoryData: Error processing TDD entries: " + ex.getMessage(), ex); + throw ex; + } + } + + pumpTime = MedtronicUtil.getPumpTime(); + + // Bolus + List treatments = getFilteredItems(PumpHistoryEntryType.Bolus); + + if (isLogEnabled()) + LOG.debug("ProcessHistoryData: Bolus [count={}, items={}]", treatments.size(), gson.toJson(treatments)); + + if (treatments.size() > 0) { + try { + processBolusEntries(treatments); + } catch (Exception ex) { + LOG.error("ProcessHistoryData: Error processing Bolus entries: " + ex.getMessage(), ex); + throw ex; + } + } + + // TBR + List tbrs = getFilteredItems(PumpHistoryEntryType.TempBasalCombined); + + if (isLogEnabled()) + LOG.debug("ProcessHistoryData: TBRs Processed [count={}, items={}]", tbrs.size(), gson.toJson(tbrs)); + + if (tbrs.size() > 0) { + try { + processTBREntries(tbrs); + } catch (Exception ex) { + LOG.error("ProcessHistoryData: Error processing TBR entries: " + ex.getMessage(), ex); + throw ex; + } + } + + // 'Delivery Suspend' + List suspends = null; + + try { + suspends = getSuspends(); + } catch (Exception ex) { + LOG.error("ProcessHistoryData: Error getting Suspend entries: " + ex.getMessage(), ex); + throw ex; + } + + if (isLogEnabled()) + LOG.debug("ProcessHistoryData: 'Delivery Suspend' Processed [count={}, items={}]", suspends.size(), + gson.toJson(suspends)); + + if (isCollectionNotEmpty(suspends)) { + try { + processSuspends(suspends); + } catch (Exception ex) { + LOG.error("ProcessHistoryData: Error processing Suspends entries: " + ex.getMessage(), ex); + throw ex; + } + } + } + + + private void processPrime(List primeRecords) { + + long maxAllowedTimeInPast = DateTimeUtil.getATDWithAddedMinutes(new GregorianCalendar(), -30); + + long lastPrimeRecord = 0L; + + for (PumpHistoryEntry primeRecord : primeRecords) { + + if (primeRecord.atechDateTime > maxAllowedTimeInPast) { + if (lastPrimeRecord < primeRecord.atechDateTime) { + lastPrimeRecord = primeRecord.atechDateTime; + } + } + } + + if (lastPrimeRecord != 0L) { + long lastPrimeFromAAPS = SP.getLong(MedtronicConst.Statistics.LastPrime, 0L); + + if (lastPrimeRecord != lastPrimeFromAAPS) { + uploadCareportalEvent(DateTimeUtil.toMillisFromATD(lastPrimeRecord), CareportalEvent.SITECHANGE); + + SP.putLong(MedtronicConst.Statistics.LastPrime, lastPrimeRecord); + } + } + } + + + private void uploadCareportalEvent(long date, String event) { + if (MainApp.getDbHelper().getCareportalEventFromTimestamp(date) != null) + return; + try { + JSONObject data = new JSONObject(); + String enteredBy = SP.getString("careportal_enteredby", ""); + if (!enteredBy.equals("")) data.put("enteredBy", enteredBy); + data.put("created_at", DateUtil.toISOString(date)); + data.put("eventType", event); + NSUpload.uploadCareportalEntryToNS(data); + } catch (JSONException e) { + e.printStackTrace(); + } + } + + + private void processTDDs(List tddsIn) { + + List tdds = filterTDDs(tddsIn); + + if (isLogEnabled()) + LOG.debug(getLogPrefix() + "TDDs found: {}.\n{}", tdds.size(), gson.toJson(tdds)); + + List tddsDb = databaseHelper.getTDDsForLastXDays(3); + + for (PumpHistoryEntry tdd : tdds) { + + TDD tddDbEntry = findTDD(tdd.atechDateTime, tddsDb); + + DailyTotalsDTO totalsDTO = (DailyTotalsDTO) tdd.getDecodedData().get("Object"); + + //LOG.debug("DailyTotals: {}", totalsDTO); + + if (tddDbEntry == null) { + TDD tddNew = new TDD(); + totalsDTO.setTDD(tddNew); + + if (isLogEnabled()) + LOG.debug("TDD Add: {}", tddNew); + + databaseHelper.createOrUpdateTDD(tddNew); + + } else { + + if (!totalsDTO.doesEqual(tddDbEntry)) { + totalsDTO.setTDD(tddDbEntry); + + if (isLogEnabled()) + LOG.debug("TDD Edit: {}", tddDbEntry); + + databaseHelper.createOrUpdateTDD(tddDbEntry); + } + } + } + } + + + private enum ProcessHistoryRecord { + Bolus("Bolus"), + TBR("TBR"), + Suspend("Suspend"); + + private String description; + + ProcessHistoryRecord(String desc) { + this.description = desc; + } + + public String getDescription() { + return this.description; + } + + } + + + private void processBolusEntries(List entryList) { + + long oldestTimestamp = getOldestTimestamp(entryList); + + Gson gson = MedtronicUtil.getGsonInstance(); + + List entriesFromHistory = getDatabaseEntriesByLastTimestamp(oldestTimestamp, ProcessHistoryRecord.Bolus); + + if (doubleBolusDebug) + LOG.debug("DoubleBolusDebug: List (before filter): {}, FromDb={}", gson.toJson(entryList), + gson.toJson(entriesFromHistory)); + + filterOutAlreadyAddedEntries(entryList, entriesFromHistory); + + if (entryList.isEmpty()) { + if (doubleBolusDebug) + LOG.debug("DoubleBolusDebug: EntryList was filtered out."); + return; + } + + if (doubleBolusDebug) + LOG.debug("DoubleBolusDebug: List (after filter): {}, FromDb={}", gson.toJson(entryList), + gson.toJson(entriesFromHistory)); + + if (isCollectionEmpty(entriesFromHistory)) { + for (PumpHistoryEntry treatment : entryList) { + if (isLogEnabled()) + LOG.debug("Add Bolus (no db entry): " + treatment); + if (doubleBolusDebug) + LOG.debug("DoubleBolusDebug: Add Bolus: FromDb=null, Treatment={}", treatment); + + addBolus(treatment, null); + } + } else { + for (PumpHistoryEntry treatment : entryList) { + DbObjectBase treatmentDb = findDbEntry(treatment, entriesFromHistory); + if (isLogEnabled()) + LOG.debug("Add Bolus {} - (entryFromDb={}) ", treatment, treatmentDb); + if (doubleBolusDebug) + LOG.debug("DoubleBolusDebug: Add Bolus: FromDb={}, Treatment={}", treatmentDb, treatment); + + addBolus(treatment, (Treatment) treatmentDb); + } + } + } + + + private void processTBREntries(List entryList) { + + Collections.reverse(entryList); + + TempBasalPair tbr = (TempBasalPair) entryList.get(0).getDecodedDataEntry("Object"); + + boolean readOldItem = false; + + if (tbr.isCancelTBR()) { + PumpHistoryEntry oneMoreEntryFromHistory = getOneMoreEntryFromHistory(PumpHistoryEntryType.TempBasalCombined); + + if (oneMoreEntryFromHistory != null) { + entryList.add(0, oneMoreEntryFromHistory); + readOldItem = true; + } else { + entryList.remove(0); + } + } + + long oldestTimestamp = getOldestTimestamp(entryList); + + List entriesFromHistory = getDatabaseEntriesByLastTimestamp(oldestTimestamp, ProcessHistoryRecord.TBR); + + if (isLogEnabled()) + LOG.debug(ProcessHistoryRecord.TBR.getDescription() + " List (before filter): {}, FromDb={}", gson.toJson(entryList), + gson.toJson(entriesFromHistory)); + + + TempBasalProcessDTO processDTO = null; + List processList = new ArrayList<>(); + + for (PumpHistoryEntry treatment : entryList) { + + TempBasalPair tbr2 = (TempBasalPair) treatment.getDecodedDataEntry("Object"); + + if (tbr2.isCancelTBR()) { + + if (processDTO != null) { + processDTO.itemTwo = treatment; + + if (readOldItem) { + processDTO.processOperation = TempBasalProcessDTO.Operation.Edit; + readOldItem = false; + } + } else { + LOG.error("processDTO was null - shouldn't happen. ItemTwo={}", treatment); + } + } else { + if (processDTO != null) { + processList.add(processDTO); + } + + processDTO = new TempBasalProcessDTO(); + processDTO.itemOne = treatment; + processDTO.processOperation = TempBasalProcessDTO.Operation.Add; + } + } + + if (processDTO != null) { + processList.add(processDTO); + } + + + if (isCollectionNotEmpty(processList)) { + + for (TempBasalProcessDTO tempBasalProcessDTO : processList) { + + if (tempBasalProcessDTO.processOperation == TempBasalProcessDTO.Operation.Edit) { + // edit + TemporaryBasal tempBasal = findTempBasalWithPumpId(tempBasalProcessDTO.itemOne.getPumpId(), entriesFromHistory); + + if (tempBasal != null) { + + tempBasal.durationInMinutes = tempBasalProcessDTO.getDuration(); + + databaseHelper.createOrUpdate(tempBasal); + + if (isLogEnabled()) + LOG.debug("Edit " + ProcessHistoryRecord.TBR.getDescription() + " - (entryFromDb={}) ", tempBasal); + } else { + LOG.error("TempBasal not found. Item: {}", tempBasalProcessDTO.itemOne); + } + + } else { + // add + + PumpHistoryEntry treatment = tempBasalProcessDTO.itemOne; + + TempBasalPair tbr2 = (TempBasalPair) treatment.getDecodedData().get("Object"); + tbr2.setDurationMinutes(tempBasalProcessDTO.getDuration()); + + TemporaryBasal tempBasal = findTempBasalWithPumpId(tempBasalProcessDTO.itemOne.getPumpId(), entriesFromHistory); + + if (tempBasal == null) { + DbObjectBase treatmentDb = findDbEntry(treatment, entriesFromHistory); + + if (isLogEnabled()) + LOG.debug("Add " + ProcessHistoryRecord.TBR.getDescription() + " {} - (entryFromDb={}) ", treatment, treatmentDb); + + addTBR(treatment, (TemporaryBasal) treatmentDb); + } else { + // this shouldn't happen + if (tempBasal.durationInMinutes != tempBasalProcessDTO.getDuration()) { + LOG.debug("Found entry with wrong duration (shouldn't happen)... updating"); + tempBasal.durationInMinutes = tempBasalProcessDTO.getDuration(); + } + + } + } // if + } // for + + } // collection + } + + + private TemporaryBasal findTempBasalWithPumpId(long pumpId, List entriesFromHistory) { + + for (DbObjectBase dbObjectBase : entriesFromHistory) { + TemporaryBasal tbr = (TemporaryBasal) dbObjectBase; + + if (tbr.pumpId == pumpId) { + return tbr; + } + } + + TemporaryBasal tempBasal = databaseHelper.findTempBasalByPumpId(pumpId); + return tempBasal; + } + + + /** + * findDbEntry - finds Db entries in database, while theoretically this should have same dateTime they + * don't. Entry on pump is few seconds before treatment in AAPS, and on manual boluses on pump there + * is no treatment at all. For now we look fro tratment that was from 0s - 1m59s within pump entry. + * + * @param treatment Pump Entry + * @param entriesFromHistory entries from history + * @return DbObject from AAPS (if found) + */ + private DbObjectBase findDbEntry(PumpHistoryEntry treatment, List entriesFromHistory) { + + long proposedTime = DateTimeUtil.toMillisFromATD(treatment.atechDateTime); + + //proposedTime += (this.pumpTime.timeDifference * 1000); + + if (doubleBolusDebug) + LOG.debug("DoubleBolusDebug: findDbEntry Treatment={}, FromDb={}", treatment, gson.toJson(entriesFromHistory)); + + if (entriesFromHistory.size() == 0) { + if (doubleBolusDebug) + LOG.debug("DoubleBolusDebug: findDbEntry Treatment={}, FromDb=null", treatment); + return null; + } else if (entriesFromHistory.size() == 1) { + if (doubleBolusDebug) + LOG.debug("DoubleBolusDebug: findDbEntry Treatment={}, FromDb={}. Type=SingleEntry", treatment, entriesFromHistory.get(0)); + + // TODO: Fix db code + // if difference is bigger than 2 minutes we discard entry + long maxMillisAllowed = DateTimeUtil.getMillisFromATDWithAddedMinutes(treatment.atechDateTime, 2); + + if (doubleBolusDebug) + LOG.debug("DoubleBolusDebug: findDbEntry maxMillisAllowed={}, AtechDateTime={} (add 2 minutes). ", maxMillisAllowed, treatment.atechDateTime); + + if (entriesFromHistory.get(0).getDate() > maxMillisAllowed) { + if (doubleBolusDebug) + LOG.debug("DoubleBolusDebug: findDbEntry entry filtered out, returning null. "); + return null; + } + + return entriesFromHistory.get(0); + } + + for (int min = 0; min < 2; min += 1) { + + for (int sec = 0; sec <= 50; sec += 10) { + + if (min == 1 && sec == 50) { + sec = 59; + } + + int diff = (sec * 1000); + + List outList = new ArrayList<>(); + + for (DbObjectBase treatment1 : entriesFromHistory) { + + if ((treatment1.getDate() > proposedTime - diff) && (treatment1.getDate() < proposedTime + diff)) { + outList.add(treatment1); + } + } + + if (outList.size() == 1) { + if (doubleBolusDebug) + LOG.debug("DoubleBolusDebug: findDbEntry Treatment={}, FromDb={}. Type=EntrySelected, AtTimeMin={}, AtTimeSec={}", treatment, entriesFromHistory.get(0), min, sec); + + return outList.get(0); + } + + if (min == 0 && sec == 10 && outList.size() > 1) { + if (isLogEnabled()) + LOG.error("Too many entries (with too small diff): (timeDiff=[min={},sec={}],count={},list={})", + min, sec, outList.size(), gson.toJson(outList)); + if (doubleBolusDebug) + LOG.debug("DoubleBolusDebug: findDbEntry Error - Too many entries (with too small diff): (timeDiff=[min={},sec={}],count={},list={})", + min, sec, outList.size(), gson.toJson(outList)); + } + } + } + + return null; + } + + + private List getDatabaseEntriesByLastTimestamp(long startTimestamp, ProcessHistoryRecord processHistoryRecord) { + if (processHistoryRecord == ProcessHistoryRecord.Bolus) { + return TreatmentsPlugin.getPlugin().getTreatmentsFromHistoryAfterTimestamp(startTimestamp); + } else { + return databaseHelper.getTemporaryBasalsDataFromTime(startTimestamp, true); + } + } + + + private void filterOutAlreadyAddedEntries(List entryList, List treatmentsFromHistory) { + + if (isCollectionEmpty(treatmentsFromHistory)) + return; + + List removeTreatmentsFromHistory = new ArrayList<>(); + + for (DbObjectBase treatment : treatmentsFromHistory) { + + if (treatment.getPumpId() != 0) { + + PumpHistoryEntry selectedBolus = null; + + for (PumpHistoryEntry bolus : entryList) { + if (bolus.getPumpId() == treatment.getPumpId()) { + selectedBolus = bolus; + break; + } + } + + if (selectedBolus != null) { + entryList.remove(selectedBolus); + + removeTreatmentsFromHistory.add(treatment); + } + } + } + + treatmentsFromHistory.removeAll(removeTreatmentsFromHistory); + } + + + private void addBolus(PumpHistoryEntry bolus, Treatment treatment) { + + BolusDTO bolusDTO = (BolusDTO) bolus.getDecodedData().get("Object"); + + if (treatment == null) { + if (doubleBolusDebug) + LOG.debug("DoubleBolusDebug: addBolus(tretament==null): Bolus={}", bolusDTO); + + switch (bolusDTO.getBolusType()) { + case Normal: { + DetailedBolusInfo detailedBolusInfo = new DetailedBolusInfo(); + + detailedBolusInfo.date = tryToGetByLocalTime(bolus.atechDateTime); + detailedBolusInfo.source = Source.PUMP; + detailedBolusInfo.pumpId = bolus.getPumpId(); + detailedBolusInfo.insulin = bolusDTO.getDeliveredAmount(); + + addCarbsFromEstimate(detailedBolusInfo, bolus); + + if (doubleBolusDebug) + LOG.debug("DoubleBolusDebug: addBolus(tretament==null): DetailedBolusInfo={}", detailedBolusInfo); + + boolean newRecord = TreatmentsPlugin.getPlugin().addToHistoryTreatment(detailedBolusInfo, false); + + bolus.setLinkedObject(detailedBolusInfo); + + if (isLogEnabled()) + LOG.debug("addBolus - [date={},pumpId={}, insulin={}, newRecord={}]", detailedBolusInfo.date, + detailedBolusInfo.pumpId, detailedBolusInfo.insulin, newRecord); + } + break; + + case Audio: + case Extended: { + ExtendedBolus extendedBolus = new ExtendedBolus(); + extendedBolus.date = tryToGetByLocalTime(bolus.atechDateTime); + extendedBolus.source = Source.PUMP; + extendedBolus.insulin = bolusDTO.getDeliveredAmount(); + extendedBolus.pumpId = bolus.getPumpId(); + extendedBolus.isValid = true; + extendedBolus.durationInMinutes = bolusDTO.getDuration(); + + bolus.setLinkedObject(extendedBolus); + + if (doubleBolusDebug) + LOG.debug("DoubleBolusDebug: addBolus(tretament==null): ExtendedBolus={}", extendedBolus); + + TreatmentsPlugin.getPlugin().addToHistoryExtendedBolus(extendedBolus); + + if (isLogEnabled()) + LOG.debug("addBolus - Extended [date={},pumpId={}, insulin={}, duration={}]", extendedBolus.date, + extendedBolus.pumpId, extendedBolus.insulin, extendedBolus.durationInMinutes); + + } + break; + } + + } else { + + DetailedBolusInfo detailedBolusInfo = DetailedBolusInfoStorage.INSTANCE.findDetailedBolusInfo(treatment.date, bolusDTO.getDeliveredAmount()); + + if (doubleBolusDebug) + LOG.debug("DoubleBolusDebug: addBolus(tretament={}): Bolus={}, DetailedBolusInfo={}", treatment, bolusDTO, detailedBolusInfo); + + if (detailedBolusInfo == null) { + detailedBolusInfo = new DetailedBolusInfo(); + + if (doubleBolusDebug) + LOG.debug("DoubleBolusDebug: detailedBolusInfoCouldNotBeRetrived !"); + } + + detailedBolusInfo.date = treatment.date; + detailedBolusInfo.source = Source.PUMP; + detailedBolusInfo.pumpId = bolus.getPumpId(); + detailedBolusInfo.insulin = bolusDTO.getDeliveredAmount(); + detailedBolusInfo.carbs = treatment.carbs; + + addCarbsFromEstimate(detailedBolusInfo, bolus); + + if (doubleBolusDebug) + LOG.debug("DoubleBolusDebug: addBolus(tretament!=null): DetailedBolusInfo(New)={}", detailedBolusInfo); + + boolean newRecord = TreatmentsPlugin.getPlugin().addToHistoryTreatment(detailedBolusInfo, false); + + bolus.setLinkedObject(detailedBolusInfo); + + if (isLogEnabled()) + LOG.debug("editBolus - [date={},pumpId={}, insulin={}, newRecord={}]", detailedBolusInfo.date, + detailedBolusInfo.pumpId, detailedBolusInfo.insulin, newRecord); + + } + } + + + private void addCarbsFromEstimate(DetailedBolusInfo detailedBolusInfo, PumpHistoryEntry bolus) { + + if (bolus.containsDecodedData("Estimate")) { + + BolusWizardDTO bolusWizard = (BolusWizardDTO) bolus.getDecodedData().get("Estimate"); + + if (doubleBolusDebug) + LOG.debug("DoubleBolusDebug: addCarbsFromEstimate: Bolus={}, BolusWizardDTO={}", bolus, bolusWizard); + + detailedBolusInfo.carbs = bolusWizard.carbs; + } + } + + + private void addTBR(PumpHistoryEntry treatment, TemporaryBasal temporaryBasalDbInput) { + + TempBasalPair tbr = (TempBasalPair) treatment.getDecodedData().get("Object"); + + TemporaryBasal temporaryBasalDb = temporaryBasalDbInput; + String operation = "editTBR"; + + if (temporaryBasalDb == null) { + temporaryBasalDb = new TemporaryBasal(); + temporaryBasalDb.date = tryToGetByLocalTime(treatment.atechDateTime); + + operation = "addTBR"; + } + + temporaryBasalDb.source = Source.PUMP; + temporaryBasalDb.pumpId = treatment.getPumpId(); + temporaryBasalDb.durationInMinutes = tbr.getDurationMinutes(); + temporaryBasalDb.absoluteRate = tbr.getInsulinRate(); + temporaryBasalDb.isAbsolute = !tbr.isPercent(); + + treatment.setLinkedObject(temporaryBasalDb); + + databaseHelper.createOrUpdate(temporaryBasalDb); + + if (isLogEnabled()) + LOG.debug(operation + " - [date={},pumpId={}, rate={} {}, duration={}]", // + temporaryBasalDb.date, // + temporaryBasalDb.pumpId, // + temporaryBasalDb.isAbsolute ? String.format(Locale.ENGLISH, "%.2f", temporaryBasalDb.absoluteRate) : + String.format(Locale.ENGLISH, "%d", temporaryBasalDb.percentRate), // + temporaryBasalDb.isAbsolute ? "U/h" : "%", // + temporaryBasalDb.durationInMinutes); + } + + + private void processSuspends(List tempBasalProcessList) { + + for (TempBasalProcessDTO tempBasalProcess : tempBasalProcessList) { + + TemporaryBasal tempBasal = databaseHelper.findTempBasalByPumpId(tempBasalProcess.itemOne.getPumpId()); + + if (tempBasal == null) { + // add + tempBasal = new TemporaryBasal(); + tempBasal.date = tryToGetByLocalTime(tempBasalProcess.itemOne.atechDateTime); + + tempBasal.source = Source.PUMP; + tempBasal.pumpId = tempBasalProcess.itemOne.getPumpId(); + tempBasal.durationInMinutes = tempBasalProcess.getDuration(); + tempBasal.absoluteRate = 0.0d; + tempBasal.isAbsolute = true; + + tempBasalProcess.itemOne.setLinkedObject(tempBasal); + tempBasalProcess.itemTwo.setLinkedObject(tempBasal); + + databaseHelper.createOrUpdate(tempBasal); + + } + } + + } + + + private List getSuspends() { + + List outList = new ArrayList<>(); + + // suspend/resume + outList.addAll(getSuspendResumeRecords()); + // no_delivery/prime & rewind/prime + outList.addAll(getNoDeliveryRewindPrimeRecords()); + + return outList; + } + + private List getSuspendResumeRecords() { + List filteredItems = getFilteredItems(this.newHistory, // + PumpHistoryEntryType.Suspend, // + PumpHistoryEntryType.Resume); + + List outList = new ArrayList<>(); + + if (filteredItems.size() > 0) { + + List filtered2Items = new ArrayList<>(); + + if ((filteredItems.size() % 2 == 0) && (filteredItems.get(0).getEntryType() == PumpHistoryEntryType.Resume)) { + // full resume suspends (S R S R) + filtered2Items.addAll(filteredItems); + } else if ((filteredItems.size() % 2 == 0) && (filteredItems.get(0).getEntryType() == PumpHistoryEntryType.Suspend)) { + // not full suspends, need to retrive one more record and discard first one (R S R S) -> ([S] R S R [xS]) + filteredItems.remove(0); + + PumpHistoryEntry oneMoreEntryFromHistory = getOneMoreEntryFromHistory(PumpHistoryEntryType.Suspend); + if (oneMoreEntryFromHistory != null) { + filteredItems.add(oneMoreEntryFromHistory); + } else { + filteredItems.remove(filteredItems.size() - 1); // remove last (unpaired R) + } + + filtered2Items.addAll(filteredItems); + } else { + if (filteredItems.get(0).getEntryType() == PumpHistoryEntryType.Resume) { + // get one more from history (R S R) -> ([S] R S R) + + PumpHistoryEntry oneMoreEntryFromHistory = getOneMoreEntryFromHistory(PumpHistoryEntryType.Suspend); + if (oneMoreEntryFromHistory != null) { + filteredItems.add(oneMoreEntryFromHistory); + } else { + filteredItems.remove(filteredItems.size() - 1); // remove last (unpaired R) + } + + filtered2Items.addAll(filteredItems); + } else { + // remove last and have paired items + filteredItems.remove(0); + filtered2Items.addAll(filteredItems); + } + } + + if (filtered2Items.size() > 0) { + sort(filtered2Items); + Collections.reverse(filtered2Items); + + for (int i = 0; i < filtered2Items.size(); i += 2) { + TempBasalProcessDTO dto = new TempBasalProcessDTO(); + + dto.itemOne = filtered2Items.get(i); + dto.itemTwo = filtered2Items.get(i + 1); + + dto.processOperation = TempBasalProcessDTO.Operation.Add; + + outList.add(dto); + } + } + } + + return outList; + } + + + private List getNoDeliveryRewindPrimeRecords() { + List primeItems = getFilteredItems(this.newHistory, // + PumpHistoryEntryType.Prime); + + List outList = new ArrayList<>(); + + if (primeItems.size() == 0) + return outList; + + List filteredItems = getFilteredItems(this.newHistory, // + PumpHistoryEntryType.Prime, + PumpHistoryEntryType.Rewind, + PumpHistoryEntryType.NoDeliveryAlarm, + PumpHistoryEntryType.Bolus, + PumpHistoryEntryType.TempBasalCombined + ); + + List tempData = new ArrayList<>(); + boolean startedItems = false; + boolean finishedItems = false; + + for (PumpHistoryEntry filteredItem : filteredItems) { + if (filteredItem.getEntryType() == PumpHistoryEntryType.Prime) { + startedItems = true; + } + + if (startedItems) { + if (filteredItem.getEntryType() == PumpHistoryEntryType.Bolus || + filteredItem.getEntryType() == PumpHistoryEntryType.TempBasalCombined) { + finishedItems = true; + break; + } + + tempData.add(filteredItem); + } + } + + + if (!finishedItems) { + + List filteredItemsOld = getFilteredItems(this.allHistory, // + PumpHistoryEntryType.Rewind, + PumpHistoryEntryType.NoDeliveryAlarm, + PumpHistoryEntryType.Bolus, + PumpHistoryEntryType.TempBasalCombined + ); + + for (PumpHistoryEntry filteredItem : filteredItemsOld) { + + if (filteredItem.getEntryType() == PumpHistoryEntryType.Bolus || + filteredItem.getEntryType() == PumpHistoryEntryType.TempBasalCombined) { + finishedItems = true; + break; + } + + tempData.add(filteredItem); + } + } + + + if (!finishedItems) { + showLogs("NoDeliveryRewindPrimeRecords: Not finished Items: ", gson.toJson(tempData)); + return outList; + } + + showLogs("NoDeliveryRewindPrimeRecords: Records to evaluate: ", gson.toJson(tempData)); + + List items = getFilteredItems(tempData, // + PumpHistoryEntryType.Prime + ); + + + TempBasalProcessDTO processDTO = new TempBasalProcessDTO(); + + processDTO.itemTwo = items.get(0); + + items = getFilteredItems(tempData, // + PumpHistoryEntryType.NoDeliveryAlarm + ); + + if (items.size() > 0) { + + processDTO.itemOne = items.get(items.size() - 1); + processDTO.processOperation = TempBasalProcessDTO.Operation.Add; + + outList.add(processDTO); + return outList; + } + + + items = getFilteredItems(tempData, // + PumpHistoryEntryType.Rewind + ); + + if (items.size() > 0) { + + processDTO.itemOne = items.get(0); + processDTO.processOperation = TempBasalProcessDTO.Operation.Add; + + outList.add(processDTO); + return outList; + } + + return outList; + } + + + private PumpHistoryEntry getOneMoreEntryFromHistory(PumpHistoryEntryType entryType) { + List filteredItems = getFilteredItems(this.allHistory, entryType); + + return filteredItems.size() == 0 ? null : filteredItems.get(0); + } + + + private List filterTDDs(List tdds) { + List tddsOut = new ArrayList<>(); + + for (PumpHistoryEntry tdd : tdds) { + if (tdd.getEntryType() != PumpHistoryEntryType.EndResultTotals) { + tddsOut.add(tdd); + } + } + + return tddsOut.size() == 0 ? tdds : tddsOut; + } + + + private TDD findTDD(long atechDateTime, List tddsDb) { + + for (TDD tdd : tddsDb) { + + if (DateTimeUtil.isSameDayATDAndMillis(atechDateTime, tdd.date)) { + return tdd; + } + } + + return null; + } + + private long tryToGetByLocalTime(long atechDateTime) { + return DateTimeUtil.toMillisFromATD(atechDateTime); + } + + + private int getOldestDateDifference(List treatments) { + + long dt = Long.MAX_VALUE; + PumpHistoryEntry currentTreatment = null; + + if (isCollectionEmpty(treatments)) { + return 8; // default return of 6 (5 for diif on history reading + 2 for max allowed difference) minutes + } + + for (PumpHistoryEntry treatment : treatments) { + + if (treatment.atechDateTime < dt) { + dt = treatment.atechDateTime; + currentTreatment = treatment; + } + } + + LocalDateTime oldestEntryTime = null; + + try { + + oldestEntryTime = DateTimeUtil.toLocalDateTime(dt); + oldestEntryTime = oldestEntryTime.minusMinutes(3); + +// if (this.pumpTime.timeDifference < 0) { +// oldestEntryTime = oldestEntryTime.plusSeconds(this.pumpTime.timeDifference); +// } + } catch (Exception ex) { + LOG.error("Problem decoding date from last record: {}" + currentTreatment); + return 8; // default return of 6 minutes + } + + LocalDateTime now = new LocalDateTime(); + + Minutes minutes = Minutes.minutesBetween(oldestEntryTime, now); + + // returns oldest time in history, with calculated time difference between pump and phone, minus 5 minutes + if (isLogEnabled()) + LOG.debug("Oldest entry: {}, pumpTimeDifference={}, newDt={}, currentTime={}, differenceMin={}", dt, + this.pumpTime.timeDifference, oldestEntryTime, now, minutes.getMinutes()); + + return minutes.getMinutes(); + } + + + private long getOldestTimestamp(List treatments) { + + long dt = Long.MAX_VALUE; + PumpHistoryEntry currentTreatment = null; + + for (PumpHistoryEntry treatment : treatments) { + + if (treatment.atechDateTime < dt) { + dt = treatment.atechDateTime; + currentTreatment = treatment; + } + } + + if (doubleBolusDebug) + LOG.debug("DoubleBolusDebug: getOldestTimestamp. Oldest entry found: time={}, object={}", dt, currentTreatment); + + try { + + GregorianCalendar oldestEntryTime = DateTimeUtil.toGregorianCalendar(dt); + if (doubleBolusDebug) + LOG.debug("DoubleBolusDebug: getOldestTimestamp. oldestEntryTime: {}", DateTimeUtil.toString(oldestEntryTime)); + oldestEntryTime.add(Calendar.MINUTE, -2); + + if (doubleBolusDebug) + LOG.debug("DoubleBolusDebug: getOldestTimestamp. oldestEntryTime (-2m): {}, timeInMillis={}", DateTimeUtil.toString(oldestEntryTime), oldestEntryTime.getTimeInMillis()); + + return oldestEntryTime.getTimeInMillis(); + + } catch (Exception ex) { + LOG.error("Problem decoding date from last record: {}", currentTreatment); + return 8; // default return of 6 minutes + } + + } + + + private PumpHistoryEntryType getTDDType() { + + if (MedtronicUtil.getMedtronicPumpModel() == null) { + return PumpHistoryEntryType.EndResultTotals; + } + + switch (MedtronicUtil.getMedtronicPumpModel()) { + + case Medtronic_515: + case Medtronic_715: + return PumpHistoryEntryType.DailyTotals515; + + case Medtronic_522: + case Medtronic_722: + return PumpHistoryEntryType.DailyTotals522; + + case Medtronic_523_Revel: + case Medtronic_723_Revel: + case Medtronic_554_Veo: + case Medtronic_754_Veo: + return PumpHistoryEntryType.DailyTotals523; + + default: { + return PumpHistoryEntryType.EndResultTotals; + } + } + } + + + public boolean hasBasalProfileChanged() { + + List filteredItems = getFilteredItems(PumpHistoryEntryType.ChangeBasalProfile_NewProfile); + + if (isLogEnabled()) + LOG.debug("hasBasalProfileChanged. Items: " + gson.toJson(filteredItems)); + + return (filteredItems.size() > 0); + } + + + public void processLastBasalProfileChange(MedtronicPumpStatus mdtPumpStatus) { + + List filteredItems = getFilteredItems(PumpHistoryEntryType.ChangeBasalProfile_NewProfile); + + if (isLogEnabled()) + LOG.debug("processLastBasalProfileChange. Items: " + filteredItems); + + PumpHistoryEntry newProfile = null; + Long lastDate = null; + + if (filteredItems.size() == 1) { + newProfile = filteredItems.get(0); + } else if (filteredItems.size() > 1) { + + for (PumpHistoryEntry filteredItem : filteredItems) { + + if (lastDate == null || lastDate < filteredItem.atechDateTime) { + newProfile = filteredItem; + lastDate = newProfile.atechDateTime; + } + } + } + + if (newProfile != null) { + if (isLogEnabled()) + LOG.debug("processLastBasalProfileChange. item found, setting new basalProfileLocally: " + newProfile); + BasalProfile basalProfile = (BasalProfile) newProfile.getDecodedData().get("Object"); + + mdtPumpStatus.basalsByHour = basalProfile.getProfilesByHour(); + } + } + + + public boolean hasPumpTimeChanged() { + return getStateFromFilteredList(PumpHistoryEntryType.NewTimeSet, // + PumpHistoryEntryType.ChangeTime); + } + + + public void setLastHistoryRecordTime(Long lastHistoryRecordTime) { + + // this.previousLastHistoryRecordTime = this.lastHistoryRecordTime; + this.lastHistoryRecordTime = lastHistoryRecordTime; + } + + + public void setIsInInit(boolean init) { + this.isInit = init; + } + + + // HELPER METHODS + + private void sort(List list) { + Collections.sort(list, new PumpHistoryEntry.Comparator()); + } + + + private List preProcessTBRs(List TBRs_Input) { + List TBRs = new ArrayList<>(); + + Map map = new HashMap<>(); + + for (PumpHistoryEntry pumpHistoryEntry : TBRs_Input) { + if (map.containsKey(pumpHistoryEntry.DT)) { + MedtronicPumpHistoryDecoder.decodeTempBasal(map.get(pumpHistoryEntry.DT), pumpHistoryEntry); + pumpHistoryEntry.setEntryType(PumpHistoryEntryType.TempBasalCombined); + TBRs.add(pumpHistoryEntry); + map.remove(pumpHistoryEntry.DT); + } else { + map.put(pumpHistoryEntry.DT, pumpHistoryEntry); + } + } + + return TBRs; + } + + + private List getFilteredItems(PumpHistoryEntryType... entryTypes) { + return getFilteredItems(this.newHistory, entryTypes); + } + + + private boolean getStateFromFilteredList(PumpHistoryEntryType... entryTypes) { + if (isInit) { + return false; + } else { + List filteredItems = getFilteredItems(entryTypes); + + if (isLogEnabled()) + LOG.debug("Items: " + filteredItems); + + return filteredItems.size() > 0; + } + } + + + private List getFilteredItems(List inList, PumpHistoryEntryType... entryTypes) { + + // LOG.debug("InList: " + inList.size()); + List outList = new ArrayList<>(); + + if (inList != null && inList.size() > 0) { + for (PumpHistoryEntry pumpHistoryEntry : inList) { + + if (!isEmpty(entryTypes)) { + for (PumpHistoryEntryType pumpHistoryEntryType : entryTypes) { + + if (pumpHistoryEntry.getEntryType() == pumpHistoryEntryType) { + outList.add(pumpHistoryEntry); + break; + } + } + } else { + outList.add(pumpHistoryEntry); + } + } + } + + // LOG.debug("OutList: " + outList.size()); + + return outList; + } + + + private boolean isEmpty(PumpHistoryEntryType... entryTypes) { + return (entryTypes == null || (entryTypes.length == 1 && entryTypes[0] == null)); + } + + + private String getLogPrefix() { + return "MedtronicHistoryData::"; + } + + private static boolean isLogEnabled() { + return (L.isEnabled(L.PUMP)); + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/BasalProfile.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/BasalProfile.java new file mode 100644 index 0000000000..25facaf9e6 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/BasalProfile.java @@ -0,0 +1,387 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.data.dto; + +import com.google.gson.annotations.Expose; + +import org.joda.time.Instant; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; + +import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.pump.common.defs.PumpType; +import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil; +import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil; + +/** + * Created by geoff on 6/1/15. + *

+ * There are three basal profiles stored on the pump. (722 only?) They are all parsed the same, the user just has 3 to + * choose from: Standard, A, and B + *

+ * The byte array is 48 times three byte entries long, plus a zero? If the profile is completely empty, it should have + * one entry: [0,0,0x3F]. The first entry of [0,0,0] marks the end of the used entries. + *

+ * Each entry is assumed to span from the specified start time to the start time of the next entry, or to midnight if + * there are no more entries. + *

+ * Individual entries are of the form [r,z,m] where r is the rate (in 0.025 U increments) z is zero (?) m is the start + * time-of-day for the basal rate period (in 30 minute increments) + */ +public class BasalProfile { + + private static final Logger LOG = LoggerFactory.getLogger(L.PUMPCOMM); + + public static final int MAX_RAW_DATA_SIZE = (48 * 3) + 1; + private static final boolean DEBUG_BASALPROFILE = false; + @Expose + private byte[] mRawData; // store as byte array to make transport (via parcel) easier + private List listEntries; + + + public BasalProfile() { + init(); + } + + + public BasalProfile(byte[] data) { + setRawData(data); + } + + + // this asUINT8 should be combined with Record.asUINT8, and placed in a new util class. + protected static int readUnsignedByte(byte b) { + return (b < 0) ? b + 256 : b; + } + + + public void init() { + mRawData = new byte[MAX_RAW_DATA_SIZE]; + mRawData[0] = 0; + mRawData[1] = 0; + mRawData[2] = 0x3f; + } + + + public boolean setRawData(byte[] data) { + if (data == null) { + LOG.error("setRawData: buffer is null!"); + return false; + } + + // if we have just one entry through all day it looks like just length 1 + if (data.length == 1) { + data = MedtronicUtil.createByteArray(data[0], (byte) 0, (byte) 0); + } + + if (data.length == MAX_RAW_DATA_SIZE) { + mRawData = data; + } else { + int len = Math.min(MAX_RAW_DATA_SIZE, data.length); + mRawData = new byte[MAX_RAW_DATA_SIZE]; + System.arraycopy(data, 0, mRawData, 0, len); + } + + return true; + } + + + public boolean setRawDataFromHistory(byte[] data) { + if (data == null) { + LOG.error("setRawData: buffer is null!"); + return false; + } + + mRawData = new byte[MAX_RAW_DATA_SIZE]; + int item = 0; + + for (int i = 0; i < data.length - 2; i += 3) { + + if ((data[i] == 0) && (data[i + 1] == 0) && (data[i + 2] == 0)) { + mRawData[i] = 0; + mRawData[i + 1] = 0; + mRawData[i + 2] = 0; + } + + mRawData[i] = data[i + 1]; + mRawData[i + 1] = data[i + 2]; + mRawData[i + 2] = data[i]; + } + + return true; + } + + + public void dumpBasalProfile() { + LOG.debug("Basal Profile entries:"); + List entries = getEntries(); + for (int i = 0; i < entries.size(); i++) { + BasalProfileEntry entry = entries.get(i); + String startString = entry.startTime.toString("HH:mm"); + // this doesn't work + LOG.debug(String.format("Entry %d, rate=%.3f (0x%02X), start=%s (0x%02X)", i + 1, entry.rate, + entry.rate_raw, startString, entry.startTime_raw)); + + } + } + + + public String getBasalProfileAsString() { + StringBuffer sb = new StringBuffer("Basal Profile entries:\n"); + List entries = getEntries(); + for (int i = 0; i < entries.size(); i++) { + BasalProfileEntry entry = entries.get(i); + String startString = entry.startTime.toString("HH:mm"); + + sb.append(String.format("Entry %d, rate=%.3f, start=%s\n", i + 1, entry.rate, startString)); + } + + return sb.toString(); + } + + public String basalProfileToStringError() { + return "Basal Profile [rawData=" + ByteUtil.shortHexString(this.getRawData()) + "]"; + } + + + public String basalProfileToString() { + StringBuffer sb = new StringBuffer("Basal Profile ["); + List entries = getEntries(); + for (int i = 0; i < entries.size(); i++) { + BasalProfileEntry entry = entries.get(i); + String startString = entry.startTime.toString("HH:mm"); + + sb.append(String.format("%s=%.3f, ", startString, entry.rate)); + } + + sb.append("]"); + + return sb.toString(); + } + + + // TODO: this function must be expanded to include changes in which profile is in use. + // and changes to the profiles themselves. + public BasalProfileEntry getEntryForTime(Instant when) { + BasalProfileEntry rval = new BasalProfileEntry(); + List entries = getEntries(); + if (entries.size() == 0) { + LOG.warn(String.format("getEntryForTime(%s): table is empty", + when.toDateTime().toLocalTime().toString("HH:mm"))); + return rval; + } + // Log.w(TAG,"Assuming first entry"); + rval = entries.get(0); + if (entries.size() == 1) { + LOG.debug("getEntryForTime: Only one entry in profile"); + return rval; + } + + int localMillis = when.toDateTime().toLocalTime().getMillisOfDay(); + boolean done = false; + int i = 1; + while (!done) { + BasalProfileEntry entry = entries.get(i); + if (DEBUG_BASALPROFILE) { + LOG.debug(String.format("Comparing 'now'=%s to entry 'start time'=%s", when.toDateTime().toLocalTime() + .toString("HH:mm"), entry.startTime.toString("HH:mm"))); + } + if (localMillis >= entry.startTime.getMillisOfDay()) { + rval = entry; + if (DEBUG_BASALPROFILE) + LOG.debug("Accepted Entry"); + } else { + // entry at i has later start time, keep older entry + if (DEBUG_BASALPROFILE) + LOG.debug("Rejected Entry"); + done = true; + } + i++; + if (i >= entries.size()) { + done = true; + } + } + if (DEBUG_BASALPROFILE) { + LOG.debug(String.format("getEntryForTime(%s): Returning entry: rate=%.3f (%d), start=%s (%d)", when + .toDateTime().toLocalTime().toString("HH:mm"), rval.rate, rval.rate_raw, + rval.startTime.toString("HH:mm"), rval.startTime_raw)); + } + return rval; + } + + + public List getEntries() { + List entries = new ArrayList<>(); + + if (mRawData == null || mRawData[2] == 0x3f) { + LOG.warn("Raw Data is empty."); + return entries; // an empty list + } + boolean done = false; + int r, st; + + for (int i = 0; i < mRawData.length - 2; i += 3) { + + if ((mRawData[i] == 0) && (mRawData[i + 1] == 0) && (mRawData[i + 2] == 0)) + break; + + if ((mRawData[i] == 0) && (mRawData[i + 1] == 0) && (mRawData[i + 2] == 0x3f)) + break; + + r = MedtronicUtil.makeUnsignedShort(mRawData[i + 1], mRawData[i]); // readUnsignedByte(mRawData[i]); + st = readUnsignedByte(mRawData[i + 2]); + + try { + entries.add(new BasalProfileEntry(r, st)); + } catch (Exception ex) { + LOG.error("Error decoding basal profile from bytes: {}", ByteUtil.shortHexString(mRawData)); + throw ex; + } + + } + + return entries; + } + + + /** + * This is used to prepare new profile + * + * @param entry + */ + public void addEntry(BasalProfileEntry entry) { + if (listEntries == null) + listEntries = new ArrayList<>(); + + listEntries.add(entry); + } + + + public void generateRawDataFromEntries() { + + List outData = new ArrayList<>(); + + for (BasalProfileEntry profileEntry : listEntries) { + + byte[] strokes = MedtronicUtil.getBasalStrokes(profileEntry.rate, true); + + outData.add(profileEntry.rate_raw[0]); + outData.add(profileEntry.rate_raw[1]); + outData.add(profileEntry.startTime_raw); + } + + this.setRawData(MedtronicUtil.createByteArray(outData)); + + // return this.mRawData; + } + + + public Double[] getProfilesByHour() { + + List entries = null; + + try { + entries = getEntries(); + } catch (Exception ex) { + LOG.error("============================================================================="); + LOG.error(" Error generating entries. Ex.: " + ex, ex); + LOG.error(" rawBasalValues: " + ByteUtil.shortHexString(this.getRawData())); + LOG.error("============================================================================="); + + //FabricUtil.createEvent("MedtronicBasalProfileGetByHourError", null); + } + + if (entries == null || entries.size() == 0) { + Double[] basalByHour = new Double[24]; + + for (int i = 0; i < 24; i++) { + basalByHour[i] = 0.0d; + } + + return basalByHour; + } + + Double[] basalByHour = new Double[24]; + + PumpType pumpType = MedtronicUtil.getPumpStatus().pumpType; + + for (int i = 0; i < entries.size(); i++) { + BasalProfileEntry current = entries.get(i); + + int currentTime = (current.startTime_raw % 2 == 0) ? current.startTime_raw : current.startTime_raw - 1; + + currentTime = (currentTime * 30) / 60; + + int lastHour = 0; + if ((i + 1) == entries.size()) { + lastHour = 24; + } else { + BasalProfileEntry basalProfileEntry = entries.get(i + 1); + + int rawTime = (basalProfileEntry.startTime_raw % 2 == 0) ? basalProfileEntry.startTime_raw + : basalProfileEntry.startTime_raw - 1; + + lastHour = (rawTime * 30) / 60; + } + + // System.out.println("Current time: " + currentTime + " Next Time: " + lastHour); + + for (int j = currentTime; j < lastHour; j++) { + if (pumpType == null) + basalByHour[j] = current.rate; + else + basalByHour[j] = pumpType.determineCorrectBasalSize(current.rate); + } + } + + return basalByHour; + } + + + public static String getProfilesByHourToString(Double[] data) { + + StringBuilder stringBuilder = new StringBuilder(); + + for (Double value : data) { + stringBuilder.append(String.format("%.3f", value)); + stringBuilder.append(" "); + } + + return stringBuilder.toString(); + + } + + + public byte[] getRawData() { + return this.mRawData; + } + + + public String toString() { + return basalProfileToString(); + } + + + private boolean isLogEnabled() { + return L.isEnabled(L.PUMPCOMM); + } + + public boolean verify() { + + try { + getEntries(); + } catch (Exception ex) { + return false; + } + + Double[] profilesByHour = getProfilesByHour(); + + for (Double aDouble : profilesByHour) { + if (aDouble > 35.0d) + return false; + } + + return true; + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/BasalProfileEntry.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/BasalProfileEntry.java new file mode 100644 index 0000000000..9768ad2fc8 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/BasalProfileEntry.java @@ -0,0 +1,89 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.data.dto; + +import org.joda.time.LocalTime; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil; + +/** + * Created by geoff on 6/1/15. + * This is a helper class for BasalProfile, only used for interpreting the contents of BasalProfile + * - fixed rate is not one bit but two + */ +public class BasalProfileEntry { + + private static final Logger LOG = LoggerFactory.getLogger(L.PUMPCOMM); + + public byte[] rate_raw; + public double rate; + public byte startTime_raw; + public LocalTime startTime; // Just a "time of day" + + + public BasalProfileEntry() { + rate = -9.999E6; + rate_raw = MedtronicUtil.getByteArrayFromUnsignedShort(0xFF, true); + startTime = new LocalTime(0); + startTime_raw = (byte)0xFF; + } + + + public BasalProfileEntry(double rate, int hour, int minutes) { + byte[] data = MedtronicUtil.getBasalStrokes(rate, true); + + rate_raw = new byte[2]; + rate_raw[0] = data[1]; + rate_raw[1] = data[0]; + + int interval = hour * 2; + + if (minutes == 30) { + interval++; + } + + startTime_raw = (byte)interval; + startTime = new LocalTime(hour, minutes == 30 ? 30 : 0); + } + + + public BasalProfileEntry(int rateStrokes, int startTimeInterval) { + // rateByte is insulin delivery rate, U/hr, in 0.025 U increments + // startTimeByte is time-of-day, in 30 minute increments + rate_raw = MedtronicUtil.getByteArrayFromUnsignedShort(rateStrokes, true); + rate = rateStrokes * 0.025; + startTime_raw = (byte)startTimeInterval; + + try { + startTime = new LocalTime(startTimeInterval / 2, (startTimeInterval % 2) * 30); + } catch (Exception ex) { + LOG.error( + "Error creating BasalProfileEntry: startTimeInterval={}, startTime_raw={}, hours={}, rateStrokes={}", + startTimeInterval, startTime_raw, startTimeInterval / 2, rateStrokes); + throw ex; + } + + } + + + public BasalProfileEntry(byte rateByte, int startTimeByte) { + // rateByte is insulin delivery rate, U/hr, in 0.025 U increments + // startTimeByte is time-of-day, in 30 minute increments + rate_raw = MedtronicUtil.getByteArrayFromUnsignedShort(rateByte, true); + rate = rateByte * 0.025; + startTime_raw = (byte)startTimeByte; + startTime = new LocalTime(startTimeByte / 2, (startTimeByte % 2) * 30); + } + + + public void setStartTime(LocalTime localTime) { + this.startTime = localTime; + } + + + public void setRate(double rate) { + this.rate = rate; + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/BatteryStatusDTO.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/BatteryStatusDTO.java new file mode 100644 index 0000000000..93b5eb10cb --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/BatteryStatusDTO.java @@ -0,0 +1,57 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.data.dto; + +import com.google.gson.annotations.Expose; + +import java.util.Locale; + +import info.nightscout.androidaps.plugins.pump.medtronic.defs.BatteryType; + +/** + * Created by andy on 6/14/18. + */ + +public class BatteryStatusDTO { + + @Expose + public BatteryStatusType batteryStatusType; + @Expose + public Double voltage; + + public boolean extendedDataReceived = false; + + + public int getCalculatedPercent(BatteryType batteryType) { + if (voltage == null || batteryType == BatteryType.None) { + return (batteryStatusType == BatteryStatusType.Low || batteryStatusType == BatteryStatusType.Unknown) ? 18 : 70; + } + + double percent = (voltage - batteryType.lowVoltage) / (batteryType.highVoltage - batteryType.lowVoltage); + + int percentInt = (int) (percent * 100.0d); + + if (percentInt<0) + percentInt = 1; + + if (percentInt > 100) + percentInt = 100; + + return percentInt; + } + + + public String toString() { + return String.format(Locale.ENGLISH, "BatteryStatusDTO [voltage=%.2f, alkaline=%d, lithium=%d, niZn={}]", + voltage == null ? 0.0f : voltage, + getCalculatedPercent(BatteryType.Alkaline), + getCalculatedPercent(BatteryType.Lithium), + getCalculatedPercent(BatteryType.NiZn)); + } + + + public enum BatteryStatusType { + Normal, + Low, + Unknown + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/BolusDTO.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/BolusDTO.java new file mode 100644 index 0000000000..8574bd831a --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/BolusDTO.java @@ -0,0 +1,160 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.data.dto; + +import com.google.gson.annotations.Expose; + +import info.nightscout.androidaps.plugins.pump.common.utils.StringUtil; +import info.nightscout.androidaps.plugins.pump.medtronic.defs.PumpBolusType; + +/** + * Application: GGC - GNU Gluco Control + * Plug-in: Pump Tool (support for Pump devices) + *

+ * See AUTHORS for copyright information. + *

+ * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later + * version. + *

+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + *

+ * You should have received a copy of the GNU General Public License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + *

+ * Filename: BolusDTO Description: Bolus DTO + *

+ * Author: Andy {andy@atech-software.com} + */ + +public class BolusDTO extends PumpTimeStampedRecord { + + @Expose + private Double requestedAmount; + @Expose + private Double deliveredAmount; + @Expose + private Double immediateAmount; // when Multiwave this is used + @Expose + private Integer duration; + @Expose + private PumpBolusType bolusType; + private Double insulinOnBoard; + + + public BolusDTO() { + // this.decimalPrecission = 2; + } + + + public Double getRequestedAmount() { + return requestedAmount; + } + + + public void setRequestedAmount(Double requestedAmount) { + this.requestedAmount = requestedAmount; + } + + + public Double getDeliveredAmount() { + return deliveredAmount; + } + + + public void setDeliveredAmount(Double deliveredAmount) { + this.deliveredAmount = deliveredAmount; + } + + + public Integer getDuration() { + return duration; + } + + + public void setDuration(Integer duration) { + this.duration = duration; + } + + + public PumpBolusType getBolusType() { + return bolusType; + } + + + public void setBolusType(PumpBolusType bolusType) { + this.bolusType = bolusType; + } + + + public Double getInsulinOnBoard() { + return insulinOnBoard; + } + + + public void setInsulinOnBoard(Double insulinOnBoard) { + this.insulinOnBoard = insulinOnBoard; + } + + + private String getDurationString() { + int minutes = this.duration; + + int h = minutes / 60; + + minutes -= (h * 60); + + return StringUtil.getLeadingZero(h, 2) + ":" + StringUtil.getLeadingZero(minutes, 2); + } + + + public String getValue() { + if ((bolusType == PumpBolusType.Normal) || (bolusType == PumpBolusType.Audio)) { + return getFormattedDecimal(this.deliveredAmount); + } else if (bolusType == PumpBolusType.Extended) { + return String.format("AMOUNT_SQUARE=%s;DURATION=%s", getFormattedDecimal(this.deliveredAmount), + getDurationString()); + } else { + return String.format("AMOUNT=%s;AMOUNT_SQUARE=%s;DURATION=%s", getFormattedDecimal(this.immediateAmount), + getFormattedDecimal(this.deliveredAmount), getDurationString()); + } + } + + + public String getDisplayableValue() { + String value = getValue(); + + value = value.replace("AMOUNT_SQUARE=", "Amount Square: "); + value = value.replace("AMOUNT=", "Amount: "); + value = value.replace("DURATION=", "Duration: "); + + return value; + } + + + public Double getImmediateAmount() { + return immediateAmount; + } + + + public void setImmediateAmount(Double immediateAmount) { + this.immediateAmount = immediateAmount; + } + + + public String getFormattedDecimal(double value) { + return StringUtil.getFormatedValueUS(value, 2); + } + + + public String getBolusKey() { + return "Bolus_" + this.bolusType.name(); + + } + + + @Override + public String toString() { + return "BolusDTO [type=" + bolusType.name() + ", " + getValue() + "]"; + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/BolusWizardDTO.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/BolusWizardDTO.java new file mode 100644 index 0000000000..5d9710a5c5 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/BolusWizardDTO.java @@ -0,0 +1,48 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.data.dto; + +import info.nightscout.androidaps.plugins.pump.common.utils.DateTimeUtil; + +/** + * Created by andy on 18.05.15. + */ +public class BolusWizardDTO extends PumpTimeStampedRecord { + + // bloodGlucose and bgTarets are in mg/dL + public Integer bloodGlucose = 0; // mg/dL + public Integer carbs = 0; + public String chUnit = "g"; + + public Float carbRatio = 0.0f; + public Float insulinSensitivity = 0.0f; + public Integer bgTargetLow = 0; + public Integer bgTargetHigh = 0; + public Float bolusTotal = 0.0f; + public Float correctionEstimate = 0.0f; + public Float foodEstimate = 0.0f; + public Float unabsorbedInsulin = 0.0f; + + + // public LocalDateTime localDateTime; + // public long atechDateTime; + + public String getValue() { + return String.format("BG=%d;CH=%d;CH_UNIT=%s;CH_INS_RATIO=%5.3f;BG_INS_RATIO=%5.3f;" + + "BG_TARGET_LOW=%d;BG_TARGET_HIGH=%d;BOLUS_TOTAL=%5.3f;" + + "BOLUS_CORRECTION=%5.3f;BOLUS_FOOD=%5.3f;UNABSORBED_INSULIN=%5.3f", // + bloodGlucose, carbs, chUnit, carbRatio, insulinSensitivity, bgTargetLow, // + bgTargetHigh, bolusTotal, correctionEstimate, foodEstimate, unabsorbedInsulin); + } + + public String getDisplayableValue() { + return String.format("Bg=%d, CH=%d %s, Ch/Ins Ratio=%5.3f, Bg/Ins Ratio=%5.3f;" + + "Bg Target(L/H)=%d/%d, Bolus: Total=%5.3f, " + + "Correction=%5.3f, Food=%5.3f, IOB=%5.3f", // + bloodGlucose, carbs, chUnit, carbRatio, insulinSensitivity, bgTargetLow, // + bgTargetHigh, bolusTotal, correctionEstimate, foodEstimate, unabsorbedInsulin); + } + + + public String toString() { + return "BolusWizardDTO [dateTime=" + DateTimeUtil.toString(atechDateTime) + ", " + getValue() + "]"; + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/ClockDTO.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/ClockDTO.java new file mode 100644 index 0000000000..1623e03ff5 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/ClockDTO.java @@ -0,0 +1,16 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.data.dto; + +import org.joda.time.LocalDateTime; + +/** + * Created by andy on 2/27/19. + */ + +public class ClockDTO { + + public LocalDateTime localDeviceTime; + + public LocalDateTime pumpTime; + + public int timeDifference; // s (pump -> local) +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/DailyTotalsDTO.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/DailyTotalsDTO.java new file mode 100644 index 0000000000..495bce8a31 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/DailyTotalsDTO.java @@ -0,0 +1,257 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.data.dto; + +import com.google.common.base.MoreObjects; +import com.google.gson.annotations.Expose; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import info.nightscout.androidaps.db.TDD; +import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil; +import info.nightscout.androidaps.plugins.pump.common.utils.DateTimeUtil; +import info.nightscout.androidaps.plugins.pump.common.utils.StringUtil; +import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.PumpHistoryEntry; + +/** + * Created by andy on 11/3/18. + */ + +/** + * NOTE: Decoding is only done for insulin part, everything else is pretty must left undecoded. + */ + +public class DailyTotalsDTO { + + private static final Logger LOG = LoggerFactory.getLogger(L.PUMPCOMM); + + // bg avg, bg low hi, number Bgs, + // Sen Avg, Sen Lo/Hi, Sens Cal/Data = 0/0, + // Insulin=19.8[8,9], Basal[10,11], Bolus[13,14], Carbs, + // Bolus=1.7, Fodd, Corr, Manual=1.7, + // Num bOlus=1, food/corr, Food+corr, manual bolus=1 + private Double bgAvg; + private Double bgLow; + private Double bgHigh; + private Integer bgCount; + + private Double sensorAvg; + private Double sensorMin; + private Double sensorMax; + private Integer sensorCalcCount; + private Integer sensorDataCount; + + @Expose + private Double insulinTotal = 0.0d; + @Expose + private Double insulinBasal = 0.0d; + @Expose + private Double insulinBolus = 0.0d; + private Double insulinCarbs; + + private Double bolusTotal; + private Double bolusFood; + private Double bolusFoodAndCorr; + private Double bolusCorrection; + private Double bolusManual; + + private Integer bolusCount; + private Integer bolusCountFoodOrCorr; + // Integer bolusCountCorr; + Integer bolusCountFoodAndCorr; + Integer bolusCountManual; + private Integer bolusCountFood; + private Integer bolusCountCorr; + + PumpHistoryEntry entry; + + + public DailyTotalsDTO(PumpHistoryEntry entry) { + this.entry = entry; + + switch (entry.getEntryType()) { + case EndResultTotals: + decodeEndResultsTotals(entry); + break; + + case DailyTotals515: + decodeDailyTotals515(entry.getBody()); + break; + + case DailyTotals522: + decodeDailyTotals522(entry.getBody()); + break; + + case DailyTotals523: + decodeDailyTotals523(entry.getBody()); + break; + + default: + break; + } + + setDisplayable(); + } + + + private void setDisplayable() { + + if (this.insulinBasal == null) { + this.entry.setDisplayableValue("Total Insulin: " + StringUtil.getFormatedValueUS(this.insulinTotal, 2)); + } else { + this.entry.setDisplayableValue("Basal Insulin: " + StringUtil.getFormatedValueUS(this.insulinBasal, 2) + + ", Total Insulin: " + StringUtil.getFormatedValueUS(this.insulinTotal, 2)); + } + + } + + + private void decodeEndResultsTotals(PumpHistoryEntry entry) { + double totals = ByteUtil.toInt((int) entry.getHead()[0], (int) entry.getHead()[1], (int) entry.getHead()[2], + (int) entry.getHead()[3], ByteUtil.BitConversion.BIG_ENDIAN) * 0.025d; + + this.insulinTotal = totals; + + entry.addDecodedData("Totals", totals); + } + + + private void testDecode(byte[] data) { + + // Daily + + byte[] body = data; // entry.getBody(); + //System.out.println("Totals 522"); + + for (int i = 0; i < body.length - 2; i++) { + + int j = ByteUtil.toInt(body[i], body[i + 1]); + int k = ByteUtil.toInt(body[i], body[i + 1], body[i + 2]); + + int j1 = ByteUtil.toInt(body[i + 1], body[i]); + int k1 = ByteUtil.toInt(body[i + 2], body[i + 1], body[i]); + + System.out.println(String.format( + "index: %d, number=%d, del/40=%.3f, del/10=%.3f, singular=%d, sing_hex=%s", i, j, j / 40.0d, j / 10.0d, + body[i], ByteUtil.shortHexString(body[i]))); + + System.out.println(String.format(" number[k,j1,k1]=%d / %d /%d, del/40=%.3f, del/40=%.3f, del/40=%.3f", + k, j1, k1, k / 40.0d, j1 / 40.0d, k1 / 40.0d)); + + } + } + + + private void decodeDailyTotals515(byte[] data) { + // LOG.debug("Can't decode DailyTotals515: Body={}", ByteUtil.getHex(data)); + + this.insulinTotal = ByteUtil.toInt(data[8], data[9]) / 40.0d; + this.insulinBasal = ByteUtil.toInt(data[10], data[11]) / 40.0d; + this.insulinBolus = ByteUtil.toInt(data[13], data[14]) / 40.0d; + + // Delivery Stats: BG AVG: Bg Low/Hi=none,Number BGs=0 + // Delivery Stats: INSULIN: Basal 22.30, Bolus=4.20, Catbs = 0g (26.5) + // Delivery Stats: BOLUS: Food=0.00, Corr=0.00, Manual=4.20 + // Delivery Stats: NUM BOLUS: Food/Corr=0,Food+Corr=0, Manual=3 + + //LOG.debug("515: {}", toString()); + } + + + private void decodeDailyTotals522(byte[] data) { + + this.insulinTotal = ByteUtil.toInt(data[8], data[9]) / 40.0d; + this.insulinBasal = ByteUtil.toInt(data[10], data[11]) / 40.0d; + this.insulinBolus = ByteUtil.toInt(data[13], data[14]) / 40.0d; + + this.bolusTotal = ByteUtil.toInt(data[17], data[18], data[19]) / 40.0d; + this.bolusFood = ByteUtil.toInt(data[21], data[22]) / 40.0d; + this.bolusCorrection = ByteUtil.toInt(data[23], data[24], data[25]) / 40.0d; + this.bolusManual = ByteUtil.toInt(data[26], data[27], data[28]) / 40.0d; + + bolusCount = ByteUtil.asUINT8(data[30]); + bolusCountFoodOrCorr = ByteUtil.asUINT8(data[31]); + bolusCountFoodAndCorr = ByteUtil.asUINT8(data[32]); + bolusCountManual = ByteUtil.asUINT8(data[33]); + + // bg avg, bg low hi, number Bgs, + // Sen Avg, Sen Lo/Hi, Sens Cal/Data = 0/0, + // Insulin=19.8[8,9], Basal[10,11], Bolus[13,14], Carbs, + // Bolus=1.7[18,19], Fodd, Corr, Manual=1.7[27,28], + // Num bOlus=1, food/corr, Food+corr, manual bolus=1 + + //LOG.debug("522: {}", toString()); + } + + + private void decodeDailyTotals523(byte[] data) { + + this.insulinTotal = ByteUtil.toInt(data[8], data[9]) / 40.0d; + this.insulinBasal = ByteUtil.toInt(data[10], data[11]) / 40.0d; + this.insulinBolus = ByteUtil.toInt(data[13], data[14]) / 40.0d; + this.insulinCarbs = ByteUtil.toInt(data[16], data[17]) * 1.0d; + + this.bolusFood = ByteUtil.toInt(data[18], data[19]) / 40.0d; + this.bolusCorrection = ByteUtil.toInt(data[20], data[21]) / 40.0d; + this.bolusFoodAndCorr = ByteUtil.toInt(data[22], data[23]) / 40.0d; + this.bolusManual = ByteUtil.toInt(data[24], data[25]) / 40.0d; + + this.bolusCountFood = ByteUtil.asUINT8(data[26]); + this.bolusCountCorr = ByteUtil.asUINT8(data[27]); + this.bolusCountFoodAndCorr = ByteUtil.asUINT8(data[28]); + this.bolusCountManual = ByteUtil.asUINT8(data[29]); // + + + // Delivery Stats: Carbs=11, Total Insulin=3.850, Basal=2.000 + // Delivery Stats: Basal 52,Bolus 1.850, Bolus=48%o + // Delivery Stats: Food only=0.9, Food only#=1, Corr only = 0.0 + // Delivery Stats: #Corr_only=0,Food+Corr=0.000, #Food+Corr=0 + // Delivery Stats: Manual = 0.95, #Manual=5 + + //LOG.debug("523: {}", toString()); + } + + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) // + .add("bgAvg", bgAvg) // + .add("bgLow", bgLow) // + .add("bgHigh", bgHigh) // + .add("bgCount", bgCount) // + .add("sensorAvg", sensorAvg) // + .add("sensorMin", sensorMin) // + .add("sensorMax", sensorMax) // + .add("sensorCalcCount", sensorCalcCount) // + .add("sensorDataCount", sensorDataCount) // + .add("insulinTotal", insulinTotal) // + .add("insulinBasal", insulinBasal) // + .add("insulinBolus", insulinBolus) // + .add("insulinCarbs", insulinCarbs) // + .add("bolusTotal", bolusTotal) // + .add("bolusFood", bolusFood) // + .add("bolusCorrection", bolusCorrection) // + .add("bolusManual", bolusManual) // + .add("bolusCount", bolusCount) // + .add("bolusCountFoodOrCorr", bolusCountFoodOrCorr) // + .add("bolusCountFoodAndCorr", bolusCountFoodAndCorr) // + .add("bolusCountFood", bolusCountFood) // + .add("bolusCountCorr", bolusCountCorr) // + .add("bolusCountManual", bolusCountManual) // + .omitNullValues() // + .toString(); + } + + + public void setTDD(TDD tdd) { + tdd.date = DateTimeUtil.toMillisFromATD(this.entry.atechDateTime); + tdd.basal = insulinBasal; + tdd.bolus = insulinBolus; + tdd.total = insulinTotal; + } + + + public boolean doesEqual(TDD tdd) { + return tdd.total == this.insulinTotal && tdd.bolus == this.insulinBolus && tdd.basal == this.insulinBasal; + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/PumpSettingDTO.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/PumpSettingDTO.java new file mode 100644 index 0000000000..1868064d8b --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/PumpSettingDTO.java @@ -0,0 +1,29 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.data.dto; + +import info.nightscout.androidaps.plugins.pump.medtronic.defs.PumpConfigurationGroup; + +/** + * Created by andy on 6/6/18. + */ + +public class PumpSettingDTO { + + public String key; + public String value; + + PumpConfigurationGroup configurationGroup; + + + public PumpSettingDTO(String key, String value, PumpConfigurationGroup configurationGroup) { + this.key = key; + this.value = value; + this.configurationGroup = configurationGroup; + } + + + @Override + public String toString() { + return "PumpSettingDTO [key=" + key + ",value=" + value + ",group=" + configurationGroup.name() + "]"; + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/PumpTimeStampedRecord.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/PumpTimeStampedRecord.java new file mode 100644 index 0000000000..84ad0914b3 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/PumpTimeStampedRecord.java @@ -0,0 +1,28 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.data.dto; + +import info.nightscout.androidaps.plugins.pump.common.utils.StringUtil; + +/** + * Created by andy on 6/2/18. + */ +public class PumpTimeStampedRecord { + + protected int decimalPrecission = 2; + public long atechDateTime; + + + public long getAtechDateTime() { + return this.atechDateTime; + } + + + public void setAtechDateTime(long atechDateTime) { + this.atechDateTime = atechDateTime; + } + + + public String getFormattedDecimal(double value) { + return StringUtil.getFormatedValueUS(value, this.decimalPrecission); + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/TempBasalPair.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/TempBasalPair.java new file mode 100644 index 0000000000..7a17767243 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/TempBasalPair.java @@ -0,0 +1,175 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.data.dto; + +import com.google.gson.annotations.Expose; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil; +import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil; + +/** + * Created by geoff on 5/29/15. + *

+ * Just need a class to keep the pair together, for parcel transport. + */ +public class TempBasalPair { + + private static final Logger LOG = LoggerFactory.getLogger(L.PUMPCOMM); + + @Expose + private double insulinRate = 0.0d; + @Expose + private int durationMinutes = 0; + @Expose + private boolean isPercent = false; + + + public TempBasalPair() { + } + + + /** + * This constructor is for use with PumpHistoryDecoder + * + * @param rateByte + * @param startTimeByte + * @param isPercent + */ + public TempBasalPair(byte rateByte, int startTimeByte, boolean isPercent) { + int rateInt = ByteUtil.asUINT8(rateByte); + + if (isPercent) + this.insulinRate = rateByte; + else + this.insulinRate = rateInt * 0.025; + this.durationMinutes = startTimeByte * 30; + this.isPercent = isPercent; + } + + + public TempBasalPair(double insulinRate, boolean isPercent, int durationMinutes) { + this.insulinRate = insulinRate; + this.isPercent = isPercent; + this.durationMinutes = durationMinutes; + } + + + public TempBasalPair(byte[] response) { + + if (L.isEnabled(L.PUMPCOMM)) + LOG.debug("Received TempBasal response: " + ByteUtil.getHex(response)); + + isPercent = response[0] == 1; + + if (isPercent) { + insulinRate = response[1]; + } else { + int strokes = MedtronicUtil.makeUnsignedShort(response[2], response[3]); + + insulinRate = strokes / 40.0d; + } + + if (response.length<6) { + durationMinutes = ByteUtil.asUINT8(response[4]); + } else { + durationMinutes = MedtronicUtil.makeUnsignedShort(response[4], response[5]); + } + + LOG.warn("TempBasalPair (with {} byte response): {}", response.length, toString()); + + } + + + public double getInsulinRate() { + return insulinRate; + } + + + public void setInsulinRate(double insulinRate) { + this.insulinRate = insulinRate; + } + + + public int getDurationMinutes() { + return durationMinutes; + } + + + public void setDurationMinutes(int durationMinutes) { + this.durationMinutes = durationMinutes; + } + + + public boolean isPercent() { + return isPercent; + } + + + public void setIsPercent(boolean yesIsPercent) { + this.isPercent = yesIsPercent; + } + + + public byte[] getAsRawData() { + + List list = new ArrayList(); + + list.add((byte) 5); + + byte[] insulinRate = MedtronicUtil.getBasalStrokes(this.insulinRate, true); + byte timeMin = (byte) MedtronicUtil.getIntervalFromMinutes(durationMinutes); + + // list.add((byte) 0); // ? + + // list.add((byte) 0); // is_absolute + + if (insulinRate.length == 1) + list.add((byte) 0x00); + else + list.add(insulinRate[0]); + + list.add(insulinRate[1]); + // list.add((byte) 0); // percent amount + + list.add(timeMin); // 3 (time) - OK + + if (insulinRate.length == 1) + list.add((byte) 0x00); + else + list.add(insulinRate[0]); + + list.add(insulinRate[1]); + + return MedtronicUtil.createByteArray(list); + } + + public boolean isCancelTBR() { + return (MedtronicUtil.isSame(insulinRate, 0.0d) && durationMinutes == 0); + } + + + public String getDescription() { + if (isCancelTBR()) { + return "Cancel TBR"; + } + + if (isPercent) { + return String.format(Locale.ENGLISH, "Rate: %.0f%%, Duration: %d min", insulinRate, durationMinutes); + } else { + return String.format(Locale.ENGLISH, "Rate: %.3f U, Duration: %d min", insulinRate, durationMinutes); + } + } + + + @Override + public String toString() { + return "TempBasalPair [" + "Rate=" + insulinRate + ", DurationMinutes=" + durationMinutes + ", IsPercent=" + + isPercent + "]"; + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/TempBasalProcessDTO.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/TempBasalProcessDTO.java new file mode 100644 index 0000000000..86cba28184 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/TempBasalProcessDTO.java @@ -0,0 +1,31 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.data.dto; + +import info.nightscout.androidaps.plugins.pump.common.utils.DateTimeUtil; +import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.PumpHistoryEntry; + +public class TempBasalProcessDTO { + + public PumpHistoryEntry itemOne; + public PumpHistoryEntry itemTwo; + + public Operation processOperation = Operation.None; + + public int getDuration() { + if (itemTwo == null) { + TempBasalPair tbr = (TempBasalPair) itemOne.getDecodedDataEntry("Object"); + return tbr.getDurationMinutes(); + } else { + int difference = DateTimeUtil.getATechDateDiferenceAsMinutes(itemOne.atechDateTime, itemTwo.atechDateTime); + return difference; + } + } + + + public enum Operation { + None, + Add, + Edit + } + + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/BasalProfileStatus.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/BasalProfileStatus.java new file mode 100644 index 0000000000..0710e6ca4c --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/BasalProfileStatus.java @@ -0,0 +1,14 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.defs; + +/** + * Created by andy on 1/20/19. + */ + +public enum BasalProfileStatus { + + NotInitialized, // + ProfileOK, // + ProfileChanged, // + ; + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/BatteryType.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/BatteryType.java new file mode 100644 index 0000000000..5c7bf245a2 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/BatteryType.java @@ -0,0 +1,47 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.defs; + +import java.util.HashMap; +import java.util.Map; + +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.R; + +/** + * Created by andy on 6/4/18. + */ + +public enum BatteryType { + + None(R.string.key_medtronic_pump_battery_no, 0, 0), + Alkaline(R.string.key_medtronic_pump_battery_alkaline, 1.20d, 1.47d), // + Lithium(R.string.key_medtronic_pump_battery_lithium, 1.22d, 1.64d), // + NiZn(R.string.key_medtronic_pump_battery_nizn, 1.40d, 1.70d) // + ; + + private final String description; + public double lowVoltage; + public double highVoltage; + + static Map mapByDescription; + + static { + mapByDescription = new HashMap<>(); + + for (BatteryType value : values()) { + mapByDescription.put(value.description, value); + } + } + + BatteryType(int resId, double lowVoltage, double highVoltage) { + this.description = MainApp.gs(resId); + this.lowVoltage = lowVoltage; + this.highVoltage = highVoltage; + } + + public static BatteryType getByDescription(String batteryTypeStr) { + if (mapByDescription.containsKey(batteryTypeStr)) { + return mapByDescription.get(batteryTypeStr); + } + return BatteryType.None; + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/CommandValueDefinitionMDTType.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/CommandValueDefinitionMDTType.java new file mode 100644 index 0000000000..f061b860cf --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/CommandValueDefinitionMDTType.java @@ -0,0 +1,33 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.defs; + +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.CommandValueDefinitionType; + +/** + * Created by andy on 4/5/19. + */ + +public enum CommandValueDefinitionMDTType implements CommandValueDefinitionType { + GetModel, // + TuneUp, // + GetProfile, // + GetTBR, // + ; + + @Override + public String getName() { + return this.name(); + } + + + @Override + public String getDescription() { + return null; + } + + + @Override + public String commandAction() { + return null; + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/MedtronicCommandType.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/MedtronicCommandType.java new file mode 100755 index 0000000000..516085269a --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/MedtronicCommandType.java @@ -0,0 +1,453 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.defs; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.plugins.pump.medtronic.comm.message.MessageBody; +import info.nightscout.androidaps.plugins.pump.medtronic.comm.message.PumpAckMessageBody; +import info.nightscout.androidaps.plugins.pump.medtronic.comm.message.UnknownMessageBody; + +/** + * Taken from GNU Gluco Control diabetes management software (ggc.sourceforge.net) + *

+ * Description: Medtronic Commands (Pump and CGMS) for all 512 and later models (just 5xx) + *

+ * Link to original/unmodified file: + * https://sourceforge.net/p/ggc/code/HEAD/tree/trunk/ggc-plugins/ggc-plugins-base/src/ + * main/java/ggc/plugin/device/impl/minimed/enums/MinimedCommandType.java + *

+ * A lot of stuff has been removed because it is not needed anymore (historical stuff from CareLink + * and Carelink USB communication. + *

+ * Author: Andy {andy@atech-software.com} + */ +public enum MedtronicCommandType implements Serializable // , MinimedCommandTypeInterface +{ + InvalidCommand(0, "Invalid Command", null, null), // + + // Pump Responses (9) + CommandACK(0x06, "ACK - Acknowledge", MedtronicDeviceType.All, MinimedCommandParameterType.NoParameters), // + CommandNAK(0x15, "NAK - Not Acknowledged", MedtronicDeviceType.All, MinimedCommandParameterType.NoParameters), // + + // All (8) + PushAck(91, "Push ACK", MedtronicDeviceType.All, MinimedCommandParameterType.FixedParameters, getByteArray(2)), // + + PushEsc(91, "Push Esc", MedtronicDeviceType.All, MinimedCommandParameterType.FixedParameters, getByteArray(1)), // + + PushButton(0x5b, "Push Button", MedtronicDeviceType.All, MinimedCommandParameterType.NoParameters), // 91 + + RFPowerOn(93, "RF Power On", MedtronicDeviceType.All, MinimedCommandParameterType.FixedParameters, getByteArray( + 1, 10)), // + + RFPowerOff(93, "RF Power Off", MedtronicDeviceType.All, MinimedCommandParameterType.FixedParameters, getByteArray( + 0, 0)), // + + // SetSuspend(77, "Set Suspend", MinimedTargetType.InitCommand, MedtronicDeviceType.All, + // MinimedCommandParameterType.FixedParameters, getByteArray(1)), // + + // CancelSuspend(77, "Cancel Suspend", MinimedTargetType.InitCommand, MedtronicDeviceType.All, + // MinimedCommandParameterType.FixedParameters, getByteArray(0)), // + + PumpState(131, "Pump State", MedtronicDeviceType.All, MinimedCommandParameterType.NoParameters), // + + ReadPumpErrorStatus(117, "Pump Error Status", MedtronicDeviceType.All, MinimedCommandParameterType.NoParameters), // + + // 511 (InitCommand = 2, Config 7, Data = 1(+3) +// DetectBolus(75, "Detect Bolus", MedtronicDeviceType.Medtronic_511, MinimedCommandParameterType.FixedParameters, getByteArray( +// 0, 0, 0)), // + + // RemoteControlIds(118, "Remote Control Ids", MinimedTargetType.PumpConfiguration_NA, MedtronicDeviceType.All, + // MinimedCommandParameterType.NoParameters), // + + // FirmwareVersion(116, "Firmware Version", MinimedTargetType.InitCommand, MedtronicDeviceType.All, + // MinimedCommandParameterType.NoParameters), // + + // PumpId(113, "Pump Id", MinimedTargetType.PumpConfiguration, MedtronicDeviceType.All, + // MinimedCommandParameterType.NoParameters), // init + + SetRealTimeClock(0x40, "Set Pump Time", MedtronicDeviceType.All, MinimedCommandParameterType.NoParameters, // + 0), // + + GetRealTimeClock(112, "Get Pump Time", MedtronicDeviceType.All, MinimedCommandParameterType.NoParameters, // + 7, R.string.medtronic_cmd_desc_get_time), // 0x70 + + GetBatteryStatus(0x72, "Get Battery Status", MedtronicDeviceType.All, MinimedCommandParameterType.NoParameters), // + // GetBattery((byte) 0x72), // + + GetRemainingInsulin(0x73, "Read Remaining Insulin", MedtronicDeviceType.All, MinimedCommandParameterType.NoParameters, 2), // 115 + + SetBolus(0x42, "Set Bolus", MedtronicDeviceType.All, MinimedCommandParameterType.NoParameters, // + 0, R.string.medtronic_cmd_desc_set_bolus), // 66 + + // 512 + ReadTemporaryBasal(0x98, "Read Temporary Basal", MedtronicDeviceType.Medtronic_512andHigher, MinimedCommandParameterType.NoParameters, // + 5, R.string.medtronic_cmd_desc_get_tbr), // 152 + + SetTemporaryBasal(76, "Set Temporay Basal", MedtronicDeviceType.Medtronic_512andHigher, MinimedCommandParameterType.NoParameters, // + 0, R.string.medtronic_cmd_desc_set_tbr), + + // 512 Config + PumpModel(141, "Pump Model", MedtronicDeviceType.Medtronic_512andHigher, MinimedCommandParameterType.NoParameters, // + 5, R.string.medtronic_cmd_desc_get_model), // 0x8D + + // BGTargets_512(140, "BG Targets", MinimedTargetType.PumpConfiguration, MedtronicDeviceType.Medtronic_512_712, + // MinimedCommandParameterType.NoParameters), // + + // BGUnits(137, "BG Units", MinimedTargetType.PumpConfiguration, MedtronicDeviceType.Medtronic_512andHigher, + // MinimedCommandParameterType.NoParameters), // + + // Language(134, "Language", MinimedTargetType.PumpConfiguration, MedtronicDeviceType.Medtronic_512andHigher, + // MinimedCommandParameterType.NoParameters), // + + Settings_512(145, "Configuration", MedtronicDeviceType.Medtronic_512_712, MinimedCommandParameterType.NoParameters, // + 64, 1, 18, R.string.medtronic_cmd_desc_get_settings), // + + // BGAlarmClocks(142, "BG Alarm Clocks", MinimedTargetType.PumpConfiguration, + // MedtronicDeviceType.Medtronic_512andHigher, MinimedCommandParameterType.NoParameters), // + + // BGAlarmEnable(151, "BG Alarm Enable", MinimedTargetType.PumpConfiguration, + // MedtronicDeviceType.Medtronic_512andHigher, MinimedCommandParameterType.NoParameters), // + + // BGReminderEnable(144, "BG Reminder Enable", MinimedTargetType.PumpConfiguration, + // MedtronicDeviceType.Medtronic_512andHigher, MinimedCommandParameterType.NoParameters), // + + // ReadInsulinSensitivities(0x8b, "Read Insulin Sensitivities", MinimedTargetType.PumpConfiguration, + // MedtronicDeviceType.Medtronic_512andHigher, MinimedCommandParameterType.NoParameters), // 139 + + // 512 Data + GetHistoryData(128, "Get History", MedtronicDeviceType.Medtronic_512andHigher, MinimedCommandParameterType.SubCommands, // + 1024, 16, 1024, R.string.medtronic_cmd_desc_get_history), // 0x80 + + GetBasalProfileSTD(146, "Get Profile Standard", MedtronicDeviceType.Medtronic_512andHigher, MinimedCommandParameterType.NoParameters, // + 64, 3, 192, R.string.medtronic_cmd_desc_get_basal_profile), // 146 + + GetBasalProfileA(147, "Get Profile A", MedtronicDeviceType.Medtronic_512andHigher, MinimedCommandParameterType.NoParameters, // + 64, 3, 192, R.string.medtronic_cmd_desc_get_basal_profile), + + GetBasalProfileB(148, "Get Profile B", MedtronicDeviceType.Medtronic_512andHigher, MinimedCommandParameterType.NoParameters, // + 64, 3, 192, R.string.medtronic_cmd_desc_get_basal_profile), // 148 + + SetBasalProfileSTD(0x6f, "Set Profile Standard", MedtronicDeviceType.Medtronic_512andHigher, MinimedCommandParameterType.NoParameters, // + 64, 3, 192, R.string.medtronic_cmd_desc_set_basal_profile), // 111 + + SetBasalProfileA(0x30, "Set Profile A", MedtronicDeviceType.Medtronic_512andHigher, MinimedCommandParameterType.NoParameters, // + 64, 3, 192, R.string.medtronic_cmd_desc_set_basal_profile), // 48 + + SetBasalProfileB(0x31, "Set Profile B", MedtronicDeviceType.Medtronic_512andHigher, MinimedCommandParameterType.NoParameters, // + 64, 3, 192, R.string.medtronic_cmd_desc_set_basal_profile), // 49 + + // 515 + PumpStatus(206, "Pump Status", MedtronicDeviceType.Medtronic_515andHigher, MinimedCommandParameterType.NoParameters), // PumpConfiguration + + Settings(192, "Configuration", MedtronicDeviceType.Medtronic_515andHigher, MinimedCommandParameterType.NoParameters, // + 64, 1, 21, R.string.medtronic_cmd_desc_get_settings), // + + // 522 + SensorSettings_522(153, "Sensor Configuration", MedtronicDeviceType.Medtronic_522andHigher, MinimedCommandParameterType.NoParameters), // + + GlucoseHistory(154, "Glucose History", MedtronicDeviceType.Medtronic_522andHigher, MinimedCommandParameterType.SubCommands, 1024, 32, 0, null), // + + // 523 + SensorSettings(207, "Sensor Configuration", MedtronicDeviceType.Medtronic_523andHigher, MinimedCommandParameterType.NoParameters), // + + // 553 + // 554 + + // var MESSAGES = { + // READ_TIME : 0x70, + // READ_BATTERY_STATUS: 0x72, + // READ_HISTORY : 0x80, + // READ_CARB_RATIOS : 0x8A, + // READ_INSULIN_SENSITIVITIES: 0x8B, + // READ_MODEL : 0x8D, + // READ_PROFILE_STD : 0x92, + // READ_PROFILE_A : 0x93, + // READ_PROFILE_B : 0x94, + // READ_CBG_HISTORY: 0x9A, + // READ_ISIG_HISTORY: 0x9B, + // READ_CURRENT_PAGE : 0x9D, + // READ_BG_TARGETS : 0x9F, + // READ_SETTINGS : 0xC0, 192 + // READ_CURRENT_CBG_PAGE : 0xCD + // }; + + // Fake Commands + + CancelTBR(), + ; + + static Map mapByCode; + + static { + MedtronicCommandType.RFPowerOn.maxAllowedTime = 17000; + MedtronicCommandType.RFPowerOn.allowedRetries = 0; + MedtronicCommandType.RFPowerOn.recordLength = 0; + MedtronicCommandType.RFPowerOn.minimalBufferSizeToStartReading = 1; + + mapByCode = new HashMap<>(); + + for (MedtronicCommandType medtronicCommandType : values()) { + mapByCode.put(medtronicCommandType.getCommandCode(), medtronicCommandType); + } + } + + public byte commandCode = 0; + public String commandDescription = ""; + public byte[] commandParameters = null; + public int commandParametersCount = 0; + public int maxRecords = 1; + private Integer resourceId; + public int command_type = 0; + public int allowedRetries = 2; + public int maxAllowedTime = 2000; + public MinimedCommandParameterType parameterType; + public int minimalBufferSizeToStartReading = 14; + public int expectedLength = 0; + //MinimedTargetType targetType; + MedtronicDeviceType devices; + private int recordLength = 64; + + + MedtronicCommandType() { + // this is for "fake" commands needed by AAPS MedtronicUITask + } + + + // MedtronicCommandType(int code, String description, MedtronicDeviceType devices, +// MinimedCommandParameterType parameterType) { +// this(code, description, devices, parameterType, 64, 1, 0, 0, 0, 0); +// } +// +// +// MedtronicCommandType(int code, String description, MedtronicDeviceType devices, +// MinimedCommandParameterType parameterType, int expectedLength) { +// this(code, description, devices, parameterType, 64, 1, 0, 0, 0, expectedLength); +// } +// +// +// MedtronicCommandType(int code, String description, MedtronicDeviceType devices, +// MinimedCommandParameterType parameterType, int recordLength, int maxRecords, int commandType) { +// this(code, description, devices, parameterType, recordLength, maxRecords, 0, 0, commandType, 0); +// } +// +// +// MedtronicCommandType(int code, String description, MedtronicDeviceType devices, +// MinimedCommandParameterType parameterType, int recordLength, int maxRecords, int commandType, +// int expectedLength) { +// this(code, description, devices, parameterType, recordLength, maxRecords, 0, 0, commandType, +// expectedLength); +// } +// +// + MedtronicCommandType(int code, String description, MedtronicDeviceType devices, + MinimedCommandParameterType parameterType, byte[] cmd_params) { + this(code, description, devices, parameterType, 0, 1, 0, 0, 11, 0); + + this.commandParameters = cmd_params; + this.commandParametersCount = cmd_params.length; + } + + + MedtronicCommandType(int code, String description, MedtronicDeviceType devices, // + MinimedCommandParameterType parameterType) { + + this(code, description, devices, parameterType, 64, 1, 0, null); + } + + + // NEW + MedtronicCommandType(int code, String description, MedtronicDeviceType devices, + MinimedCommandParameterType parameterType, int recordLength, int maxRecords, int commandType) { + this(code, description, devices, parameterType, recordLength, maxRecords, 0, null); + } + + + // NEW + MedtronicCommandType(int code, String description, MedtronicDeviceType devices, // + MinimedCommandParameterType parameterType, int expectedLength) { + this(code, description, devices, parameterType, 64, 1, expectedLength, null); + } + + + // NEW + MedtronicCommandType(int code, String description, MedtronicDeviceType devices, // + MinimedCommandParameterType parameterType, int expectedLength, int resourceId) { + this(code, description, devices, parameterType, 64, 1, expectedLength, resourceId); + } + + + // NEW + MedtronicCommandType(int code, String description, + MedtronicDeviceType devices, // + MinimedCommandParameterType parameterType, int recordLength, int max_recs, int expectedLength, + Integer resourceId) { + this.commandCode = (byte) code; + this.commandDescription = description; + this.devices = devices; + this.recordLength = recordLength; + this.maxRecords = max_recs; + this.resourceId = resourceId; + + this.commandParametersCount = 0; + this.allowedRetries = 2; + this.parameterType = parameterType; + this.expectedLength = expectedLength; + + if (this.parameterType == MinimedCommandParameterType.SubCommands) { + this.minimalBufferSizeToStartReading = 200; + } + } + + + @Deprecated + MedtronicCommandType(int code, String description, MedtronicDeviceType devices, // + MinimedCommandParameterType parameterType, int recordLength, int max_recs, int addy, // + int addy_len, int cmd_type, int expectedLength) { + this.commandCode = (byte) code; + this.commandDescription = description; + //this.targetType = targetType; + this.devices = devices; + this.recordLength = recordLength; + this.maxRecords = max_recs; + + this.command_type = cmd_type; + this.commandParametersCount = 0; + this.allowedRetries = 2; + this.parameterType = parameterType; + this.expectedLength = expectedLength; + + if (this.parameterType == MinimedCommandParameterType.SubCommands) { + this.minimalBufferSizeToStartReading = 200; + } + + } + + + private static HashMap getDeviceTypesArray(MedtronicDeviceType... types) { + HashMap hashMap = new HashMap(); + + for (MedtronicDeviceType type : types) { + hashMap.put(type, null); + } + + return hashMap; + } + + + private static byte[] getByteArray(int... data) { + byte[] array = new byte[data.length]; + + for (int i = 0; i < data.length; i++) { + array[i] = (byte) data[i]; + } + + return array; + } + + + private static int[] getIntArray(int... data) { + return data; + } + + + public static MedtronicCommandType getByCode(byte code) { + if (mapByCode.containsKey(code)) { + return mapByCode.get(code); + } else { + return MedtronicCommandType.InvalidCommand; + } + } + + + public static MessageBody constructMessageBody(MedtronicCommandType messageType, byte[] bodyData) { + switch (messageType) { + case CommandACK: + return new PumpAckMessageBody(bodyData); + default: + return new UnknownMessageBody(bodyData); + } + } + + + public static MedtronicCommandType getSettings(MedtronicDeviceType medtronicPumpModel) { + if (MedtronicDeviceType.isSameDevice(medtronicPumpModel, MedtronicDeviceType.Medtronic_512_712)) + return MedtronicCommandType.Settings_512; + else + return MedtronicCommandType.Settings; + } + + + /** + * Get Full Command Description + * + * @return command description + */ + public String getFullCommandDescription() { + return "Command [name=" + this.name() + ", id=" + this.commandCode + ",description=" + this.commandDescription + + "] "; + } + + + public boolean canReturnData() { + System.out.println("CanReturnData: ]id=" + this.name() + "max=" + this.maxRecords + "recLen=" + recordLength); + return (this.maxRecords * this.recordLength) > 0; + } + + + public int getRecordLength() { + return recordLength; + } + + + public int getMaxRecords() { + return maxRecords; + } + + + public byte getCommandCode() { + return commandCode; + } + + + public int getCommandParametersCount() { + if (this.commandParameters == null) { + return 0; + } else { + return this.commandParameters.length; + } + } + + + public byte[] getCommandParameters() { + return commandParameters; + } + + + public boolean hasCommandParameters() { + return (getCommandParametersCount() > 0); + } + + + public String toString() { + return name(); + } + + + public String getCommandDescription() { + return this.commandDescription; + } + + + public Integer getResourceId() { + return resourceId; + } + + public enum MinimedCommandParameterType { + NoParameters, // + FixedParameters, // + SubCommands // + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/MedtronicCustomActionType.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/MedtronicCustomActionType.java new file mode 100644 index 0000000000..af0be33cfa --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/MedtronicCustomActionType.java @@ -0,0 +1,20 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.defs; + +import info.nightscout.androidaps.plugins.general.actions.defs.CustomActionType; + +/** + * Created by andy on 11/3/18. + */ + +public enum MedtronicCustomActionType implements CustomActionType { + + WakeUpAndTune(), // + ClearBolusBlock(), // + ResetRileyLinkConfiguration(), // + ; + + @Override + public String getKey() { + return this.name(); + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/MedtronicDeviceType.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/MedtronicDeviceType.java new file mode 100644 index 0000000000..f13974999b --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/MedtronicDeviceType.java @@ -0,0 +1,140 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.defs; + +import java.util.HashMap; +import java.util.Map; + +/** + * Taken from GNU Gluco Control diabetes management software (ggc.sourceforge.net) + *

+ * Author: Andy {andy@atech-software.com} + */ + +public enum MedtronicDeviceType { + Unknown_Device, // + + // Pump + Medtronic_511("511"), // + + Medtronic_512("512"), // + Medtronic_712("712"), // + Medtronic_512_712(Medtronic_512, Medtronic_712), // + + Medtronic_515("515"), // + Medtronic_715("715"), // + Medtronic_515_715(Medtronic_515, Medtronic_715), // + + Medtronic_522("522"), // + Medtronic_722("722"), // + Medtronic_522_722(Medtronic_522, Medtronic_722), // + + Medtronic_523_Revel("523"), // + Medtronic_723_Revel("723"), // + + Medtronic_554_Veo("554"), // + Medtronic_754_Veo("754"), // + + Medtronic_512andHigher(Medtronic_512, Medtronic_712, Medtronic_515, Medtronic_715, Medtronic_522, Medtronic_722, // + Medtronic_523_Revel, Medtronic_723_Revel, Medtronic_554_Veo, Medtronic_754_Veo), // + + Medtronic_515andHigher(Medtronic_515, Medtronic_715, Medtronic_522, Medtronic_722, Medtronic_523_Revel, Medtronic_723_Revel, // + Medtronic_554_Veo, Medtronic_754_Veo), // + Medtronic_522andHigher(Medtronic_522, Medtronic_722, Medtronic_523_Revel, Medtronic_723_Revel, // + Medtronic_554_Veo, Medtronic_754_Veo), // + Medtronic_523andHigher(Medtronic_523_Revel, Medtronic_723_Revel, Medtronic_554_Veo, // + Medtronic_754_Veo), // + + Medtronic_554andHigher(Medtronic_554_Veo, Medtronic_754_Veo), // + + + // + All; + + static Map mapByDescription; + + static { + + mapByDescription = new HashMap<>(); + + for (MedtronicDeviceType minimedDeviceType : values()) { + + if (!minimedDeviceType.isFamily) { + mapByDescription.put(minimedDeviceType.pumpModel, minimedDeviceType); + } + } + + } + + private String pumpModel; + + private boolean isFamily; + private MedtronicDeviceType[] familyMembers = null; + + + MedtronicDeviceType(String pumpModel) { + this.isFamily = false; + this.pumpModel = pumpModel; + } + + + MedtronicDeviceType(MedtronicDeviceType... familyMembers) { + this.familyMembers = familyMembers; + this.isFamily = true; + } + + + public static boolean isSameDevice(MedtronicDeviceType deviceWeCheck, MedtronicDeviceType deviceSources) { + if (deviceSources.isFamily) { + for (MedtronicDeviceType mdt : deviceSources.familyMembers) { + if (mdt == deviceWeCheck) + return true; + } + } else { + return (deviceWeCheck == deviceSources); + } + + return false; + } + + + public static MedtronicDeviceType getByDescription(String desc) { + if (mapByDescription.containsKey(desc)) { + return mapByDescription.get(desc); + } else { + return MedtronicDeviceType.Unknown_Device; + } + } + + +// public static boolean isLargerFormat(MedtronicDeviceType model) { +// return isSameDevice(model, Medtronic_523andHigher); +// } + + + public boolean isFamily() { + return isFamily; + } + + + public MedtronicDeviceType[] getFamilyMembers() { + return familyMembers; + } + + +// public boolean isLargerFormat() { +// return isSameDevice(this, Medtronic_523andHigher); +// } + + public boolean isMedtronic_523orHigher() { + return isSameDevice(this, Medtronic_523andHigher); + } + + + public int getBolusStrokes() { + return (isMedtronic_523orHigher()) ? 40 : 10; + } + + + public String getPumpModel() { + return pumpModel; + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/MedtronicNotificationType.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/MedtronicNotificationType.java new file mode 100644 index 0000000000..d11d6ad64c --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/MedtronicNotificationType.java @@ -0,0 +1,65 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.defs; + +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.plugins.general.overview.notifications.Notification; + +/** + * Created by andy on 10/15/18. + */ + +public enum MedtronicNotificationType { + + PumpUnreachable(Notification.RILEYLINK_CONNECTION, R.string.medtronic_pump_status_pump_unreachable, Notification.NORMAL), // + PumpTypeNotSame(R.string.medtronic_error_pump_type_set_differs_from_detected, Notification.NORMAL), // + PumpBasalProfilesNotEnabled(R.string.medtronic_error_pump_basal_profiles_not_enabled, Notification.URGENT), // + PumpIncorrectBasalProfileSelected(R.string.medtronic_error_pump_incorrect_basal_profile_selected, Notification.URGENT), // + PumpWrongTBRTypeSet(R.string.medtronic_error_pump_wrong_tbr_type_set, Notification.URGENT), // + PumpWrongMaxBolusSet(R.string.medtronic_error_pump_wrong_max_bolus_set, Notification.NORMAL), // + PumpWrongMaxBasalSet(R.string.medtronic_error_pump_wrong_max_basal_set, Notification.NORMAL), // + PumpWrongTimeUrgent(R.string.combo_notification_check_time_date, Notification.URGENT), + PumpWrongTimeNormal(R.string.combo_notification_check_time_date, Notification.NORMAL), + TimeChangeOver24h(Notification.OVER_24H_TIME_CHANGE_REQUESTED, R.string.medtronic_error_pump_24h_time_change_requested, Notification.URGENT), + // + ; + + private int notificationType; + private int resourceId; + private int notificationUrgency; + + + MedtronicNotificationType(int resourceId, int notificationUrgency) { + this(Notification.MEDTRONIC_PUMP_ALARM, resourceId, notificationUrgency); + } + + + MedtronicNotificationType(int notificationType, int resourceId, int notificationUrgency) { + this.notificationType = notificationType; + this.resourceId = resourceId; + this.notificationUrgency = notificationUrgency; + } + + + public int getNotificationType() { + return notificationType; + } + + + public void setNotificationType(int notificationType) { + this.notificationType = notificationType; + } + + + public int getResourceId() { + + return resourceId; + } + + + public int getNotificationUrgency() { + + return notificationUrgency; + } + + // Notification.MEDTRONIC_PUMP_ALARM R.string.medtronic_pump_status_pump_unreachable, Notification.NORMAL + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/MedtronicStatusRefreshType.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/MedtronicStatusRefreshType.java new file mode 100644 index 0000000000..55dc9b1a49 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/MedtronicStatusRefreshType.java @@ -0,0 +1,39 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.defs; + +import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil; + +/** + * Created by andy on 6/28/18. + */ + +public enum MedtronicStatusRefreshType { + + PumpHistory(5, null), // + Configuration(0, null), // + RemainingInsulin(-1, MedtronicCommandType.GetRemainingInsulin), // + BatteryStatus(55, MedtronicCommandType.GetBatteryStatus), // + PumpTime(60, MedtronicCommandType.GetRealTimeClock) // + ; + + private int refreshTime; + private MedtronicCommandType commandType; + + + MedtronicStatusRefreshType(int refreshTime, MedtronicCommandType commandType) { + this.refreshTime = refreshTime; + this.commandType = commandType; + } + + + public int getRefreshTime() { + return refreshTime; + } + + + public MedtronicCommandType getCommandType() { + if (this == Configuration) { + return MedtronicCommandType.getSettings(MedtronicUtil.getMedtronicPumpModel()); + } else + return commandType; + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/MedtronicUIResponseType.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/MedtronicUIResponseType.java new file mode 100644 index 0000000000..8ab1fc0871 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/MedtronicUIResponseType.java @@ -0,0 +1,13 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.defs; + +/** + * Created by andy on 10/18/18. + */ + +public enum MedtronicUIResponseType { + + Data, + Error, + Invalid + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/PumpBolusType.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/PumpBolusType.java new file mode 100644 index 0000000000..820bff8a5b --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/PumpBolusType.java @@ -0,0 +1,129 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.defs; + +import java.util.HashMap; + +/** + * Application: GGC - GNU Gluco Control + * Plug-in: Pump Tool (support for Pump devices) + *

+ * See AUTHORS for copyright information. + *

+ * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later + * version. + *

+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + *

+ * You should have received a copy of the GNU General Public License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + *

+ * Filename: PumpBolusType Description: Pump Bolus Types + *

+ * Author: Andy {andy@atech-software.com} + */ + +public enum PumpBolusType // implements CodeEnumWithTranslation +{ + None(0, "NONE"), // + Normal(1, "BOLUS_STANDARD"), // + Audio(2, "BOLUS_AUDIO"), // + Extended(3, "BOLUS_SQUARE", "AMOUNT_SQUARE=%s;DURATION=%s"), // + Multiwave(4, "BOLUS_MULTIWAVE", "AMOUNT=%s;AMOUNT_SQUARE=%s;DURATION=%s"); + + static String[] descriptions; + // static HashMap translationMapping = new HashMap(); + static HashMap codeMapping = new HashMap(); + private static boolean translated; + + static { + for (PumpBolusType pbt : values()) { + codeMapping.put(pbt.code, pbt); + } + } + + // public static void translateKeywords(I18nControlAbstract ic) + // { + // if (translated) + // return; + // + // for (PumpBolusType pbt : values()) + // { + // pbt.setTranslation(ic.getMessage(pbt.i18nKey)); + // translationMapping.put(pbt.getTranslation(), pbt); + // } + // + // String[] bolusDescriptions = { ic.getMessage("SELECT_BOLUS_TYPE"), // + // ic.getMessage("BOLUS_STANDARD"), // + // ic.getMessage("BOLUS_AUDIO"), // + // ic.getMessage("BOLUS_SQUARE"), // + // ic.getMessage("BOLUS_MULTIWAVE"), }; + // + // descriptions = bolusDescriptions; + // + // translated = true; + // } + + int code; + String i18nKey; + String translation; + String valueTemplate; + + + PumpBolusType(int code, String i18nKey) { + this.code = code; + this.i18nKey = i18nKey; + } + + + PumpBolusType(int code, String i18nKey, String valueTemplate) { + this.code = code; + this.i18nKey = i18nKey; + this.valueTemplate = valueTemplate; + } + + + public static PumpBolusType getByCode(int code) { + if (codeMapping.containsKey(code)) { + return codeMapping.get(code); + } else { + return PumpBolusType.None; + } + } + + + /** + * Get Descriptions (array) + * + * @return array of strings with description + */ + public static String[] getDescriptions() { + return descriptions; + } + + + public String getTranslation() { + return translation; + } + + + public void setTranslation(String translation) { + this.translation = translation; + } + + + public int getCode() { + return code; + } + + + public String getI18nKey() { + return i18nKey; + } + + + public String getName() { + return this.name(); + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/PumpConfigurationGroup.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/PumpConfigurationGroup.java new file mode 100644 index 0000000000..1c69641c98 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/PumpConfigurationGroup.java @@ -0,0 +1,58 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.defs; + +/** + * Created by andy on 27.02.15. + */ +public enum PumpConfigurationGroup { + General(1, "GROUP_GENERAL"), // + Device(2, "GROUP_DEVICE"), // + + Insulin(3, "GROUP_INSULIN"), // + + Basal(4, "GROUP_BASAL"), // + Bolus(5, "GROUP_BOLUS"), // + Sound(6, "GROUP_SOUND"), // + + Other(20, "GROUP_OTHER"), // + + UnknownGroup(21, "GROUP_UNKNOWN"), // + + ; // + + static boolean translated; + int code; + String i18nKey; + String translation; + + + PumpConfigurationGroup(int code, String i18nKey) { + this.code = code; + this.i18nKey = i18nKey; + } + + + public String getTranslation() { + return translation; + } + + + public void setTranslation(String translation) { + this.translation = translation; + } + + + public int getCode() { + return code; + } + + + public String getI18nKey() { + return i18nKey; + } + + + public String getName() { + return this.name(); + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/PumpDeviceState.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/PumpDeviceState.java new file mode 100644 index 0000000000..ba86963869 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/defs/PumpDeviceState.java @@ -0,0 +1,30 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.defs; + +import info.nightscout.androidaps.R; + +/** + * Created by andy on 6/11/18. + */ + +public enum PumpDeviceState { + + NeverContacted(R.string.medtronic_pump_status_never_contacted), // + Sleeping(R.string.medtronic_pump_status_sleeping), // + WakingUp(R.string.medtronic_pump_status_waking_up), // + Active(R.string.medtronic_pump_status_active), // + ErrorWhenCommunicating(R.string.medtronic_pump_status_error_comm), // + TimeoutWhenCommunicating(R.string.medtronic_pump_status_timeout_comm), // + // ProblemContacting(R.string.medtronic_pump_status_problem_contacting), // + PumpUnreachable(R.string.medtronic_pump_status_pump_unreachable), // + InvalidConfiguration(R.string.medtronic_pump_status_invalid_config); + + Integer resourceId; + + PumpDeviceState(int resourceId) { + this.resourceId = resourceId; + } + + public Integer getResourceId() { + return resourceId; + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/dialog/MedtronicHistoryActivity.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/dialog/MedtronicHistoryActivity.java new file mode 100644 index 0000000000..2d1a3a871e --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/dialog/MedtronicHistoryActivity.java @@ -0,0 +1,254 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.dialog; + +import android.os.Bundle; +import android.os.SystemClock; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.Spinner; +import android.widget.TextView; + +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; + +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.activities.NoSplashActivity; +import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.pump.medtronic.MedtronicPumpPlugin; +import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.PumpHistoryEntry; +import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.PumpHistoryEntryGroup; + +public class MedtronicHistoryActivity extends NoSplashActivity { + + private static Logger LOG = LoggerFactory.getLogger(L.PUMP); + + Spinner historyTypeSpinner; + TextView statusView; + RecyclerView recyclerView; + LinearLayoutManager llm; + + static TypeList showingType = null; + static PumpHistoryEntryGroup selectedGroup = PumpHistoryEntryGroup.All; + List filteredHistoryList = new ArrayList<>(); + + RecyclerViewAdapter recyclerViewAdapter; + boolean manualChange = false; + + List typeListFull; + + + public MedtronicHistoryActivity() { + super(); + } + + + private void filterHistory(PumpHistoryEntryGroup group) { + + this.filteredHistoryList.clear(); + + List list = new ArrayList<>(); + list.addAll(MedtronicPumpPlugin.getPlugin().getMedtronicHistoryData().getAllHistory()); + + //LOG.debug("Items on full list: {}", list.size()); + + if (group == PumpHistoryEntryGroup.All) { + this.filteredHistoryList.addAll(list); + } else { + for (PumpHistoryEntry pumpHistoryEntry : list) { + if (pumpHistoryEntry.getEntryType().getGroup() == group) { + this.filteredHistoryList.add(pumpHistoryEntry); + } + } + } + + if (this.recyclerViewAdapter != null) { + this.recyclerViewAdapter.setHistoryList(this.filteredHistoryList); + this.recyclerViewAdapter.notifyDataSetChanged(); + } + + //LOG.debug("Items on filtered list: {}", filteredHistoryList.size()); + } + + + @Override + protected void onResume() { + super.onResume(); + filterHistory(selectedGroup); + setHistoryTypeSpinner(); + } + + + private void setHistoryTypeSpinner() { + this.manualChange = true; + + for (int i = 0; i < typeListFull.size(); i++) { + if (typeListFull.get(i).entryGroup == selectedGroup) { + historyTypeSpinner.setSelection(i); + break; + } + } + + SystemClock.sleep(200); + this.manualChange = false; + } + + + @Override + protected void onPause() { + super.onPause(); + } + + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.medtronic_history_activity); + + historyTypeSpinner = (Spinner) findViewById(R.id.medtronic_historytype); + statusView = (TextView) findViewById(R.id.medtronic_historystatus); + recyclerView = (RecyclerView) findViewById(R.id.medtronic_history_recyclerview); + + recyclerView.setHasFixedSize(true); + llm = new LinearLayoutManager(this); + recyclerView.setLayoutManager(llm); + + recyclerViewAdapter = new RecyclerViewAdapter(filteredHistoryList); + recyclerView.setAdapter(recyclerViewAdapter); + + statusView.setVisibility(View.GONE); + + typeListFull = getTypeList(PumpHistoryEntryGroup.getList()); + + ArrayAdapter spinnerAdapter = new ArrayAdapter<>(this, R.layout.spinner_centered, typeListFull); + historyTypeSpinner.setAdapter(spinnerAdapter); + + historyTypeSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + if (manualChange) + return; + TypeList selected = (TypeList) historyTypeSpinner.getSelectedItem(); + showingType = selected; + selectedGroup = selected.entryGroup; + filterHistory(selectedGroup); + } + + + @Override + public void onNothingSelected(AdapterView parent) { + if (manualChange) + return; + filterHistory(PumpHistoryEntryGroup.All); + } + }); + + } + + + private List getTypeList(List list) { + + ArrayList typeList = new ArrayList<>(); + + for (PumpHistoryEntryGroup pumpHistoryEntryGroup : list) { + typeList.add(new TypeList(pumpHistoryEntryGroup)); + } + + return typeList; + } + + public static class TypeList { + + PumpHistoryEntryGroup entryGroup; + String name; + + + TypeList(PumpHistoryEntryGroup entryGroup) { + this.entryGroup = entryGroup; + this.name = entryGroup.getTranslated(); + } + + + @Override + public String toString() { + return name; + } + } + + public static class RecyclerViewAdapter extends RecyclerView.Adapter { + + List historyList; + + + RecyclerViewAdapter(List historyList) { + this.historyList = historyList; + } + + + public void setHistoryList(List historyList) { + // this.historyList.clear(); + // this.historyList.addAll(historyList); + + this.historyList = historyList; + + // this.notifyDataSetChanged(); + } + + + @Override + public HistoryViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) { + View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.medtronic_history_item, // + viewGroup, false); + return new HistoryViewHolder(v); + } + + + @Override + public void onBindViewHolder(HistoryViewHolder holder, int position) { + PumpHistoryEntry record = historyList.get(position); + + if (record != null) { + holder.timeView.setText(record.getDateTimeString()); + holder.typeView.setText(record.getEntryType().getDescription()); + holder.valueView.setText(record.getDisplayableValue()); + } + } + + + @Override + public int getItemCount() { + return historyList.size(); + } + + + @Override + public void onAttachedToRecyclerView(RecyclerView recyclerView) { + super.onAttachedToRecyclerView(recyclerView); + } + + static class HistoryViewHolder extends RecyclerView.ViewHolder { + + TextView timeView; + TextView typeView; + TextView valueView; + + + HistoryViewHolder(View itemView) { + super(itemView); + // cv = (CardView)itemView.findViewById(R.id.rileylink_history_item); + timeView = (TextView) itemView.findViewById(R.id.medtronic_history_time); + typeView = (TextView) itemView.findViewById(R.id.medtronic_history_source); + valueView = (TextView) itemView.findViewById(R.id.medtronic_history_description); + } + } + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/dialog/RileyLinkStatusDeviceMedtronic.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/dialog/RileyLinkStatusDeviceMedtronic.java new file mode 100644 index 0000000000..2d7252cb7c --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/dialog/RileyLinkStatusDeviceMedtronic.java @@ -0,0 +1,159 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.dialog; + +import android.os.Bundle; +import androidx.fragment.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.ListView; +import android.widget.TextView; + +import java.util.ArrayList; +import java.util.List; + +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.plugins.pump.common.dialog.RefreshableInterface; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.data.RLHistoryItem; +import info.nightscout.androidaps.plugins.pump.common.utils.StringUtil; + +/** + * Created by andy on 5/19/18. + *

+ * This is for 3rd tab, called Medtronic (in RileyLink stats), that should work similarly as the one in Loop. + *

+ * Showing currently selected RL, speed of RL, ability to issue simple commands (getModel, tuneUp, gerProfile) + */ + +// TODO needs to be implemented +public class RileyLinkStatusDeviceMedtronic extends Fragment implements RefreshableInterface { + + // @BindView(R.id.rileylink_history_list) + ListView listView; + + RileyLinkCommandListAdapter adapter; + + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View rootView = inflater.inflate(R.layout.rileylink_status_device, container, false); + + adapter = new RileyLinkCommandListAdapter(); + + return rootView; + } + + + @Override + public void onStart() { + super.onStart(); + + this.listView = (ListView) getActivity().findViewById(R.id.rileylink_history_list); + + listView.setAdapter(adapter); + + refreshData(); + } + + + @Override + public void refreshData() { + // adapter.addItemsAndClean(RileyLinkUtil.getRileyLinkHistory()); + } + + static class ViewHolder { + + TextView itemTime; + TextView itemSource; + TextView itemDescription; + } + + private class RileyLinkCommandListAdapter extends BaseAdapter { + + private List historyItemList; + private LayoutInflater mInflator; + + + public RileyLinkCommandListAdapter() { + super(); + historyItemList = new ArrayList<>(); + mInflator = RileyLinkStatusDeviceMedtronic.this.getLayoutInflater(); + } + + + public void addItem(RLHistoryItem item) { + if (!historyItemList.contains(item)) { + historyItemList.add(item); + notifyDataSetChanged(); + } + } + + + public RLHistoryItem getHistoryItem(int position) { + return historyItemList.get(position); + } + + + public void addItemsAndClean(List items) { + this.historyItemList.clear(); + + for (RLHistoryItem item : items) { + + if (!historyItemList.contains(item)) { + historyItemList.add(item); + } + } + + notifyDataSetChanged(); + } + + + public void clear() { + historyItemList.clear(); + notifyDataSetChanged(); + } + + + @Override + public int getCount() { + return historyItemList.size(); + } + + + @Override + public Object getItem(int i) { + return historyItemList.get(i); + } + + + @Override + public long getItemId(int i) { + return i; + } + + + @Override + public View getView(int i, View view, ViewGroup viewGroup) { + RileyLinkStatusDeviceMedtronic.ViewHolder viewHolder; + // General ListView optimization code. + if (view == null) { + view = mInflator.inflate(R.layout.rileylink_status_device_item, null); + viewHolder = new RileyLinkStatusDeviceMedtronic.ViewHolder(); + viewHolder.itemTime = (TextView) view.findViewById(R.id.rileylink_history_time); + viewHolder.itemSource = (TextView) view.findViewById(R.id.rileylink_history_source); + viewHolder.itemDescription = (TextView) view.findViewById(R.id.rileylink_history_description); + view.setTag(viewHolder); + } else { + viewHolder = (RileyLinkStatusDeviceMedtronic.ViewHolder) view.getTag(); + } + + RLHistoryItem item = historyItemList.get(i); + viewHolder.itemTime.setText(StringUtil.toDateTimeString(item.getDateTime())); + viewHolder.itemSource.setText("Riley Link"); // for now + viewHolder.itemDescription.setText(item.getDescription()); + + return view; + } + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/driver/MedtronicPumpStatus.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/driver/MedtronicPumpStatus.java new file mode 100644 index 0000000000..fc1208a340 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/driver/MedtronicPumpStatus.java @@ -0,0 +1,390 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.driver; + +import org.joda.time.LocalDateTime; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.HashMap; +import java.util.Map; + +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.interfaces.PumpDescription; +import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.pump.common.data.PumpStatus; +import info.nightscout.androidaps.plugins.pump.common.defs.PumpType; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkConst; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkUtil; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.RileyLinkEncodingType; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.RileyLinkTargetFrequency; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkError; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkServiceState; +import info.nightscout.androidaps.plugins.pump.medtronic.data.MedtronicHistoryData; +import info.nightscout.androidaps.plugins.pump.medtronic.defs.BasalProfileStatus; +import info.nightscout.androidaps.plugins.pump.medtronic.defs.BatteryType; +import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicDeviceType; +import info.nightscout.androidaps.plugins.pump.medtronic.defs.PumpDeviceState; +import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicConst; +import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil; +import info.nightscout.androidaps.utils.SP; + +/** + * Created by andy on 4/28/18. + */ + +public class MedtronicPumpStatus extends PumpStatus { + + private static Logger LOG = LoggerFactory.getLogger(L.PUMP); + + public String errorDescription = null; + public String serialNumber; + public String pumpFrequency = null; + public String rileyLinkAddress = null; + public Double maxBolus; + public Double maxBasal; + public boolean inPreInit = true; + + // statuses + public RileyLinkServiceState rileyLinkServiceState = RileyLinkServiceState.NotStarted; + public RileyLinkError rileyLinkError; + public PumpDeviceState pumpDeviceState = PumpDeviceState.NeverContacted; + public MedtronicDeviceType medtronicDeviceType = null; + public double currentBasal = 0; + public int tempBasalInProgress = 0; + public int tempBasalRatio = 0; + public int tempBasalRemainMin = 0; + public Date tempBasalStart; + public Double tempBasalAmount = 0.0d; + + // fixme + public Integer tempBasalLength = 0; + + private String regexMac = "([\\da-fA-F]{1,2}(?:\\:|$)){6}"; + private String regexSN = "[0-9]{6}"; + + private boolean serialChanged = false; + private boolean rileyLinkAddressChanged = false; + private boolean encodingChanged = false; + private boolean targetFrequencyChanged = false; + + private RileyLinkEncodingType encodingType; + private String[] frequencies; + private boolean isFrequencyUS = false; + private Map medtronicPumpMap = null; + private Map medtronicDeviceTypeMap = null; + private RileyLinkTargetFrequency targetFrequency; + public BasalProfileStatus basalProfileStatus = BasalProfileStatus.NotInitialized; + public BatteryType batteryType = BatteryType.None; + + + public MedtronicPumpStatus(PumpDescription pumpDescription) { + super(pumpDescription); + } + + + @Override + public void initSettings() { + + this.activeProfileName = "STD"; + this.reservoirRemainingUnits = 75d; + this.batteryRemaining = 75; + + if (this.medtronicPumpMap == null) + createMedtronicPumpMap(); + + if (this.medtronicDeviceTypeMap == null) + createMedtronicDeviceTypeMap(); + + this.lastConnection = SP.getLong(MedtronicConst.Statistics.LastGoodPumpCommunicationTime, 0L); + this.lastDataTime = new LocalDateTime(this.lastConnection); + } + + + private void createMedtronicDeviceTypeMap() { + medtronicDeviceTypeMap = new HashMap<>(); + medtronicDeviceTypeMap.put("512", MedtronicDeviceType.Medtronic_512); + medtronicDeviceTypeMap.put("712", MedtronicDeviceType.Medtronic_712); + medtronicDeviceTypeMap.put("515", MedtronicDeviceType.Medtronic_515); + medtronicDeviceTypeMap.put("715", MedtronicDeviceType.Medtronic_715); + + medtronicDeviceTypeMap.put("522", MedtronicDeviceType.Medtronic_522); + medtronicDeviceTypeMap.put("722", MedtronicDeviceType.Medtronic_722); + medtronicDeviceTypeMap.put("523", MedtronicDeviceType.Medtronic_523_Revel); + medtronicDeviceTypeMap.put("723", MedtronicDeviceType.Medtronic_723_Revel); + medtronicDeviceTypeMap.put("554", MedtronicDeviceType.Medtronic_554_Veo); + medtronicDeviceTypeMap.put("754", MedtronicDeviceType.Medtronic_754_Veo); + } + + + private void createMedtronicPumpMap() { + + medtronicPumpMap = new HashMap<>(); + medtronicPumpMap.put("512", PumpType.Medtronic_512_712); + medtronicPumpMap.put("712", PumpType.Medtronic_512_712); + medtronicPumpMap.put("515", PumpType.Medtronic_515_715); + medtronicPumpMap.put("715", PumpType.Medtronic_515_715); + + medtronicPumpMap.put("522", PumpType.Medtronic_522_722); + medtronicPumpMap.put("722", PumpType.Medtronic_522_722); + medtronicPumpMap.put("523", PumpType.Medtronic_523_723_Revel); + medtronicPumpMap.put("723", PumpType.Medtronic_523_723_Revel); + medtronicPumpMap.put("554", PumpType.Medtronic_554_754_Veo); + medtronicPumpMap.put("754", PumpType.Medtronic_554_754_Veo); + + frequencies = new String[2]; + frequencies[0] = MainApp.gs(R.string.key_medtronic_pump_frequency_us_ca); + frequencies[1] = MainApp.gs(R.string.key_medtronic_pump_frequency_worldwide); + } + + + public boolean verifyConfiguration() { + try { + + // FIXME don't reload information several times + if (this.medtronicPumpMap == null) + createMedtronicPumpMap(); + + if (this.medtronicDeviceTypeMap == null) + createMedtronicDeviceTypeMap(); + + this.errorDescription = "-"; + + String serialNr = SP.getString(MedtronicConst.Prefs.PumpSerial, null); + + if (serialNr == null) { + this.errorDescription = MainApp.gs(R.string.medtronic_error_serial_not_set); + return false; + } else { + if (!serialNr.matches(regexSN)) { + this.errorDescription = MainApp.gs(R.string.medtronic_error_serial_invalid); + return false; + } else { + if (!serialNr.equals(this.serialNumber)) { + this.serialNumber = serialNr; + serialChanged = true; + } + } + } + + String pumpType = SP.getString(MedtronicConst.Prefs.PumpType, null); + + if (pumpType == null) { + this.errorDescription = MainApp.gs(R.string.medtronic_error_pump_type_not_set); + return false; + } else { + String pumpTypePart = pumpType.substring(0, 3); + + if (!pumpTypePart.matches("[0-9]{3}")) { + this.errorDescription = MainApp.gs(R.string.medtronic_error_pump_type_invalid); + return false; + } else { + this.pumpType = medtronicPumpMap.get(pumpTypePart); + this.medtronicDeviceType = medtronicDeviceTypeMap.get(pumpTypePart); + this.pumpDescription.setPumpDescription(this.pumpType); + + if (pumpTypePart.startsWith("7")) + this.reservoirFullUnits = 300; + else + this.reservoirFullUnits = 176; + } + } + + String pumpFrequency = SP.getString(MedtronicConst.Prefs.PumpFrequency, null); + + if (pumpFrequency == null) { + this.errorDescription = MainApp.gs(R.string.medtronic_error_pump_frequency_not_set); + return false; + } else { + if (!pumpFrequency.equals(frequencies[0]) && !pumpFrequency.equals(frequencies[1])) { + this.errorDescription = MainApp.gs(R.string.medtronic_error_pump_frequency_invalid); + return false; + } else { + this.pumpFrequency = pumpFrequency; + this.isFrequencyUS = pumpFrequency.equals(frequencies[0]); + + RileyLinkTargetFrequency newTargetFrequency = this.isFrequencyUS ? // + RileyLinkTargetFrequency.Medtronic_US + : RileyLinkTargetFrequency.Medtronic_WorldWide; + + if (targetFrequency != newTargetFrequency) { + RileyLinkUtil.setRileyLinkTargetFrequency(newTargetFrequency); + targetFrequency = newTargetFrequency; + targetFrequencyChanged = true; + } + + } + } + + String rileyLinkAddress = SP.getString(RileyLinkConst.Prefs.RileyLinkAddress, null); + + if (rileyLinkAddress == null) { + if (isLogEnabled()) + LOG.debug("RileyLink address invalid: null"); + this.errorDescription = MainApp.gs(R.string.medtronic_error_rileylink_address_invalid); + return false; + } else { + if (!rileyLinkAddress.matches(regexMac)) { + this.errorDescription = MainApp.gs(R.string.medtronic_error_rileylink_address_invalid); + if (isLogEnabled()) + LOG.debug("RileyLink address invalid: {}", rileyLinkAddress); + } else { + if (!rileyLinkAddress.equals(this.rileyLinkAddress)) { + this.rileyLinkAddress = rileyLinkAddress; + rileyLinkAddressChanged = true; + } + } + } + + double maxBolusLcl = checkParameterValue(MedtronicConst.Prefs.MaxBolus, "25.0", 25.0d); + + if (maxBolus == null || !maxBolus.equals(maxBolusLcl)) { + maxBolus = maxBolusLcl; + + //LOG.debug("Max Bolus from AAPS settings is " + maxBolus); + } + + double maxBasalLcl = checkParameterValue(MedtronicConst.Prefs.MaxBasal, "35.0", 35.0d); + + if (maxBasal == null || !maxBasal.equals(maxBasalLcl)) { + maxBasal = maxBasalLcl; + + //LOG.debug("Max Basal from AAPS settings is " + maxBasal); + } + + + String encodingTypeStr = SP.getString(MedtronicConst.Prefs.Encoding, null); + + if (encodingTypeStr == null) { + return false; + } + + RileyLinkEncodingType newEncodingType = RileyLinkEncodingType.getByDescription(encodingTypeStr); + + if (this.encodingType == null) { + this.encodingType = newEncodingType; + } else if (this.encodingType != newEncodingType) { + this.encodingType = newEncodingType; + this.encodingChanged = true; + } + + String batteryTypeStr = SP.getString(MedtronicConst.Prefs.BatteryType, null); + + if (batteryTypeStr == null) + return false; + + BatteryType batteryType = BatteryType.getByDescription(batteryTypeStr); + + if (this.batteryType != batteryType) { + this.batteryType = batteryType; + MedtronicUtil.setBatteryType(this.batteryType); + } + + String bolusDebugEnabled = SP.getString(MedtronicConst.Prefs.BolusDebugEnabled, null); + + boolean bolusDebug = bolusDebugEnabled != null && bolusDebugEnabled.equals(MainApp.gs(R.string.common_on)); + + MedtronicHistoryData.doubleBolusDebug = bolusDebug; + + reconfigureService(); + + return true; + + } catch (Exception ex) { + this.errorDescription = ex.getMessage(); + LOG.error("Error on Verification: " + ex.getMessage(), ex); + return false; + } + } + + + private boolean reconfigureService() { + + if (!inPreInit && MedtronicUtil.getMedtronicService() != null) { + + if (serialChanged) { + MedtronicUtil.getMedtronicService().setPumpIDString(this.serialNumber); // short operation + serialChanged = false; + } + + if (rileyLinkAddressChanged) { + MedtronicUtil.sendBroadcastMessage(RileyLinkConst.Intents.RileyLinkNewAddressSet); + rileyLinkAddressChanged = false; + } + + if (encodingChanged) { + RileyLinkUtil.getRileyLinkService().changeRileyLinkEncoding(encodingType); + encodingChanged = false; + } + } + + + // if (targetFrequencyChanged && !inPreInit && MedtronicUtil.getMedtronicService() != null) { + // RileyLinkUtil.setRileyLinkTargetFrequency(targetFrequency); + // // RileyLinkUtil.getRileyLinkCommunicationManager().refreshRileyLinkTargetFrequency(); + // targetFrequencyChanged = false; + // } + + return (!rileyLinkAddressChanged && !serialChanged && !encodingChanged); // && !targetFrequencyChanged); + } + + + private double checkParameterValue(int key, String defaultValue, double defaultValueDouble) { + double val = 0.0d; + + String value = SP.getString(key, defaultValue); + + try { + val = Double.parseDouble(value); + } catch (Exception ex) { + LOG.error("Error parsing setting: {}, value found {}", key, value); + val = defaultValueDouble; + } + + if (val > defaultValueDouble) { + SP.putString(key, defaultValue); + val = defaultValueDouble; + } + + return val; + } + + + public String getErrorInfo() { + verifyConfiguration(); + + return (this.errorDescription == null) ? "-" : this.errorDescription; + } + + + @Override + public void refreshConfiguration() { + verifyConfiguration(); + } + + + public boolean setNotInPreInit() { + this.inPreInit = false; + + return reconfigureService(); + } + + + public double getBasalProfileForHour() { + if (basalsByHour != null) { + GregorianCalendar c = new GregorianCalendar(); + int hour = c.get(Calendar.HOUR_OF_DAY); + + return basalsByHour[hour]; + } + + return 0; + } + + private boolean isLogEnabled() { + return L.isEnabled(L.PUMP); + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/events/EventMedtronicDeviceStatusChange.kt b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/events/EventMedtronicDeviceStatusChange.kt new file mode 100644 index 0000000000..37e6649252 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/events/EventMedtronicDeviceStatusChange.kt @@ -0,0 +1,30 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.events + +import info.nightscout.androidaps.events.Event +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkError +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkServiceState +import info.nightscout.androidaps.plugins.pump.medtronic.defs.PumpDeviceState + +class EventMedtronicDeviceStatusChange : Event { + + private var rileyLinkServiceState: RileyLinkServiceState? = null + private var rileyLinkError: RileyLinkError? = null + + private var pumpDeviceState: PumpDeviceState? = null + private var errorDescription: String? = null + + + constructor(rileyLinkServiceState: RileyLinkServiceState?, rileyLinkError: RileyLinkError?) { + this.rileyLinkServiceState = rileyLinkServiceState + this.rileyLinkError = rileyLinkError + } + + constructor(pumpDeviceState: PumpDeviceState?) { + this.pumpDeviceState = pumpDeviceState + } + + constructor(pumpDeviceState: PumpDeviceState?, errorDescription: String?) { + this.pumpDeviceState = pumpDeviceState + this.errorDescription = errorDescription + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/events/EventMedtronicPumpConfigurationChanged.kt b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/events/EventMedtronicPumpConfigurationChanged.kt new file mode 100644 index 0000000000..458c055454 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/events/EventMedtronicPumpConfigurationChanged.kt @@ -0,0 +1,5 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.events + +import info.nightscout.androidaps.events.Event + +class EventMedtronicPumpConfigurationChanged : Event() diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/events/EventMedtronicPumpValuesChanged.kt b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/events/EventMedtronicPumpValuesChanged.kt new file mode 100644 index 0000000000..b314b9db19 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/events/EventMedtronicPumpValuesChanged.kt @@ -0,0 +1,5 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.events + +import info.nightscout.androidaps.events.Event + +class EventMedtronicPumpValuesChanged : Event() diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/events/EventRefreshButtonState.kt b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/events/EventRefreshButtonState.kt new file mode 100644 index 0000000000..81b9af4ff3 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/events/EventRefreshButtonState.kt @@ -0,0 +1,5 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.events + +import info.nightscout.androidaps.events.Event + +class EventRefreshButtonState (val newState : Boolean): Event() \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/service/RileyLinkMedtronicService.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/service/RileyLinkMedtronicService.java new file mode 100644 index 0000000000..cba33353f1 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/service/RileyLinkMedtronicService.java @@ -0,0 +1,205 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.service; + +import android.content.Intent; +import android.content.IntentFilter; +import android.content.res.Configuration; +import android.os.Binder; +import android.os.IBinder; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkCommunicationManager; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkConst; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkUtil; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.RFSpy; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.RileyLinkBLE; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.RileyLinkEncodingType; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkServiceState; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkTargetDevice; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.RileyLinkService; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.RileyLinkServiceData; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.tasks.ServiceTask; +import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil; +import info.nightscout.androidaps.plugins.pump.medtronic.MedtronicPumpPlugin; +import info.nightscout.androidaps.plugins.pump.medtronic.comm.MedtronicCommunicationManager; +import info.nightscout.androidaps.plugins.pump.medtronic.defs.PumpDeviceState; +import info.nightscout.androidaps.plugins.pump.medtronic.driver.MedtronicPumpStatus; +import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicConst; +import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil; +import info.nightscout.androidaps.utils.SP; + +/** + * RileyLinkMedtronicService is intended to stay running when the gui-app is closed. + */ +public class RileyLinkMedtronicService extends RileyLinkService { + + private static final Logger LOG = LoggerFactory.getLogger(L.PUMPCOMM); + + private static RileyLinkMedtronicService instance; + private static ServiceTask currentTask = null; + + // cache of most recently received set of pump history pages. Probably shouldn't be here. + public MedtronicCommunicationManager medtronicCommunicationManager; + MedtronicPumpStatus pumpStatus = null; + private IBinder mBinder = new LocalBinder(); + + + public RileyLinkMedtronicService() { + super(MainApp.instance().getApplicationContext()); + instance = this; + if (isLogEnabled()) + LOG.debug("RileyLinkMedtronicService newly constructed"); + MedtronicUtil.setMedtronicService(this); + pumpStatus = (MedtronicPumpStatus) MedtronicPumpPlugin.getPlugin().getPumpStatusData(); + } + + + public static RileyLinkMedtronicService getInstance() { + return instance; + } + + + public static MedtronicCommunicationManager getCommunicationManager() { + return instance.medtronicCommunicationManager; + } + + + @Override + public void onConfigurationChanged(Configuration newConfig) { + if (isLogEnabled()) + LOG.warn("onConfigurationChanged"); + super.onConfigurationChanged(newConfig); + } + + + @Override + public IBinder onBind(Intent intent) { + return mBinder; + } + + + @Override + public RileyLinkEncodingType getEncoding() { + return RileyLinkEncodingType.FourByteSixByteLocal; + } + + + /** + * If you have customized RileyLinkServiceData you need to override this + */ + public void initRileyLinkServiceData() { + + rileyLinkServiceData = new RileyLinkServiceData(RileyLinkTargetDevice.MedtronicPump); + + RileyLinkUtil.setRileyLinkServiceData(rileyLinkServiceData); + RileyLinkUtil.setTargetDevice(RileyLinkTargetDevice.MedtronicPump); + + setPumpIDString(SP.getString(MedtronicConst.Prefs.PumpSerial, "000000")); + + // get most recently used RileyLink address + rileyLinkServiceData.rileylinkAddress = SP.getString(RileyLinkConst.Prefs.RileyLinkAddress, ""); + + rileyLinkBLE = new RileyLinkBLE(this.context); // or this + rfspy = new RFSpy(rileyLinkBLE); + rfspy.startReader(); + + RileyLinkUtil.setRileyLinkBLE(rileyLinkBLE); + + // init rileyLinkCommunicationManager + medtronicCommunicationManager = new MedtronicCommunicationManager(context, rfspy); + } + + + public void resetRileyLinkConfiguration() { + rfspy.resetRileyLinkConfiguration(); + } + + + @Override + public RileyLinkCommunicationManager getDeviceCommunicationManager() { + return this.medtronicCommunicationManager; + } + + + public void setPumpIDString(String pumpID) { + if (pumpID.length() != 6) { + LOG.error("setPumpIDString: invalid pump id string: " + pumpID); + return; + } + + byte[] pumpIDBytes = ByteUtil.fromHexString(pumpID); + + if (pumpIDBytes == null) { + LOG.error("Invalid pump ID? - PumpID is null."); + + rileyLinkServiceData.setPumpID("000000", new byte[]{0, 0, 0}); + + } else if (pumpIDBytes.length != 3) { + LOG.error("Invalid pump ID? " + ByteUtil.shortHexString(pumpIDBytes)); + + rileyLinkServiceData.setPumpID("000000", new byte[]{0, 0, 0}); + + } else if (pumpID.equals("000000")) { + LOG.error("Using pump ID " + pumpID); + + rileyLinkServiceData.setPumpID(pumpID, new byte[]{0, 0, 0}); + + } else { + LOG.info("Using pump ID " + pumpID); + + String oldId = rileyLinkServiceData.pumpID; + + rileyLinkServiceData.setPumpID(pumpID, pumpIDBytes); + + if (oldId != null && !oldId.equals(pumpID)) { + MedtronicUtil.setMedtronicPumpModel(null); // if we change pumpId, model probably changed too + } + + return; + } + + MedtronicUtil.setPumpDeviceState(PumpDeviceState.InvalidConfiguration); + + // LOG.info("setPumpIDString: saved pumpID " + idString); + } + + public class LocalBinder extends Binder { + + public RileyLinkMedtronicService getServiceInstance() { + return RileyLinkMedtronicService.this; + } + } + + + /* private functions */ + + // PumpInterface - REMOVE + + public boolean isInitialized() { + return RileyLinkServiceState.isReady(RileyLinkUtil.getRileyLinkServiceData().serviceState); + } + + + @Override + public String getDeviceSpecificBroadcastsIdentifierPrefix() { + return null; + } + + + public boolean handleDeviceSpecificBroadcasts(Intent intent) { + return false; + } + + + @Override + public void registerDeviceSpecificBroadcasts(IntentFilter intentFilter) { + } + + + private boolean isLogEnabled() { + return L.isEnabled(L.PUMPCOMM); + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/util/MedtronicConst.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/util/MedtronicConst.java new file mode 100644 index 0000000000..fef95087de --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/util/MedtronicConst.java @@ -0,0 +1,38 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.util; + +import info.nightscout.androidaps.R; + +/** + * Created by andy on 5/12/18. + */ + +public class MedtronicConst { + + static final String Prefix = "AAPS.Medtronic."; + + public class Prefs { + public static final int PumpSerial = R.string.key_medtronic_serial; + public static final int PumpType = R.string.key_medtronic_pump_type; + public static final int PumpFrequency = R.string.key_medtronic_frequency; + public static final int MaxBolus = R.string.key_medtronic_max_bolus; + public static final int MaxBasal = R.string.key_medtronic_max_basal; + public static final int BolusDelay = R.string.key_medtronic_bolus_delay; + public static final int Encoding = R.string.key_medtronic_encoding; + public static final int BatteryType = R.string.key_medtronic_battery_type; + public static final int BolusDebugEnabled = R.string.key_medtronic_bolus_debug; + } + + public class Statistics { + + public static final String StatsPrefix = "medtronic_"; + public static final String FirstPumpStart = Prefix + "first_pump_use"; + public static final String LastGoodPumpCommunicationTime = Prefix + "lastGoodPumpCommunicationTime"; + public static final String LastGoodPumpFrequency = Prefix + "LastGoodPumpFrequency"; + public static final String TBRsSet = StatsPrefix + "tbrs_set"; + public static final String StandardBoluses = StatsPrefix + "std_boluses_delivered"; + public static final String SMBBoluses = StatsPrefix + "smb_boluses_delivered"; + public static final String LastPumpHistoryEntry = StatsPrefix + "pump_history_entry"; + public static final String LastPrime = StatsPrefix + "last_sent_prime"; + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/util/MedtronicUtil.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/util/MedtronicUtil.java new file mode 100644 index 0000000000..96bf0a5d44 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/util/MedtronicUtil.java @@ -0,0 +1,537 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.util; + +import android.content.Context; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import org.joda.time.LocalTime; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.interfaces.PluginType; +import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.bus.RxBus; +import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification; +import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification; +import info.nightscout.androidaps.plugins.general.overview.notifications.Notification; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkUtil; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.data.RLHistoryItem; +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkTargetDevice; +import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil; +import info.nightscout.androidaps.plugins.pump.medtronic.MedtronicPumpPlugin; +import info.nightscout.androidaps.plugins.pump.medtronic.comm.MedtronicCommunicationManager; +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.ClockDTO; +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.PumpSettingDTO; +import info.nightscout.androidaps.plugins.pump.medtronic.defs.BatteryType; +import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicCommandType; +import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicDeviceType; +import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicNotificationType; +import info.nightscout.androidaps.plugins.pump.medtronic.defs.PumpDeviceState; +import info.nightscout.androidaps.plugins.pump.medtronic.driver.MedtronicPumpStatus; +import info.nightscout.androidaps.plugins.pump.medtronic.events.EventMedtronicDeviceStatusChange; +import info.nightscout.androidaps.plugins.pump.medtronic.service.RileyLinkMedtronicService; +import info.nightscout.androidaps.utils.OKDialog; + +/** + * Created by andy on 5/9/18. + */ + +public class MedtronicUtil extends RileyLinkUtil { + + private static final Logger LOG = LoggerFactory.getLogger(L.PUMPCOMM); + static int ENVELOPE_SIZE = 4; // 0xA7 S1 S2 S3 CMD PARAM_COUNT [PARAMS] + static int CRC_SIZE = 1; + private static boolean lowLevelDebug = true; + private static PumpDeviceState pumpDeviceState; + private static MedtronicDeviceType medtronicPumpModel; + private static RileyLinkMedtronicService medtronicService; + private static MedtronicPumpStatus medtronicPumpStatus; + private static MedtronicCommandType currentCommand; + private static Map settings; + private static int BIG_FRAME_LENGTH = 65; + private static int doneBit = 1 << 7; + private static ClockDTO pumpTime; + public static Gson gsonInstance = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create(); + public static Gson gsonInstancePretty = new GsonBuilder().excludeFieldsWithoutExposeAnnotation() + .setPrettyPrinting().create(); + private static BatteryType batteryType = BatteryType.None; + + + public static Gson getGsonInstance() { + return gsonInstance; + } + + public static Gson getGsonInstancePretty() { + return gsonInstancePretty; + } + + + public static LocalTime getTimeFrom30MinInterval(int interval) { + if (interval % 2 == 0) { + return new LocalTime(interval / 2, 0); + } else { + return new LocalTime((interval - 1) / 2, 30); + } + } + + + public static int getIntervalFromMinutes(int minutes) { + return minutes / 30; + } + + + public static int makeUnsignedShort(int b2, int b1) { + int k = (b2 & 0xff) << 8 | b1 & 0xff; + return k; + } + + public static boolean isMedtronicPump() { + return MedtronicPumpPlugin.getPlugin().isEnabled(PluginType.PUMP); + //return ConfigBuilderPlugin.getPlugin().getActivePump().deviceID().equals("Medtronic"); + } + + + public static byte[] getByteArrayFromUnsignedShort(int shortValue, boolean returnFixedSize) { + byte highByte = (byte) (shortValue >> 8 & 0xFF); + byte lowByte = (byte) (shortValue & 0xFF); + + if (highByte > 0) { + return createByteArray(highByte, lowByte); + } else { + return returnFixedSize ? createByteArray(highByte, lowByte) : createByteArray(lowByte); + } + + } + + + public static byte[] createByteArray(byte... data) { + return data; + } + + + public static byte[] createByteArray(List data) { + + byte[] array = new byte[data.size()]; + + for (int i = 0; i < data.size(); i++) { + array[i] = data.get(i); + } + + return array; + } + + + public static double decodeBasalInsulin(int i, int j) { + return decodeBasalInsulin(makeUnsignedShort(i, j)); + } + + + public static double decodeBasalInsulin(int i) { + return (double) i / 40.0d; + } + + + public static byte[] getBasalStrokes(double amount) { + return getBasalStrokes(amount, false); + } + + + public static byte[] getBasalStrokes(double amount, boolean returnFixedSize) { + return getStrokes(amount, 40, returnFixedSize); + } + + + public static int getBasalStrokesInt(double amount) { + return getStrokesInt(amount, 40); + } + + + public static byte[] getBolusStrokes(double amount) { + + int strokesPerUnit = medtronicPumpModel.getBolusStrokes(); + + int length; + int scrollRate; + + if (strokesPerUnit >= 40) { + length = 2; + + // 40-stroke pumps scroll faster for higher unit values + + if (amount > 10) + scrollRate = 4; + else if (amount > 1) + scrollRate = 2; + else + scrollRate = 1; + + } else { + length = 1; + scrollRate = 1; + } + + int strokes = (int) (amount * ((strokesPerUnit * 1.0d) / (scrollRate * 1.0d))) * scrollRate; + + byte[] body = ByteUtil.fromHexString(String.format("%02x%0" + (2 * length) + "x", length, strokes)); + + return body; + } + + + public static byte[] createCommandBody(byte[] input) { + + return ByteUtil.concat((byte) input.length, input); + } + + + public static byte[] getStrokes(double amount, int strokesPerUnit, boolean returnFixedSize) { + + int strokes = getStrokesInt(amount, strokesPerUnit); + + return getByteArrayFromUnsignedShort(strokes, returnFixedSize); + + } + + + public static int getStrokesInt(double amount, int strokesPerUnit) { + + int length = 1; + int scrollRate = 1; + + if (strokesPerUnit >= 40) { + length = 2; + + // 40-stroke pumps scroll faster for higher unit values + if (amount > 10) + scrollRate = 4; + else if (amount > 1) + scrollRate = 2; + } + + int strokes = (int) (amount * (strokesPerUnit / (scrollRate * 1.0d))); + + strokes *= scrollRate; + + return strokes; + + } + + + public static void sendNotification(MedtronicNotificationType notificationType) { + Notification notification = new Notification( // + notificationType.getNotificationType(), // + MainApp.gs(notificationType.getResourceId()), // + notificationType.getNotificationUrgency()); + RxBus.INSTANCE.send(new EventNewNotification(notification)); + } + + + public static void sendNotification(MedtronicNotificationType notificationType, Object... parameters) { + Notification notification = new Notification( // + notificationType.getNotificationType(), // + MainApp.gs(notificationType.getResourceId(), parameters), // + notificationType.getNotificationUrgency()); + RxBus.INSTANCE.send(new EventNewNotification(notification)); + } + + + public static void dismissNotification(MedtronicNotificationType notificationType) { + RxBus.INSTANCE.send(new EventDismissNotification(notificationType.getNotificationType())); + } + + +// public static byte[] buildCommandPayload(MessageType commandType, byte[] parameters) { +// return buildCommandPayload(commandType.getValue(), parameters); +// } + + + public static byte[] buildCommandPayload(MedtronicCommandType commandType, byte[] parameters) { + return buildCommandPayload((byte) commandType.commandCode, parameters); + } + + + public static byte[] buildCommandPayload(byte commandType, byte[] parameters) { + // A7 31 65 51 C0 00 52 + + byte commandLength = (byte) (parameters == null ? 2 : 2 + parameters.length); + + ByteBuffer sendPayloadBuffer = ByteBuffer.allocate(ENVELOPE_SIZE + commandLength); // + CRC_SIZE + sendPayloadBuffer.order(ByteOrder.BIG_ENDIAN); + + byte[] serialNumberBCD = RileyLinkUtil.getRileyLinkServiceData().pumpIDBytes; + + sendPayloadBuffer.put((byte) 0xA7); + sendPayloadBuffer.put(serialNumberBCD[0]); + sendPayloadBuffer.put(serialNumberBCD[1]); + sendPayloadBuffer.put(serialNumberBCD[2]); + + sendPayloadBuffer.put(commandType); + + if (parameters == null) { + sendPayloadBuffer.put((byte) 0x00); + } else { + sendPayloadBuffer.put((byte) parameters.length); // size + + for (byte val : parameters) { + sendPayloadBuffer.put(val); + } + } + + byte[] payload = sendPayloadBuffer.array(); + + if (L.isEnabled(L.PUMPCOMM)) + LOG.debug("buildCommandPayload [{}]", ByteUtil.shortHexString(payload)); + + // int crc = computeCRC8WithPolynomial(payload, 0, payload.length - 1); + + // LOG.info("crc: " + crc); + + // sendPayloadBuffer.put((byte) crc); + + return sendPayloadBuffer.array(); + } + + + // Note: at the moment supported only for 24 items, if you will use it for more than + // that you will need to add + public static List> getBasalProfileFrames(byte[] data) { + + boolean done = false; + int start = 0; + int frame = 1; + + List> frames = new ArrayList<>(); + boolean lastFrame = false; + + do { + int frameLength = BIG_FRAME_LENGTH - 1; + + if (start + frameLength > data.length) { + frameLength = data.length - start; + } + + // System.out.println("Framelength: " + frameLength); + + byte[] substring = ByteUtil.substring(data, start, frameLength); + + // System.out.println("Subarray: " + ByteUtil.getCompactString(substring)); + // System.out.println("Subarray Lenths: " + substring.length); + + List frameData = ByteUtil.getListFromByteArray(substring); + + if (isEmptyFrame(frameData)) { + byte b = (byte) frame; + // b |= 0x80; + b |= 0b1000_0000; + // b |= doneBit; + + frameData.add(0, b); + + checkAndAppenLastFrame(frameData); + + lastFrame = true; + + done = true; + } else { + frameData.add(0, (byte) frame); + } + + // System.out.println("Subarray: " + ByteUtil.getCompactString(substring)); + + frames.add(frameData); + + frame++; + start += (BIG_FRAME_LENGTH - 1); + + if (start == data.length) { + done = true; + } + + } while (!done); + + if (!lastFrame) { + List frameData = new ArrayList<>(); + + byte b = (byte) frame; + b |= 0b1000_0000; + // b |= doneBit; + + frameData.add(b); + + checkAndAppenLastFrame(frameData); + } + + return frames; + + } + + + private static void checkAndAppenLastFrame(List frameData) { + + if (frameData.size() == BIG_FRAME_LENGTH) + return; + + int missing = BIG_FRAME_LENGTH - frameData.size(); + + for (int i = 0; i < missing; i++) { + frameData.add((byte) 0x00); + } + } + + + private static boolean isEmptyFrame(List frameData) { + + for (Byte frameDateEntry : frameData) { + if (frameDateEntry != 0x00) { + return false; + } + } + + return true; + } + + + public static boolean isLowLevelDebug() { + return lowLevelDebug; + } + + + public static void setLowLevelDebug(boolean lowLevelDebug) { + MedtronicUtil.lowLevelDebug = lowLevelDebug; + } + + + public static PumpDeviceState getPumpDeviceState() { + return pumpDeviceState; + } + + + public static void setPumpDeviceState(PumpDeviceState pumpDeviceState) { + MedtronicUtil.pumpDeviceState = pumpDeviceState; + + historyRileyLink.add(new RLHistoryItem(pumpDeviceState, RileyLinkTargetDevice.MedtronicPump)); + + RxBus.INSTANCE.send(new EventMedtronicDeviceStatusChange(pumpDeviceState)); + } + + + public static boolean isModelSet() { + return MedtronicUtil.medtronicPumpModel != null; + } + + + public static MedtronicDeviceType getMedtronicPumpModel() { + return MedtronicUtil.medtronicPumpModel; + } + + + public static void setMedtronicPumpModel(MedtronicDeviceType medtronicPumpModel) { + MedtronicUtil.medtronicPumpModel = medtronicPumpModel; + } + + + public static MedtronicCommunicationManager getMedtronicCommunicationManager() { + return (MedtronicCommunicationManager) RileyLinkUtil.rileyLinkCommunicationManager; + } + + + public static RileyLinkMedtronicService getMedtronicService() { + return MedtronicUtil.medtronicService; + } + + + public static void setMedtronicService(RileyLinkMedtronicService medtronicService) { + MedtronicUtil.medtronicService = medtronicService; + } + + + public static MedtronicPumpStatus getPumpStatus() { + return MedtronicUtil.medtronicPumpStatus; + } + + + public static void setPumpStatus(MedtronicPumpStatus medtronicPumpStatus) { + MedtronicUtil.medtronicPumpStatus = medtronicPumpStatus; + } + + + public static MedtronicCommandType getCurrentCommand() { + return MedtronicUtil.currentCommand; + } + + + public static void setCurrentCommand(MedtronicCommandType currentCommand) { + MedtronicUtil.currentCommand = currentCommand; + + if (currentCommand != null) + historyRileyLink.add(new RLHistoryItem(currentCommand)); + + } + + public static int pageNumber; + public static Integer frameNumber; + + + public static void setCurrentCommand(MedtronicCommandType currentCommand, int pageNumber_, Integer frameNumber_) { + pageNumber = pageNumber_; + frameNumber = frameNumber_; + + if (MedtronicUtil.currentCommand != currentCommand) { + setCurrentCommand(currentCommand); + } + + RxBus.INSTANCE.send(new EventMedtronicDeviceStatusChange(pumpDeviceState)); + } + + + public static boolean isSame(Double d1, Double d2) { + double diff = d1 - d2; + + return (Math.abs(diff) <= 0.000001); + } + + + public static Map getSettings() { + return settings; + } + + + public static void setSettings(Map settings) { + MedtronicUtil.settings = settings; + } + + + public static void setPumpTime(ClockDTO pumpTime) { + MedtronicUtil.pumpTime = pumpTime; + } + + + public static ClockDTO getPumpTime() { + return MedtronicUtil.pumpTime; + } + + public static void setBatteryType(BatteryType batteryType) { + MedtronicUtil.batteryType = batteryType; + } + + + public static BatteryType getBatteryType() { + return MedtronicUtil.batteryType; + } + + + public static void displayNotConfiguredDialog(Context context) { + OKDialog.show(context, MainApp.gs(R.string.combo_warning), + MainApp.gs(R.string.medtronic_error_operation_not_possible_no_configuration), null); + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/virtual/VirtualPumpFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/virtual/VirtualPumpFragment.java deleted file mode 100644 index 0b764f5382..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/virtual/VirtualPumpFragment.java +++ /dev/null @@ -1,127 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.virtual; - - -import android.app.Activity; -import android.os.Bundle; -import android.os.Handler; -import android.support.annotation.Nullable; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -import com.squareup.otto.Subscribe; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import info.nightscout.androidaps.MainApp; -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.db.ExtendedBolus; -import info.nightscout.androidaps.db.TemporaryBasal; -import info.nightscout.androidaps.plugins.common.SubscriberFragment; -import info.nightscout.androidaps.plugins.general.actions.defs.CustomAction; -import info.nightscout.androidaps.plugins.general.actions.defs.CustomActionType; -import info.nightscout.androidaps.plugins.pump.common.defs.PumpType; -import info.nightscout.androidaps.plugins.pump.virtual.events.EventVirtualPumpUpdateGui; -import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; -import info.nightscout.androidaps.utils.FabricPrivacy; - - -public class VirtualPumpFragment extends SubscriberFragment { - private static Logger log = LoggerFactory.getLogger(VirtualPumpFragment.class); - - TextView basaBasalRateView; - TextView tempBasalView; - TextView extendedBolusView; - TextView batteryView; - TextView reservoirView; - TextView pumpTypeView; - TextView pumpSettingsView; - - - private static Handler sLoopHandler = new Handler(); - private static Runnable sRefreshLoop = null; - - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - if (sRefreshLoop == null) { - sRefreshLoop = new Runnable() { - @Override - public void run() { - updateGUI(); - sLoopHandler.postDelayed(sRefreshLoop, 60 * 1000L); - } - }; - sLoopHandler.postDelayed(sRefreshLoop, 60 * 1000L); - } - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - try { - View view = inflater.inflate(R.layout.virtualpump_fragment, container, false); - basaBasalRateView = (TextView) view.findViewById(R.id.virtualpump_basabasalrate); - tempBasalView = (TextView) view.findViewById(R.id.virtualpump_tempbasal); - extendedBolusView = (TextView) view.findViewById(R.id.virtualpump_extendedbolus); - batteryView = (TextView) view.findViewById(R.id.virtualpump_battery); - reservoirView = (TextView) view.findViewById(R.id.virtualpump_reservoir); - pumpTypeView = (TextView) view.findViewById(R.id.virtualpump_type); - pumpSettingsView = (TextView) view.findViewById(R.id.virtualpump_type_def); - - return view; - } catch (Exception e) { - FabricPrivacy.logException(e); - } - - return null; - } - - @Subscribe - public void onStatusEvent(final EventVirtualPumpUpdateGui ev) { - updateGUI(); - } - - @Override - protected void updateGUI() { - Activity activity = getActivity(); - if (activity != null && basaBasalRateView != null) - activity.runOnUiThread(new Runnable() { - @Override - public void run() { - VirtualPumpPlugin virtualPump = VirtualPumpPlugin.getPlugin(); - basaBasalRateView.setText(virtualPump.getBaseBasalRate() + "U"); - TemporaryBasal activeTemp = TreatmentsPlugin.getPlugin().getTempBasalFromHistory(System.currentTimeMillis()); - if (activeTemp != null) { - tempBasalView.setText(activeTemp.toStringFull()); - } else { - tempBasalView.setText(""); - } - ExtendedBolus activeExtendedBolus = TreatmentsPlugin.getPlugin().getExtendedBolusFromHistory(System.currentTimeMillis()); - if (activeExtendedBolus != null) { - extendedBolusView.setText(activeExtendedBolus.toString()); - } else { - extendedBolusView.setText(""); - } - batteryView.setText(virtualPump.batteryPercent + "%"); - reservoirView.setText(virtualPump.reservoirInUnits + "U"); - - virtualPump.refreshConfiguration(); - - PumpType pumpType = virtualPump.getPumpType(); - - pumpTypeView.setText(pumpType.getDescription()); - - String template = MainApp.gs(R.string.virtualpump_pump_def); - - - pumpSettingsView.setText(pumpType.getFullDescription(template, pumpType.hasExtendedBasals())); - - } - }); - } - - -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/virtual/VirtualPumpFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/pump/virtual/VirtualPumpFragment.kt new file mode 100644 index 0000000000..08402520c2 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/virtual/VirtualPumpFragment.kt @@ -0,0 +1,85 @@ +package info.nightscout.androidaps.plugins.pump.virtual + +import android.os.Bundle +import android.os.Handler +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import info.nightscout.androidaps.MainApp +import info.nightscout.androidaps.R +import info.nightscout.androidaps.events.EventExtendedBolusChange +import info.nightscout.androidaps.events.EventTempBasalChange +import info.nightscout.androidaps.plugins.bus.RxBus +import info.nightscout.androidaps.plugins.pump.virtual.events.EventVirtualPumpUpdateGui +import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin +import info.nightscout.androidaps.utils.FabricPrivacy +import info.nightscout.androidaps.utils.T +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.CompositeDisposable +import kotlinx.android.synthetic.main.virtualpump_fragment.* +import org.slf4j.LoggerFactory + +class VirtualPumpFragment : Fragment() { + private val disposable = CompositeDisposable() + + private val loopHandler = Handler() + private lateinit var refreshLoop: Runnable + + init { + refreshLoop = Runnable { + activity?.runOnUiThread { updateGui() } + loopHandler.postDelayed(refreshLoop, T.mins(1).msecs()) + } + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.virtualpump_fragment, container, false) + } + + @Synchronized + override fun onResume() { + super.onResume() + disposable.add(RxBus + .toObservable(EventVirtualPumpUpdateGui::class.java) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ updateGui() }, { FabricPrivacy.logException(it) }) + ) + disposable.add(RxBus + .toObservable(EventTempBasalChange::class.java) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ updateGui() }, { FabricPrivacy.logException(it) }) + ) + disposable.add(RxBus + .toObservable(EventExtendedBolusChange::class.java) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ updateGui() }, { FabricPrivacy.logException(it) }) + ) + loopHandler.postDelayed(refreshLoop, T.mins(1).msecs()) + updateGui() + } + + @Synchronized + override fun onPause() { + super.onPause() + disposable.clear() + loopHandler.removeCallbacks(refreshLoop) + } + + @Synchronized + private fun updateGui() { + val virtualPump = VirtualPumpPlugin.getPlugin() + virtualpump_basabasalrate?.text = MainApp.gs(R.string.pump_basebasalrate, virtualPump.baseBasalRate) + virtualpump_tempbasal?.text = TreatmentsPlugin.getPlugin().getTempBasalFromHistory(System.currentTimeMillis())?.toStringFull() + ?: "" + virtualpump_extendedbolus?.text = TreatmentsPlugin.getPlugin().getExtendedBolusFromHistory(System.currentTimeMillis())?.toString() ?: "" + virtualpump_battery?.text = MainApp.gs(R.string.format_percent, virtualPump.batteryPercent) + virtualpump_reservoir?.text = MainApp.gs(R.string.formatinsulinunits, virtualPump.reservoirInUnits.toDouble()) + + virtualPump.refreshConfiguration() + val pumpType = virtualPump.pumpType + + virtualpump_type?.text = pumpType.description + virtualpump_type_def?.text = pumpType.getFullDescription(MainApp.gs(R.string.virtualpump_pump_def), pumpType.hasExtendedBasals()) + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/virtual/VirtualPumpPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/virtual/VirtualPumpPlugin.java index 461f71c31d..447367c510 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/virtual/VirtualPumpPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/virtual/VirtualPumpPlugin.java @@ -2,8 +2,6 @@ package info.nightscout.androidaps.plugins.pump.virtual; import android.os.SystemClock; -import com.squareup.otto.Subscribe; - import org.json.JSONException; import org.json.JSONObject; import org.slf4j.Logger; @@ -28,6 +26,8 @@ import info.nightscout.androidaps.interfaces.PluginType; import info.nightscout.androidaps.interfaces.PumpDescription; import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.bus.RxBus; +import info.nightscout.androidaps.plugins.common.ManufacturerType; import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; import info.nightscout.androidaps.plugins.general.actions.defs.CustomAction; import info.nightscout.androidaps.plugins.general.actions.defs.CustomActionType; @@ -39,7 +39,11 @@ import info.nightscout.androidaps.plugins.pump.common.defs.PumpType; import info.nightscout.androidaps.plugins.pump.virtual.events.EventVirtualPumpUpdateGui; import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; import info.nightscout.androidaps.utils.DateUtil; +import info.nightscout.androidaps.utils.FabricPrivacy; +import info.nightscout.androidaps.utils.InstanceId; import info.nightscout.androidaps.utils.SP; +import io.reactivex.disposables.CompositeDisposable; +import io.reactivex.schedulers.Schedulers; /** @@ -47,6 +51,7 @@ import info.nightscout.androidaps.utils.SP; */ public class VirtualPumpPlugin extends PluginBase implements PumpInterface { private Logger log = LoggerFactory.getLogger(L.PUMP); + private CompositeDisposable disposable = new CompositeDisposable(); Integer batteryPercent = 50; Integer reservoirInUnits = 50; @@ -119,19 +124,21 @@ public class VirtualPumpPlugin extends PluginBase implements PumpInterface { @Override protected void onStart() { super.onStart(); - MainApp.bus().register(this); + disposable.add(RxBus.INSTANCE + .toObservable(EventPreferenceChange.class) + .observeOn(Schedulers.io()) + .subscribe(event -> { + if (event.isChanged(R.string.key_virtualpump_type)) + refreshConfiguration(); + }, FabricPrivacy::logException) + ); refreshConfiguration(); } @Override protected void onStop() { - MainApp.bus().unregister(this); - } - - @Subscribe - public void onStatusEvent(final EventPreferenceChange s) { - if (s.isChanged(R.string.key_virtualpump_type)) - refreshConfiguration(); + disposable.clear(); + super.onStop(); } @Override @@ -216,7 +223,7 @@ public class VirtualPumpPlugin extends PluginBase implements PumpInterface { PumpEnactResult result = new PumpEnactResult(); result.success = true; Notification notification = new Notification(Notification.PROFILE_SET_OK, MainApp.gs(R.string.profile_set_ok), Notification.INFO, 60); - MainApp.bus().post(new EventNewNotification(notification)); + RxBus.INSTANCE.send(new EventNewNotification(notification)); return result; } @@ -241,10 +248,14 @@ public class VirtualPumpPlugin extends PluginBase implements PumpInterface { @Override - public double getReservoirLevel() { return reservoirInUnits; } + public double getReservoirLevel() { + return reservoirInUnits; + } @Override - public int getBatteryLevel() { return batteryPercent; } + public int getBatteryLevel() { + return batteryPercent; + } @Override public PumpEnactResult deliverTreatment(DetailedBolusInfo detailedBolusInfo) { @@ -260,21 +271,21 @@ public class VirtualPumpPlugin extends PluginBase implements PumpInterface { while (delivering < detailedBolusInfo.insulin) { SystemClock.sleep(200); - EventOverviewBolusProgress bolusingEvent = EventOverviewBolusProgress.getInstance(); - bolusingEvent.status = String.format(MainApp.gs(R.string.bolusdelivering), delivering); - bolusingEvent.percent = Math.min((int) (delivering / detailedBolusInfo.insulin * 100), 100); - MainApp.bus().post(bolusingEvent); + EventOverviewBolusProgress bolusingEvent = EventOverviewBolusProgress.INSTANCE; + bolusingEvent.setStatus(String.format(MainApp.gs(R.string.bolusdelivering), delivering)); + bolusingEvent.setPercent(Math.min((int) (delivering / detailedBolusInfo.insulin * 100), 100)); + RxBus.INSTANCE.send(bolusingEvent); delivering += 0.1d; } SystemClock.sleep(200); - EventOverviewBolusProgress bolusingEvent = EventOverviewBolusProgress.getInstance(); - bolusingEvent.status = String.format(MainApp.gs(R.string.bolusdelivered), detailedBolusInfo.insulin); - bolusingEvent.percent = 100; - MainApp.bus().post(bolusingEvent); + EventOverviewBolusProgress bolusingEvent = EventOverviewBolusProgress.INSTANCE; + bolusingEvent.setStatus(String.format(MainApp.gs(R.string.bolusdelivered), detailedBolusInfo.insulin)); + bolusingEvent.setPercent(100); + RxBus.INSTANCE.send(bolusingEvent); SystemClock.sleep(1000); if (L.isEnabled(L.PUMPCOMM)) log.debug("Delivering treatment insulin: " + detailedBolusInfo.insulin + "U carbs: " + detailedBolusInfo.carbs + "g " + result); - MainApp.bus().post(new EventVirtualPumpUpdateGui()); + RxBus.INSTANCE.send(new EventVirtualPumpUpdateGui()); lastDataTime = System.currentTimeMillis(); TreatmentsPlugin.getPlugin().addToHistoryTreatment(detailedBolusInfo, false); return result; @@ -302,7 +313,7 @@ public class VirtualPumpPlugin extends PluginBase implements PumpInterface { TreatmentsPlugin.getPlugin().addToHistoryTempBasal(tempBasal); if (L.isEnabled(L.PUMPCOMM)) log.debug("Setting temp basal absolute: " + result); - MainApp.bus().post(new EventVirtualPumpUpdateGui()); + RxBus.INSTANCE.send(new EventVirtualPumpUpdateGui()); lastDataTime = System.currentTimeMillis(); return result; } @@ -325,7 +336,7 @@ public class VirtualPumpPlugin extends PluginBase implements PumpInterface { TreatmentsPlugin.getPlugin().addToHistoryTempBasal(tempBasal); if (L.isEnabled(L.PUMPCOMM)) log.debug("Settings temp basal percent: " + result); - MainApp.bus().post(new EventVirtualPumpUpdateGui()); + RxBus.INSTANCE.send(new EventVirtualPumpUpdateGui()); lastDataTime = System.currentTimeMillis(); return result; } @@ -350,7 +361,7 @@ public class VirtualPumpPlugin extends PluginBase implements PumpInterface { TreatmentsPlugin.getPlugin().addToHistoryExtendedBolus(extendedBolus); if (L.isEnabled(L.PUMPCOMM)) log.debug("Setting extended bolus: " + result); - MainApp.bus().post(new EventVirtualPumpUpdateGui()); + RxBus.INSTANCE.send(new EventVirtualPumpUpdateGui()); lastDataTime = System.currentTimeMillis(); return result; } @@ -368,7 +379,7 @@ public class VirtualPumpPlugin extends PluginBase implements PumpInterface { //tempBasal = null; if (L.isEnabled(L.PUMPCOMM)) log.debug("Canceling temp basal: " + result); - MainApp.bus().post(new EventVirtualPumpUpdateGui()); + RxBus.INSTANCE.send(new EventVirtualPumpUpdateGui()); } lastDataTime = System.currentTimeMillis(); return result; @@ -388,7 +399,7 @@ public class VirtualPumpPlugin extends PluginBase implements PumpInterface { result.comment = MainApp.gs(R.string.virtualpump_resultok); if (L.isEnabled(L.PUMPCOMM)) log.debug("Canceling extended bolus: " + result); - MainApp.bus().post(new EventVirtualPumpUpdateGui()); + RxBus.INSTANCE.send(new EventVirtualPumpUpdateGui()); lastDataTime = System.currentTimeMillis(); return result; } @@ -437,8 +448,18 @@ public class VirtualPumpPlugin extends PluginBase implements PumpInterface { } @Override - public String deviceID() { - return "VirtualPump"; + public ManufacturerType manufacturer() { + return pumpDescription.pumpType.getManufacturer(); + } + + @Override + public PumpType model() { + return pumpDescription.pumpType; + } + + @Override + public String serialNumber() { + return InstanceId.INSTANCE.instanceId(); } @Override @@ -481,4 +502,11 @@ public class VirtualPumpPlugin extends PluginBase implements PumpInterface { } -} + + @Override + public void timeDateOrTimeZoneChanged() { + + } + + +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/virtual/events/EventVirtualPumpUpdateGui.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/virtual/events/EventVirtualPumpUpdateGui.java deleted file mode 100644 index 795134997a..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/virtual/events/EventVirtualPumpUpdateGui.java +++ /dev/null @@ -1,9 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.virtual.events; - -import info.nightscout.androidaps.events.EventUpdateGui; - -/** - * Created by mike on 05.08.2016. - */ -public class EventVirtualPumpUpdateGui extends EventUpdateGui { -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/virtual/events/EventVirtualPumpUpdateGui.kt b/app/src/main/java/info/nightscout/androidaps/plugins/pump/virtual/events/EventVirtualPumpUpdateGui.kt new file mode 100644 index 0000000000..a0498aa14d --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/virtual/events/EventVirtualPumpUpdateGui.kt @@ -0,0 +1,5 @@ +package info.nightscout.androidaps.plugins.pump.virtual.events + +import info.nightscout.androidaps.events.EventUpdateGui + +class EventVirtualPumpUpdateGui : EventUpdateGui() \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/sensitivity/SensitivityAAPSPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/sensitivity/SensitivityAAPSPlugin.java index 8e33b16302..dfc4bed25d 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/sensitivity/SensitivityAAPSPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sensitivity/SensitivityAAPSPlugin.java @@ -1,6 +1,6 @@ package info.nightscout.androidaps.plugins.sensitivity; -import android.support.v4.util.LongSparseArray; +import androidx.collection.LongSparseArray; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/sensitivity/SensitivityOref0Plugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/sensitivity/SensitivityOref0Plugin.java index 83800e1721..cfcaf0eb22 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/sensitivity/SensitivityOref0Plugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sensitivity/SensitivityOref0Plugin.java @@ -1,6 +1,6 @@ package info.nightscout.androidaps.plugins.sensitivity; -import android.support.v4.util.LongSparseArray; +import androidx.collection.LongSparseArray; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/sensitivity/SensitivityOref1Plugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/sensitivity/SensitivityOref1Plugin.java index 3ce5c4861c..5e0f5d3d85 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/sensitivity/SensitivityOref1Plugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sensitivity/SensitivityOref1Plugin.java @@ -1,6 +1,6 @@ package info.nightscout.androidaps.plugins.sensitivity; -import android.support.v4.util.LongSparseArray; +import androidx.collection.LongSparseArray; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -134,7 +134,7 @@ public class SensitivityOref1Plugin extends AbstractSensitivityPlugin { if (L.isEnabled(L.AUTOSENS)) log.debug("Using most recent " + deviationsArray.size() + " deviations"); if (deviationsArray.size() < 96) { - int pad = Math.round((1 - deviationsArray.size() / 96) * 18); + int pad = (int) Math.round((1 - (double) deviationsArray.size() / 96) * 18); if (L.isEnabled(L.AUTOSENS)) log.debug("Adding " + pad + " more zero deviations"); for (int d = 0; d < pad; d++) { @@ -156,7 +156,7 @@ public class SensitivityOref1Plugin extends AbstractSensitivityPlugin { log.debug("Records: " + index + " " + pastSensitivity); Arrays.sort(deviations); - + /* Not used in calculation for (double i = 0.9; i > 0.1; i = i - 0.01) { if (IobCobCalculatorPlugin.percentile(deviations, (i + 0.01)) >= 0 && IobCobCalculatorPlugin.percentile(deviations, i) < 0) { if (L.isEnabled(L.AUTOSENS)) @@ -164,9 +164,10 @@ public class SensitivityOref1Plugin extends AbstractSensitivityPlugin { } if (IobCobCalculatorPlugin.percentile(deviations, (i + 0.01)) > 0 && IobCobCalculatorPlugin.percentile(deviations, i) <= 0) { if (L.isEnabled(L.AUTOSENS)) - log.debug(Math.round(100 * i) + "% of non-meal deviations negative (>50% = resistance)"); + log.debug(Math.round(100 * i) + "% of non-meal deviations positive (>50% = resistance)"); } } + */ double pSensitive = IobCobCalculatorPlugin.percentile(deviations, 0.50); double pResistant = IobCobCalculatorPlugin.percentile(deviations, 0.50); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/sensitivity/SensitivityWeightedAveragePlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/sensitivity/SensitivityWeightedAveragePlugin.java index 5b7268b560..91b69162bb 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/sensitivity/SensitivityWeightedAveragePlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sensitivity/SensitivityWeightedAveragePlugin.java @@ -1,6 +1,6 @@ package info.nightscout.androidaps.plugins.sensitivity; -import android.support.v4.util.LongSparseArray; +import androidx.collection.LongSparseArray; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/source/BGSourceFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/source/BGSourceFragment.java index 211cbb0bb0..6023d70d12 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/source/BGSourceFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/source/BGSourceFragment.java @@ -1,19 +1,18 @@ package info.nightscout.androidaps.plugins.source; -import android.app.Activity; import android.content.DialogInterface; import android.graphics.Paint; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.v7.app.AlertDialog; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; -import com.squareup.otto.Subscribe; +import androidx.annotation.NonNull; +import androidx.appcompat.app.AlertDialog; +import androidx.fragment.app.Fragment; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; import java.util.List; @@ -21,19 +20,22 @@ import info.nightscout.androidaps.Constants; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.db.BgReading; -import info.nightscout.androidaps.plugins.common.SubscriberFragment; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.general.nsclient.NSUpload; import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventAutosensCalculationFinished; import info.nightscout.androidaps.utils.DateUtil; import info.nightscout.androidaps.utils.FabricPrivacy; import info.nightscout.androidaps.utils.T; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.CompositeDisposable; /** * Created by mike on 16.10.2017. */ -public class BGSourceFragment extends SubscriberFragment { +public class BGSourceFragment extends Fragment { + private CompositeDisposable disposable = new CompositeDisposable(); RecyclerView recyclerView; String units = Constants.MGDL; @@ -66,19 +68,25 @@ public class BGSourceFragment extends SubscriberFragment { return null; } - @Subscribe - public void onStatusEvent(final EventAutosensCalculationFinished unused) { - updateGUI(); + @Override + public synchronized void onResume() { + super.onResume(); + disposable.add(RxBus.INSTANCE + .toObservable(EventAutosensCalculationFinished.class) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(event -> updateGUI(), FabricPrivacy::logException) + ); } @Override + public synchronized void onPause() { + disposable.clear(); + super.onPause(); + } + protected void updateGUI() { - Activity activity = getActivity(); - if (activity != null) - activity.runOnUiThread(() -> { - long now = System.currentTimeMillis(); - recyclerView.swapAdapter(new RecyclerViewAdapter(MainApp.getDbHelper().getAllBgreadingsDataFromTime(now - MILLS_TO_THE_PAST, false)), true); - }); + long now = System.currentTimeMillis(); + recyclerView.swapAdapter(new RecyclerViewAdapter(MainApp.getDbHelper().getAllBgreadingsDataFromTime(now - MILLS_TO_THE_PAST, false)), true); } public class RecyclerViewAdapter extends RecyclerView.Adapter { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/source/SourceDexcomG5Plugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/source/SourceDexcomG5Plugin.java deleted file mode 100644 index 3ba10564e6..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/source/SourceDexcomG5Plugin.java +++ /dev/null @@ -1,232 +0,0 @@ -package info.nightscout.androidaps.plugins.source; - -import android.content.Intent; -import android.os.Bundle; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.Arrays; - -import info.nightscout.androidaps.Constants; -import info.nightscout.androidaps.MainApp; -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.db.BgReading; -import info.nightscout.androidaps.db.CareportalEvent; -import info.nightscout.androidaps.interfaces.BgSourceInterface; -import info.nightscout.androidaps.interfaces.PluginBase; -import info.nightscout.androidaps.interfaces.PluginDescription; -import info.nightscout.androidaps.interfaces.PluginType; -import info.nightscout.androidaps.logging.L; -import info.nightscout.androidaps.plugins.general.nsclient.NSUpload; -import info.nightscout.androidaps.services.Intents; -import info.nightscout.androidaps.utils.DateUtil; -import info.nightscout.androidaps.utils.SP; - -/** - * Created by mike on 28.11.2017. - */ - -public class SourceDexcomG5Plugin extends PluginBase implements BgSourceInterface { - private static Logger log = LoggerFactory.getLogger(L.BGSOURCE); - - private static SourceDexcomG5Plugin plugin = null; - - public static SourceDexcomG5Plugin getPlugin() { - if (plugin == null) - plugin = new SourceDexcomG5Plugin(); - return plugin; - } - - private SourceDexcomG5Plugin() { - super(new PluginDescription() - .mainType(PluginType.BGSOURCE) - .fragmentClass(BGSourceFragment.class.getName()) - .pluginName(R.string.DexcomG5) - .shortName(R.string.dexcomG5_shortname) - .preferencesId(R.xml.pref_bgsource) - .description(R.string.description_source_dexcom_g5) - ); - } - - @Override - public boolean advancedFilteringSupported() { - return true; - } - - @Override - public void handleNewData(Intent intent) { - // onHandleIntent Bundle{ data => [{"m_time":1511939180,"m_trend":"NotComputable","m_value":335}]; android.support.content.wakelockid => 95; }Bundle - - if (!isEnabled(PluginType.BGSOURCE)) return; - - if (intent.getAction().equals(Intents.DEXCOMG5_BG)) - handleNewDataOld(intent); - - if (intent.getAction().equals(Intents.DEXCOMG5_BG_NEW)) - handleNewDataNew(intent); - } - - public void handleNewDataOld(Intent intent) { - // onHandleIntent Bundle{ data => [{"m_time":1511939180,"m_trend":"NotComputable","m_value":335}]; android.support.content.wakelockid => 95; }Bundle - - Bundle bundle = intent.getExtras(); - if (bundle == null) return; - - BgReading bgReading = new BgReading(); - - String data = bundle.getString("data"); - if (L.isEnabled(L.BGSOURCE)) - log.debug("Received Dexcom Data", data); - - if (data == null) return; - - try { - JSONArray jsonArray = new JSONArray(data); - if (L.isEnabled(L.BGSOURCE)) - log.debug("Received Dexcom Data size:" + jsonArray.length()); - for (int i = 0; i < jsonArray.length(); i++) { - JSONObject json = jsonArray.getJSONObject(i); - bgReading.value = json.getInt("m_value"); - bgReading.direction = json.getString("m_trend"); - bgReading.date = json.getLong("m_time") * 1000L; - bgReading.raw = 0; - boolean isNew = MainApp.getDbHelper().createIfNotExists(bgReading, "DexcomG5"); - if (isNew && SP.getBoolean(R.string.key_dexcomg5_nsupload, false)) { - NSUpload.uploadBg(bgReading, "AndroidAPS-DexcomG5"); - } - if (isNew && SP.getBoolean(R.string.key_dexcomg5_xdripupload, false)) { - NSUpload.sendToXdrip(bgReading); - } - } - - } catch (JSONException e) { - log.error("Exception: ", e); - } - } - - public void handleNewDataNew(Intent intent) { - - Bundle bundle = intent.getExtras(); - if (bundle == null) return; - - if (L.isEnabled(L.BGSOURCE)) { - if (bundle.containsKey("transmitterSystemTime")) - log.debug("transmitterSystemTime: " + DateUtil.dateAndTimeFullString(bundle.getLong("transmitterSystemTime"))); - if (bundle.containsKey("transmitterRemainingTime")) - log.debug("transmitterRemainingTime: " + DateUtil.dateAndTimeFullString(bundle.getLong("transmitterRemainingTime"))); - log.debug("transmitterId: " + bundle.getString("transmitterId")); - if (bundle.containsKey("transmitterActivatedOn")) - log.debug("transmitterActivatedOn: " + DateUtil.dateAndTimeFullString(bundle.getLong("transmitterActivatedOn"))); - log.debug("transmitterVersion: " + bundle.getString("transmitterVersion")); - log.debug("transmitterSoftwareNumber: " + bundle.getString("transmitterSoftwareNumber")); - log.debug("transmitterStorageTimeDays: " + bundle.getInt("transmitterStorageTimeDays")); - log.debug("transmitterApiVersion: " + bundle.getInt("transmitterApiVersion")); - log.debug("transmitterMaxRuntimeDays: " + bundle.getInt("transmitterMaxRuntimeDays")); - log.debug("transmitterMaxStorageTimeDays: " + bundle.getInt("transmitterMaxStorageTimeDays")); - log.debug("transmitterCGMProcessorFirmwareVersion: " + bundle.getString("transmitterCGMProcessorFirmwareVersion")); - log.debug("transmitterBleRadioFirmwareVersion: " + bundle.getString("transmitterBleRadioFirmwareVersion")); - log.debug("transmitterHardwareVersion: " + bundle.getInt("transmitterHardwareVersion")); - log.debug("transmitterBleSoftDeviceVersion: " + bundle.getString("transmitterBleSoftDeviceVersion")); - log.debug("transmitterNordicAsicHwID: " + bundle.getInt("transmitterNordicAsicHwID")); - log.debug("transmitterSessionTimeDays: " + bundle.getInt("transmitterSessionTimeDays")); - log.debug("transmitterFeatureFlags: " + bundle.getInt("transmitterFeatureFlags")); - } - - if (bundle.containsKey("sensorInsertionTime")) { - long sensorInsertionTime = bundle.getLong("sensorInsertionTime"); - if (L.isEnabled(L.BGSOURCE)) - log.debug("sensorInsertionTime: " + DateUtil.dateAndTimeFullString(sensorInsertionTime)); - if (SP.getBoolean(R.string.key_dexcom_lognssensorchange, false)) { - try { - if (MainApp.getDbHelper().getCareportalEventFromTimestamp(sensorInsertionTime) == null) { - JSONObject data = new JSONObject(); - data.put("enteredBy", "AndroidAPS-DexcomG5"); - data.put("created_at", DateUtil.toISOString(sensorInsertionTime)); - data.put("eventType", CareportalEvent.SENSORCHANGE); - NSUpload.uploadCareportalEntryToNS(data); - } - } catch (JSONException e) { - log.error("Unhandled exception", e); - } - } - } - - if (bundle.containsKey("glucoseValues")) { - int[] glucoseValues = bundle.getIntArray("glucoseValues"); - int[] glucoseRecordIDs = bundle.getIntArray("glucoseRecordIDs"); - long[] glucoseRecordedTimestamps = bundle.getLongArray("glucoseRecordedTimestamps"); - long[] glucoseSessionStartTimes = bundle.getLongArray("glucoseSessionStartTimes"); - long[] glucoseSystemTimestamps = bundle.getLongArray("glucoseSystemTimestamps"); - String[] glucoseTransmitterIDS = bundle.getStringArray("glucoseTransmitterIDS"); - long[] glucoseTransmitterTimestamps = bundle.getLongArray("glucoseTransmitterTimestamps"); - String[] glucoseTrendsArrows = bundle.getStringArray("glucoseTrendsArrows"); - boolean[] glucoseWasBackfilled = bundle.getBooleanArray("glucoseWasBackfilled"); - - if (L.isEnabled(L.BGSOURCE)) { - log.debug("glucoseValues", Arrays.toString(glucoseValues)); - log.debug("glucoseRecordIDs", Arrays.toString(glucoseRecordIDs)); - log.debug("glucoseRecordedTimestamps", Arrays.toString(glucoseRecordedTimestamps)); - log.debug("glucoseSessionStartTimes", Arrays.toString(glucoseSessionStartTimes)); - log.debug("glucoseSystemTimestamps", Arrays.toString(glucoseSystemTimestamps)); - log.debug("glucoseTransmitterIDS", Arrays.toString(glucoseTransmitterIDS)); - log.debug("glucoseTransmitterTimestamps", Arrays.toString(glucoseTransmitterTimestamps)); - log.debug("glucoseTrendsArrows", Arrays.toString(glucoseTrendsArrows)); - log.debug("glucoseWasBackfilled", Arrays.toString(glucoseWasBackfilled)); - } - - for (int i = 0; i < glucoseValues.length; i++) { - BgReading bgReading = new BgReading(); - bgReading.value = glucoseValues[i]; - bgReading.direction = glucoseTrendsArrows[i]; - bgReading.date = glucoseTransmitterTimestamps[i]; - bgReading.raw = 0; - boolean isNew = MainApp.getDbHelper().createIfNotExists(bgReading, "DexcomG5"); - if (isNew && SP.getBoolean(R.string.key_dexcomg5_nsupload, false)) { - NSUpload.uploadBg(bgReading, "AndroidAPS-DexcomG5"); - } - if (isNew && SP.getBoolean(R.string.key_dexcomg5_xdripupload, false)) { - NSUpload.sendToXdrip(bgReading); - } - } - } - - if (bundle.containsKey("meterValues")) { - String[] meterEntryTypes = bundle.getStringArray("meterEntryTypes"); - long[] meterTimestamps = bundle.getLongArray("meterTimestamps"); - int[] meterValues = bundle.getIntArray("meterValues"); - long[] meterRecordedTimestamps = bundle.getLongArray("meterRecordedTimestamps"); - int[] meterTransmitterIDs = bundle.getIntArray("meterTransmitterIDs"); - long[] meterTransmitterTimestamps = bundle.getLongArray("meterTransmitterTimestamps"); - - if (L.isEnabled(L.BGSOURCE)) { - log.debug("meterValues", Arrays.toString(meterValues)); - log.debug("meterEntryTypes", Arrays.toString(meterEntryTypes)); - log.debug("meterTimestamps", Arrays.toString(meterTimestamps)); - log.debug("meterTransmitterTimestamps", Arrays.toString(meterTransmitterTimestamps)); - log.debug("meterRecordedTimestamps", Arrays.toString(meterRecordedTimestamps)); - log.debug("meterTransmitterIDs", Arrays.toString(meterTransmitterIDs)); - } - - for (int i = 0; i < meterValues.length; i++) { - try { - if (MainApp.getDbHelper().getCareportalEventFromTimestamp(meterTimestamps[i]) == null) { - JSONObject data = new JSONObject(); - data.put("enteredBy", "AndroidAPS-DexcomG5"); - data.put("created_at", DateUtil.toISOString(meterTimestamps[i])); - data.put("eventType", CareportalEvent.BGCHECK); - data.put("glucoseType", "Finger"); - data.put("glucose", meterValues[i]); - data.put("units", Constants.MGDL); - NSUpload.uploadCareportalEntryToNS(data); - } - } catch (JSONException e) { - log.error("Unhandled exception", e); - } - } - } - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/source/SourceDexcomG6Plugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/source/SourceDexcomG6Plugin.java deleted file mode 100644 index ad63a9b9ae..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/source/SourceDexcomG6Plugin.java +++ /dev/null @@ -1,174 +0,0 @@ -package info.nightscout.androidaps.plugins.source; - -import android.content.Intent; -import android.os.Bundle; - -import org.json.JSONException; -import org.json.JSONObject; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.Arrays; - -import info.nightscout.androidaps.Constants; -import info.nightscout.androidaps.MainApp; -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.db.BgReading; -import info.nightscout.androidaps.db.CareportalEvent; -import info.nightscout.androidaps.interfaces.BgSourceInterface; -import info.nightscout.androidaps.interfaces.PluginBase; -import info.nightscout.androidaps.interfaces.PluginDescription; -import info.nightscout.androidaps.interfaces.PluginType; -import info.nightscout.androidaps.logging.L; -import info.nightscout.androidaps.plugins.general.nsclient.NSUpload; -import info.nightscout.androidaps.utils.DateUtil; -import info.nightscout.androidaps.utils.SP; - -/** - * Created by mike on 30.11.2018. - */ - -public class SourceDexcomG6Plugin extends PluginBase implements BgSourceInterface { - private static Logger log = LoggerFactory.getLogger(L.BGSOURCE); - - private static SourceDexcomG6Plugin plugin = null; - - public static SourceDexcomG6Plugin getPlugin() { - if (plugin == null) - plugin = new SourceDexcomG6Plugin(); - return plugin; - } - - private SourceDexcomG6Plugin() { - super(new PluginDescription() - .mainType(PluginType.BGSOURCE) - .fragmentClass(BGSourceFragment.class.getName()) - .pluginName(R.string.DexcomG6) - .shortName(R.string.dexcomG6_shortname) - .preferencesId(R.xml.pref_bgsource) - .description(R.string.description_source_dexcom_g6) - ); - } - - @Override - public boolean advancedFilteringSupported() { - return true; - } - - @Override - public void handleNewData(Intent intent) { - if (!isEnabled(PluginType.BGSOURCE)) return; - - Bundle bundle = intent.getExtras(); - if (bundle == null) return; - - if (L.isEnabled(L.BGSOURCE)) { - if (bundle.containsKey("transmitterSystemTime")) - log.debug("transmitterSystemTime: " + DateUtil.dateAndTimeFullString(bundle.getLong("transmitterSystemTime"))); - log.debug("transmitterId: " + bundle.getString("transmitterId")); - if (bundle.containsKey("transmitterActivatedOn")) - log.debug("transmitterActivatedOn: " + DateUtil.dateAndTimeFullString(bundle.getLong("transmitterActivatedOn"))); - log.debug("transmitterVersion: " + bundle.getString("transmitterVersion")); - log.debug("transmitterSoftwareNumber: " + bundle.getString("transmitterSoftwareNumber")); - log.debug("transmitterStorageTimeDays: " + bundle.getInt("transmitterStorageTimeDays")); - log.debug("transmitterApiVersion: " + bundle.getInt("transmitterApiVersion")); - log.debug("transmitterMaxRuntimeDays: " + bundle.getInt("transmitterMaxRuntimeDays")); - log.debug("transmitterMaxStorageTimeDays: " + bundle.getInt("transmitterMaxStorageTimeDays")); - log.debug("transmitterCGMProcessorFirmwareVersion: " + bundle.getString("transmitterCGMProcessorFirmwareVersion")); - log.debug("transmitterBleRadioFirmwareVersion: " + bundle.getString("transmitterBleRadioFirmwareVersion")); - log.debug("transmitterHardwareVersion: " + bundle.getInt("transmitterHardwareVersion")); - log.debug("transmitterBleSoftDeviceVersion: " + bundle.getString("transmitterBleSoftDeviceVersion")); - log.debug("transmitterNordicAsicHwID: " + bundle.getInt("transmitterNordicAsicHwID")); - log.debug("transmitterSessionTimeDays: " + bundle.getInt("transmitterSessionTimeDays")); - log.debug("transmitterFeatureFlags: " + bundle.getInt("transmitterFeatureFlags")); - - if (bundle.containsKey("sensorCode")) - log.debug("sensorCode: " + bundle.getString("sensorCode")); - } - - if (bundle.containsKey("sensorInsertionTime")) { - long sensorInsertionTime = bundle.getLong("sensorInsertionTime"); - if (L.isEnabled(L.BGSOURCE)) - log.debug("sensorInsertionTime: " + DateUtil.dateAndTimeFullString(sensorInsertionTime)); - if (SP.getBoolean(R.string.key_dexcom_lognssensorchange, false)) { - try { - if (MainApp.getDbHelper().getCareportalEventFromTimestamp(sensorInsertionTime) == null) { - JSONObject data = new JSONObject(); - data.put("enteredBy", "AndroidAPS-DexcomG6"); - data.put("created_at", DateUtil.toISOString(sensorInsertionTime)); - data.put("eventType", CareportalEvent.SENSORCHANGE); - NSUpload.uploadCareportalEntryToNS(data); - } - } catch (JSONException e) { - log.error("Unhandled exception", e); - } - } - } - - if (bundle.containsKey("evgTimestamps")) { - long[] timestamps = bundle.getLongArray("evgTimestamps"); - long[] transmitterTimes = bundle.getLongArray("transmitterTimes"); - int[] evgs = bundle.getIntArray("evgs"); - int[] predictiveEVGs = bundle.getIntArray("predictiveEVGs"); - String[] trendArrows = bundle.getStringArray("trendArrows"); - - if (L.isEnabled(L.BGSOURCE)) { - log.debug("timestamps", Arrays.toString(timestamps)); - log.debug("transmitterTimes", Arrays.toString(transmitterTimes)); - log.debug("evgs", Arrays.toString(evgs)); - log.debug("predictiveEVGs", Arrays.toString(predictiveEVGs)); - log.debug("trendArrows", Arrays.toString(trendArrows)); - } - - for (int i = 0; i < transmitterTimes.length; i++) { - BgReading bgReading = new BgReading(); - bgReading.value = evgs[i]; - bgReading.direction = trendArrows[i]; - bgReading.date = timestamps[i]; - bgReading.raw = 0; - boolean isNew = MainApp.getDbHelper().createIfNotExists(bgReading, "DexcomG6"); - if (isNew && SP.getBoolean(R.string.key_dexcomg5_nsupload, false)) { - NSUpload.uploadBg(bgReading, "AndroidAPS-DexcomG6"); - } - if (isNew && SP.getBoolean(R.string.key_dexcomg5_xdripupload, false)) { - NSUpload.sendToXdrip(bgReading); - } - } - } - - if (bundle.containsKey("meterValues")) { - int[] meterValues = bundle.getIntArray("meterValues"); - String[] meterEntryTypes = bundle.getStringArray("meterEntryTypes"); - long[] meterTimestamps = bundle.getLongArray("meterTimestamps"); - long[] meterTransmitterTimestamps = bundle.getLongArray("meterTransmitterTimestamps"); - long[] meterRecordedTimestamps = bundle.getLongArray("meterRecordedTimestamps"); - int[] meterRecordIDs = bundle.getIntArray("meterRecordIDs"); - - if (L.isEnabled(L.BGSOURCE)) { - log.debug("meterValues", Arrays.toString(meterValues)); - log.debug("meterEntryTypes", Arrays.toString(meterEntryTypes)); - log.debug("meterTimestamps", Arrays.toString(meterTimestamps)); - log.debug("meterTransmitterTimestamps", Arrays.toString(meterTransmitterTimestamps)); - log.debug("meterRecordedTimestamps", Arrays.toString(meterRecordedTimestamps)); - log.debug("meterRecordIDs", Arrays.toString(meterRecordIDs)); - } - - for (int i = 0; i < meterValues.length; i++) { - try { - if (MainApp.getDbHelper().getCareportalEventFromTimestamp(meterTimestamps[i]) == null) { - JSONObject data = new JSONObject(); - data.put("enteredBy", "AndroidAPS-DexcomG6"); - data.put("created_at", DateUtil.toISOString(meterTimestamps[i])); - data.put("eventType", CareportalEvent.BGCHECK); - data.put("glucoseType", "Finger"); - data.put("glucose", meterValues[i]); - data.put("units", Constants.MGDL); - NSUpload.uploadCareportalEntryToNS(data); - } - } catch (JSONException e) { - log.error("Unhandled exception", e); - } - } - } - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/source/SourceDexcomPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/source/SourceDexcomPlugin.kt new file mode 100644 index 0000000000..dfbe2fff39 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/source/SourceDexcomPlugin.kt @@ -0,0 +1,110 @@ +package info.nightscout.androidaps.plugins.source + +import android.content.Intent +import android.content.pm.PackageManager +import androidx.core.content.ContextCompat +import info.nightscout.androidaps.Constants +import info.nightscout.androidaps.MainApp +import info.nightscout.androidaps.R +import info.nightscout.androidaps.activities.RequestDexcomPermissionActivity +import info.nightscout.androidaps.db.BgReading +import info.nightscout.androidaps.db.CareportalEvent +import info.nightscout.androidaps.interfaces.BgSourceInterface +import info.nightscout.androidaps.interfaces.PluginBase +import info.nightscout.androidaps.interfaces.PluginDescription +import info.nightscout.androidaps.interfaces.PluginType +import info.nightscout.androidaps.logging.L +import info.nightscout.androidaps.plugins.general.nsclient.NSUpload +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.SP +import org.json.JSONObject +import org.slf4j.LoggerFactory + +object SourceDexcomPlugin : PluginBase(PluginDescription() + .mainType(PluginType.BGSOURCE) + .fragmentClass(BGSourceFragment::class.java.name) + .pluginName(R.string.dexcom_app_patched) + .shortName(R.string.dexcom_short) + .preferencesId(R.xml.pref_bgsource) + .description(R.string.description_source_dexcom)), BgSourceInterface { + + private val log = LoggerFactory.getLogger(L.BGSOURCE) + + private val PACKAGE_NAMES = arrayOf("com.dexcom.cgm.region1.mgdl", "com.dexcom.cgm.region1.mmol", + "com.dexcom.cgm.region2.mgdl", "com.dexcom.cgm.region2.mmol", + "com.dexcom.g6.region1.mmol", "com.dexcom.g6.region2.mgdl", + "com.dexcom.g6.region3.mgdl", "com.dexcom.g6.region3.mmol") + + const val PERMISSION = "com.dexcom.cgm.EXTERNAL_PERMISSION" + + override fun advancedFilteringSupported(): Boolean { + return true + } + + override fun onStart() { + super.onStart() + if (ContextCompat.checkSelfPermission(MainApp.instance(), PERMISSION) != PackageManager.PERMISSION_GRANTED) { + val intent = Intent(MainApp.instance(), RequestDexcomPermissionActivity::class.java) + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + MainApp.instance().startActivity(intent) + } + } + + fun findDexcomPackageName(): String? { + val packageManager = MainApp.instance().packageManager; + for (packageInfo in packageManager.getInstalledPackages(0)) { + if (PACKAGE_NAMES.contains(packageInfo.packageName)) return packageInfo.packageName + } + return null + } + + override fun handleNewData(intent: Intent) { + if (!isEnabled(PluginType.BGSOURCE)) return + try { + val sensorType = intent.getStringExtra("sensorType") ?: "" + val glucoseValues = intent.getBundleExtra("glucoseValues") + for (i in 0 until glucoseValues.size()) { + val glucoseValue = glucoseValues.getBundle(i.toString()) + val bgReading = BgReading() + bgReading.value = glucoseValue!!.getInt("glucoseValue").toDouble() + bgReading.direction = glucoseValue.getString("trendArrow") + bgReading.date = glucoseValue.getLong("timestamp") * 1000 + bgReading.raw = 0.0 + if (MainApp.getDbHelper().createIfNotExists(bgReading, "Dexcom$sensorType")) { + if (SP.getBoolean(R.string.key_dexcomg5_nsupload, false)) { + NSUpload.uploadBg(bgReading, "AndroidAPS-Dexcom$sensorType") + } + if (SP.getBoolean(R.string.key_dexcomg5_xdripupload, false)) { + NSUpload.sendToXdrip(bgReading) + } + } + } + val meters = intent.getBundleExtra("meters") + for (i in 0 until meters.size()) { + val meter = meters.getBundle(i.toString()) + val timestamp = meter!!.getLong("timestamp") * 1000 + if (MainApp.getDbHelper().getCareportalEventFromTimestamp(timestamp) != null) continue + val jsonObject = JSONObject() + jsonObject.put("enteredBy", "AndroidAPS-Dexcom$sensorType") + jsonObject.put("created_at", DateUtil.toISOString(timestamp)) + jsonObject.put("eventType", CareportalEvent.BGCHECK) + jsonObject.put("glucoseType", "Finger") + jsonObject.put("glucose", meter.getInt("meterValue")) + jsonObject.put("units", Constants.MGDL) + NSUpload.uploadCareportalEntryToNS(jsonObject) + } + if (SP.getBoolean(R.string.key_dexcom_lognssensorchange, false) && intent.hasExtra("sensorInsertionTime")) { + val sensorInsertionTime = intent.extras!!.getLong("sensorInsertionTime") * 1000 + if (MainApp.getDbHelper().getCareportalEventFromTimestamp(sensorInsertionTime) == null) { + val jsonObject = JSONObject() + jsonObject.put("enteredBy", "AndroidAPS-Dexcom$sensorType") + jsonObject.put("created_at", DateUtil.toISOString(sensorInsertionTime)) + jsonObject.put("eventType", CareportalEvent.SENSORCHANGE) + NSUpload.uploadCareportalEntryToNS(jsonObject) + } + } + } catch (e: Exception) { + log.error("Error while processing intent from Dexcom App", e) + } + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/source/SourceNSClientPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/source/SourceNSClientPlugin.java index 328f054aad..dd0a2c5e88 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/source/SourceNSClientPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/source/SourceNSClientPlugin.java @@ -16,7 +16,6 @@ import info.nightscout.androidaps.interfaces.PluginBase; import info.nightscout.androidaps.interfaces.PluginDescription; import info.nightscout.androidaps.interfaces.PluginType; import info.nightscout.androidaps.logging.L; -import info.nightscout.androidaps.plugins.constraints.objectives.ObjectivesPlugin; import info.nightscout.androidaps.plugins.general.nsclient.data.NSSgv; import info.nightscout.androidaps.utils.JsonHelper; import info.nightscout.androidaps.utils.SP; @@ -85,8 +84,7 @@ public class SourceNSClientPlugin extends PluginBase implements BgSourceInterfac } // Objectives 0 - ObjectivesPlugin.getPlugin().bgIsAvailableInNS = true; - ObjectivesPlugin.getPlugin().saveProgress(); + SP.putBoolean(R.string.key_ObjectivesbgIsAvailableInNS, true); } private void storeSgv(JSONObject sgvJson) { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/CarbsGenerator.java b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/CarbsGenerator.java index ac9be50a23..0e7215b368 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/CarbsGenerator.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/CarbsGenerator.java @@ -1,7 +1,7 @@ package info.nightscout.androidaps.plugins.treatments; import android.content.Intent; -import android.support.annotation.Nullable; +import androidx.annotation.Nullable; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/Treatment.java b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/Treatment.java index dbf1a89c37..19b41a0944 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/Treatment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/Treatment.java @@ -1,7 +1,7 @@ package info.nightscout.androidaps.plugins.treatments; import android.graphics.Color; -import android.support.annotation.Nullable; +import androidx.annotation.Nullable; import com.j256.ormlite.field.DatabaseField; import com.j256.ormlite.table.DatabaseTable; @@ -16,9 +16,12 @@ import info.nightscout.androidaps.Constants; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.data.Iob; +import info.nightscout.androidaps.db.DbObjectBase; +import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.db.Source; import info.nightscout.androidaps.interfaces.InsulinInterface; import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; +import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; import info.nightscout.androidaps.plugins.general.overview.OverviewPlugin; import info.nightscout.androidaps.plugins.general.overview.graphExtensions.DataPointWithLabelInterface; import info.nightscout.androidaps.plugins.general.overview.graphExtensions.PointsWithLabelGraphSeries; @@ -27,7 +30,7 @@ import info.nightscout.androidaps.utils.DecimalFormatter; import info.nightscout.androidaps.utils.JsonHelper; @DatabaseTable(tableName = Treatment.TABLE_TREATMENTS) -public class Treatment implements DataPointWithLabelInterface { +public class Treatment implements DataPointWithLabelInterface, DbObjectBase { public static final String TABLE_TREATMENTS = "Treatments"; @DatabaseField(id = true) @@ -101,15 +104,16 @@ public class Treatment implements DataPointWithLabelInterface { ", insulin= " + insulin + ", carbs= " + carbs + ", mealBolus= " + mealBolus + + ", source= " + source + "}"; } public boolean isDataChanging(Treatment other) { if (date != other.date) return true; - if (insulin != other.insulin) + if (!isSame(insulin, other.insulin)) return true; - if (carbs != other.carbs) + if (!isSame(carbs, other.carbs)) return true; return false; } @@ -117,9 +121,9 @@ public class Treatment implements DataPointWithLabelInterface { public boolean isEqual(Treatment other) { if (date != other.date) return false; - if (insulin != other.insulin) + if (!isSame(insulin, other.insulin)) return false; - if (carbs != other.carbs) + if (!isSame(carbs, other.carbs)) return false; if (mealBolus != other.mealBolus) return false; @@ -132,16 +136,47 @@ public class Treatment implements DataPointWithLabelInterface { return true; } + public boolean isEqualWithoutPumpId(Treatment other) { + if (date != other.date) + return false; + if (!isSame(insulin, other.insulin)) + return false; + if (!isSame(carbs, other.carbs)) + return false; + if (mealBolus != other.mealBolus) + return false; + if (isSMB != other.isSMB) + return false; + if (!Objects.equals(_id, other._id)) + return false; + return true; + } + + public boolean isSame(Double d1, Double d2) { + double diff = d1 - d2; + + return (Math.abs(diff) <= 0.000001); + } + @Nullable public JSONObject getBoluscalc() { try { if (boluscalc != null) - return new JSONObject(boluscalc); + return new JSONObject(boluscalc); } catch (JSONException ignored) { } return null; } + public double getIc() { + JSONObject bw = getBoluscalc(); + if (bw == null || !bw.has("ic")) { + Profile profile = ProfileFunctions.getInstance().getProfile(date); + return profile.getIc(date); + } + return JsonHelper.safeGetDouble(bw, "ic"); + } + /* * mealBolus, _id and isSMB cannot be known coming from pump. Only compare rest * TODO: remove debug toasts @@ -150,12 +185,13 @@ public class Treatment implements DataPointWithLabelInterface { if (date != other.date) { return false; } - if (insulin != other.insulin) { + + if (!isSame(insulin, other.insulin)) return false; - } - if (carbs != other.carbs) { + + if (!isSame(carbs, other.carbs)) return false; - } + return true; } @@ -188,7 +224,7 @@ public class Treatment implements DataPointWithLabelInterface { @Override public double getY() { - return isSMB ? OverviewPlugin.getPlugin().determineLowLine() : yValue; + return isSMB ? OverviewPlugin.INSTANCE.determineLowLine() : yValue; } @Override @@ -242,4 +278,14 @@ public class Treatment implements DataPointWithLabelInterface { InsulinInterface insulinInterface = ConfigBuilderPlugin.getPlugin().getActiveInsulin(); return insulinInterface.iobCalcForTreatment(this, time, dia); } -} + + @Override + public long getDate() { + return this.date; + } + + @Override + public long getPumpId() { + return this.pumpId; + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentService.java b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentService.java index 7bb4774911..7123b6c96b 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentService.java @@ -2,7 +2,8 @@ package info.nightscout.androidaps.plugins.treatments; import android.content.Intent; import android.os.IBinder; -import android.support.annotation.Nullable; + +import androidx.annotation.Nullable; import com.j256.ormlite.android.apptools.OpenHelperManager; import com.j256.ormlite.android.apptools.OrmLiteBaseService; @@ -13,8 +14,8 @@ import com.j256.ormlite.stmt.QueryBuilder; import com.j256.ormlite.stmt.Where; import com.j256.ormlite.support.ConnectionSource; import com.j256.ormlite.table.TableUtils; -import com.squareup.otto.Subscribe; +import org.apache.commons.lang3.StringUtils; import org.json.JSONException; import org.json.JSONObject; import org.slf4j.Logger; @@ -28,7 +29,6 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; -import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.db.DatabaseHelper; import info.nightscout.androidaps.db.ICallback; import info.nightscout.androidaps.db.Source; @@ -37,8 +37,14 @@ import info.nightscout.androidaps.events.EventNsTreatment; import info.nightscout.androidaps.events.EventReloadTreatmentData; import info.nightscout.androidaps.events.EventTreatmentChange; import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventNewHistoryData; +import info.nightscout.androidaps.plugins.pump.medtronic.data.MedtronicHistoryData; +import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil; +import info.nightscout.androidaps.utils.FabricPrivacy; import info.nightscout.androidaps.utils.JsonHelper; +import io.reactivex.disposables.CompositeDisposable; +import io.reactivex.schedulers.Schedulers; /** @@ -47,6 +53,7 @@ import info.nightscout.androidaps.utils.JsonHelper; public class TreatmentService extends OrmLiteBaseService { private static Logger log = LoggerFactory.getLogger(L.DATATREATMENTS); + private CompositeDisposable disposable = new CompositeDisposable(); private static final ScheduledExecutorService treatmentEventWorker = Executors.newSingleThreadScheduledExecutor(); private static ScheduledFuture scheduledTreatmentEventPost = null; @@ -54,7 +61,20 @@ public class TreatmentService extends OrmLiteBaseService { public TreatmentService() { onCreate(); dbInitialize(); - MainApp.bus().register(this); + disposable.add(RxBus.INSTANCE + .toObservable(EventNsTreatment.class) + .observeOn(Schedulers.io()) + .subscribe(event -> { + int mode = event.getMode(); + JSONObject payload = event.getPayload(); + + if (mode == EventNsTreatment.Companion.getADD() || mode == EventNsTreatment.Companion.getUPDATE()) { + this.createTreatmentFromJsonIfNotExists(payload); + } else { // EventNsTreatment.REMOVE + this.deleteNS(payload); + } + }, FabricPrivacy::logException) + ); } /** @@ -85,19 +105,6 @@ public class TreatmentService extends OrmLiteBaseService { return null; } - @Subscribe - @SuppressWarnings("unused") - public void handleNsEvent(EventNsTreatment event) { - int mode = event.getMode(); - JSONObject payload = event.getPayload(); - - if (mode == EventNsTreatment.ADD || mode == EventNsTreatment.UPDATE) { - this.createTreatmentFromJsonIfNotExists(payload); - } else { // EventNsTreatment.REMOVE - this.deleteNS(payload); - } - } - @Override public void onCreate() { super.onCreate(); @@ -178,11 +185,11 @@ public class TreatmentService extends OrmLiteBaseService { public void run() { if (L.isEnabled(L.DATATREATMENTS)) log.debug("Firing EventReloadTreatmentData"); - MainApp.bus().post(event); + RxBus.INSTANCE.send(event); if (DatabaseHelper.earliestDataChange != null) { if (L.isEnabled(L.DATATREATMENTS)) log.debug("Firing EventNewHistoryData"); - MainApp.bus().post(new EventNewHistoryData(DatabaseHelper.earliestDataChange)); + RxBus.INSTANCE.send(new EventNewHistoryData(DatabaseHelper.earliestDataChange)); } DatabaseHelper.earliestDataChange = null; callback.setPost(null); @@ -190,8 +197,9 @@ public class TreatmentService extends OrmLiteBaseService { } // prepare task for execution in 1 sec // cancel waiting task to prevent sending multiple posts - if (callback.getPost() != null) - callback.getPost().cancel(false); + ScheduledFuture scheduledFuture = callback.getPost(); + if (scheduledFuture != null) + scheduledFuture.cancel(false); Runnable task = new PostRunnable(); final int sec = 1; callback.setPost(eventWorker.schedule(task, sec, TimeUnit.SECONDS)); @@ -241,15 +249,23 @@ public class TreatmentService extends OrmLiteBaseService { public void createTreatmentFromJsonIfNotExists(JSONObject json) { try { Treatment treatment = Treatment.createFromJson(json); - if (treatment != null) - createOrUpdate(treatment); - else + if (treatment != null) { + + if (MedtronicHistoryData.doubleBolusDebug) + log.debug("DoubleBolusDebug: createTreatmentFromJsonIfNotExists:: medtronicPump={}", MedtronicUtil.isMedtronicPump()); + + if (!MedtronicUtil.isMedtronicPump()) + createOrUpdate(treatment); + else + createOrUpdateMedtronic(treatment, true); + } else log.error("Date is null: " + treatment.toString()); } catch (JSONException e) { log.error("Unhandled exception", e); } } + // return true if new record is created public UpdateReturn createOrUpdate(Treatment treatment) { try { @@ -379,6 +395,196 @@ public class TreatmentService extends OrmLiteBaseService { return new UpdateReturn(false, false); } + + public UpdateReturn createOrUpdateMedtronic(Treatment treatment, boolean fromNightScout) { + + if (MedtronicHistoryData.doubleBolusDebug) + log.debug("DoubleBolusDebug: createOrUpdateMedtronic:: originalTreatment={}, fromNightScout={}", treatment, fromNightScout); + + try { + treatment.date = DatabaseHelper.roundDateToSec(treatment.date); + + Treatment existingTreatment = getRecord(treatment.pumpId, treatment.date); + + if (MedtronicHistoryData.doubleBolusDebug) + log.debug("DoubleBolusDebug: createOrUpdateMedtronic:: existingTreatment={}", treatment); + + if (existingTreatment == null) { + getDao().create(treatment); + if (L.isEnabled(L.DATATREATMENTS)) + log.debug("New record from: " + Source.getString(treatment.source) + " " + treatment.toString()); + DatabaseHelper.updateEarliestDataChange(treatment.date); + scheduleTreatmentChange(treatment); + return new UpdateReturn(true, true); + } else { + + if (existingTreatment.date == treatment.date) { + if (MedtronicHistoryData.doubleBolusDebug) + log.debug("DoubleBolusDebug: createOrUpdateMedtronic::(existingTreatment.date==treatment.date)"); + + // we will do update only, if entry changed + if (!optionalTreatmentCopy(existingTreatment, treatment, fromNightScout)) { + return new UpdateReturn(true, false); + } + getDao().update(existingTreatment); + DatabaseHelper.updateEarliestDataChange(existingTreatment.date); + scheduleTreatmentChange(treatment); + return new UpdateReturn(true, false); + } else { + if (MedtronicHistoryData.doubleBolusDebug) + log.debug("DoubleBolusDebug: createOrUpdateMedtronic::(existingTreatment.date != treatment.date)"); + + // date is different, we need to remove entry + getDao().delete(existingTreatment); + optionalTreatmentCopy(existingTreatment, treatment, fromNightScout); + getDao().create(existingTreatment); + DatabaseHelper.updateEarliestDataChange(existingTreatment.date); + scheduleTreatmentChange(treatment); + return new UpdateReturn(true, false); //updating a pump treatment with another one from the pump is not counted as clash + } + } + + } catch (SQLException e) { + log.error("Unhandled SQL exception: {}", e.getMessage(), e); + } + return new UpdateReturn(false, false); + } + + + private boolean optionalTreatmentCopy(Treatment oldTreatment, Treatment newTreatment, boolean fromNightScout) { + + log.debug("optionalTreatmentCopy [old={}, new={}]", oldTreatment.toString(), newTreatment.toString()); + + boolean changed = false; + + if (oldTreatment.date != newTreatment.date) { + oldTreatment.date = newTreatment.date; + changed = true; + } + + if (oldTreatment.isSMB != newTreatment.isSMB) { + if (!oldTreatment.isSMB) { + oldTreatment.isSMB = newTreatment.isSMB; + changed = true; + } + } + + if (!isSame(oldTreatment.carbs, newTreatment.carbs)) { + if (isSame(oldTreatment.carbs, 0.0d)) { + oldTreatment.carbs = newTreatment.carbs; + changed = true; + } + } + + if (oldTreatment.mealBolus != (oldTreatment.carbs > 0)) { + oldTreatment.mealBolus = (oldTreatment.carbs > 0); + changed = true; + } + + if (!isSame(oldTreatment.insulin, newTreatment.insulin)) { + if (!fromNightScout) { + oldTreatment.insulin = newTreatment.insulin; + changed = true; + } + } + + if (!StringUtils.equals(oldTreatment._id, newTreatment._id)) { + if (StringUtils.isBlank(oldTreatment._id)) { + oldTreatment._id = newTreatment._id; + changed = true; + } + } + + int source = Source.NONE; + + if (oldTreatment.pumpId == 0) { + if (newTreatment.pumpId > 0) { + oldTreatment.pumpId = newTreatment.pumpId; + source = Source.PUMP; + changed = true; + } + } + + if (source == Source.NONE) { + + if (oldTreatment.source == newTreatment.source) { + source = oldTreatment.source; + } else { + source = (oldTreatment.source == Source.NIGHTSCOUT || newTreatment.source == Source.NIGHTSCOUT) ? Source.NIGHTSCOUT : Source.USER; + } + } + + if (oldTreatment.source != source) { + oldTreatment.source = source; + changed = true; + } + + log.debug("optionalTreatmentCopy [changed={}, newAfterChange={}]", changed, oldTreatment.toString()); + return changed; + } + + + public static boolean isSame(Double d1, Double d2) { + double diff = d1 - d2; + + return (Math.abs(diff) <= 0.00001); + } + + @Deprecated + private void treatmentCopy(Treatment oldTreatment, Treatment newTreatment, boolean fromNightScout) { + + log.debug("treatmentCopy [old={}, new={}]", oldTreatment.toString(), newTreatment.toString()); + + + if (fromNightScout) { + long pumpId_old = oldTreatment.pumpId; + boolean isSMB = (oldTreatment.isSMB || newTreatment.isSMB); + + oldTreatment.copyFrom(newTreatment); + + if (pumpId_old != 0) { + oldTreatment.pumpId = pumpId_old; + } + + if (oldTreatment.pumpId != 0 && oldTreatment.source != Source.PUMP) { + oldTreatment.source = Source.PUMP; + } + + oldTreatment.isSMB = isSMB; + + } else { + oldTreatment.copyFrom(newTreatment); + } + + log.debug("treatmentCopy [newAfterChange={}]", oldTreatment.toString()); + + } + + + public Treatment getRecord(long pumpId, long date) { + + Treatment record = null; + + if (pumpId > 0) { + + record = getPumpRecordById(pumpId); + + if (record != null) { + return record; + } + } + + try { + record = getDao().queryForId(date); + } catch (SQLException ex) { + log.error("Error getting entry by id ({}", date); + } + + return record; + + } + + /** * Returns the record for the given id, null if none, throws RuntimeException * if multiple records with the same pump id exist. @@ -499,6 +705,23 @@ public class TreatmentService extends OrmLiteBaseService { return new ArrayList<>(); } + public List getTreatmentDataFromTime(long from, long to, boolean ascending) { + try { + Dao daoTreatments = getDao(); + List treatments; + QueryBuilder queryBuilder = daoTreatments.queryBuilder(); + queryBuilder.orderBy("date", ascending); + Where where = queryBuilder.where(); + where.between("date", from, to); + PreparedQuery preparedQuery = queryBuilder.prepare(); + treatments = daoTreatments.query(preparedQuery); + return treatments; + } catch (SQLException e) { + log.error("Unhandled exception", e); + } + return new ArrayList<>(); + } + @Nullable @Override public IBinder onBind(Intent intent) { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentsFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentsFragment.java index d30565b4a7..731042c237 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentsFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentsFragment.java @@ -1,19 +1,18 @@ package info.nightscout.androidaps.plugins.treatments; import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentTransaction; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; -import com.squareup.otto.Subscribe; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentTransaction; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.events.EventExtendedBolusChange; -import info.nightscout.androidaps.plugins.common.SubscriberFragment; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.treatments.fragments.TreatmentsBolusFragment; import info.nightscout.androidaps.plugins.treatments.fragments.TreatmentsCareportalFragment; @@ -22,8 +21,12 @@ import info.nightscout.androidaps.plugins.treatments.fragments.TreatmentsProfile import info.nightscout.androidaps.plugins.treatments.fragments.TreatmentsTempTargetFragment; import info.nightscout.androidaps.plugins.treatments.fragments.TreatmentsTemporaryBasalsFragment; import info.nightscout.androidaps.utils.FabricPrivacy; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.CompositeDisposable; + +public class TreatmentsFragment extends Fragment implements View.OnClickListener { + private CompositeDisposable disposable = new CompositeDisposable(); -public class TreatmentsFragment extends SubscriberFragment implements View.OnClickListener { TextView treatmentsTab; TextView extendedBolusesTab; TextView tempBasalsTab; @@ -34,32 +37,42 @@ public class TreatmentsFragment extends SubscriberFragment implements View.OnCli @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - try { - View view = inflater.inflate(R.layout.treatments_fragment, container, false); + View view = inflater.inflate(R.layout.treatments_fragment, container, false); - treatmentsTab = (TextView) view.findViewById(R.id.treatments_treatments); - extendedBolusesTab = (TextView) view.findViewById(R.id.treatments_extendedboluses); - tempBasalsTab = (TextView) view.findViewById(R.id.treatments_tempbasals); - tempTargetTab = (TextView) view.findViewById(R.id.treatments_temptargets); - profileSwitchTab = (TextView) view.findViewById(R.id.treatments_profileswitches); - careportalTab = (TextView) view.findViewById(R.id.treatments_careportal); - treatmentsTab.setOnClickListener(this); - extendedBolusesTab.setOnClickListener(this); - tempBasalsTab.setOnClickListener(this); - tempTargetTab.setOnClickListener(this); - profileSwitchTab.setOnClickListener(this); - careportalTab.setOnClickListener(this); + treatmentsTab = (TextView) view.findViewById(R.id.treatments_treatments); + extendedBolusesTab = (TextView) view.findViewById(R.id.treatments_extendedboluses); + tempBasalsTab = (TextView) view.findViewById(R.id.treatments_tempbasals); + tempTargetTab = (TextView) view.findViewById(R.id.treatments_temptargets); + profileSwitchTab = (TextView) view.findViewById(R.id.treatments_profileswitches); + careportalTab = (TextView) view.findViewById(R.id.treatments_careportal); + treatmentsTab.setOnClickListener(this); + extendedBolusesTab.setOnClickListener(this); + tempBasalsTab.setOnClickListener(this); + tempTargetTab.setOnClickListener(this); + profileSwitchTab.setOnClickListener(this); + careportalTab.setOnClickListener(this); - setFragment(new TreatmentsBolusFragment()); - setBackgroundColorOnSelected(treatmentsTab); + setFragment(new TreatmentsBolusFragment()); + setBackgroundColorOnSelected(treatmentsTab); - return view; - } catch (Exception e) { - FabricPrivacy.logException(e); - } + return view; + } - return null; + @Override + public synchronized void onResume() { + super.onResume(); + disposable.add(RxBus.INSTANCE + .toObservable(EventExtendedBolusChange.class) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(event -> updateGui(), FabricPrivacy::logException) + ); + updateGui(); + } + @Override + public synchronized void onPause() { + super.onPause(); + disposable.clear(); } @Override @@ -111,13 +124,7 @@ public class TreatmentsFragment extends SubscriberFragment implements View.OnCli selected.setBackgroundColor(MainApp.gc(R.color.tabBgColorSelected)); } - @Subscribe - public void onStatusEvent(final EventExtendedBolusChange ev) { - updateGUI(); - } - - @Override - protected void updateGUI() { + private void updateGui() { if (ConfigBuilderPlugin.getPlugin().getActivePump().getPumpDescription().isExtendedBolusCapable || TreatmentsPlugin.getPlugin().getExtendedBolusesFromHistory().size() > 0) { extendedBolusesTab.setVisibility(View.VISIBLE); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentsPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentsPlugin.java index 2c94d6713d..28181da190 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentsPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentsPlugin.java @@ -2,10 +2,10 @@ package info.nightscout.androidaps.plugins.treatments; import android.content.Intent; import android.os.Bundle; -import android.support.annotation.Nullable; + +import androidx.annotation.Nullable; import com.google.firebase.analytics.FirebaseAnalytics; -import com.squareup.otto.Subscribe; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -41,6 +41,7 @@ import info.nightscout.androidaps.interfaces.PluginType; import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.interfaces.TreatmentsInterface; import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; import info.nightscout.androidaps.plugins.general.nsclient.NSUpload; @@ -48,13 +49,18 @@ import info.nightscout.androidaps.plugins.general.overview.dialogs.ErrorHelperAc import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification; import info.nightscout.androidaps.plugins.general.overview.notifications.Notification; import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensData; +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult; import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin; +import info.nightscout.androidaps.plugins.pump.medtronic.data.MedtronicHistoryData; +import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil; import info.nightscout.androidaps.plugins.sensitivity.SensitivityAAPSPlugin; import info.nightscout.androidaps.plugins.sensitivity.SensitivityWeightedAveragePlugin; import info.nightscout.androidaps.utils.DateUtil; import info.nightscout.androidaps.utils.FabricPrivacy; import info.nightscout.androidaps.utils.SP; import info.nightscout.androidaps.utils.T; +import io.reactivex.disposables.CompositeDisposable; +import io.reactivex.schedulers.Schedulers; /** * Created by mike on 05.08.2016. @@ -62,6 +68,8 @@ import info.nightscout.androidaps.utils.T; public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface { private Logger log = LoggerFactory.getLogger(L.DATATREATMENTS); + private CompositeDisposable disposable = new CompositeDisposable(); + private static TreatmentsPlugin treatmentsPlugin; public static TreatmentsPlugin getPlugin() { @@ -95,18 +103,54 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface @Override protected void onStart() { - MainApp.bus().register(this); initializeTempBasalData(); initializeTreatmentData(); initializeExtendedBolusData(); initializeTempTargetData(); initializeProfileSwitchData(); super.onStart(); + disposable.add(RxBus.INSTANCE + .toObservable(EventReloadTreatmentData.class) + .observeOn(Schedulers.io()) + .subscribe(event -> { + if (L.isEnabled(L.DATATREATMENTS)) + log.debug("EventReloadTreatmentData"); + initializeTreatmentData(); + initializeExtendedBolusData(); + updateTotalIOBTreatments(); + RxBus.INSTANCE.send(event.getNext()); + }, + FabricPrivacy::logException + )); + disposable.add(RxBus.INSTANCE + .toObservable(EventReloadProfileSwitchData.class) + .observeOn(Schedulers.io()) + .subscribe(event -> initializeProfileSwitchData(), + FabricPrivacy::logException + )); + disposable.add(RxBus.INSTANCE + .toObservable(EventTempTargetChange.class) + .observeOn(Schedulers.io()) + .subscribe(event -> initializeTempTargetData(), + FabricPrivacy::logException + )); + disposable.add(RxBus.INSTANCE + .toObservable(EventReloadTempBasalData.class) + .observeOn(Schedulers.io()) + .subscribe(event -> { + if (L.isEnabled(L.DATATREATMENTS)) + log.debug("EventReloadTempBasalData"); + initializeTempBasalData(); + updateTotalIOBTempBasals(); + }, + FabricPrivacy::logException + )); } @Override protected void onStop() { - MainApp.bus().register(this); + disposable.clear(); + super.onStop(); } public TreatmentService getService() { @@ -290,6 +334,36 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface } } + + /** + * Returns all Treatments after specified timestamp. Also returns invalid entries (required to + * map "Fill Canulla" entries to history (and not to add double bolus for it) + * + * @param fromTimestamp + * @return + */ + @Override + public List getTreatmentsFromHistoryAfterTimestamp(long fromTimestamp) { + List in5minback = new ArrayList<>(); + + long time = System.currentTimeMillis(); + synchronized (treatments) { + if (MedtronicHistoryData.doubleBolusDebug) + log.debug("DoubleBolusDebug: AllTreatmentsInDb: {}", MedtronicUtil.getGsonInstance().toJson(treatments)); + + for (Treatment t : treatments) { + if (t.date <= time && t.date >= fromTimestamp) + in5minback.add(t); + } + + if (MedtronicHistoryData.doubleBolusDebug) + log.debug("DoubleBolusDebug: FilteredTreatments: AfterTime={}, Items={}", fromTimestamp, MedtronicUtil.getGsonInstance().toJson(in5minback)); + + return in5minback; + } + } + + @Override public List getTreatments5MinBackFromHistory(long time) { List in5minback = new ArrayList<>(); @@ -322,6 +396,22 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface return last; } + public long getLastBolusTime(boolean isSMB) { + long now = System.currentTimeMillis(); + long last = 0; + synchronized (treatments) { + for (Treatment t : treatments) { + if (!t.isValid) + continue; + if (t.date > last && t.insulin > 0 && t.isValid && t.date <= now && isSMB == t.isSMB) + last = t.date; + } + } + if (L.isEnabled(L.DATATREATMENTS)) + log.debug("Last manual bolus time: " + new Date(last).toLocaleString()); + return last; + } + @Override public boolean isInHistoryRealTempBasalInProgress() { return getRealTempBasalFromHistory(System.currentTimeMillis()) != null; @@ -344,36 +434,17 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface return getExtendedBolusFromHistory(System.currentTimeMillis()) != null; //TODO: crosscheck here } - @Subscribe - public void onStatusEvent(final EventReloadTreatmentData ev) { - if (L.isEnabled(L.DATATREATMENTS)) - log.debug("EventReloadTreatmentData"); - initializeTreatmentData(); - initializeExtendedBolusData(); - updateTotalIOBTreatments(); - MainApp.bus().post(ev.next); - } - - @Subscribe - @SuppressWarnings("unused") - public void onStatusEvent(final EventReloadTempBasalData ev) { - if (L.isEnabled(L.DATATREATMENTS)) - log.debug("EventReloadTempBasalData"); - initializeTempBasalData(); - updateTotalIOBTempBasals(); - } - @Override public IobTotal getLastCalculationTempBasals() { return lastTempBasalsCalculation; } @Override - public IobTotal getCalculationToTimeTempBasals(long time, Profile profile) { - return getCalculationToTimeTempBasals(time, profile, false, 0); + public IobTotal getCalculationToTimeTempBasals(long time) { + return getCalculationToTimeTempBasals(time, false, 0); } - public IobTotal getCalculationToTimeTempBasals(long time, Profile profile, boolean truncate, long truncateTime) { + public IobTotal getCalculationToTimeTempBasals(long time, boolean truncate, long truncateTime) { IobTotal total = new IobTotal(time); InsulinInterface insulinInterface = ConfigBuilderPlugin.getPlugin().getActiveInsulin(); @@ -385,6 +456,8 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface TemporaryBasal t = tempBasals.get(pos); if (t.date > time) continue; IobTotal calc; + Profile profile = ProfileFunctions.getInstance().getProfile(t.date); + if (profile == null) continue; if (truncate && t.end() > truncateTime) { TemporaryBasal dummyTemp = new TemporaryBasal(); dummyTemp.copyFrom(t); @@ -404,6 +477,8 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface ExtendedBolus e = extendedBoluses.get(pos); if (e.date > time) continue; IobTotal calc; + Profile profile = ProfileFunctions.getInstance().getProfile(e.date); + if (profile == null) continue; if (truncate && e.end() > truncateTime) { ExtendedBolus dummyExt = new ExtendedBolus(); dummyExt.copyFrom(e); @@ -425,11 +500,65 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface return total; } + public IobTotal getCalculationToTimeTempBasals(long time, long truncateTime, AutosensResult lastAutosensResult, boolean exercise_mode, int half_basal_exercise_target, boolean isTempTarget) { + IobTotal total = new IobTotal(time); + + InsulinInterface insulinInterface = ConfigBuilderPlugin.getPlugin().getActiveInsulin(); + if (insulinInterface == null) + return total; + + synchronized (tempBasals) { + for (int pos = 0; pos < tempBasals.size(); pos++) { + TemporaryBasal t = tempBasals.get(pos); + if (t.date > time) continue; + IobTotal calc; + Profile profile = ProfileFunctions.getInstance().getProfile(t.date); + if (profile == null) continue; + if (t.end() > truncateTime) { + TemporaryBasal dummyTemp = new TemporaryBasal(); + dummyTemp.copyFrom(t); + dummyTemp.cutEndTo(truncateTime); + calc = dummyTemp.iobCalc(time, profile, lastAutosensResult, exercise_mode, half_basal_exercise_target, isTempTarget); + } else { + calc = t.iobCalc(time, profile, lastAutosensResult, exercise_mode, half_basal_exercise_target, isTempTarget); + } + //log.debug("BasalIOB " + new Date(time) + " >>> " + calc.basaliob); + total.plus(calc); + } + } + if (ConfigBuilderPlugin.getPlugin().getActivePump().isFakingTempsByExtendedBoluses()) { + IobTotal totalExt = new IobTotal(time); + synchronized (extendedBoluses) { + for (int pos = 0; pos < extendedBoluses.size(); pos++) { + ExtendedBolus e = extendedBoluses.get(pos); + if (e.date > time) continue; + IobTotal calc; + Profile profile = ProfileFunctions.getInstance().getProfile(e.date); + if (profile == null) continue; + if (e.end() > truncateTime) { + ExtendedBolus dummyExt = new ExtendedBolus(); + dummyExt.copyFrom(e); + dummyExt.cutEndTo(truncateTime); + calc = dummyExt.iobCalc(time, profile, lastAutosensResult, exercise_mode, half_basal_exercise_target, isTempTarget); + } else { + calc = e.iobCalc(time, profile, lastAutosensResult, exercise_mode, half_basal_exercise_target, isTempTarget); + } + totalExt.plus(calc); + } + } + // Convert to basal iob + totalExt.basaliob = totalExt.iob; + totalExt.iob = 0d; + totalExt.netbasalinsulin = totalExt.extendedBolusInsulin; + totalExt.hightempinsulin = totalExt.extendedBolusInsulin; + total.plus(totalExt); + } + return total; + } + @Override public void updateTotalIOBTempBasals() { - Profile profile = ProfileFunctions.getInstance().getProfile(); - if (profile != null) - lastTempBasalsCalculation = getCalculationToTimeTempBasals(DateUtil.now(), profile); + lastTempBasalsCalculation = getCalculationToTimeTempBasals(DateUtil.now()); } @Nullable @@ -477,7 +606,7 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface } @Override - public Intervals getTemporaryBasalsFromHistory() { + public NonOverlappingIntervals getTemporaryBasalsFromHistory() { synchronized (tempBasals) { return new NonOverlappingIntervals<>(tempBasals); } @@ -501,6 +630,11 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface // return true if new record is created @Override public boolean addToHistoryTreatment(DetailedBolusInfo detailedBolusInfo, boolean allowUpdate) { + boolean medtronicPump = MedtronicUtil.isMedtronicPump(); + + if (MedtronicHistoryData.doubleBolusDebug) + log.debug("DoubleBolusDebug: addToHistoryTreatment::isMedtronicPump={}", medtronicPump); + Treatment treatment = new Treatment(); treatment.date = detailedBolusInfo.date; treatment.source = detailedBolusInfo.source; @@ -513,17 +647,34 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface treatment.source = detailedBolusInfo.source; treatment.mealBolus = treatment.carbs > 0; treatment.boluscalc = detailedBolusInfo.boluscalc != null ? detailedBolusInfo.boluscalc.toString() : null; - TreatmentService.UpdateReturn creatOrUpdateResult = getService().createOrUpdate(treatment); + TreatmentService.UpdateReturn creatOrUpdateResult; + + if (medtronicPump && MedtronicHistoryData.doubleBolusDebug) + log.debug("DoubleBolusDebug: addToHistoryTreatment::treatment={}", treatment); + + if (!medtronicPump) + creatOrUpdateResult = getService().createOrUpdate(treatment); + else + creatOrUpdateResult = getService().createOrUpdateMedtronic(treatment, false); + boolean newRecordCreated = creatOrUpdateResult.newRecord; //log.debug("Adding new Treatment record" + treatment.toString()); if (detailedBolusInfo.carbTime != 0) { + Treatment carbsTreatment = new Treatment(); carbsTreatment.source = detailedBolusInfo.source; carbsTreatment.pumpId = detailedBolusInfo.pumpId; // but this should never happen carbsTreatment.date = detailedBolusInfo.date + detailedBolusInfo.carbTime * 60 * 1000L + 1000L; // add 1 sec to make them different records carbsTreatment.carbs = detailedBolusInfo.carbs; carbsTreatment.source = detailedBolusInfo.source; - getService().createOrUpdate(carbsTreatment); + + if (medtronicPump && MedtronicHistoryData.doubleBolusDebug) + log.debug("DoubleBolusDebug: carbTime!=0, creating second treatment. CarbsTreatment={}", carbsTreatment); + + if (!medtronicPump) + getService().createOrUpdate(carbsTreatment); + else + getService().createOrUpdateMedtronic(carbsTreatment, false); //log.debug("Adding new Treatment record" + carbsTreatment); } if (newRecordCreated && detailedBolusInfo.isValid) @@ -569,13 +720,6 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface return oldestTime; } - // TempTargets - @Subscribe - @SuppressWarnings("unused") - public void onStatusEvent(final EventTempTargetChange ev) { - initializeTempTargetData(); - } - @Nullable @Override public TempTarget getTempTargetFromHistory() { @@ -606,14 +750,8 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface NSUpload.uploadTempTarget(tempTarget); } - // Profile Switch - @Subscribe - @SuppressWarnings("unused") - public void onStatusEvent(final EventReloadProfileSwitchData ev) { - initializeProfileSwitchData(); - } - @Override + @Nullable public ProfileSwitch getProfileSwitchFromHistory(long time) { synchronized (profiles) { return (ProfileSwitch) profiles.getValueToTime(time); @@ -630,7 +768,7 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface @Override public void addToHistoryProfileSwitch(ProfileSwitch profileSwitch) { //log.debug("Adding new TemporaryBasal record" + profileSwitch.log()); - MainApp.bus().post(new EventDismissNotification(Notification.PROFILE_SWITCH_MISSING)); + RxBus.INSTANCE.send(new EventDismissNotification(Notification.PROFILE_SWITCH_MISSING)); MainApp.getDbHelper().createOrUpdate(profileSwitch); NSUpload.uploadProfileSwitch(profileSwitch); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/dialogs/WizardInfoDialog.java b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/dialogs/WizardInfoDialog.java deleted file mode 100644 index c38672311b..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/dialogs/WizardInfoDialog.java +++ /dev/null @@ -1,86 +0,0 @@ -package info.nightscout.androidaps.plugins.treatments.dialogs; - -import android.os.Bundle; -import android.support.v4.app.DialogFragment; -import android.view.LayoutInflater; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.ViewGroup; -import android.view.WindowManager; -import android.widget.CheckBox; -import android.widget.TextView; - -import org.json.JSONObject; - -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.utils.DecimalFormatter; -import info.nightscout.androidaps.utils.JsonHelper; - -public class WizardInfoDialog extends DialogFragment implements OnClickListener { - JSONObject json; - - public WizardInfoDialog() { - super(); - } - - public void setData(JSONObject json) { - this.json = json; - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.treatments_wizardinfo_dialog, container, false); - - getDialog().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN); - - view.findViewById(R.id.ok).setOnClickListener(this); - - // BG - ((TextView) view.findViewById(R.id.treatments_wizard_bg)).setText(DecimalFormatter.to1Decimal(JsonHelper.safeGetDouble(json, "bg")) + " ISF: " + DecimalFormatter.to1Decimal(JsonHelper.safeGetDouble(json, "isf"))); - ((TextView) view.findViewById(R.id.treatments_wizard_bginsulin)).setText(DecimalFormatter.to2Decimal(JsonHelper.safeGetDouble(json, "insulinbg")) + "U"); - ((CheckBox) view.findViewById(R.id.treatments_wizard_bgcheckbox)).setChecked(JsonHelper.safeGetBoolean(json, "insulinbgused")); - ((CheckBox) view.findViewById(R.id.treatments_wizard_ttcheckbox)).setChecked(JsonHelper.safeGetBoolean(json, "ttused")); - // Trend - ((TextView) view.findViewById(R.id.treatments_wizard_bgtrend)).setText(JsonHelper.safeGetString(json, "trend")); - ((TextView) view.findViewById(R.id.treatments_wizard_bgtrendinsulin)).setText(DecimalFormatter.to2Decimal(JsonHelper.safeGetDouble(json, "insulintrend")) + "U"); - ((CheckBox) view.findViewById(R.id.treatments_wizard_bgtrendcheckbox)).setChecked(JsonHelper.safeGetBoolean(json, "trendused")); - // COB - ((TextView) view.findViewById(R.id.treatments_wizard_cob)).setText(DecimalFormatter.to0Decimal(JsonHelper.safeGetDouble(json, "cob")) + "g IC: " + DecimalFormatter.to1Decimal(JsonHelper.safeGetDouble(json, "ic"))); - ((TextView) view.findViewById(R.id.treatments_wizard_cobinsulin)).setText(DecimalFormatter.to2Decimal(JsonHelper.safeGetDouble(json, "insulincob")) + "U"); - ((CheckBox) view.findViewById(R.id.treatments_wizard_cobcheckbox)).setChecked(JsonHelper.safeGetBoolean(json, "cobused")); - // Bolus IOB - ((TextView) view.findViewById(R.id.treatments_wizard_bolusiobinsulin)).setText(DecimalFormatter.to2Decimal(JsonHelper.safeGetDouble(json, "bolusiob")) + "U"); - ((CheckBox) view.findViewById(R.id.treatments_wizard_bolusiobcheckbox)).setChecked(JsonHelper.safeGetBoolean(json, "bolusiobused")); - // Basal IOB - ((TextView) view.findViewById(R.id.treatments_wizard_basaliobinsulin)).setText(DecimalFormatter.to2Decimal(JsonHelper.safeGetDouble(json, "basaliob")) + "U"); - ((CheckBox) view.findViewById(R.id.treatments_wizard_basaliobcheckbox)).setChecked(JsonHelper.safeGetBoolean(json, "basaliobused")); - // Superbolus - ((TextView) view.findViewById(R.id.treatments_wizard_sbinsulin)).setText(DecimalFormatter.to2Decimal(JsonHelper.safeGetDouble(json, "insulinsuperbolus")) + "U"); - ((CheckBox) view.findViewById(R.id.treatments_wizard_sbcheckbox)).setChecked(JsonHelper.safeGetBoolean(json, "superbolusused")); - // Carbs - ((TextView) view.findViewById(R.id.treatments_wizard_carbs)).setText(DecimalFormatter.to0Decimal(JsonHelper.safeGetDouble(json, "carbs")) + "g IC: " + DecimalFormatter.to1Decimal(JsonHelper.safeGetDouble(json, "ic"))); - ((TextView) view.findViewById(R.id.treatments_wizard_carbsinsulin)).setText(DecimalFormatter.to2Decimal(JsonHelper.safeGetDouble(json, "insulincarbs")) + "U"); - // Correction - ((TextView) view.findViewById(R.id.treatments_wizard_correctioninsulin)).setText(DecimalFormatter.to2Decimal(JsonHelper.safeGetDouble(json, "othercorrection")) + "U"); - // Profile - ((TextView) view.findViewById(R.id.treatments_wizard_profile)).setText(JsonHelper.safeGetString(json, "profile")); - // Notes - ((TextView) view.findViewById(R.id.treatments_wizard_notes)).setText(JsonHelper.safeGetString(json, "notes")); - // Total - ((TextView) view.findViewById(R.id.treatments_wizard_totalinsulin)).setText(DecimalFormatter.to2Decimal(JsonHelper.safeGetDouble(json, "insulin")) + "U"); - - setCancelable(true); - return view; - } - - @Override - public synchronized void onClick(View view) { - switch (view.getId()) { - case R.id.ok: - dismiss(); - break; - } - } - -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/dialogs/WizardInfoDialog.kt b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/dialogs/WizardInfoDialog.kt new file mode 100644 index 0000000000..c65c75c00e --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/dialogs/WizardInfoDialog.kt @@ -0,0 +1,70 @@ +package info.nightscout.androidaps.plugins.treatments.dialogs + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.view.WindowManager +import androidx.fragment.app.DialogFragment +import info.nightscout.androidaps.R +import info.nightscout.androidaps.utils.DecimalFormatter +import info.nightscout.androidaps.utils.JsonHelper +import kotlinx.android.synthetic.main.treatments_wizardinfo_dialog.* +import org.json.JSONObject + +class WizardInfoDialog : DialogFragment() { + private var json: JSONObject? = null + + fun setData(json: JSONObject) { + this.json = json + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + dialog?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN) + isCancelable = true + return inflater.inflate(R.layout.treatments_wizardinfo_dialog, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + close.setOnClickListener { dismiss() } + + // BG + treatments_wizard_bg.text = DecimalFormatter.to1Decimal(JsonHelper.safeGetDouble(json, "bg")) + " ISF: " + DecimalFormatter.to1Decimal(JsonHelper.safeGetDouble(json, "isf")) + treatments_wizard_bginsulin.text = DecimalFormatter.to2Decimal(JsonHelper.safeGetDouble(json, "insulinbg")) + "U" + treatments_wizard_bgcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "insulinbgused") + treatments_wizard_ttcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "ttused") + // Trend + treatments_wizard_bgtrend.text = JsonHelper.safeGetString(json, "trend") + treatments_wizard_bgtrendinsulin.text = DecimalFormatter.to2Decimal(JsonHelper.safeGetDouble(json, "insulintrend")) + "U" + treatments_wizard_bgtrendcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "trendused") + // COB + treatments_wizard_cob.text = DecimalFormatter.to0Decimal(JsonHelper.safeGetDouble(json, "cob")) + "g IC: " + DecimalFormatter.to1Decimal(JsonHelper.safeGetDouble(json, "ic")) + treatments_wizard_cobinsulin.text = DecimalFormatter.to2Decimal(JsonHelper.safeGetDouble(json, "insulincob")) + "U" + treatments_wizard_cobcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "cobused") + // Bolus IOB + treatments_wizard_bolusiobinsulin.text = DecimalFormatter.to2Decimal(JsonHelper.safeGetDouble(json, "bolusiob")) + "U" + treatments_wizard_bolusiobcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "bolusiobused") + // Basal IOB + treatments_wizard_basaliobinsulin.text = DecimalFormatter.to2Decimal(JsonHelper.safeGetDouble(json, "basaliob")) + "U" + treatments_wizard_basaliobcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "basaliobused") + // Superbolus + treatments_wizard_sbinsulin.text = DecimalFormatter.to2Decimal(JsonHelper.safeGetDouble(json, "insulinsuperbolus")) + "U" + treatments_wizard_sbcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "superbolusused") + // Carbs + treatments_wizard_carbs.text = DecimalFormatter.to0Decimal(JsonHelper.safeGetDouble(json, "carbs")) + "g IC: " + DecimalFormatter.to1Decimal(JsonHelper.safeGetDouble(json, "ic")) + treatments_wizard_carbsinsulin.text = DecimalFormatter.to2Decimal(JsonHelper.safeGetDouble(json, "insulincarbs")) + "U" + // Correction + treatments_wizard_correctioninsulin.text = DecimalFormatter.to2Decimal(JsonHelper.safeGetDouble(json, "othercorrection")) + "U" + // Profile + treatments_wizard_profile.text = JsonHelper.safeGetString(json, "profile") + // Notes + treatments_wizard_notes.text = JsonHelper.safeGetString(json, "notes") + // Percentage + treatments_wizard_percent_used.text = DecimalFormatter.to0Decimal(JsonHelper.safeGetDouble(json, "percentageCorrection", 100.0)) + "%" + // Total + treatments_wizard_totalinsulin.text = DecimalFormatter.to2Decimal(JsonHelper.safeGetDouble(json, "insulin")) + "U" + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/ProfileViewerDialog.java b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/ProfileViewerDialog.java deleted file mode 100644 index c8db29b617..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/ProfileViewerDialog.java +++ /dev/null @@ -1,135 +0,0 @@ -package info.nightscout.androidaps.plugins.treatments.fragments; - -import android.os.Bundle; -import android.support.v4.app.DialogFragment; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.Button; -import android.widget.LinearLayout; -import android.widget.TextView; - -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.Unbinder; -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.data.Profile; -import info.nightscout.androidaps.db.ProfileSwitch; -import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; -import info.nightscout.androidaps.utils.DateUtil; -import info.nightscout.androidaps.utils.DecimalFormatter; - -/** - * Created by adrian on 17/08/17. - */ - -public class ProfileViewerDialog extends DialogFragment { - - private long time; - - @BindView(R.id.profileview_noprofile) - TextView noProfile; - @BindView(R.id.profileview_invalidprofile) - TextView invalidProfile; - @BindView(R.id.profileview_units) - TextView units; - @BindView(R.id.profileview_dia) - TextView dia; - @BindView(R.id.profileview_activeprofile) - TextView activeProfile; - @BindView(R.id.profileview_ic) - TextView ic; - @BindView(R.id.profileview_isf) - TextView isf; - @BindView(R.id.profileview_basal) - TextView basal; - @BindView(R.id.profileview_target) - TextView target; - @BindView(R.id.profileview_datedelimiter) - View dateDelimiter; - @BindView(R.id.profileview_datelayout) - LinearLayout dateLayout; - @BindView(R.id.profileview_date) - TextView dateTextView; - @BindView(R.id.profileview_reload) - Button refreshButton; - @BindView(R.id.basal_graph) - ProfileGraph basalGraph; - - private Unbinder unbinder; - - public static ProfileViewerDialog newInstance(long time) { - ProfileViewerDialog dialog = new ProfileViewerDialog(); - - Bundle args = new Bundle(); - args.putLong("time", time); - dialog.setArguments(args); - - return dialog; - } - - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - time = getArguments().getLong("time"); - } - - @Override - public void onDestroyView() { - super.onDestroyView(); - if (unbinder != null) - unbinder.unbind(); - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.profileviewer_fragment, container, false); - - unbinder = ButterKnife.bind(this, view); - - refreshButton.setVisibility(View.GONE); - dateDelimiter.setVisibility(View.VISIBLE); - dateLayout.setVisibility(View.VISIBLE); - - setContent(); - return view; - } - - @Override - public void onResume() { - ViewGroup.LayoutParams params = getDialog().getWindow().getAttributes(); - params.width = ViewGroup.LayoutParams.MATCH_PARENT; - params.height = ViewGroup.LayoutParams.MATCH_PARENT; - getDialog().getWindow().setAttributes((android.view.WindowManager.LayoutParams) params); - super.onResume(); - } - - private void setContent() { - Profile profile = null; - ProfileSwitch profileSwitch = TreatmentsPlugin.getPlugin().getProfileSwitchFromHistory(time); - if (profileSwitch != null && profileSwitch.profileJson != null) { - profile = profileSwitch.getProfileObject(); - } - if (profile != null) { - noProfile.setVisibility(View.GONE); - units.setText(profile.getUnits()); - dia.setText(DecimalFormatter.to2Decimal(profile.getDia()) + " h"); - activeProfile.setText(profileSwitch.getCustomizedName()); - dateTextView.setText(DateUtil.dateAndTimeString(profileSwitch.date)); - ic.setText(profile.getIcList()); - isf.setText(profile.getIsfList()); - basal.setText(profile.getBasalList()); - target.setText(profile.getTargetList()); - basalGraph.show(profile); - - if (profile.isValid("ProfileViewDialog")) - invalidProfile.setVisibility(View.GONE); - else - invalidProfile.setVisibility(View.VISIBLE); - } else { - noProfile.setVisibility(View.VISIBLE); - } - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/ProfileViewerDialog.kt b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/ProfileViewerDialog.kt new file mode 100644 index 0000000000..a27ebfb656 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/ProfileViewerDialog.kt @@ -0,0 +1,97 @@ +package info.nightscout.androidaps.plugins.treatments.fragments + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.DialogFragment +import info.nightscout.androidaps.MainApp +import info.nightscout.androidaps.R +import info.nightscout.androidaps.data.Profile +import info.nightscout.androidaps.interfaces.ProfileInterface +import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin +import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin +import info.nightscout.androidaps.utils.DateUtil +import kotlinx.android.synthetic.main.close.* +import kotlinx.android.synthetic.main.profileviewer_fragment.* + +class ProfileViewerDialog : DialogFragment() { + private var time: Long = 0 + + enum class Mode(val i: Int) { + RUNNING_PROFILE(1), + PUMP_PROFILE(2) + } + + private var mode: Mode = Mode.RUNNING_PROFILE; + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + // load data from bundle + (savedInstanceState ?: arguments)?.let { bundle -> + time = bundle.getLong("time", 0) + mode = Mode.values()[bundle.getInt("mode", Mode.RUNNING_PROFILE.ordinal)] + } + + return inflater.inflate(R.layout.profileviewer_fragment, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + close.setOnClickListener { dismiss() } + profileview_reload.setOnClickListener { + ConfigBuilderPlugin.getPlugin().commandQueue.readStatus("ProfileViewDialog", null) + dismiss() + } + + val profile: Profile? + val profileName: String? + val date: String? + when (mode) { + Mode.RUNNING_PROFILE -> { + profile = TreatmentsPlugin.getPlugin().getProfileSwitchFromHistory(time)?.profileObject + profileName = TreatmentsPlugin.getPlugin().getProfileSwitchFromHistory(time)?.customizedName + date = DateUtil.dateAndTimeString(TreatmentsPlugin.getPlugin().getProfileSwitchFromHistory(time)?.date + ?: 0) + profileview_reload.visibility = View.GONE + profileview_datelayout.visibility = View.VISIBLE + } + Mode.PUMP_PROFILE -> { + profile = (ConfigBuilderPlugin.getPlugin().activePump as ProfileInterface?)?.profile?.defaultProfile + profileName = (ConfigBuilderPlugin.getPlugin().activePump as ProfileInterface?)?.profileName + date = "" + profileview_reload.visibility = View.VISIBLE + profileview_datelayout.visibility = View.GONE + } + } + profileview_noprofile.visibility = View.VISIBLE + + profile?.let { + profileview_units.text = it.units + profileview_dia.text = MainApp.gs(R.string.format_hours, it.dia) + profileview_activeprofile.text = profileName + profileview_date.text = date + profileview_ic.text = it.icList + profileview_isf.text = it.isfList + profileview_basal.text = it.basalList + profileview_target.text = it.targetList + basal_graph.show(it) + + profileview_noprofile.visibility = View.GONE + profileview_invalidprofile.visibility = if (it.isValid("ProfileViewDialog")) View.GONE else View.VISIBLE + } + } + + override fun onResume() { + dialog?.window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT) + super.onResume() + } + + override fun onSaveInstanceState(bundle: Bundle) { + super.onSaveInstanceState(bundle) + bundle.putLong("time", time) + bundle.putInt("mode", mode.ordinal) + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsBolusFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsBolusFragment.java index 26a8494dd7..ee78c6748b 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsBolusFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsBolusFragment.java @@ -1,24 +1,22 @@ package info.nightscout.androidaps.plugins.treatments.fragments; -import android.app.Activity; import android.content.Context; import android.content.DialogInterface; -import android.content.Intent; import android.graphics.Paint; import android.os.Bundle; -import android.support.v4.app.FragmentManager; -import android.support.v4.content.ContextCompat; -import android.support.v7.app.AlertDialog; -import android.support.v7.widget.CardView; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.TextView; -import com.squareup.otto.Subscribe; +import androidx.appcompat.app.AlertDialog; +import androidx.cardview.widget.CardView; +import androidx.core.content.ContextCompat; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; import java.util.List; @@ -28,22 +26,27 @@ import info.nightscout.androidaps.data.Iob; import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.db.Source; import info.nightscout.androidaps.events.EventTreatmentChange; -import info.nightscout.androidaps.plugins.common.SubscriberFragment; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; import info.nightscout.androidaps.plugins.general.nsclient.NSUpload; import info.nightscout.androidaps.plugins.general.nsclient.UploadQueue; +import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart; import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventAutosensCalculationFinished; import info.nightscout.androidaps.plugins.treatments.Treatment; import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; import info.nightscout.androidaps.plugins.treatments.dialogs.WizardInfoDialog; -import info.nightscout.androidaps.services.Intents; import info.nightscout.androidaps.utils.DateUtil; import info.nightscout.androidaps.utils.DecimalFormatter; +import info.nightscout.androidaps.utils.FabricPrivacy; import info.nightscout.androidaps.utils.SP; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.CompositeDisposable; import static info.nightscout.androidaps.utils.DateUtil.now; -public class TreatmentsBolusFragment extends SubscriberFragment implements View.OnClickListener { +public class TreatmentsBolusFragment extends Fragment implements View.OnClickListener { + private CompositeDisposable disposable = new CompositeDisposable(); + RecyclerView recyclerView; LinearLayoutManager llm; @@ -162,7 +165,7 @@ public class TreatmentsBolusFragment extends SubscriberFragment implements View. } TreatmentsPlugin.getPlugin().getService().delete(treatment); } - updateGUI(); + updateGui(); } }); builder.setNegativeButton(MainApp.gs(R.string.cancel), null); @@ -213,7 +216,6 @@ public class TreatmentsBolusFragment extends SubscriberFragment implements View. context = getContext(); - updateGUI(); return view; } @@ -227,8 +229,7 @@ public class TreatmentsBolusFragment extends SubscriberFragment implements View. builder.setMessage(MainApp.gs(R.string.refresheventsfromnightscout) + "?"); builder.setPositiveButton(MainApp.gs(R.string.ok), (dialog, id) -> { TreatmentsPlugin.getPlugin().getService().resetTreatments(); - Intent restartNSClient = new Intent(Intents.ACTION_RESTART); - MainApp.instance().getApplicationContext().sendBroadcast(restartNSClient); + RxBus.INSTANCE.send(new EventNSClientRestart()); }); builder.setNegativeButton(MainApp.gs(R.string.cancel), null); builder.show(); @@ -249,7 +250,7 @@ public class TreatmentsBolusFragment extends SubscriberFragment implements View. } TreatmentsPlugin.getPlugin().getService().delete(treatment); } - updateGUI(); + updateGui(); }); builder.setNegativeButton(MainApp.gs(R.string.cancel), null); builder.show(); @@ -257,32 +258,39 @@ public class TreatmentsBolusFragment extends SubscriberFragment implements View. } } - @Subscribe - public void onStatusEvent(final EventTreatmentChange ev) { - updateGUI(); - } - - @Subscribe - public void onStatusEvent(final EventAutosensCalculationFinished ev) { - updateGUI(); + @Override + public synchronized void onResume() { + super.onResume(); + disposable.add(RxBus.INSTANCE + .toObservable(EventTreatmentChange.class) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(event -> updateGui(), FabricPrivacy::logException) + ); + disposable.add(RxBus.INSTANCE + .toObservable(EventAutosensCalculationFinished.class) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(event -> updateGui(), FabricPrivacy::logException) + ); + updateGui(); } @Override - protected void updateGUI() { - Activity activity = getActivity(); - if (activity != null) - activity.runOnUiThread(() -> { - recyclerView.swapAdapter(new RecyclerViewAdapter(TreatmentsPlugin.getPlugin().getTreatmentsFromHistory()), false); - if (TreatmentsPlugin.getPlugin().getLastCalculationTreatments() != null) { - iobTotal.setText(DecimalFormatter.to2Decimal(TreatmentsPlugin.getPlugin().getLastCalculationTreatments().iob) + " " + MainApp.gs(R.string.insulin_unit_shortname)); - activityTotal.setText(DecimalFormatter.to3Decimal(TreatmentsPlugin.getPlugin().getLastCalculationTreatments().activity) + " " + MainApp.gs(R.string.insulin_unit_shortname)); - } - if (!TreatmentsPlugin.getPlugin().getService().getTreatmentDataFromTime(now() + 1000, true).isEmpty()) { - deleteFutureTreatments.setVisibility(View.VISIBLE); - } else { - deleteFutureTreatments.setVisibility(View.GONE); - } - }); + public synchronized void onPause() { + super.onPause(); + disposable.clear(); + } + + private void updateGui() { + recyclerView.swapAdapter(new RecyclerViewAdapter(TreatmentsPlugin.getPlugin().getTreatmentsFromHistory()), false); + if (TreatmentsPlugin.getPlugin().getLastCalculationTreatments() != null) { + iobTotal.setText(DecimalFormatter.to2Decimal(TreatmentsPlugin.getPlugin().getLastCalculationTreatments().iob) + " " + MainApp.gs(R.string.insulin_unit_shortname)); + activityTotal.setText(DecimalFormatter.to3Decimal(TreatmentsPlugin.getPlugin().getLastCalculationTreatments().activity) + " " + MainApp.gs(R.string.insulin_unit_shortname)); + } + if (!TreatmentsPlugin.getPlugin().getService().getTreatmentDataFromTime(now() + 1000, true).isEmpty()) { + deleteFutureTreatments.setVisibility(View.VISIBLE); + } else { + deleteFutureTreatments.setVisibility(View.GONE); + } } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsCareportalFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsCareportalFragment.java index 2f45f67c3b..4810a1ed8c 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsCareportalFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsCareportalFragment.java @@ -1,42 +1,44 @@ package info.nightscout.androidaps.plugins.treatments.fragments; -import android.app.Activity; import android.content.Context; import android.content.DialogInterface; -import android.content.Intent; import android.graphics.Paint; import android.os.Bundle; -import android.support.v7.app.AlertDialog; -import android.support.v7.widget.CardView; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.TextView; -import com.squareup.otto.Subscribe; +import androidx.appcompat.app.AlertDialog; +import androidx.cardview.widget.CardView; +import androidx.fragment.app.Fragment; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; import java.util.List; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; -import info.nightscout.androidaps.services.Intents; import info.nightscout.androidaps.db.CareportalEvent; import info.nightscout.androidaps.events.EventCareportalEventChange; -import info.nightscout.androidaps.plugins.common.SubscriberFragment; -import info.nightscout.androidaps.plugins.general.nsclient.UploadQueue; -import info.nightscout.androidaps.utils.DateUtil; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.general.nsclient.NSUpload; +import info.nightscout.androidaps.plugins.general.nsclient.UploadQueue; +import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart; +import info.nightscout.androidaps.utils.DateUtil; +import info.nightscout.androidaps.utils.FabricPrivacy; import info.nightscout.androidaps.utils.SP; import info.nightscout.androidaps.utils.Translator; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.CompositeDisposable; /** * Created by mike on 13/01/17. */ -public class TreatmentsCareportalFragment extends SubscriberFragment implements View.OnClickListener { +public class TreatmentsCareportalFragment extends Fragment implements View.OnClickListener { + private CompositeDisposable disposable = new CompositeDisposable(); RecyclerView recyclerView; LinearLayoutManager llm; @@ -148,10 +150,26 @@ public class TreatmentsCareportalFragment extends SubscriberFragment implements if (nsUploadOnly) refreshFromNS.setVisibility(View.GONE); - updateGUI(); return view; } + @Override + public synchronized void onResume() { + super.onResume(); + disposable.add(RxBus.INSTANCE + .toObservable(EventCareportalEventChange.class) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(event -> updateGui(), FabricPrivacy::logException) + ); + updateGui(); + } + + @Override + public synchronized void onPause() { + super.onPause(); + disposable.clear(); + } + @Override public void onClick(View view) { switch (view.getId()) { @@ -162,8 +180,7 @@ public class TreatmentsCareportalFragment extends SubscriberFragment implements builder.setPositiveButton(MainApp.gs(R.string.ok), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { MainApp.getDbHelper().resetCareportalEvents(); - Intent restartNSClient = new Intent(Intents.ACTION_RESTART); - MainApp.instance().getApplicationContext().sendBroadcast(restartNSClient); + RxBus.INSTANCE.send(new EventNSClientRestart()); } }); builder.setNegativeButton(MainApp.gs(R.string.cancel), null); @@ -183,21 +200,8 @@ public class TreatmentsCareportalFragment extends SubscriberFragment implements } - @Subscribe - public void onStatusEvent(final EventCareportalEventChange ev) { - updateGUI(); - } - - @Override - protected void updateGUI() { - Activity activity = getActivity(); - if (activity != null) - activity.runOnUiThread(new Runnable() { - @Override - public void run() { - recyclerView.swapAdapter(new RecyclerViewAdapter(MainApp.getDbHelper().getCareportalEvents(false)), false); - } - }); + private void updateGui() { + recyclerView.swapAdapter(new RecyclerViewAdapter(MainApp.getDbHelper().getCareportalEvents(false)), false); } private void removeAndroidAPSStatedEvents() { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsExtendedBolusesFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsExtendedBolusesFragment.java index 766190e378..5ce7b57564 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsExtendedBolusesFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsExtendedBolusesFragment.java @@ -1,21 +1,20 @@ package info.nightscout.androidaps.plugins.treatments.fragments; -import android.app.Activity; import android.content.Context; import android.content.DialogInterface; import android.graphics.Paint; import android.os.Bundle; -import android.support.v4.content.ContextCompat; -import android.support.v7.app.AlertDialog; -import android.support.v7.widget.CardView; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; -import com.squareup.otto.Subscribe; +import androidx.appcompat.app.AlertDialog; +import androidx.cardview.widget.CardView; +import androidx.core.content.ContextCompat; +import androidx.fragment.app.Fragment; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; @@ -24,16 +23,21 @@ import info.nightscout.androidaps.data.IobTotal; import info.nightscout.androidaps.db.ExtendedBolus; import info.nightscout.androidaps.db.Source; import info.nightscout.androidaps.events.EventExtendedBolusChange; -import info.nightscout.androidaps.plugins.common.SubscriberFragment; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.general.nsclient.NSUpload; import info.nightscout.androidaps.plugins.general.nsclient.UploadQueue; import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventAutosensCalculationFinished; import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; import info.nightscout.androidaps.utils.DateUtil; import info.nightscout.androidaps.utils.DecimalFormatter; +import info.nightscout.androidaps.utils.FabricPrivacy; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.CompositeDisposable; -public class TreatmentsExtendedBolusesFragment extends SubscriberFragment { +public class TreatmentsExtendedBolusesFragment extends Fragment { + private CompositeDisposable disposable = new CompositeDisposable(); + RecyclerView recyclerView; LinearLayoutManager llm; @@ -173,30 +177,33 @@ public class TreatmentsExtendedBolusesFragment extends SubscriberFragment { context = getContext(); - updateGUI(); return view; } - @Subscribe - public void onStatusEvent(final EventExtendedBolusChange ev) { - updateGUI(); - } - - @Subscribe - public void onStatusEvent(final EventAutosensCalculationFinished ev) { - updateGUI(); + @Override + public synchronized void onResume() { + super.onResume(); + disposable.add(RxBus.INSTANCE + .toObservable(EventExtendedBolusChange.class) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(event -> updateGui(), FabricPrivacy::logException) + ); + disposable.add(RxBus.INSTANCE + .toObservable(EventAutosensCalculationFinished.class) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(event -> updateGui(), FabricPrivacy::logException) + ); + updateGui(); } @Override - protected void updateGUI() { - Activity activity = getActivity(); - if (activity != null && recyclerView != null) - activity.runOnUiThread(new Runnable() { - @Override - public void run() { - recyclerView.swapAdapter(new RecyclerViewAdapter(TreatmentsPlugin.getPlugin().getExtendedBolusesFromHistory()), false); - } - }); + public synchronized void onPause() { + super.onPause(); + disposable.clear(); + } + + private void updateGui() { + recyclerView.swapAdapter(new RecyclerViewAdapter(TreatmentsPlugin.getPlugin().getExtendedBolusesFromHistory()), false); } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsProfileSwitchFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsProfileSwitchFragment.java index c59d5e1836..2d06b2824f 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsProfileSwitchFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsProfileSwitchFragment.java @@ -1,24 +1,22 @@ package info.nightscout.androidaps.plugins.treatments.fragments; -import android.app.Activity; import android.content.Context; import android.content.DialogInterface; -import android.content.Intent; import android.graphics.Paint; import android.os.Bundle; -import android.support.v4.app.FragmentManager; -import android.support.v4.content.ContextCompat; -import android.support.v7.app.AlertDialog; -import android.support.v7.widget.CardView; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.TextView; -import com.squareup.otto.Subscribe; +import androidx.appcompat.app.AlertDialog; +import androidx.cardview.widget.CardView; +import androidx.core.content.ContextCompat; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -27,26 +25,30 @@ import java.util.List; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; -import info.nightscout.androidaps.events.EventProfileNeedsUpdate; -import info.nightscout.androidaps.logging.L; -import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; -import info.nightscout.androidaps.services.Intents; import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.db.ProfileSwitch; import info.nightscout.androidaps.db.Source; -import info.nightscout.androidaps.plugins.common.SubscriberFragment; +import info.nightscout.androidaps.events.EventProfileNeedsUpdate; +import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.bus.RxBus; +import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; +import info.nightscout.androidaps.plugins.general.nsclient.NSUpload; import info.nightscout.androidaps.plugins.general.nsclient.UploadQueue; +import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart; import info.nightscout.androidaps.utils.DateUtil; import info.nightscout.androidaps.utils.DecimalFormatter; -import info.nightscout.androidaps.plugins.general.nsclient.NSUpload; +import info.nightscout.androidaps.utils.FabricPrivacy; import info.nightscout.androidaps.utils.SP; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.CompositeDisposable; /** * Created by mike on 13/01/17. */ -public class TreatmentsProfileSwitchFragment extends SubscriberFragment implements View.OnClickListener { +public class TreatmentsProfileSwitchFragment extends Fragment implements View.OnClickListener { private Logger log = LoggerFactory.getLogger(L.UI); + private CompositeDisposable disposable = new CompositeDisposable(); RecyclerView recyclerView; LinearLayoutManager llm; @@ -159,10 +161,14 @@ public class TreatmentsProfileSwitchFragment extends SubscriberFragment implemen break; case R.id.profileswitch_date: case R.id.profileswitch_name: - long time = ((ProfileSwitch) v.getTag()).date; - ProfileViewerDialog pvd = ProfileViewerDialog.newInstance(time); + Bundle args = new Bundle(); + args.putLong("time", ((ProfileSwitch) v.getTag()).date); + args.putLong("mode", ProfileViewerDialog.Mode.RUNNING_PROFILE.ordinal()); + ProfileViewerDialog pvd = new ProfileViewerDialog(); + pvd.setArguments(args); FragmentManager manager = getFragmentManager(); - pvd.show(manager, "ProfileViewDialog"); + if (manager != null) + pvd.show(manager, "ProfileViewDialog"); break; } } @@ -191,10 +197,26 @@ public class TreatmentsProfileSwitchFragment extends SubscriberFragment implemen if (nsUploadOnly) refreshFromNS.setVisibility(View.GONE); - updateGUI(); return view; } + @Override + public synchronized void onResume() { + super.onResume(); + disposable.add(RxBus.INSTANCE + .toObservable(EventProfileNeedsUpdate.class) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(event -> updateGUI(), FabricPrivacy::logException) + ); + updateGUI(); + } + + @Override + public synchronized void onPause() { + super.onPause(); + disposable.clear(); + } + @Override public void onClick(View view) { switch (view.getId()) { @@ -205,8 +227,7 @@ public class TreatmentsProfileSwitchFragment extends SubscriberFragment implemen builder.setPositiveButton(MainApp.gs(R.string.ok), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { MainApp.getDbHelper().resetProfileSwitch(); - Intent restartNSClient = new Intent(Intents.ACTION_RESTART); - MainApp.instance().getApplicationContext().sendBroadcast(restartNSClient); + RxBus.INSTANCE.send(new EventNSClientRestart()); } }); builder.setNegativeButton(MainApp.gs(R.string.cancel), null); @@ -215,20 +236,7 @@ public class TreatmentsProfileSwitchFragment extends SubscriberFragment implemen } } - @Subscribe - public void onStatusEvent(final EventProfileNeedsUpdate ev) { - updateGUI(); - } - - @Override protected void updateGUI() { - Activity activity = getActivity(); - if (activity != null) - activity.runOnUiThread(new Runnable() { - @Override - public void run() { - recyclerView.swapAdapter(new RecyclerViewAdapter(MainApp.getDbHelper().getProfileSwitchData(false)), false); - } - }); + recyclerView.swapAdapter(new RecyclerViewAdapter(MainApp.getDbHelper().getProfileSwitchData(false)), false); } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsTempTargetFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsTempTargetFragment.java index eda62a8318..ccd1b327c9 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsTempTargetFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsTempTargetFragment.java @@ -3,43 +3,46 @@ package info.nightscout.androidaps.plugins.treatments.fragments; import android.app.Activity; import android.content.Context; import android.content.DialogInterface; -import android.content.Intent; import android.graphics.Paint; import android.os.Bundle; -import android.support.v4.content.ContextCompat; -import android.support.v7.app.AlertDialog; -import android.support.v7.widget.CardView; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.TextView; -import com.squareup.otto.Subscribe; +import androidx.appcompat.app.AlertDialog; +import androidx.cardview.widget.CardView; +import androidx.core.content.ContextCompat; +import androidx.fragment.app.Fragment; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; -import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; -import info.nightscout.androidaps.services.Intents; import info.nightscout.androidaps.data.Intervals; import info.nightscout.androidaps.db.Source; import info.nightscout.androidaps.db.TempTarget; import info.nightscout.androidaps.events.EventTempTargetChange; -import info.nightscout.androidaps.plugins.common.SubscriberFragment; +import info.nightscout.androidaps.plugins.bus.RxBus; +import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; +import info.nightscout.androidaps.plugins.general.nsclient.NSUpload; import info.nightscout.androidaps.plugins.general.nsclient.UploadQueue; +import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart; import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; import info.nightscout.androidaps.utils.DateUtil; import info.nightscout.androidaps.utils.DecimalFormatter; -import info.nightscout.androidaps.plugins.general.nsclient.NSUpload; +import info.nightscout.androidaps.utils.FabricPrivacy; import info.nightscout.androidaps.utils.SP; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.CompositeDisposable; /** * Created by mike on 13/01/17. */ -public class TreatmentsTempTargetFragment extends SubscriberFragment implements View.OnClickListener { +public class TreatmentsTempTargetFragment extends Fragment implements View.OnClickListener { + private CompositeDisposable disposable = new CompositeDisposable(); RecyclerView recyclerView; LinearLayoutManager llm; @@ -184,10 +187,26 @@ public class TreatmentsTempTargetFragment extends SubscriberFragment implements if (nsUploadOnly) refreshFromNS.setVisibility(View.GONE); - updateGUI(); return view; } + @Override + public synchronized void onResume() { + super.onResume(); + disposable.add(RxBus.INSTANCE + .toObservable(EventTempTargetChange.class) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(event -> updateGui(), FabricPrivacy::logException) + ); + updateGui(); + } + + @Override + public synchronized void onPause() { + super.onPause(); + disposable.clear(); + } + @Override public void onClick(View view) { switch (view.getId()) { @@ -198,8 +217,7 @@ public class TreatmentsTempTargetFragment extends SubscriberFragment implements builder.setPositiveButton(MainApp.gs(R.string.ok), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { MainApp.getDbHelper().resetTempTargets(); - Intent restartNSClient = new Intent(Intents.ACTION_RESTART); - MainApp.instance().getApplicationContext().sendBroadcast(restartNSClient); + RxBus.INSTANCE.send(new EventNSClientRestart()); } }); builder.setNegativeButton(MainApp.gs(R.string.cancel), null); @@ -209,20 +227,7 @@ public class TreatmentsTempTargetFragment extends SubscriberFragment implements } - @Subscribe - public void onStatusEvent(final EventTempTargetChange ev) { - updateGUI(); - } - - @Override - protected void updateGUI() { - Activity activity = getActivity(); - if (activity != null) - activity.runOnUiThread(new Runnable() { - @Override - public void run() { - recyclerView.swapAdapter(new RecyclerViewAdapter(TreatmentsPlugin.getPlugin().getTempTargetsFromHistory()), false); - } - }); + private void updateGui() { + recyclerView.swapAdapter(new RecyclerViewAdapter(TreatmentsPlugin.getPlugin().getTempTargetsFromHistory()), false); } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsTemporaryBasalsFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsTemporaryBasalsFragment.java index 7b935d354e..45f930410e 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsTemporaryBasalsFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsTemporaryBasalsFragment.java @@ -1,21 +1,20 @@ package info.nightscout.androidaps.plugins.treatments.fragments; -import android.app.Activity; import android.content.Context; import android.graphics.Paint; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.v4.content.ContextCompat; -import android.support.v7.app.AlertDialog; -import android.support.v7.widget.CardView; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; -import com.squareup.otto.Subscribe; +import androidx.annotation.NonNull; +import androidx.appcompat.app.AlertDialog; +import androidx.cardview.widget.CardView; +import androidx.core.content.ContextCompat; +import androidx.fragment.app.Fragment; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; @@ -25,7 +24,7 @@ import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.db.Source; import info.nightscout.androidaps.db.TemporaryBasal; import info.nightscout.androidaps.events.EventTempBasalChange; -import info.nightscout.androidaps.plugins.common.SubscriberFragment; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; import info.nightscout.androidaps.plugins.general.nsclient.NSUpload; import info.nightscout.androidaps.plugins.general.nsclient.UploadQueue; @@ -33,9 +32,14 @@ import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventAutos import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; import info.nightscout.androidaps.utils.DateUtil; import info.nightscout.androidaps.utils.DecimalFormatter; +import info.nightscout.androidaps.utils.FabricPrivacy; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.CompositeDisposable; -public class TreatmentsTemporaryBasalsFragment extends SubscriberFragment { +public class TreatmentsTemporaryBasalsFragment extends Fragment { + private CompositeDisposable disposable = new CompositeDisposable(); + RecyclerView recyclerView; LinearLayoutManager llm; @@ -199,30 +203,36 @@ public class TreatmentsTemporaryBasalsFragment extends SubscriberFragment { context = getContext(); - updateGUI(); return view; } - @Subscribe - public void onStatusEvent(final EventTempBasalChange ignored) { - updateGUI(); - } - - @Subscribe - public void onStatusEvent(final EventAutosensCalculationFinished ignored) { - updateGUI(); + @Override + public synchronized void onResume() { + super.onResume(); + disposable.add(RxBus.INSTANCE + .toObservable(EventTempBasalChange.class) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(event -> updateGui(), FabricPrivacy::logException) + ); + disposable.add(RxBus.INSTANCE + .toObservable(EventAutosensCalculationFinished.class) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(event -> updateGui(), FabricPrivacy::logException) + ); + updateGui(); } @Override - protected void updateGUI() { - Activity activity = getActivity(); - if (activity != null) - activity.runOnUiThread(() -> { - recyclerView.swapAdapter(new RecyclerViewAdapter(TreatmentsPlugin.getPlugin().getTemporaryBasalsFromHistory()), false); - IobTotal tempBasalsCalculation = TreatmentsPlugin.getPlugin().getLastCalculationTempBasals(); - if (tempBasalsCalculation != null) - tempBasalTotalView.setText(DecimalFormatter.to2Decimal(tempBasalsCalculation.basaliob, " U")); - }); + public synchronized void onPause() { + super.onPause(); + disposable.clear(); + } + + private void updateGui() { + recyclerView.swapAdapter(new RecyclerViewAdapter(TreatmentsPlugin.getPlugin().getTemporaryBasalsFromHistory()), false); + IobTotal tempBasalsCalculation = TreatmentsPlugin.getPlugin().getLastCalculationTempBasals(); + if (tempBasalsCalculation != null) + tempBasalTotalView.setText(DecimalFormatter.to2Decimal(tempBasalsCalculation.basaliob, " U")); } } diff --git a/app/src/main/java/info/nightscout/androidaps/queue/CommandQueue.java b/app/src/main/java/info/nightscout/androidaps/queue/CommandQueue.java index e4fbacc43c..f24b02171f 100644 --- a/app/src/main/java/info/nightscout/androidaps/queue/CommandQueue.java +++ b/app/src/main/java/info/nightscout/androidaps/queue/CommandQueue.java @@ -3,10 +3,11 @@ package info.nightscout.androidaps.queue; import android.content.Context; import android.content.Intent; import android.os.SystemClock; -import android.support.v7.app.AppCompatActivity; import android.text.Html; import android.text.Spanned; +import androidx.appcompat.app.AppCompatActivity; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -21,11 +22,12 @@ import info.nightscout.androidaps.events.EventBolusRequested; import info.nightscout.androidaps.interfaces.Constraint; import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; import info.nightscout.androidaps.plugins.general.overview.dialogs.BolusProgressDialog; import info.nightscout.androidaps.plugins.general.overview.dialogs.BolusProgressHelperActivity; -import info.nightscout.androidaps.plugins.general.overview.events.EventDismissBolusprogressIfRunning; +import info.nightscout.androidaps.plugins.general.overview.events.EventDismissBolusProgressIfRunning; import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification; import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification; import info.nightscout.androidaps.plugins.general.overview.notifications.Notification; @@ -99,16 +101,20 @@ public class CommandQueue { } private synchronized void removeAll(Command.CommandType type) { - for (int i = queue.size() - 1; i >= 0; i--) { - if (queue.get(i).commandType == type) { - queue.remove(i); + synchronized (queue) { + for (int i = queue.size() - 1; i >= 0; i--) { + if (queue.get(i).commandType == type) { + queue.remove(i); + } } } } private synchronized boolean isLastScheduled(Command.CommandType type) { - if (queue.size() > 0 && queue.get(queue.size() - 1).commandType == type) { - return true; + synchronized (queue) { + if (queue.size() > 0 && queue.get(queue.size() - 1).commandType == type) { + return true; + } } return false; } @@ -117,37 +123,44 @@ public class CommandQueue { // inject as a first command if (L.isEnabled(L.PUMPQUEUE)) log.debug("Adding as first: " + command.getClass().getSimpleName() + " - " + command.status()); - queue.addFirst(command); + synchronized (queue) { + queue.addFirst(command); + } } private synchronized void add(Command command) { if (L.isEnabled(L.PUMPQUEUE)) log.debug("Adding: " + command.getClass().getSimpleName() + " - " + command.status()); - queue.add(command); + synchronized (queue) { + queue.add(command); + } } synchronized void pickup() { - performing = queue.poll(); + synchronized (queue) { + performing = queue.poll(); + } } synchronized void clear() { performing = null; - for (int i = 0; i < queue.size(); i++) { - queue.get(i).cancel(); + synchronized (queue) { + for (int i = 0; i < queue.size(); i++) { + queue.get(i).cancel(); + } + queue.clear(); } - - queue.clear(); } public int size() { return queue.size(); } - public Command performing() { + Command performing() { return performing; } - public void resetPerforming() { + void resetPerforming() { performing = null; } @@ -179,9 +192,11 @@ public class CommandQueue { public synchronized boolean bolusInQueue() { if (isRunning(Command.CommandType.BOLUS)) return true; - for (int i = 0; i < queue.size(); i++) { - if (queue.get(i).commandType == Command.CommandType.BOLUS) { - return true; + synchronized (queue) { + for (int i = 0; i < queue.size(); i++) { + if (queue.get(i).commandType == Command.CommandType.BOLUS) { + return true; + } } } return false; @@ -192,7 +207,7 @@ public class CommandQueue { Command.CommandType type = detailedBolusInfo.isSMB ? Command.CommandType.SMB_BOLUS : Command.CommandType.BOLUS; if (type == Command.CommandType.SMB_BOLUS) { - if (isRunning(Command.CommandType.BOLUS) || bolusInQueue()) { + if (isRunning(Command.CommandType.BOLUS) || isRunning(Command.CommandType.SMB_BOLUS) || bolusInQueue()) { if (L.isEnabled(L.PUMPQUEUE)) log.debug("Rejecting SMB since a bolus is queue/running"); return false; @@ -202,6 +217,7 @@ public class CommandQueue { log.debug("Rejecting bolus, another bolus was issued since request time"); return false; } + removeAll(Command.CommandType.SMB_BOLUS); } @@ -233,7 +249,7 @@ public class CommandQueue { // not when the Bolus command is starting. The command closes the dialog upon completion). showBolusProgressDialog(detailedBolusInfo.insulin, detailedBolusInfo.context); // Notify Wear about upcoming bolus - MainApp.bus().post(new EventBolusRequested(detailedBolusInfo.insulin)); + RxBus.INSTANCE.send(new EventBolusRequested(detailedBolusInfo.insulin)); } } @@ -259,7 +275,7 @@ public class CommandQueue { public synchronized void cancelAllBoluses() { if (!isRunning(Command.CommandType.BOLUS)) { - MainApp.bus().post(new EventDismissBolusprogressIfRunning(new PumpEnactResult().success(true).enacted(false))); + RxBus.INSTANCE.send(new EventDismissBolusProgressIfRunning(new PumpEnactResult().success(true).enacted(false))); } removeAll(Command.CommandType.BOLUS); removeAll(Command.CommandType.SMB_BOLUS); @@ -379,27 +395,27 @@ public class CommandQueue { if (!MainApp.isEngineeringModeOrRelease()) { Notification notification = new Notification(Notification.NOT_ENG_MODE_OR_RELEASE, MainApp.gs(R.string.not_eng_mode_or_release), Notification.URGENT); - MainApp.bus().post(new EventNewNotification(notification)); + RxBus.INSTANCE.send(new EventNewNotification(notification)); if (callback != null) callback.result(new PumpEnactResult().success(false).enacted(false).comment(MainApp.gs(R.string.not_eng_mode_or_release))).run(); return false; } // Compare with pump limits - Profile.BasalValue[] basalValues = profile.getBasalValues(); + Profile.ProfileValue[] basalValues = profile.getBasalValues(); PumpInterface pump = ConfigBuilderPlugin.getPlugin().getActivePump(); - for (Profile.BasalValue basalValue : basalValues) { + for (Profile.ProfileValue basalValue : basalValues) { if (basalValue.value < pump.getPumpDescription().basalMinimumRate) { Notification notification = new Notification(Notification.BASAL_VALUE_BELOW_MINIMUM, MainApp.gs(R.string.basalvaluebelowminimum), Notification.URGENT); - MainApp.bus().post(new EventNewNotification(notification)); + RxBus.INSTANCE.send(new EventNewNotification(notification)); if (callback != null) callback.result(new PumpEnactResult().success(false).enacted(false).comment(MainApp.gs(R.string.basalvaluebelowminimum))).run(); return false; } } - MainApp.bus().post(new EventDismissNotification(Notification.BASAL_VALUE_BELOW_MINIMUM)); + RxBus.INSTANCE.send(new EventDismissNotification(Notification.BASAL_VALUE_BELOW_MINIMUM)); // remove all unfinished removeAll(Command.CommandType.BASALPROFILE); @@ -433,6 +449,21 @@ public class CommandQueue { return true; } + + public synchronized boolean statusInQueue() { + if (isRunning(Command.CommandType.READSTATUS)) + return true; + synchronized (queue) { + for (int i = 0; i < queue.size(); i++) { + if (queue.get(i).commandType == Command.CommandType.READSTATUS) { + return true; + } + } + } + return false; + } + + // returns true if command is queued public boolean loadHistory(byte type, Callback callback) { if (isRunning(Command.CommandType.LOADHISTORY)) { @@ -512,15 +543,18 @@ public class CommandQueue { public Spanned spannedStatus() { String s = ""; int line = 0; - if (performing != null) { - s += "" + performing.status() + ""; + Command perf = performing; + if (perf != null) { + s += "" + perf.status() + ""; line++; } - for (int i = 0; i < queue.size(); i++) { - if (line != 0) - s += "
"; - s += queue.get(i).status(); - line++; + synchronized (queue) { + for (int i = 0; i < queue.size(); i++) { + if (line != 0) + s += "
"; + s += queue.get(i).status(); + line++; + } } return Html.fromHtml(s); } diff --git a/app/src/main/java/info/nightscout/androidaps/queue/QueueThread.java b/app/src/main/java/info/nightscout/androidaps/queue/QueueThread.java index 0962952221..85823c2c30 100644 --- a/app/src/main/java/info/nightscout/androidaps/queue/QueueThread.java +++ b/app/src/main/java/info/nightscout/androidaps/queue/QueueThread.java @@ -14,8 +14,9 @@ import info.nightscout.androidaps.R; import info.nightscout.androidaps.events.EventPumpStatusChanged; import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; -import info.nightscout.androidaps.plugins.general.overview.events.EventDismissBolusprogressIfRunning; +import info.nightscout.androidaps.plugins.general.overview.events.EventDismissBolusProgressIfRunning; import info.nightscout.androidaps.queue.events.EventQueueChanged; import info.nightscout.androidaps.utils.SP; import info.nightscout.androidaps.utils.T; @@ -50,7 +51,7 @@ public class QueueThread extends Thread { public final void run() { if (mWakeLock != null) mWakeLock.acquire(T.mins(10).msecs()); - MainApp.bus().post(new EventQueueChanged()); + RxBus.INSTANCE.send(new EventQueueChanged()); long lastCommandTime; long connectionStartTime = lastCommandTime = System.currentTimeMillis(); @@ -60,15 +61,15 @@ public class QueueThread extends Thread { if (pump == null) { if (L.isEnabled(L.PUMPQUEUE)) log.debug("pump == null"); - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.pumpNotInitialized))); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.pumpNotInitialized))); SystemClock.sleep(1000); continue; } long secondsElapsed = (System.currentTimeMillis() - connectionStartTime) / 1000; if (!pump.isConnected() && secondsElapsed > Constants.PUMP_MAX_CONNECTION_TIME_IN_SECONDS) { - MainApp.bus().post(new EventDismissBolusprogressIfRunning(null)); - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.connectiontimedout))); + RxBus.INSTANCE.send(new EventDismissBolusProgressIfRunning(null)); + RxBus.INSTANCE.send(new EventPumpStatusChanged(MainApp.gs(R.string.connectiontimedout))); if (L.isEnabled(L.PUMPQUEUE)) log.debug("timed out"); pump.stopConnecting(); @@ -93,16 +94,16 @@ public class QueueThread extends Thread { SystemClock.sleep(1000); //start over again once after watchdog barked //Notification notification = new Notification(Notification.OLD_NSCLIENT, "Watchdog", Notification.URGENT); - //MainApp.bus().post(new EventNewNotification(notification)); + //RxBus.INSTANCE.send(new EventNewNotification(notification)); connectionStartTime = lastCommandTime = System.currentTimeMillis(); pump.connect("watchdog"); } else { queue.clear(); if (L.isEnabled(L.PUMPQUEUE)) log.debug("no connection possible"); - MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.DISCONNECTING)); + RxBus.INSTANCE.send(new EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTING)); pump.disconnect("Queue empty"); - MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.DISCONNECTED)); + RxBus.INSTANCE.send(new EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTED)); return; } } @@ -110,7 +111,7 @@ public class QueueThread extends Thread { if (pump.isHandshakeInProgress()) { if (L.isEnabled(L.PUMPQUEUE)) log.debug("handshaking " + secondsElapsed); - MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.HANDSHAKING, (int) secondsElapsed)); + RxBus.INSTANCE.send(new EventPumpStatusChanged(EventPumpStatusChanged.Status.HANDSHAKING, (int) secondsElapsed)); SystemClock.sleep(100); continue; } @@ -118,7 +119,7 @@ public class QueueThread extends Thread { if (pump.isConnecting()) { if (L.isEnabled(L.PUMPQUEUE)) log.debug("connecting " + secondsElapsed); - MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.CONNECTING, (int) secondsElapsed)); + RxBus.INSTANCE.send(new EventPumpStatusChanged(EventPumpStatusChanged.Status.CONNECTING, (int) secondsElapsed)); SystemClock.sleep(1000); continue; } @@ -126,7 +127,7 @@ public class QueueThread extends Thread { if (!pump.isConnected()) { if (L.isEnabled(L.PUMPQUEUE)) log.debug("connect"); - MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.CONNECTING, (int) secondsElapsed)); + RxBus.INSTANCE.send(new EventPumpStatusChanged(EventPumpStatusChanged.Status.CONNECTING, (int) secondsElapsed)); pump.connect("Connection needed"); SystemClock.sleep(1000); continue; @@ -141,15 +142,17 @@ public class QueueThread extends Thread { // Pickup 1st command and set performing variable if (queue.size() > 0) { queue.pickup(); - if (L.isEnabled(L.PUMPQUEUE)) - log.debug("performing " + queue.performing().status()); - MainApp.bus().post(new EventQueueChanged()); - queue.performing().execute(); - queue.resetPerforming(); - MainApp.bus().post(new EventQueueChanged()); - lastCommandTime = System.currentTimeMillis(); - SystemClock.sleep(100); - continue; + if (queue.performing() != null) { + if (L.isEnabled(L.PUMPQUEUE)) + log.debug("performing " + queue.performing().status()); + RxBus.INSTANCE.send(new EventQueueChanged()); + queue.performing().execute(); + queue.resetPerforming(); + RxBus.INSTANCE.send(new EventQueueChanged()); + lastCommandTime = System.currentTimeMillis(); + SystemClock.sleep(100); + continue; + } } } @@ -159,9 +162,9 @@ public class QueueThread extends Thread { waitingForDisconnect = true; if (L.isEnabled(L.PUMPQUEUE)) log.debug("queue empty. disconnect"); - MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.DISCONNECTING)); + RxBus.INSTANCE.send(new EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTING)); pump.disconnect("Queue empty"); - MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.DISCONNECTED)); + RxBus.INSTANCE.send(new EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTED)); if (L.isEnabled(L.PUMPQUEUE)) log.debug("disconnected"); return; @@ -173,7 +176,7 @@ public class QueueThread extends Thread { } } } finally { - if (mWakeLock != null) + if (mWakeLock != null && mWakeLock.isHeld()) mWakeLock.release(); if (L.isEnabled(L.PUMPQUEUE)) log.debug("thread end"); diff --git a/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandBolus.java b/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandBolus.java index 4dc132e128..6348ea31b9 100644 --- a/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandBolus.java +++ b/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandBolus.java @@ -3,13 +3,13 @@ package info.nightscout.androidaps.queue.commands; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.data.DetailedBolusInfo; import info.nightscout.androidaps.data.PumpEnactResult; import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.general.overview.dialogs.BolusProgressDialog; -import info.nightscout.androidaps.plugins.general.overview.events.EventDismissBolusprogressIfRunning; +import info.nightscout.androidaps.plugins.general.overview.events.EventDismissBolusProgressIfRunning; import info.nightscout.androidaps.queue.Callback; import info.nightscout.androidaps.utils.DecimalFormatter; @@ -33,7 +33,7 @@ public class CommandBolus extends Command { PumpEnactResult r = ConfigBuilderPlugin.getPlugin().getActivePump().deliverTreatment(detailedBolusInfo); BolusProgressDialog.bolusEnded = true; - MainApp.bus().post(new EventDismissBolusprogressIfRunning(r)); + RxBus.INSTANCE.send(new EventDismissBolusProgressIfRunning(r)); if (L.isEnabled(L.PUMPQUEUE)) log.debug("Result success: " + r.success + " enacted: " + r.enacted); diff --git a/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandSetProfile.java b/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandSetProfile.java index 6b8a610fa6..62e14285eb 100644 --- a/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandSetProfile.java +++ b/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandSetProfile.java @@ -50,8 +50,8 @@ public class CommandSetProfile extends Command { // Send SMS notification if ProfileSwitch is comming from NS ProfileSwitch profileSwitch = TreatmentsPlugin.getPlugin().getProfileSwitchFromHistory(System.currentTimeMillis()); if (profileSwitch != null && r.enacted && profileSwitch.source == Source.NIGHTSCOUT) { - SmsCommunicatorPlugin smsCommunicatorPlugin = MainApp.getSpecificPlugin(SmsCommunicatorPlugin.class); - if (smsCommunicatorPlugin != null && smsCommunicatorPlugin.isEnabled(PluginType.GENERAL)) { + SmsCommunicatorPlugin smsCommunicatorPlugin = SmsCommunicatorPlugin.getPlugin(); + if (smsCommunicatorPlugin.isEnabled(PluginType.GENERAL)) { smsCommunicatorPlugin.sendNotificationToAllNumbers(MainApp.gs(R.string.profile_set_ok)); } } diff --git a/app/src/main/java/info/nightscout/androidaps/queue/events/EventQueueChanged.java b/app/src/main/java/info/nightscout/androidaps/queue/events/EventQueueChanged.java deleted file mode 100644 index b0a53afd13..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/queue/events/EventQueueChanged.java +++ /dev/null @@ -1,8 +0,0 @@ -package info.nightscout.androidaps.queue.events; - -/** - * Created by mike on 11.11.2017. - */ - -public class EventQueueChanged { -} diff --git a/app/src/main/java/info/nightscout/androidaps/queue/events/EventQueueChanged.kt b/app/src/main/java/info/nightscout/androidaps/queue/events/EventQueueChanged.kt new file mode 100644 index 0000000000..297d443976 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/queue/events/EventQueueChanged.kt @@ -0,0 +1,5 @@ +package info.nightscout.androidaps.queue.events + +import info.nightscout.androidaps.events.Event + +class EventQueueChanged : Event() diff --git a/app/src/main/java/info/nightscout/androidaps/receivers/ChargingStateReceiver.java b/app/src/main/java/info/nightscout/androidaps/receivers/ChargingStateReceiver.java index b10c2e99e5..9af15e9ddc 100644 --- a/app/src/main/java/info/nightscout/androidaps/receivers/ChargingStateReceiver.java +++ b/app/src/main/java/info/nightscout/androidaps/receivers/ChargingStateReceiver.java @@ -5,17 +5,20 @@ import android.content.Context; import android.content.Intent; import android.os.BatteryManager; -import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.events.EventChargingState; +import info.nightscout.androidaps.plugins.bus.RxBus; public class ChargingStateReceiver extends BroadcastReceiver { + private static EventChargingState lastEvent; + @Override public void onReceive(Context context, Intent intent) { EventChargingState event = grabChargingState(context); if (event != null) - MainApp.bus().post(event); + RxBus.INSTANCE.send(event); + lastEvent = event; } public EventChargingState grabChargingState(Context context) { @@ -32,4 +35,11 @@ public class ChargingStateReceiver extends BroadcastReceiver { return event; } + static public boolean isCharging() { + return lastEvent != null && lastEvent.isCharging(); + } + + static public EventChargingState getLastEvent() { + return lastEvent; + } } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/receivers/DataReceiver.java b/app/src/main/java/info/nightscout/androidaps/receivers/DataReceiver.java index 087cd3a5f5..6c2e7c6920 100644 --- a/app/src/main/java/info/nightscout/androidaps/receivers/DataReceiver.java +++ b/app/src/main/java/info/nightscout/androidaps/receivers/DataReceiver.java @@ -2,7 +2,7 @@ package info.nightscout.androidaps.receivers; import android.content.Context; import android.content.Intent; -import android.support.v4.content.WakefulBroadcastReceiver; +import androidx.legacy.content.WakefulBroadcastReceiver; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/app/src/main/java/info/nightscout/androidaps/receivers/KeepAliveReceiver.java b/app/src/main/java/info/nightscout/androidaps/receivers/KeepAliveReceiver.java index 7a9aa31c46..4c8a3f660e 100644 --- a/app/src/main/java/info/nightscout/androidaps/receivers/KeepAliveReceiver.java +++ b/app/src/main/java/info/nightscout/androidaps/receivers/KeepAliveReceiver.java @@ -16,6 +16,7 @@ import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.events.EventProfileNeedsUpdate; import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; import info.nightscout.androidaps.queue.commands.Command; @@ -73,7 +74,7 @@ public class KeepAliveReceiver extends BroadcastReceiver { } if (!pump.isThisProfileSet(profile) && !ConfigBuilderPlugin.getPlugin().getCommandQueue().isRunning(Command.CommandType.BASALPROFILE)) { - MainApp.bus().post(new EventProfileNeedsUpdate()); + RxBus.INSTANCE.send(new EventProfileNeedsUpdate()); } else if (isStatusOutdated && !pump.isBusy()) { lastReadStatus = System.currentTimeMillis(); ConfigBuilderPlugin.getPlugin().getCommandQueue().readStatus("KeepAlive. Status outdated.", null); diff --git a/app/src/main/java/info/nightscout/androidaps/receivers/NSAlarmReceiver.java b/app/src/main/java/info/nightscout/androidaps/receivers/NSAlarmReceiver.java index 19d54b14aa..3449bf2943 100644 --- a/app/src/main/java/info/nightscout/androidaps/receivers/NSAlarmReceiver.java +++ b/app/src/main/java/info/nightscout/androidaps/receivers/NSAlarmReceiver.java @@ -10,8 +10,8 @@ import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.general.nsclient.data.NSAlarm; import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification; import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification; @@ -41,11 +41,11 @@ public class NSAlarmReceiver extends BroadcastReceiver { case Intents.ACTION_URGENT_ALARM: Notification notification = new Notification(nsAlarm); if (notification.isEnabled()) - MainApp.bus().post(new EventNewNotification(notification)); + RxBus.INSTANCE.send(new EventNewNotification(notification)); break; case Intents.ACTION_CLEAR_ALARM: - MainApp.bus().post(new EventDismissNotification(Notification.NSALARM)); - MainApp.bus().post(new EventDismissNotification(Notification.NSURGENTALARM)); + RxBus.INSTANCE.send(new EventDismissNotification(Notification.NSALARM)); + RxBus.INSTANCE.send(new EventDismissNotification(Notification.NSURGENTALARM)); break; } } diff --git a/app/src/main/java/info/nightscout/androidaps/receivers/NetworkChangeReceiver.java b/app/src/main/java/info/nightscout/androidaps/receivers/NetworkChangeReceiver.java index 9a3108e98c..23e826e380 100644 --- a/app/src/main/java/info/nightscout/androidaps/receivers/NetworkChangeReceiver.java +++ b/app/src/main/java/info/nightscout/androidaps/receivers/NetworkChangeReceiver.java @@ -8,7 +8,7 @@ import android.net.NetworkInfo; import android.net.wifi.SupplicantState; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; -import android.support.annotation.Nullable; +import androidx.annotation.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -16,16 +16,30 @@ import org.slf4j.LoggerFactory; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.events.EventNetworkChange; import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.bus.RxBus; public class NetworkChangeReceiver extends BroadcastReceiver { private static Logger log = LoggerFactory.getLogger(L.CORE); + private static EventNetworkChange lastEvent = null; + + public static final NetworkChangeReceiver instance = new NetworkChangeReceiver(); + + // TODO: Split NSClient into network state component that can be used by several plugins and logic for plugin + public static void fetch() { + NetworkChangeReceiver.instance.grabNetworkStatus(MainApp.instance().getApplicationContext()); + } + + private NetworkChangeReceiver() { + super(); + } + @Override public void onReceive(final Context context, final Intent intent) { EventNetworkChange event = grabNetworkStatus(context); if (event != null) - MainApp.bus().post(event); + RxBus.INSTANCE.send(event); } @Nullable @@ -38,29 +52,42 @@ public class NetworkChangeReceiver extends BroadcastReceiver { if (activeNetwork != null) { if (activeNetwork.getType() == ConnectivityManager.TYPE_WIFI && activeNetwork.isConnected()) { - event.wifiConnected = true; + event.setWifiConnected(true); WifiManager wifiManager = (WifiManager) MainApp.instance().getApplicationContext().getSystemService(Context.WIFI_SERVICE); if (wifiManager != null) { WifiInfo wifiInfo = wifiManager.getConnectionInfo(); if (wifiInfo.getSupplicantState() == SupplicantState.COMPLETED) { - event.ssid = wifiInfo.getSSID(); + event.setSsid(wifiInfo.getSSID()); } if (L.isEnabled(L.CORE)) - log.debug("NETCHANGE: Wifi connected. SSID: " + event.ssid); + log.debug("NETCHANGE: Wifi connected. SSID: " + event.connectedSsid()); } } if (activeNetwork.getType() == ConnectivityManager.TYPE_MOBILE) { - event.mobileConnected = true; - event.roaming = activeNetwork.isRoaming(); + event.setMobileConnected(true); + event.setRoaming(activeNetwork.isRoaming()); if (L.isEnabled(L.CORE)) - log.debug("NETCHANGE: Mobile connected. Roaming: " + event.roaming); + log.debug("NETCHANGE: Mobile connected. Roaming: " + event.getRoaming()); } } else { if (L.isEnabled(L.CORE)) log.debug("NETCHANGE: Disconnected."); } + lastEvent = event; return event; } + + public static boolean isWifiConnected() { + return lastEvent != null && lastEvent.getWifiConnected(); + } + + public static boolean isConnected() { + return lastEvent != null && (lastEvent.getWifiConnected() || lastEvent.getMobileConnected()); + } + + public static EventNetworkChange getLastEvent() { + return lastEvent; + } } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/receivers/TimeDateOrTZChangeReceiver.java b/app/src/main/java/info/nightscout/androidaps/receivers/TimeDateOrTZChangeReceiver.java new file mode 100644 index 0000000000..e74dfec20d --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/receivers/TimeDateOrTZChangeReceiver.java @@ -0,0 +1,45 @@ +package info.nightscout.androidaps.receivers; + +import android.bluetooth.BluetoothAdapter; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.interfaces.PumpInterface; +import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; + +public class TimeDateOrTZChangeReceiver extends BroadcastReceiver { + + private static Logger LOG = LoggerFactory.getLogger(L.PUMP); + + @Override + public void onReceive(Context context, Intent intent) { + final String action = intent.getAction(); + + PumpInterface activePump = ConfigBuilderPlugin.getPlugin().getActivePump(); + + LOG.debug("Date, Time and/or TimeZone changed."); + + if (action != null && activePump != null) { + LOG.debug("Date, Time and/or TimeZone changed. Notifying pump driver."); + activePump.timeDateOrTimeZoneChanged(); + } + } + + + public void registerBroadcasts(MainApp mainApp) { + IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_TIME_CHANGED); + filter.addAction(Intent.ACTION_DATE_CHANGED); + filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); + mainApp.registerReceiver(this, filter); + } + + +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/services/AlarmSoundService.java b/app/src/main/java/info/nightscout/androidaps/services/AlarmSoundService.java index 4917789db1..2e9130f7db 100644 --- a/app/src/main/java/info/nightscout/androidaps/services/AlarmSoundService.java +++ b/app/src/main/java/info/nightscout/androidaps/services/AlarmSoundService.java @@ -1,5 +1,6 @@ package info.nightscout.androidaps.services; +import android.app.Notification; import android.app.Service; import android.content.Context; import android.content.Intent; @@ -16,6 +17,7 @@ import java.io.IOException; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.general.persistentNotification.PersistentNotificationPlugin; public class AlarmSoundService extends Service { private static Logger log = LoggerFactory.getLogger(L.CORE); @@ -28,8 +30,7 @@ public class AlarmSoundService extends Service { @Override public IBinder onBind(Intent intent) { - // TODO: Return the communication channel to the service. - throw new UnsupportedOperationException("Not yet implemented"); + return null; } @Override @@ -37,9 +38,13 @@ public class AlarmSoundService extends Service { super.onCreate(); if (L.isEnabled(L.CORE)) log.debug("onCreate"); + Notification notification = PersistentNotificationPlugin.getPlugin().getLastNotification(); + startForeground(PersistentNotificationPlugin.ONGOING_NOTIFICATION_ID, notification); } public int onStartCommand(Intent intent, int flags, int startId) { + Notification notification = PersistentNotificationPlugin.getPlugin().getLastNotification(); + startForeground(PersistentNotificationPlugin.ONGOING_NOTIFICATION_ID, notification); if (player != null && player.isPlaying()) player.stop(); if (L.isEnabled(L.CORE)) @@ -48,28 +53,22 @@ public class AlarmSoundService extends Service { resourceId = intent.getIntExtra("soundid", R.raw.error); player = new MediaPlayer(); - AssetFileDescriptor afd = MainApp.sResources.openRawResourceFd(resourceId); - if (afd == null) - return START_STICKY; try { + AssetFileDescriptor afd = MainApp.sResources.openRawResourceFd(resourceId); + if (afd == null) + return START_STICKY; player.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength()); afd.close(); - } catch (IOException e) { - log.error("Unhandled exception", e); - } - player.setLooping(true); // Set looping - AudioManager manager = (AudioManager) this.getSystemService(Context.AUDIO_SERVICE); - if (manager == null || !manager.isMusicActive()) { - player.setVolume(100, 100); - } - - try { + player.setLooping(true); // Set looping + AudioManager manager = (AudioManager) this.getSystemService(Context.AUDIO_SERVICE); + if (manager == null || !manager.isMusicActive()) { + player.setVolume(100, 100); + } player.prepare(); player.start(); - } catch (IOException e) { + } catch (Exception e) { log.error("Unhandled exception", e); } - return START_STICKY; } diff --git a/app/src/main/java/info/nightscout/androidaps/services/DataService.java b/app/src/main/java/info/nightscout/androidaps/services/DataService.java index b98ff6bf94..80a42e145a 100644 --- a/app/src/main/java/info/nightscout/androidaps/services/DataService.java +++ b/app/src/main/java/info/nightscout/androidaps/services/DataService.java @@ -16,17 +16,18 @@ import info.nightscout.androidaps.R; import info.nightscout.androidaps.db.CareportalEvent; import info.nightscout.androidaps.events.EventNsFood; import info.nightscout.androidaps.events.EventNsTreatment; +import info.nightscout.androidaps.logging.BundleLogger; import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.general.nsclient.data.NSDeviceStatus; import info.nightscout.androidaps.plugins.general.nsclient.data.NSMbg; import info.nightscout.androidaps.plugins.general.nsclient.data.NSSettingsStatus; import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification; import info.nightscout.androidaps.plugins.general.overview.notifications.Notification; +import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorPlugin; import info.nightscout.androidaps.plugins.profile.ns.NSProfilePlugin; import info.nightscout.androidaps.plugins.pump.danaR.activities.DanaRNSHistorySync; -import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorPlugin; -import info.nightscout.androidaps.plugins.source.SourceDexcomG5Plugin; -import info.nightscout.androidaps.plugins.source.SourceDexcomG6Plugin; +import info.nightscout.androidaps.plugins.source.SourceDexcomPlugin; import info.nightscout.androidaps.plugins.source.SourceEversensePlugin; import info.nightscout.androidaps.plugins.source.SourceGlimpPlugin; import info.nightscout.androidaps.plugins.source.SourceMM640gPlugin; @@ -35,7 +36,6 @@ import info.nightscout.androidaps.plugins.source.SourcePoctechPlugin; import info.nightscout.androidaps.plugins.source.SourceTomatoPlugin; import info.nightscout.androidaps.plugins.source.SourceXdripPlugin; import info.nightscout.androidaps.receivers.DataReceiver; -import info.nightscout.androidaps.logging.BundleLogger; import info.nightscout.androidaps.utils.JsonHelper; import info.nightscout.androidaps.utils.SP; @@ -45,7 +45,6 @@ public class DataService extends IntentService { public DataService() { super("DataService"); - registerBus(); } @Override @@ -69,12 +68,8 @@ public class DataService extends IntentService { SourceMM640gPlugin.getPlugin().handleNewData(intent); } else if (Intents.GLIMP_BG.equals(action)) { SourceGlimpPlugin.getPlugin().handleNewData(intent); - } else if (Intents.DEXCOMG5_BG.equals(action)) { - SourceDexcomG5Plugin.getPlugin().handleNewData(intent); - } else if (Intents.DEXCOMG5_BG_NEW.equals(action)) { - SourceDexcomG5Plugin.getPlugin().handleNewData(intent); - } else if (Intents.DEXCOMG6_BG.equals(action)) { - SourceDexcomG6Plugin.getPlugin().handleNewData(intent); + } else if (Intents.DEXCOM_BG.equals(action)) { + SourceDexcomPlugin.INSTANCE.handleNewData(intent); } else if (Intents.POCTECH_BG.equals(action)) { SourcePoctechPlugin.getPlugin().handleNewData(intent); } else if (Intents.TOMATO_BG.equals(action)) { @@ -91,21 +86,21 @@ public class DataService extends IntentService { } else if (Intents.ACTION_NEW_STATUS.equals(action)) { NSSettingsStatus.getInstance().handleNewData(intent); } else if (Intents.ACTION_NEW_FOOD.equals(action)) { - EventNsFood evt = new EventNsFood(EventNsFood.ADD, bundles); - MainApp.bus().post(evt); + EventNsFood evt = new EventNsFood(EventNsFood.Companion.getADD(), bundles); + RxBus.INSTANCE.send(evt); } else if (Intents.ACTION_CHANGED_FOOD.equals(action)) { - EventNsFood evt = new EventNsFood(EventNsFood.UPDATE, bundles); - MainApp.bus().post(evt); + EventNsFood evt = new EventNsFood(EventNsFood.Companion.getUPDATE(), bundles); + RxBus.INSTANCE.send(evt); } else if (Intents.ACTION_REMOVED_FOOD.equals(action)) { - EventNsFood evt = new EventNsFood(EventNsFood.REMOVE, bundles); - MainApp.bus().post(evt); + EventNsFood evt = new EventNsFood(EventNsFood.Companion.getREMOVE(), bundles); + RxBus.INSTANCE.send(evt); } else if (acceptNSData && (Intents.ACTION_NEW_TREATMENT.equals(action) || Intents.ACTION_CHANGED_TREATMENT.equals(action) || Intents.ACTION_REMOVED_TREATMENT.equals(action) || Intents.ACTION_NEW_CAL.equals(action) || Intents.ACTION_NEW_MBG.equals(action)) - ) { + ) { handleNewDataFromNSClient(intent); } else if (Telephony.Sms.Intents.SMS_RECEIVED_ACTION.equals(action)) { SmsCommunicatorPlugin.getPlugin().handleNewData(intent); @@ -119,16 +114,6 @@ public class DataService extends IntentService { @Override public void onDestroy() { super.onDestroy(); - MainApp.bus().unregister(this); - } - - private void registerBus() { - try { - MainApp.bus().unregister(this); - } catch (RuntimeException x) { - // Ignore - } - MainApp.bus().register(this); } private void handleNewDataFromNSClient(Intent intent) { @@ -202,8 +187,8 @@ public class DataService extends IntentService { private void handleRemovedTreatmentFromNS(JSONObject json) { // new DB model - EventNsTreatment evtTreatment = new EventNsTreatment(EventNsTreatment.REMOVE, json); - MainApp.bus().post(evtTreatment); + EventNsTreatment evtTreatment = new EventNsTreatment(EventNsTreatment.Companion.getREMOVE(), json); + RxBus.INSTANCE.send(evtTreatment); // old DB model String _id = JsonHelper.safeGetString(json, "_id"); MainApp.getDbHelper().deleteTempTargetById(_id); @@ -215,7 +200,7 @@ public class DataService extends IntentService { private void handleTreatmentFromNS(JSONObject json, Intent intent) { // new DB model - int mode = Intents.ACTION_NEW_TREATMENT.equals(intent.getAction()) ? EventNsTreatment.ADD : EventNsTreatment.UPDATE; + int mode = Intents.ACTION_NEW_TREATMENT.equals(intent.getAction()) ? EventNsTreatment.Companion.getADD() : EventNsTreatment.Companion.getUPDATE(); double insulin = JsonHelper.safeGetDouble(json, "insulin"); double carbs = JsonHelper.safeGetDouble(json, "carbs"); String eventType = JsonHelper.safeGetString(json, "eventType"); @@ -225,7 +210,7 @@ public class DataService extends IntentService { } if (insulin > 0 || carbs > 0) { EventNsTreatment evtTreatment = new EventNsTreatment(mode, json); - MainApp.bus().post(evtTreatment); + RxBus.INSTANCE.send(evtTreatment); } else if (json.has(DanaRNSHistorySync.DANARSIGNATURE)) { // old DB model MainApp.getDbHelper().updateDanaRHistoryRecordId(json); @@ -259,7 +244,7 @@ public class DataService extends IntentService { if (date > now - 15 * 60 * 1000L && !notes.isEmpty() && !enteredBy.equals(SP.getString("careportal_enteredby", "AndroidAPS"))) { Notification announcement = new Notification(Notification.NSANNOUNCEMENT, notes, Notification.ANNOUNCEMENT, 60); - MainApp.bus().post(new EventNewNotification(announcement)); + RxBus.INSTANCE.send(new EventNewNotification(announcement)); } } } diff --git a/app/src/main/java/info/nightscout/androidaps/services/Intents.java b/app/src/main/java/info/nightscout/androidaps/services/Intents.java index 5011215b45..98c2355a4f 100644 --- a/app/src/main/java/info/nightscout/androidaps/services/Intents.java +++ b/app/src/main/java/info/nightscout/androidaps/services/Intents.java @@ -23,8 +23,6 @@ public interface Intents { // App -> NSClient String ACTION_DATABASE = "info.nightscout.client.DBACCESS"; - String ACTION_RESTART = "info.nightscout.client.RESTART"; - String ACTION_RESEND = "info.nightscout.client.RESEND"; String ACTION_ACK_ALARM = "info.nightscout.client.ACK_ALARM"; // xDrip -> App @@ -48,9 +46,7 @@ public interface Intents { String GLIMP_BG = "it.ct.glicemia.ACTION_GLUCOSE_MEASURED"; - String DEXCOMG5_BG = "com.dexcom.cgm.DATA"; - String DEXCOMG5_BG_NEW = "com.dexcom.cgm.g5.AndroidAPSEVGCallback.BROADCAST"; - String DEXCOMG6_BG = "com.dexcom.cgm.AndroidAPSEVGCallback.BROADCAST"; + String DEXCOM_BG = "com.dexcom.cgm.EXTERNAL_BROADCAST"; String EVERSENSE_BG = "com.senseonics.AndroidAPSEventSubscriber.BROADCAST"; String POCTECH_BG = "com.china.poctech.data"; diff --git a/app/src/main/java/info/nightscout/androidaps/services/LocationService.java b/app/src/main/java/info/nightscout/androidaps/services/LocationService.java new file mode 100644 index 0000000000..9600515d6f --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/services/LocationService.java @@ -0,0 +1,168 @@ +package info.nightscout.androidaps.services; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.location.Location; +import android.location.LocationManager; +import android.os.Bundle; +import android.os.IBinder; + +import androidx.core.app.ActivityCompat; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.events.EventAppExit; +import info.nightscout.androidaps.events.EventLocationChange; +import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.bus.RxBus; +import info.nightscout.androidaps.plugins.general.persistentNotification.PersistentNotificationPlugin; +import info.nightscout.androidaps.utils.FabricPrivacy; +import info.nightscout.androidaps.utils.SP; +import info.nightscout.androidaps.utils.T; +import io.reactivex.disposables.CompositeDisposable; +import io.reactivex.schedulers.Schedulers; + +public class LocationService extends Service { + private static Logger log = LoggerFactory.getLogger(L.LOCATION); + private CompositeDisposable disposable = new CompositeDisposable(); + + private LocationManager mLocationManager = null; + private static final float LOCATION_DISTANCE = 10f; + + private static final long LOCATION_INTERVAL_ACTIVE = T.mins(5).msecs(); + private static final long LOCATION_INTERVAL_PASSIVE = T.mins(1).msecs(); // this doesn't cost more power + + private static Location mLastLocation; + + private class LocationListener implements android.location.LocationListener { + + LocationListener(String provider) { + if (L.isEnabled(L.LOCATION)) + log.debug("LocationListener " + provider); + mLastLocation = new Location(provider); + } + + @Override + public void onLocationChanged(Location location) { + if (L.isEnabled(L.LOCATION)) + log.debug("onLocationChanged: " + location); + mLastLocation.set(location); + RxBus.INSTANCE.send(new EventLocationChange(location)); + } + + @Override + public void onProviderDisabled(String provider) { + if (L.isEnabled(L.LOCATION)) + log.debug("onProviderDisabled: " + provider); + } + + @Override + public void onProviderEnabled(String provider) { + if (L.isEnabled(L.LOCATION)) + log.debug("onProviderEnabled: " + provider); + } + + @Override + public void onStatusChanged(String provider, int status, Bundle extras) { + if (L.isEnabled(L.LOCATION)) + log.debug("onStatusChanged: " + provider); + } + } + + LocationListener mLocationListener; + + @Override + public IBinder onBind(Intent arg0) { + return null; + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + super.onStartCommand(intent, flags, startId); + if (L.isEnabled(L.LOCATION)) + log.debug("onStartCommand"); + startForeground(PersistentNotificationPlugin.ONGOING_NOTIFICATION_ID, PersistentNotificationPlugin.getPlugin().getLastNotification()); + return START_STICKY; + } + + @Override + public void onCreate() { + super.onCreate(); + startForeground(PersistentNotificationPlugin.ONGOING_NOTIFICATION_ID, PersistentNotificationPlugin.getPlugin().getLastNotification()); + + if (L.isEnabled(L.LOCATION)) + log.debug("onCreate"); + + initializeLocationManager(); + + try { + if (SP.getString(R.string.key_location, "NONE").equals("NETWORK")) + mLocationManager.requestLocationUpdates( + LocationManager.NETWORK_PROVIDER, + LOCATION_INTERVAL_ACTIVE, + LOCATION_DISTANCE, + mLocationListener = new LocationListener(LocationManager.NETWORK_PROVIDER) + ); + if (SP.getString(R.string.key_location, "NONE").equals("GPS")) + mLocationManager.requestLocationUpdates( + LocationManager.GPS_PROVIDER, + LOCATION_INTERVAL_ACTIVE, + LOCATION_DISTANCE, + mLocationListener = new LocationListener(LocationManager.GPS_PROVIDER) + ); + if (SP.getString(R.string.key_location, "NONE").equals("PASSIVE")) + mLocationManager.requestLocationUpdates( + LocationManager.PASSIVE_PROVIDER, + LOCATION_INTERVAL_PASSIVE, + LOCATION_DISTANCE, + mLocationListener = new LocationListener(LocationManager.PASSIVE_PROVIDER) + ); + } catch (java.lang.SecurityException ex) { + log.error("fail to request location update, ignore", ex); + } catch (IllegalArgumentException ex) { + log.error("network provider does not exist, " + ex.getMessage()); + } + disposable.add(RxBus.INSTANCE + .toObservable(EventAppExit.class) + .observeOn(Schedulers.io()) + .subscribe(event -> { + if (L.isEnabled(L.CORE)) log.debug("EventAppExit received"); + stopSelf(); + }, FabricPrivacy::logException) + ); + } + + @Override + public void onDestroy() { + if (L.isEnabled(L.LOCATION)) + log.debug("onDestroy"); + super.onDestroy(); + if (mLocationManager != null) { + try { + if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { + return; + } + mLocationManager.removeUpdates(mLocationListener); + } catch (Exception ex) { + log.error("fail to remove location listener, ignore", ex); + } + } + disposable.clear(); + } + + private void initializeLocationManager() { + if (L.isEnabled(L.LOCATION)) + log.debug("initializeLocationManager - Provider: " + SP.getString(R.string.key_location, "NONE")); + if (mLocationManager == null) { + mLocationManager = (LocationManager) getApplicationContext().getSystemService(Context.LOCATION_SERVICE); + } + } + + public static Location getLastLocation() { + return mLastLocation; + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/SWDefinition.java b/app/src/main/java/info/nightscout/androidaps/setupwizard/SWDefinition.java index 1d143d073f..2c9e1ea51b 100644 --- a/app/src/main/java/info/nightscout/androidaps/setupwizard/SWDefinition.java +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/SWDefinition.java @@ -2,13 +2,8 @@ package info.nightscout.androidaps.setupwizard; import android.Manifest; import android.content.Intent; -import android.os.Build; -import android.support.v7.app.AppCompatActivity; -import com.squareup.otto.Subscribe; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import androidx.appcompat.app.AppCompatActivity; import java.util.ArrayList; import java.util.List; @@ -21,18 +16,19 @@ import info.nightscout.androidaps.events.EventConfigBuilderChange; import info.nightscout.androidaps.events.EventPumpStatusChanged; import info.nightscout.androidaps.interfaces.PluginBase; import info.nightscout.androidaps.interfaces.PluginType; -import info.nightscout.androidaps.plugins.general.careportal.CareportalFragment; -import info.nightscout.androidaps.plugins.general.careportal.Dialogs.NewNSTreatmentDialog; -import info.nightscout.androidaps.plugins.general.careportal.OptionsToShow; -import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderFragment; +import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; import info.nightscout.androidaps.plugins.constraints.objectives.ObjectivesFragment; import info.nightscout.androidaps.plugins.constraints.objectives.ObjectivesPlugin; -import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin; +import info.nightscout.androidaps.plugins.general.careportal.CareportalFragment; +import info.nightscout.androidaps.plugins.general.careportal.Dialogs.NewNSTreatmentDialog; +import info.nightscout.androidaps.plugins.general.careportal.OptionsToShow; import info.nightscout.androidaps.plugins.general.maintenance.ImportExportPrefs; import info.nightscout.androidaps.plugins.general.nsclient.NSClientPlugin; import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientStatus; +import info.nightscout.androidaps.plugins.general.nsclient.services.NSClientService; import info.nightscout.androidaps.plugins.profile.local.LocalProfileFragment; import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin; import info.nightscout.androidaps.plugins.profile.ns.NSProfileFragment; @@ -48,7 +44,6 @@ import info.nightscout.androidaps.setupwizard.elements.SWHtmlLink; import info.nightscout.androidaps.setupwizard.elements.SWInfotext; import info.nightscout.androidaps.setupwizard.elements.SWPlugin; import info.nightscout.androidaps.setupwizard.elements.SWRadioButton; -import info.nightscout.androidaps.setupwizard.events.EventSWLabel; import info.nightscout.androidaps.setupwizard.events.EventSWUpdate; import info.nightscout.androidaps.utils.AndroidPermission; import info.nightscout.androidaps.utils.LocaleHelper; @@ -56,8 +51,6 @@ import info.nightscout.androidaps.utils.PasswordProtection; import info.nightscout.androidaps.utils.SP; public class SWDefinition { - private static Logger log = LoggerFactory.getLogger(SWDefinition.class); - private AppCompatActivity activity; private List screens = new ArrayList<>(); @@ -69,569 +62,419 @@ public class SWDefinition { return activity; } - public List getScreens() { + List getScreens() { return screens; } - SWDefinition add(SWScreen newScreen) { + private SWDefinition add(SWScreen newScreen) { screens.add(newScreen); return this; } SWDefinition() { - if (Config.APS || Config.PUMPCONTROL) + if (Config.APS) SWDefinitionFull(); + else if (Config.PUMPCONTROL) + SWDefinitionPumpControl(); else if (Config.NSCLIENT) SWDefinitionNSClient(); } + private SWScreen screenSetupWizard = new SWScreen(R.string.nav_setupwizard) + .add(new SWInfotext() + .label(R.string.welcometosetupwizard)); + + private SWScreen screenLanguage = new SWScreen(R.string.language) + .skippable(false) + .add(new SWRadioButton() + .option(R.array.languagesArray, R.array.languagesValues) + .preferenceId(R.string.key_language).label(R.string.language) + .comment(R.string.setupwizard_language_prompt)) + .validator(() -> { + LocaleHelper.INSTANCE.update(MainApp.instance().getApplicationContext()); + return SP.contains(R.string.key_language); + }); + + private SWScreen screenEula = new SWScreen(R.string.end_user_license_agreement) + .skippable(false) + .add(new SWInfotext() + .label(R.string.end_user_license_agreement_text)) + .add(new SWBreak()) + .add(new SWButton() + .text(R.string.end_user_license_agreement_i_understand) + .visibility(() -> !SP.getBoolean(R.string.key_i_understand, false)) + .action(() -> { + SP.putBoolean(R.string.key_i_understand, true); + RxBus.INSTANCE.send(new EventSWUpdate(false)); + })) + .visibility(() -> !SP.getBoolean(R.string.key_i_understand, false)) + .validator(() -> SP.getBoolean(R.string.key_i_understand, false)); + + private SWScreen screenPermissionBattery = new SWScreen(R.string.permission) + .skippable(false) + .add(new SWInfotext() + .label(String.format(MainApp.gs(R.string.needwhitelisting), MainApp.gs(R.string.app_name)))) + .add(new SWBreak()) + .add(new SWButton() + .text(R.string.askforpermission) + .visibility(() -> AndroidPermission.permissionNotGranted(getActivity(), Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS)) + .action(() -> AndroidPermission.askForPermission(getActivity(), Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS, AndroidPermission.CASE_BATTERY))) + .visibility(() -> AndroidPermission.permissionNotGranted(getActivity(), Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS)) + .validator(() -> !(AndroidPermission.permissionNotGranted(getActivity(), Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS))); + + private SWScreen screenPermissionBt = new SWScreen(R.string.permission) + .skippable(false) + .add(new SWInfotext() + .label(MainApp.gs(R.string.needlocationpermission))) + .add(new SWBreak()) + .add(new SWButton() + .text(R.string.askforpermission) + .visibility(() -> AndroidPermission.permissionNotGranted(getActivity(), Manifest.permission.ACCESS_FINE_LOCATION)) + .action(() -> AndroidPermission.askForPermission(getActivity(), Manifest.permission.ACCESS_FINE_LOCATION, AndroidPermission.CASE_LOCATION))) + .visibility(() -> AndroidPermission.permissionNotGranted(getActivity(), Manifest.permission.ACCESS_FINE_LOCATION)) + .validator(() -> !(AndroidPermission.permissionNotGranted(getActivity(), Manifest.permission.ACCESS_FINE_LOCATION))); + + private SWScreen screenPermissionStore = new SWScreen(R.string.permission) + .skippable(false) + .add(new SWInfotext() + .label(MainApp.gs(R.string.needstoragepermission))) + .add(new SWBreak()) + .add(new SWButton() + .text(R.string.askforpermission) + .visibility(() -> AndroidPermission.permissionNotGranted(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE)) + .action(() -> AndroidPermission.askForPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE, AndroidPermission.CASE_STORAGE))) + .visibility(() -> AndroidPermission.permissionNotGranted(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE)) + .validator(() -> !(AndroidPermission.permissionNotGranted(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE))); + + private SWScreen screenImport = new SWScreen(R.string.nav_import) + .add(new SWInfotext() + .label(R.string.storedsettingsfound)) + .add(new SWBreak()) + .add(new SWButton() + .text(R.string.nav_import) + .action(() -> ImportExportPrefs.importSharedPreferences(getActivity()))) + .visibility(() -> ImportExportPrefs.file.exists() && !(AndroidPermission.permissionNotGranted(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE))); + + private SWScreen screenNsClient = new SWScreen(R.string.nsclientinternal_title) + .skippable(true) + .add(new SWInfotext() + .label(R.string.nsclientinfotext)) + .add(new SWBreak()) + .add(new SWButton() + .text(R.string.enable_nsclient) + .action(() -> { + NSClientPlugin.getPlugin().setPluginEnabled(PluginType.GENERAL, true); + NSClientPlugin.getPlugin().setFragmentVisible(PluginType.GENERAL, true); + ConfigBuilderPlugin.getPlugin().processOnEnabledCategoryChanged(NSClientPlugin.getPlugin(), PluginType.GENERAL); + ConfigBuilderPlugin.getPlugin().storeSettings("SetupWizard"); + RxBus.INSTANCE.send(new EventConfigBuilderChange()); + RxBus.INSTANCE.send(new EventSWUpdate(true)); + }) + .visibility(() -> !NSClientPlugin.getPlugin().isEnabled(PluginType.GENERAL))) + .add(new SWEditUrl() + .preferenceId(R.string.key_nsclientinternal_url) + .updateDelay(5) + .label(R.string.nsclientinternal_url_title) + .comment(R.string.nsclientinternal_url_dialogmessage)) + .add(new SWEditString() + .validator(text -> text.length() >= 12) + .preferenceId(R.string.key_nsclientinternal_api_secret) + .updateDelay(5) + .label(R.string.nsclientinternal_secret_dialogtitle) + .comment(R.string.nsclientinternal_secret_dialogmessage)) + .add(new SWBreak()) + .add(new SWEventListener(this, EventNSClientStatus.class) + .label(R.string.status) + .initialStatus(NSClientPlugin.getPlugin().status) + ) + .add(new SWBreak()) + .validator(() -> NSClientPlugin.getPlugin().nsClientService != null && NSClientService.isConnected && NSClientService.hasWriteAuth) + .visibility(() -> !(NSClientPlugin.getPlugin().nsClientService != null && NSClientService.isConnected && NSClientService.hasWriteAuth)); + + private SWScreen screenAge = new SWScreen(R.string.patientage) + .skippable(false) + .add(new SWBreak()) + .add(new SWRadioButton() + .option(R.array.ageArray, R.array.ageValues) + .preferenceId(R.string.key_age) + .label(R.string.patientage) + .comment(R.string.patientage_summary)) + .validator(() -> SP.contains(R.string.key_age)); + + private SWScreen screenInsulin = new SWScreen(R.string.configbuilder_insulin) + .skippable(false) + .add(new SWPlugin() + .option(PluginType.INSULIN, R.string.configbuilder_insulin_description) + .makeVisible(false) + .label(R.string.configbuilder_insulin)) + .add(new SWBreak()) + .add(new SWInfotext() + .label(R.string.diawarning)) + .add(new SWBreak()) + .add(new SWButton() + .text(R.string.insulinsourcesetup) + .action(() -> { + final PluginBase plugin = (PluginBase) ConfigBuilderPlugin.getPlugin().getActiveInsulin(); + if (plugin != null) { + PasswordProtection.QueryPassword(activity, R.string.settings_password, "settings_password", () -> { + Intent i = new Intent(activity, PreferencesActivity.class); + i.putExtra("id", plugin.getPreferencesId()); + activity.startActivity(i); + }, null); + } + }) + .visibility(() -> ConfigBuilderPlugin.getPlugin().getActiveInsulin() != null && ((PluginBase) ConfigBuilderPlugin.getPlugin().getActiveInsulin()).getPreferencesId() > 0)) + .validator(() -> ConfigBuilderPlugin.getPlugin().getActiveInsulin() != null); + + private SWScreen screenBgSource = new SWScreen(R.string.configbuilder_bgsource) + .skippable(false) + .add(new SWPlugin() + .option(PluginType.BGSOURCE, R.string.configbuilder_bgsource_description) + .label(R.string.configbuilder_bgsource)) + .add(new SWBreak()) + .add(new SWButton() + .text(R.string.bgsourcesetup) + .action(() -> { + final PluginBase plugin = (PluginBase) ConfigBuilderPlugin.getPlugin().getActiveBgSource(); + if (plugin != null) { + PasswordProtection.QueryPassword(activity, R.string.settings_password, "settings_password", () -> { + Intent i = new Intent(activity, PreferencesActivity.class); + i.putExtra("id", plugin.getPreferencesId()); + activity.startActivity(i); + }, null); + } + }) + .visibility(() -> ConfigBuilderPlugin.getPlugin().getActiveBgSource() != null && ((PluginBase) ConfigBuilderPlugin.getPlugin().getActiveBgSource()).getPreferencesId() > 0)) + .validator(() -> ConfigBuilderPlugin.getPlugin().getActiveBgSource() != null); + + private SWScreen screenProfile = new SWScreen(R.string.configbuilder_profile) + .skippable(false) + .add(new SWInfotext() + .label(R.string.setupwizard_profile_description)) + .add(new SWBreak()) + .add(new SWPlugin() + .option(PluginType.PROFILE, R.string.configbuilder_profile_description) + .label(R.string.configbuilder_profile)) + .validator(() -> ConfigBuilderPlugin.getPlugin().getActiveProfileInterface() != null); + + private SWScreen screenNsProfile = new SWScreen(R.string.nsprofile) + .skippable(false) + .add(new SWInfotext() + .label(R.string.adjustprofileinns)) + .add(new SWFragment(this) + .add(new NSProfileFragment())) + .validator(() -> NSProfilePlugin.getPlugin().getProfile() != null && NSProfilePlugin.getPlugin().getProfile().getDefaultProfile() != null && NSProfilePlugin.getPlugin().getProfile().getDefaultProfile().isValid("StartupWizard")) + .visibility(() -> NSProfilePlugin.getPlugin().isEnabled(PluginType.PROFILE)); + + private SWScreen screenLocalProfile = new SWScreen(R.string.localprofile) + .skippable(false) + .add(new SWFragment(this) + .add(new LocalProfileFragment())) + .validator(() -> LocalProfilePlugin.getPlugin().getProfile() != null && LocalProfilePlugin.getPlugin().getProfile().getDefaultProfile() != null && LocalProfilePlugin.getPlugin().getProfile().getDefaultProfile().isValid("StartupWizard")) + .visibility(() -> LocalProfilePlugin.getPlugin().isEnabled(PluginType.PROFILE)); + + private SWScreen screenSimpleProfile = new SWScreen(R.string.simpleprofile) + .skippable(false) + .add(new SWFragment(this) + .add(new SimpleProfileFragment())) + .validator(() -> SimpleProfilePlugin.getPlugin().getProfile() != null && SimpleProfilePlugin.getPlugin().getProfile().getDefaultProfile() != null && SimpleProfilePlugin.getPlugin().getProfile().getDefaultProfile().isValid("StartupWizard")) + .visibility(() -> SimpleProfilePlugin.getPlugin().isEnabled(PluginType.PROFILE)); + + private SWScreen screenProfileSwitch = new SWScreen(R.string.profileswitch) + .skippable(false) + .add(new SWInfotext() + .label(R.string.profileswitch_ismissing)) + .add(new SWButton() + .text(R.string.profileswitch) + .action(() -> { + NewNSTreatmentDialog newDialog = new NewNSTreatmentDialog(); + final OptionsToShow profileSwitch = CareportalFragment.PROFILESWITCHDIRECT; + profileSwitch.executeProfileSwitch = true; + newDialog.setOptions(profileSwitch, R.string.careportal_profileswitch); + newDialog.show(getActivity().getSupportFragmentManager(), "NewNSTreatmentDialog"); + })) + .validator(() -> ProfileFunctions.getInstance().getProfile() != null) + .visibility(() -> ProfileFunctions.getInstance().getProfile() == null); + + private SWScreen screenPump = new SWScreen(R.string.configbuilder_pump) + .skippable(false) + .add(new SWPlugin() + .option(PluginType.PUMP, R.string.configbuilder_pump_description) + .label(R.string.configbuilder_pump)) + .add(new SWBreak()) + .add(new SWButton() + .text(R.string.pumpsetup) + .action(() -> { + final PluginBase plugin = (PluginBase) ConfigBuilderPlugin.getPlugin().getActivePump(); + if (plugin != null) { + PasswordProtection.QueryPassword(activity, R.string.settings_password, "settings_password", () -> { + Intent i = new Intent(activity, PreferencesActivity.class); + i.putExtra("id", plugin.getPreferencesId()); + activity.startActivity(i); + }, null); + } + }) + .visibility(() -> (ConfigBuilderPlugin.getPlugin().getActivePump() != null && ((PluginBase) ConfigBuilderPlugin.getPlugin().getActivePump()).getPreferencesId() > 0))) + .add(new SWButton() + .text(R.string.readstatus) + .action(() -> ConfigBuilderPlugin.getPlugin().getCommandQueue().readStatus("Clicked connect to pump", null)) + .visibility(() -> ConfigBuilderPlugin.getPlugin().getActivePump() != null)) + .add(new SWEventListener(this, EventPumpStatusChanged.class)) + .validator(() -> ConfigBuilderPlugin.getPlugin().getActivePump() != null && ConfigBuilderPlugin.getPlugin().getActivePump().isInitialized()); + + private SWScreen screenAps = new SWScreen(R.string.configbuilder_aps) + .skippable(false) + .add(new SWInfotext() + .label(R.string.setupwizard_aps_description)) + .add(new SWBreak()) + .add(new SWHtmlLink() + .label("https://openaps.readthedocs.io/en/latest/")) + .add(new SWBreak()) + .add(new SWPlugin() + .option(PluginType.APS, R.string.configbuilder_aps_description) + .label(R.string.configbuilder_aps)) + .add(new SWButton() + .text(R.string.apssetup) + .action(() -> { + final PluginBase plugin = (PluginBase) ConfigBuilderPlugin.getPlugin().getActiveAPS(); + if (plugin != null) { + PasswordProtection.QueryPassword(activity, R.string.settings_password, "settings_password", () -> { + Intent i = new Intent(activity, PreferencesActivity.class); + i.putExtra("id", plugin.getPreferencesId()); + activity.startActivity(i); + }, null); + } + }) + .visibility(() -> ConfigBuilderPlugin.getPlugin().getActiveAPS() != null && ((PluginBase) ConfigBuilderPlugin.getPlugin().getActiveAPS()).getPreferencesId() > 0)) + .validator(() -> ConfigBuilderPlugin.getPlugin().getActiveAPS() != null) + .visibility(() -> Config.APS); + + private SWScreen screenApsMode = new SWScreen(R.string.apsmode_title) + .skippable(false) + .add(new SWRadioButton() + .option(R.array.aps_modeArray, R.array.aps_modeValues) + .preferenceId(R.string.key_aps_mode).label(R.string.apsmode_title) + .comment(R.string.setupwizard_preferred_aps_mode)) + .validator(() -> SP.contains(R.string.key_aps_mode)); + + private SWScreen screenLoop = new SWScreen(R.string.configbuilder_loop) + .skippable(false) + .add(new SWInfotext() + .label(R.string.setupwizard_loop_description)) + .add(new SWBreak()) + .add(new SWButton() + .text(R.string.enableloop) + .action(() -> { + LoopPlugin.getPlugin().setPluginEnabled(PluginType.LOOP, true); + LoopPlugin.getPlugin().setFragmentVisible(PluginType.LOOP, true); + ConfigBuilderPlugin.getPlugin().processOnEnabledCategoryChanged(LoopPlugin.getPlugin(), PluginType.LOOP); + ConfigBuilderPlugin.getPlugin().storeSettings("SetupWizard"); + RxBus.INSTANCE.send(new EventConfigBuilderChange()); + RxBus.INSTANCE.send(new EventSWUpdate(true)); + }) + .visibility(() -> !LoopPlugin.getPlugin().isEnabled(PluginType.LOOP))) + .validator(() -> LoopPlugin.getPlugin().isEnabled(PluginType.LOOP)) + .visibility(() -> !LoopPlugin.getPlugin().isEnabled(PluginType.LOOP) && Config.APS); + + private SWScreen screenSensitivity = new SWScreen(R.string.configbuilder_sensitivity) + .skippable(false) + .add(new SWInfotext() + .label(R.string.setupwizard_sensitivity_description)) + .add(new SWHtmlLink() + .label(R.string.setupwizard_sensitivity_url)) + .add(new SWBreak()) + .add(new SWPlugin() + .option(PluginType.SENSITIVITY, R.string.configbuilder_sensitivity_description) + .label(R.string.configbuilder_sensitivity)) + .add(new SWBreak()) + .add(new SWButton() + .text(R.string.sensitivitysetup) + .action(() -> { + final PluginBase plugin = (PluginBase) ConfigBuilderPlugin.getPlugin().getActiveSensitivity(); + if (plugin != null) { + PasswordProtection.QueryPassword(activity, R.string.settings_password, "settings_password", () -> { + Intent i = new Intent(activity, PreferencesActivity.class); + i.putExtra("id", plugin.getPreferencesId()); + activity.startActivity(i); + }, null); + } + }) + .visibility(() -> ConfigBuilderPlugin.getPlugin().getActiveSensitivity() != null && ((PluginBase) ConfigBuilderPlugin.getPlugin().getActiveSensitivity()).getPreferencesId() > 0)) + .validator(() -> ConfigBuilderPlugin.getPlugin().getActiveSensitivity() != null); + + private SWScreen getScreenObjectives = new SWScreen(R.string.objectives) + .skippable(false) + .add(new SWInfotext() + .label(R.string.startobjective)) + .add(new SWBreak()) + .add(new SWFragment(this) + .add(new ObjectivesFragment())) + .validator(() -> ObjectivesPlugin.INSTANCE.getObjectives().get(ObjectivesPlugin.INSTANCE.getFIRST_OBJECTIVE()).isStarted()) + .visibility(() -> !ObjectivesPlugin.INSTANCE.getObjectives().get(ObjectivesPlugin.INSTANCE.getFIRST_OBJECTIVE()).isStarted() && Config.APS); + private void SWDefinitionFull() { // List all the screens here - add(new SWScreen(R.string.nav_setupwizard) - .add(new SWInfotext() - .label(R.string.welcometosetupwizard)) - ) - .add(new SWScreen(R.string.language) - .skippable(false) - .add(new SWRadioButton() - .option(R.array.languagesArray, R.array.languagesValues) - .preferenceId(R.string.key_language).label(R.string.language) - .comment(R.string.setupwizard_language_prompt)) - .validator(() -> { - String lang = SP.getString("language", "en"); - LocaleHelper.setLocale(MainApp.instance().getApplicationContext(), lang); - return SP.contains(R.string.key_language); - }) - ) - .add(new SWScreen(R.string.end_user_license_agreement) - .skippable(false) - .add(new SWInfotext() - .label(R.string.end_user_license_agreement_text)) - .add(new SWBreak()) - .add(new SWButton() - .text(R.string.end_user_license_agreement_i_understand) - .visibility(() -> !SP.getBoolean(R.string.key_i_understand, false)) - .action(() -> { - SP.putBoolean(R.string.key_i_understand, true); - MainApp.bus().post(new EventSWUpdate(false)); - })) - .visibility(() -> !SP.getBoolean(R.string.key_i_understand, false)) - .validator(() -> SP.getBoolean(R.string.key_i_understand, false)) - ) - .add(new SWScreen(R.string.permission) - .skippable(false) - .add(new SWInfotext() - .label(String.format(MainApp.gs(R.string.needwhitelisting), MainApp.gs(R.string.app_name)))) - .add(new SWBreak()) - .add(new SWButton() - .text(R.string.askforpermission) - .visibility(() -> Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !AndroidPermission.checkForPermission(getActivity(), Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS)) - .action(() -> AndroidPermission.askForPermission(getActivity(), Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS, AndroidPermission.CASE_BATTERY))) - .visibility(() -> Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !AndroidPermission.checkForPermission(getActivity(), Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS)) - .validator(() -> !(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !AndroidPermission.checkForPermission(getActivity(), Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS))) - ) - .add(new SWScreen(R.string.permission) - .skippable(false) - .add(new SWInfotext() - .label(MainApp.gs(R.string.needlocationpermission))) - .add(new SWBreak()) - .add(new SWButton() - .text(R.string.askforpermission) - .visibility(() -> Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !AndroidPermission.checkForPermission(getActivity(), Manifest.permission.ACCESS_FINE_LOCATION)) - .action(() -> AndroidPermission.askForPermission(getActivity(), Manifest.permission.ACCESS_FINE_LOCATION, AndroidPermission.CASE_LOCATION))) - .visibility(() -> Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !AndroidPermission.checkForPermission(getActivity(), Manifest.permission.ACCESS_FINE_LOCATION)) - .validator(() -> !(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !AndroidPermission.checkForPermission(getActivity(), Manifest.permission.ACCESS_FINE_LOCATION))) - ) - .add(new SWScreen(R.string.permission) - .skippable(false) - .add(new SWInfotext() - .label(MainApp.gs(R.string.needstoragepermission))) - .add(new SWBreak()) - .add(new SWButton() - .text(R.string.askforpermission) - .visibility(() -> Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !AndroidPermission.checkForPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE)) - .action(() -> AndroidPermission.askForPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE, AndroidPermission.CASE_STORAGE))) - .visibility(() -> Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !AndroidPermission.checkForPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE)) - .validator(() -> !(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !AndroidPermission.checkForPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE))) - ) - .add(new SWScreen(R.string.nav_import) - .add(new SWInfotext() - .label(R.string.storedsettingsfound)) - .add(new SWBreak()) - .add(new SWButton() - .text(R.string.nav_import) - .action(() -> ImportExportPrefs.importSharedPreferences(getActivity()))) - .visibility(() -> ImportExportPrefs.file.exists() && !(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !AndroidPermission.checkForPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE))) - ) - .add(new SWScreen(R.string.nsclientinternal_title) - .skippable(true) - .add(new SWInfotext() - .label(R.string.nsclientinfotext)) - .add(new SWBreak()) - .add(new SWButton() - .text(R.string.enable_nsclient) - .action(() -> { - NSClientPlugin.getPlugin().setPluginEnabled(PluginType.GENERAL, true); - NSClientPlugin.getPlugin().setFragmentVisible(PluginType.GENERAL, true); - ConfigBuilderFragment.processOnEnabledCategoryChanged(NSClientPlugin.getPlugin(), PluginType.GENERAL); - ConfigBuilderPlugin.getPlugin().storeSettings("SetupWizard"); - MainApp.bus().post(new EventConfigBuilderChange()); - MainApp.bus().post(new EventSWUpdate(true)); - }) - .visibility(() -> !NSClientPlugin.getPlugin().isEnabled(PluginType.GENERAL))) - .add(new SWEditUrl() - .preferenceId(R.string.key_nsclientinternal_url) - .updateDelay(5) - .label(R.string.nsclientinternal_url_title) - .comment(R.string.nsclientinternal_url_dialogmessage)) - .add(new SWEditString() - .validator(text -> text.length() >= 12) - .preferenceId(R.string.key_nsclientinternal_api_secret) - .updateDelay(5) - .label(R.string.nsclientinternal_secret_dialogtitle) - .comment(R.string.nsclientinternal_secret_dialogmessage)) - .add(new SWBreak()) - .add(new SWEventListener(this) - .label(R.string.status) - .initialStatus(NSClientPlugin.getPlugin().status) - .listener(new Object() { - @Subscribe - public void onEventNSClientStatus(EventNSClientStatus event) { - MainApp.bus().post(new EventSWLabel(event.status)); - } - }) - ) - .add(new SWBreak()) - .validator(() -> NSClientPlugin.getPlugin().nsClientService != null && NSClientPlugin.getPlugin().nsClientService.isConnected && NSClientPlugin.getPlugin().nsClientService.hasWriteAuth) - .visibility(() -> !(NSClientPlugin.getPlugin().nsClientService != null && NSClientPlugin.getPlugin().nsClientService.isConnected && NSClientPlugin.getPlugin().nsClientService.hasWriteAuth)) - ) - .add(new SWScreen(R.string.patientage) - .skippable(false) - .add(new SWInfotext() - .label(R.string.patientage_summary)) - .add(new SWBreak()) - .add(new SWRadioButton() - .option(R.array.ageArray, R.array.ageValues) - .preferenceId(R.string.key_age) - .label(R.string.patientage) - .comment(R.string.patientage_summary)) - .validator(() -> SP.contains(R.string.key_age)) - ) - .add(new SWScreen(R.string.configbuilder_insulin) - .skippable(false) - .add(new SWPlugin() - .option(PluginType.INSULIN, R.string.configbuilder_insulin_description) - .makeVisible(false) - .label(R.string.configbuilder_insulin)) - .add(new SWBreak()) - .add(new SWInfotext() - .label(R.string.diawarning)) - .add(new SWBreak()) - .add(new SWButton() - .text(R.string.insulinsourcesetup) - .action(() -> { - final PluginBase plugin = (PluginBase) ConfigBuilderPlugin.getPlugin().getActiveInsulin(); - PasswordProtection.QueryPassword(activity, R.string.settings_password, "settings_password", () -> { - Intent i = new Intent(activity, PreferencesActivity.class); - i.putExtra("id", plugin.getPreferencesId()); - activity.startActivity(i); - }, null); - }) - .visibility(() -> ConfigBuilderPlugin.getPlugin().getActiveInsulin()!= null && ((PluginBase) ConfigBuilderPlugin.getPlugin().getActiveInsulin()).getPreferencesId() > 0)) - .validator(() -> ConfigBuilderPlugin.getPlugin().getActiveInsulin() != null) - ) - .add(new SWScreen(R.string.configbuilder_bgsource) - .skippable(false) - .add(new SWPlugin() - .option(PluginType.BGSOURCE, R.string.configbuilder_bgsource_description) - .label(R.string.configbuilder_bgsource)) - .add(new SWBreak()) - .add(new SWButton() - .text(R.string.bgsourcesetup) - .action(() -> { - final PluginBase plugin = (PluginBase) ConfigBuilderPlugin.getPlugin().getActiveBgSource(); - PasswordProtection.QueryPassword(activity, R.string.settings_password, "settings_password", () -> { - Intent i = new Intent(activity, PreferencesActivity.class); - i.putExtra("id", plugin.getPreferencesId()); - activity.startActivity(i); - }, null); - }) - .visibility(() -> ConfigBuilderPlugin.getPlugin().getActiveBgSource()!= null && ((PluginBase) ConfigBuilderPlugin.getPlugin().getActiveBgSource()).getPreferencesId() > 0)) - .validator(() -> ConfigBuilderPlugin.getPlugin().getActiveBgSource() != null) - ) - .add(new SWScreen(R.string.configbuilder_profile) - .skippable(false) - .add(new SWInfotext() - .label(R.string.setupwizard_profile_description)) - .add(new SWBreak()) - .add(new SWPlugin() - .option(PluginType.PROFILE, R.string.configbuilder_profile_description) - .label(R.string.configbuilder_profile)) - .validator(() -> ConfigBuilderPlugin.getPlugin().getActiveProfileInterface() != null) - ) - .add(new SWScreen(R.string.nsprofile) - .skippable(false) - .add(new SWInfotext() - .label(R.string.adjustprofileinns)) - .add(new SWFragment(this) - .add(new NSProfileFragment())) - .validator(() -> NSProfilePlugin.getPlugin().getProfile() != null && NSProfilePlugin.getPlugin().getProfile().getDefaultProfile().isValid("StartupWizard")) - .visibility(() -> NSProfilePlugin.getPlugin().isEnabled(PluginType.PROFILE)) - ) - .add(new SWScreen(R.string.localprofile) - .skippable(false) - .add(new SWFragment(this) - .add(new LocalProfileFragment())) - .validator(() -> LocalProfilePlugin.getPlugin().getProfile() != null && LocalProfilePlugin.getPlugin().getProfile().getDefaultProfile().isValid("StartupWizard")) - .visibility(() -> LocalProfilePlugin.getPlugin().isEnabled(PluginType.PROFILE)) - ) - .add(new SWScreen(R.string.simpleprofile) - .skippable(false) - .add(new SWFragment(this) - .add(new SimpleProfileFragment())) - .validator(() -> SimpleProfilePlugin.getPlugin().getProfile() != null && SimpleProfilePlugin.getPlugin().getProfile().getDefaultProfile().isValid("StartupWizard")) - .visibility(() -> SimpleProfilePlugin.getPlugin().isEnabled(PluginType.PROFILE)) - ) - .add(new SWScreen(R.string.profileswitch) - .skippable(false) - .add(new SWInfotext() - .label(R.string.profileswitch_ismissing)) - .add(new SWButton() - .text(R.string.profileswitch) - .action(() -> { - NewNSTreatmentDialog newDialog = new NewNSTreatmentDialog(); - final OptionsToShow profileswitch = CareportalFragment.PROFILESWITCHDIRECT; - profileswitch.executeProfileSwitch = true; - newDialog.setOptions(profileswitch, R.string.careportal_profileswitch); - newDialog.show(getActivity().getSupportFragmentManager(), "NewNSTreatmentDialog"); - })) - .validator(() -> ProfileFunctions.getInstance().getProfile() != null) - .visibility(() -> ProfileFunctions.getInstance().getProfile() == null) - ) - .add(new SWScreen(R.string.configbuilder_pump) - .skippable(false) - .add(new SWPlugin() - .option(PluginType.PUMP, R.string.configbuilder_pump_description) - .label(R.string.configbuilder_pump)) - .add(new SWBreak()) - .add(new SWButton() - .text(R.string.pumpsetup) - .action(() -> { - final PluginBase plugin = (PluginBase) ConfigBuilderPlugin.getPlugin().getActivePump(); - PasswordProtection.QueryPassword(activity, R.string.settings_password, "settings_password", () -> { - Intent i = new Intent(activity, PreferencesActivity.class); - i.putExtra("id", plugin.getPreferencesId()); - activity.startActivity(i); - }, null); - }) - .visibility(() -> ((PluginBase) ConfigBuilderPlugin.getPlugin().getActivePump()).getPreferencesId() > 0)) - .add(new SWButton() - .text(R.string.readstatus) - .action(() -> ConfigBuilderPlugin.getPlugin().getCommandQueue().readStatus("Clicked connect to pump", null)) - .visibility(() -> ConfigBuilderPlugin.getPlugin().getActivePump() != null)) - .add(new SWEventListener(this) - .listener(new Object() { - @Subscribe - public void onEventPumpStatusChanged(EventPumpStatusChanged event) { - MainApp.bus().post(new EventSWLabel(event.textStatus())); - } - }) - ) - .validator(() -> ConfigBuilderPlugin.getPlugin().getActivePump() != null && ConfigBuilderPlugin.getPlugin().getActivePump().isInitialized()) - ) - .add(new SWScreen(R.string.configbuilder_aps) - .skippable(false) - .add(new SWInfotext() - .label(R.string.setupwizard_aps_description)) - .add(new SWBreak()) - .add(new SWHtmlLink() - .label("https://openaps.readthedocs.io/en/latest/")) - .add(new SWBreak()) - .add(new SWPlugin() - .option(PluginType.APS, R.string.configbuilder_aps_description) - .label(R.string.configbuilder_aps)) - .add(new SWButton() - .text(R.string.apssetup) - .action(() -> { - final PluginBase plugin = (PluginBase) ConfigBuilderPlugin.getPlugin().getActiveAPS(); - PasswordProtection.QueryPassword(activity, R.string.settings_password, "settings_password", () -> { - Intent i = new Intent(activity, PreferencesActivity.class); - i.putExtra("id", plugin.getPreferencesId()); - activity.startActivity(i); - }, null); - }) - .visibility(() -> ConfigBuilderPlugin.getPlugin().getActiveAPS() != null && ((PluginBase) ConfigBuilderPlugin.getPlugin().getActiveAPS()).getPreferencesId() > 0)) - .validator(() -> ConfigBuilderPlugin.getPlugin().getActiveAPS() != null) - .visibility(() -> Config.APS) - ) - .add(new SWScreen(R.string.apsmode_title) - .skippable(false) - .add(new SWRadioButton() - .option(R.array.aps_modeArray, R.array.aps_modeValues) - .preferenceId(R.string.key_aps_mode).label(R.string.apsmode_title) - .comment(R.string.setupwizard_preferred_aps_mode)) - .validator(() -> SP.contains(R.string.key_aps_mode)) - ) - .add(new SWScreen(R.string.configbuilder_loop) - .skippable(false) - .add(new SWInfotext() - .label(R.string.setupwizard_loop_description)) - .add(new SWBreak()) - .add(new SWButton() - .text(R.string.enableloop) - .action(() -> { - LoopPlugin.getPlugin().setPluginEnabled(PluginType.LOOP, true); - LoopPlugin.getPlugin().setFragmentVisible(PluginType.LOOP, true); - ConfigBuilderFragment.processOnEnabledCategoryChanged(LoopPlugin.getPlugin(), PluginType.LOOP); - ConfigBuilderPlugin.getPlugin().storeSettings("SetupWizard"); - MainApp.bus().post(new EventConfigBuilderChange()); - MainApp.bus().post(new EventSWUpdate(true)); - }) - .visibility(() -> !LoopPlugin.getPlugin().isEnabled(PluginType.LOOP))) - .validator(() -> LoopPlugin.getPlugin().isEnabled(PluginType.LOOP)) - .visibility(() -> !LoopPlugin.getPlugin().isEnabled(PluginType.LOOP) && Config.APS) - ) - .add(new SWScreen(R.string.configbuilder_sensitivity) - .skippable(false) - .add(new SWInfotext() - .label(R.string.setupwizard_sensitivity_description)) - .add(new SWHtmlLink() - .label(R.string.setupwizard_sensitivity_url)) - .add(new SWBreak()) - .add(new SWPlugin() - .option(PluginType.SENSITIVITY, R.string.configbuilder_sensitivity_description) - .label(R.string.configbuilder_sensitivity)) - .add(new SWBreak()) - .add(new SWButton() - .text(R.string.sensitivitysetup) - .action(() -> { - final PluginBase plugin = (PluginBase) ConfigBuilderPlugin.getPlugin().getActiveSensitivity(); - PasswordProtection.QueryPassword(activity, R.string.settings_password, "settings_password", () -> { - Intent i = new Intent(activity, PreferencesActivity.class); - i.putExtra("id", plugin.getPreferencesId()); - activity.startActivity(i); - }, null); - }) - .visibility(() -> ConfigBuilderPlugin.getPlugin().getActiveSensitivity() != null && ((PluginBase) ConfigBuilderPlugin.getPlugin().getActiveSensitivity()).getPreferencesId() > 0)) - .validator(() -> ConfigBuilderPlugin.getPlugin().getActiveSensitivity() != null) - ) - .add(new SWScreen(R.string.objectives) - .skippable(false) - .add(new SWInfotext() - .label(R.string.setupwizard_objectives_description)) - .add(new SWButton() - .text(R.string.enableobjectives) - .action(() -> { - ObjectivesPlugin.getPlugin().setPluginEnabled(PluginType.CONSTRAINTS, true); - ObjectivesPlugin.getPlugin().setFragmentVisible(PluginType.CONSTRAINTS, true); - ConfigBuilderFragment.processOnEnabledCategoryChanged(ObjectivesPlugin.getPlugin(), PluginType.CONSTRAINTS); - ConfigBuilderPlugin.getPlugin().storeSettings("SetupWizard"); - MainApp.bus().post(new EventConfigBuilderChange()); - MainApp.bus().post(new EventSWUpdate(true)); - }) - .visibility(() -> !ObjectivesPlugin.getPlugin().isFragmentVisible())) - .validator(() -> ObjectivesPlugin.getPlugin().isEnabled(PluginType.CONSTRAINTS)) - .visibility(() -> !ObjectivesPlugin.getPlugin().isFragmentVisible() && Config.APS) - ) - .add(new SWScreen(R.string.objectives) - .skippable(false) - .add(new SWInfotext() - .label(R.string.startobjective)) - .add(new SWBreak()) - .add(new SWFragment(this) - .add(new ObjectivesFragment())) - .validator(() -> ObjectivesPlugin.getPlugin().objectives.get(0).isStarted()) - .visibility(() -> !ObjectivesPlugin.getPlugin().objectives.get(0).isStarted() && Config.APS) - ) + add(screenSetupWizard) + .add(screenLanguage) + .add(screenEula) + .add(screenPermissionBattery) + .add(screenPermissionBt) + .add(screenPermissionStore) + .add(screenImport) + .add(screenNsClient) + .add(screenAge) + .add(screenInsulin) + .add(screenBgSource) + .add(screenProfile) + .add(screenNsProfile) + .add(screenLocalProfile) + .add(screenSimpleProfile) + .add(screenProfileSwitch) + .add(screenPump) + .add(screenAps) + .add(screenApsMode) + .add(screenLoop) + .add(screenSensitivity) + .add(getScreenObjectives) + ; + } + + private void SWDefinitionPumpControl() { + // List all the screens here + add(screenSetupWizard) + .add(screenLanguage) + .add(screenEula) + .add(screenPermissionBattery) + .add(screenPermissionBt) + .add(screenPermissionStore) + .add(screenImport) + .add(screenNsClient) + .add(screenAge) + .add(screenInsulin) + .add(screenBgSource) + .add(screenProfile) + .add(screenNsProfile) + .add(screenLocalProfile) + .add(screenSimpleProfile) + .add(screenProfileSwitch) + .add(screenPump) + .add(screenSensitivity) ; } private void SWDefinitionNSClient() { // List all the screens here - add(new SWScreen(R.string.nav_setupwizard) - .add(new SWInfotext() - .label(R.string.welcometosetupwizard)) - ) - .add(new SWScreen(R.string.language) - .skippable(false) - .add(new SWRadioButton() - .option(R.array.languagesArray, R.array.languagesValues) - .preferenceId(R.string.key_language).label(R.string.language) - .comment(R.string.setupwizard_language_prompt)) - .validator(() -> { - String lang = SP.getString("language", "en"); - LocaleHelper.setLocale(MainApp.instance().getApplicationContext(), lang); - return SP.contains(R.string.key_language); - }) - ) - .add(new SWScreen(R.string.end_user_license_agreement) - .skippable(false) - .add(new SWInfotext() - .label(R.string.end_user_license_agreement_text)) - .add(new SWBreak()) - .add(new SWButton() - .text(R.string.end_user_license_agreement_i_understand) - .visibility(() -> !SP.getBoolean(R.string.key_i_understand, false)) - .action(() -> { - SP.putBoolean(R.string.key_i_understand, true); - MainApp.bus().post(new EventSWUpdate(false)); - })) - .visibility(() -> !SP.getBoolean(R.string.key_i_understand, false)) - .validator(() -> SP.getBoolean(R.string.key_i_understand, false)) - ) - .add(new SWScreen(R.string.permission) - .skippable(false) - .add(new SWInfotext() - .label(String.format(MainApp.gs(R.string.needwhitelisting), MainApp.gs(R.string.app_name)))) - .add(new SWBreak()) - .add(new SWButton() - .text(R.string.askforpermission) - .visibility(() -> Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !AndroidPermission.checkForPermission(getActivity(), Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS)) - .action(() -> AndroidPermission.askForPermission(getActivity(), Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS, AndroidPermission.CASE_BATTERY))) - .visibility(() -> Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !AndroidPermission.checkForPermission(getActivity(), Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS)) - .validator(() -> !(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !AndroidPermission.checkForPermission(getActivity(), Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS))) - ) - .add(new SWScreen(R.string.permission) - .skippable(false) - .add(new SWInfotext() - .label(MainApp.gs(R.string.needstoragepermission))) - .add(new SWBreak()) - .add(new SWButton() - .text(R.string.askforpermission) - .visibility(() -> Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !AndroidPermission.checkForPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE)) - .action(() -> AndroidPermission.askForPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE, AndroidPermission.CASE_STORAGE))) - .visibility(() -> Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !AndroidPermission.checkForPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE)) - .validator(() -> !(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !AndroidPermission.checkForPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE))) - ) - .add(new SWScreen(R.string.nav_import) - .add(new SWInfotext() - .label(R.string.storedsettingsfound)) - .add(new SWBreak()) - .add(new SWButton() - .text(R.string.nav_import) - .action(() -> ImportExportPrefs.importSharedPreferences(getActivity()))) - .visibility(() -> ImportExportPrefs.file.exists() && !(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !AndroidPermission.checkForPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE))) - ) - .add(new SWScreen(R.string.nsclientinternal_title) - .skippable(true) - .add(new SWInfotext() - .label(R.string.nsclientinfotext)) - .add(new SWBreak()) - .add(new SWButton() - .text(R.string.enable_nsclient) - .action(() -> { - NSClientPlugin.getPlugin().setPluginEnabled(PluginType.GENERAL, true); - NSClientPlugin.getPlugin().setFragmentVisible(PluginType.GENERAL, true); - ConfigBuilderFragment.processOnEnabledCategoryChanged(NSClientPlugin.getPlugin(), PluginType.GENERAL); - ConfigBuilderPlugin.getPlugin().storeSettings("SetupWizard"); - MainApp.bus().post(new EventConfigBuilderChange()); - MainApp.bus().post(new EventSWUpdate(true)); - }) - .visibility(() -> !NSClientPlugin.getPlugin().isEnabled(PluginType.GENERAL))) - .add(new SWEditUrl() - .preferenceId(R.string.key_nsclientinternal_url) - .updateDelay(5) - .label(R.string.nsclientinternal_url_title) - .comment(R.string.nsclientinternal_url_dialogmessage)) - .add(new SWEditString() - .validator(text -> text.length() >= 12) - .updateDelay(5) - .preferenceId(R.string.key_nsclientinternal_api_secret) - .label(R.string.nsclientinternal_secret_dialogtitle) - .comment(R.string.nsclientinternal_secret_dialogmessage)) - .add(new SWBreak()) - .add(new SWEventListener(this) - .label(R.string.status) - .initialStatus(NSClientPlugin.getPlugin().status) - .listener(new Object() { - @Subscribe - public void onEventNSClientStatus(EventNSClientStatus event) { - MainApp.bus().post(new EventSWLabel(event.status)); - } - }) - ) - .add(new SWBreak()) - .validator(() -> NSClientPlugin.getPlugin().nsClientService != null && NSClientPlugin.getPlugin().nsClientService.isConnected && NSClientPlugin.getPlugin().nsClientService.hasWriteAuth) - .visibility(() -> !(NSClientPlugin.getPlugin().nsClientService != null && NSClientPlugin.getPlugin().nsClientService.isConnected && NSClientPlugin.getPlugin().nsClientService.hasWriteAuth)) - ) - .add(new SWScreen(R.string.configbuilder_bgsource) - .skippable(false) - .add(new SWPlugin() - .option(PluginType.BGSOURCE, R.string.configbuilder_bgsource_description) - .label(R.string.configbuilder_bgsource)) - .add(new SWBreak()) - .add(new SWButton() - .text(R.string.bgsourcesetup) - .action(() -> { - final PluginBase plugin = (PluginBase) ConfigBuilderPlugin.getPlugin().getActiveBgSource(); - PasswordProtection.QueryPassword(activity, R.string.settings_password, "settings_password", () -> { - Intent i = new Intent(activity, PreferencesActivity.class); - i.putExtra("id", plugin.getPreferencesId()); - activity.startActivity(i); - }, null); - }) - .visibility(() -> ConfigBuilderPlugin.getPlugin().getActiveBgSource()!= null && ((PluginBase) ConfigBuilderPlugin.getPlugin().getActiveBgSource()).getPreferencesId() > 0)) - .validator(() -> ConfigBuilderPlugin.getPlugin().getActiveBgSource() != null) - ) - .add(new SWScreen(R.string.patientage) - .skippable(false) - .add(new SWInfotext() - .label(R.string.patientage_summary)) - .add(new SWBreak()) - .add(new SWRadioButton() - .option(R.array.ageArray, R.array.ageValues) - .preferenceId(R.string.key_age) - .label(R.string.patientage) - .comment(R.string.patientage_summary)) - .validator(() -> SP.contains(R.string.key_age)) - ) - .add(new SWScreen(R.string.configbuilder_insulin) - .skippable(false) - .add(new SWPlugin() - .option(PluginType.INSULIN, R.string.configbuilder_insulin_description) - .makeVisible(false) - .label(R.string.configbuilder_insulin)) - .add(new SWBreak()) - .add(new SWInfotext() - .label(R.string.diawarning)) - .add(new SWBreak()) - .add(new SWButton() - .text(R.string.insulinsourcesetup) - .action(() -> { - final PluginBase plugin = (PluginBase) ConfigBuilderPlugin.getPlugin().getActiveInsulin(); - PasswordProtection.QueryPassword(activity, R.string.settings_password, "settings_password", () -> { - Intent i = new Intent(activity, PreferencesActivity.class); - i.putExtra("id", plugin.getPreferencesId()); - activity.startActivity(i); - }, null); - }) - .visibility(() -> ConfigBuilderPlugin.getPlugin().getActiveInsulin()!= null && ((PluginBase) ConfigBuilderPlugin.getPlugin().getActiveInsulin()).getPreferencesId() > 0)) - .validator(() -> ConfigBuilderPlugin.getPlugin().getActiveInsulin() != null) - ) - .add(new SWScreen(R.string.configbuilder_sensitivity) - .skippable(false) - .add(new SWInfotext() - .label(R.string.setupwizard_sensitivity_description)) - .add(new SWHtmlLink() - .label(R.string.setupwizard_sensitivity_url)) - .add(new SWBreak()) - .add(new SWPlugin() - .option(PluginType.SENSITIVITY, R.string.configbuilder_sensitivity_description) - .label(R.string.configbuilder_sensitivity)) - .add(new SWBreak()) - .add(new SWButton() - .text(R.string.sensitivitysetup) - .action(() -> { - final PluginBase plugin = (PluginBase) ConfigBuilderPlugin.getPlugin().getActiveSensitivity(); - PasswordProtection.QueryPassword(activity, R.string.settings_password, "settings_password", () -> { - Intent i = new Intent(activity, PreferencesActivity.class); - i.putExtra("id", plugin.getPreferencesId()); - activity.startActivity(i); - }, null); - }) - .visibility(() -> ConfigBuilderPlugin.getPlugin().getActiveSensitivity() != null && ((PluginBase) ConfigBuilderPlugin.getPlugin().getActiveSensitivity()).getPreferencesId() > 0)) - .validator(() -> ConfigBuilderPlugin.getPlugin().getActiveSensitivity() != null) - ) + add(screenSetupWizard) + .add(screenLanguage) + .add(screenEula) + .add(screenPermissionBattery) + .add(screenPermissionStore) + .add(screenImport) + .add(screenNsClient) + .add(screenBgSource) + .add(screenAge) + .add(screenInsulin) + .add(screenSensitivity) ; } diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/SWEventListener.java b/app/src/main/java/info/nightscout/androidaps/setupwizard/SWEventListener.java index 487a80d91c..26944effbf 100644 --- a/app/src/main/java/info/nightscout/androidaps/setupwizard/SWEventListener.java +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/SWEventListener.java @@ -1,33 +1,42 @@ package info.nightscout.androidaps.setupwizard; import android.content.Context; -import android.view.View; import android.widget.LinearLayout; import android.widget.TextView; -import com.squareup.otto.Subscribe; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.events.EventStatus; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.setupwizard.elements.SWItem; -import info.nightscout.androidaps.setupwizard.events.EventSWLabel; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.CompositeDisposable; public class SWEventListener extends SWItem { private static Logger log = LoggerFactory.getLogger(SWEventListener.class); + private CompositeDisposable disposable = new CompositeDisposable(); private int textLabel = 0; private String status = ""; TextView textView; - Object listener; SWDefinition definition; - SWEventListener(SWDefinition definition) { + SWEventListener(SWDefinition definition, Class clazz) { super(Type.LISTENER); this.definition = definition; - MainApp.bus().register(this); + disposable.add(RxBus.INSTANCE + .toObservable(clazz) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(event -> { + status = ((EventStatus) event).getStatus(); + if (textView != null) + textView.setText((textLabel != 0 ? MainApp.gs(textLabel) : "") + " " + status); + }) + ); + } public SWEventListener label(int newLabel) { @@ -35,16 +44,11 @@ public class SWEventListener extends SWItem { return this; } - public SWEventListener initialStatus(String status) { + SWEventListener initialStatus(String status) { this.status = status; return this; } - public SWEventListener listener(Object listener) { - this.listener = listener; - return this; - } - @Override public void generateDialog(LinearLayout layout) { Context context = layout.getContext(); @@ -53,20 +57,5 @@ public class SWEventListener extends SWItem { textView.setId(layout.generateViewId()); textView.setText((textLabel != 0 ? MainApp.gs(textLabel) : "") + " " + status); layout.addView(textView); - if (listener != null) - try { - MainApp.bus().register(listener); - } catch (Exception ignored) {} } - - @Subscribe - public void onEventSWLabel(final EventSWLabel l) { - status = l.label; - if (definition != null && definition.getActivity() != null) - definition.getActivity().runOnUiThread(() -> { - if (textView != null) - textView.setText((textLabel != 0 ? MainApp.gs(textLabel) : "") + " " + status); - }); - } - } diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/SetupWizardActivity.java b/app/src/main/java/info/nightscout/androidaps/setupwizard/SetupWizardActivity.java index 9049ad7463..e6a4831693 100644 --- a/app/src/main/java/info/nightscout/androidaps/setupwizard/SetupWizardActivity.java +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/SetupWizardActivity.java @@ -3,15 +3,14 @@ package info.nightscout.androidaps.setupwizard; import android.content.Intent; import android.content.pm.PackageManager; import android.os.Bundle; -import android.support.v4.app.ActivityCompat; -import android.support.v7.app.AlertDialog; -import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.LinearLayout; import android.widget.ScrollView; import android.widget.TextView; -import com.squareup.otto.Subscribe; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; +import androidx.core.app.ActivityCompat; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -21,21 +20,26 @@ import java.util.List; import info.nightscout.androidaps.MainActivity; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; -import info.nightscout.androidaps.events.EventProfileStoreChanged; +import info.nightscout.androidaps.activities.NoSplashAppCompatActivity; import info.nightscout.androidaps.events.EventProfileNeedsUpdate; +import info.nightscout.androidaps.events.EventProfileStoreChanged; import info.nightscout.androidaps.events.EventPumpStatusChanged; -import info.nightscout.androidaps.plugins.constraints.objectives.events.EventObjectivesSaved; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientStatus; import info.nightscout.androidaps.setupwizard.elements.SWItem; import info.nightscout.androidaps.setupwizard.events.EventSWUpdate; import info.nightscout.androidaps.utils.AndroidPermission; +import info.nightscout.androidaps.utils.FabricPrivacy; import info.nightscout.androidaps.utils.LocaleHelper; import info.nightscout.androidaps.utils.OKDialog; import info.nightscout.androidaps.utils.SP; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.CompositeDisposable; -public class SetupWizardActivity extends AppCompatActivity { +public class SetupWizardActivity extends NoSplashAppCompatActivity { //logging private static Logger log = LoggerFactory.getLogger(SetupWizardActivity.class); + private CompositeDisposable disposable = new CompositeDisposable(); ScrollView scrollView; @@ -45,9 +49,9 @@ public class SetupWizardActivity extends AppCompatActivity { public static final String INTENT_MESSAGE = "WIZZARDPAGE"; @Override - protected void onCreate(Bundle savedInstanceState) { + public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - LocaleHelper.onCreate(this, "en"); + LocaleHelper.INSTANCE.update(getApplicationContext()); setContentView(R.layout.activity_setupwizard); scrollView = (ScrollView) findViewById(R.id.sw_scrollview); @@ -70,7 +74,8 @@ public class SetupWizardActivity extends AppCompatActivity { @Override public void onBackPressed() { - if (currentWizardPage == 0) OKDialog.showConfirmation(this, MainApp.gs(R.string.exitwizard), this::finish); + if (currentWizardPage == 0) + OKDialog.showConfirmation(this, MainApp.gs(R.string.exitwizard), this::finish); else showPreviousPage(null); } @@ -82,46 +87,41 @@ public class SetupWizardActivity extends AppCompatActivity { @Override public void onPause() { super.onPause(); - MainApp.bus().unregister(this); + disposable.clear(); } @Override protected void onResume() { super.onResume(); - MainApp.bus().register(this); swDefinition.setActivity(this); - } - - @Subscribe - public void onContentUpdate(EventSWUpdate ev) { - if (ev.redraw) - generateLayout(); - updateButtons(); - } - - @Subscribe - public void onEventNSClientStatus(EventNSClientStatus ignored) { - updateButtons(); - } - - @Subscribe - public void onEventPumpStatusChanged(EventPumpStatusChanged ignored) { - updateButtons(); - } - - @Subscribe - public void onEventProfileStoreChanged(EventProfileStoreChanged ignored) { - updateButtons(); - } - - @Subscribe - public void onEventProfileSwitchChange(EventProfileNeedsUpdate ignored) { - updateButtons(); - } - - @Subscribe - public void onEventObjectivesSaved(EventObjectivesSaved ignored) { - updateButtons(); + disposable.add(RxBus.INSTANCE + .toObservable(EventPumpStatusChanged.class) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(event -> updateButtons(), FabricPrivacy::logException) + ); + disposable.add(RxBus.INSTANCE + .toObservable(EventNSClientStatus.class) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(event -> updateButtons(), FabricPrivacy::logException) + ); + disposable.add(RxBus.INSTANCE + .toObservable(EventProfileNeedsUpdate.class) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(event -> updateButtons(), FabricPrivacy::logException) + ); + disposable.add(RxBus.INSTANCE + .toObservable(EventProfileStoreChanged.class) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(event -> updateButtons(), FabricPrivacy::logException) + ); + disposable.add(RxBus.INSTANCE + .toObservable(EventSWUpdate.class) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(event -> { + if (event.getRedraw()) generateLayout(); + updateButtons(); + }, FabricPrivacy::logException) + ); } private void generateLayout() { @@ -131,7 +131,7 @@ public class SetupWizardActivity extends AppCompatActivity { SWItem currentItem = currentScreen.items.get(i); currentItem.generateDialog(layout); } - scrollView.smoothScrollTo(0,0); + scrollView.smoothScrollTo(0, 0); } private void updateButtons() { @@ -223,4 +223,10 @@ public class SetupWizardActivity extends AppCompatActivity { updateButtons(); } + @Override + protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { + super.onActivityResult(requestCode, resultCode, data); + if (requestCode == AndroidPermission.CASE_BATTERY) + updateButtons(); + } } diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWEditUrl.java b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWEditUrl.java index 7e6ead7d72..f35decbf53 100644 --- a/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWEditUrl.java +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWEditUrl.java @@ -16,6 +16,7 @@ import org.slf4j.LoggerFactory; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.setupwizard.events.EventSWLabel; import info.nightscout.androidaps.utils.SP; @@ -62,7 +63,7 @@ public class SWEditUrl extends SWItem { if (Patterns.WEB_URL.matcher(s).matches()) save(s.toString(), updateDelay); else - MainApp.bus().post(new EventSWLabel(MainApp.gs(R.string.error_url_not_valid))); + RxBus.INSTANCE.send(new EventSWLabel(MainApp.gs(R.string.error_url_not_valid))); } @Override diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWFragment.java b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWFragment.java index a061f57eb6..29149e5b81 100644 --- a/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWFragment.java @@ -1,7 +1,7 @@ package info.nightscout.androidaps.setupwizard.elements; -import android.support.v4.app.Fragment; -import android.view.View; +import androidx.fragment.app.Fragment; + import android.widget.LinearLayout; import org.slf4j.Logger; diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWItem.java b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWItem.java index 4e8113c73c..6931344acf 100644 --- a/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWItem.java +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWItem.java @@ -14,6 +14,7 @@ import java.util.concurrent.TimeUnit; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.events.EventPreferenceChange; import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.setupwizard.events.EventSWUpdate; import info.nightscout.androidaps.utils.SP; @@ -97,8 +98,8 @@ public class SWItem { public void run() { if (L.isEnabled(L.CORE)) log.debug("Firing EventPreferenceChange"); - MainApp.bus().post(new EventPreferenceChange(preferenceId)); - MainApp.bus().post(new EventSWUpdate()); + RxBus.INSTANCE.send(new EventPreferenceChange(preferenceId)); + RxBus.INSTANCE.send(new EventSWUpdate(false)); scheduledEventPost = null; } } diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWPlugin.java b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWPlugin.java index 060102ba9c..e5dde501bc 100644 --- a/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWPlugin.java @@ -17,7 +17,7 @@ import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.events.EventConfigBuilderChange; import info.nightscout.androidaps.interfaces.PluginBase; import info.nightscout.androidaps.interfaces.PluginType; -import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderFragment; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.setupwizard.events.EventSWUpdate; @@ -86,10 +86,10 @@ public class SWPlugin extends SWItem { PluginBase plugin = (PluginBase) rb.getTag(); plugin.setPluginEnabled(pType, rb.isChecked()); plugin.setFragmentVisible(pType, rb.isChecked() && makeVisible); - ConfigBuilderFragment.processOnEnabledCategoryChanged(plugin, pType); + ConfigBuilderPlugin.getPlugin().processOnEnabledCategoryChanged(plugin, pType); ConfigBuilderPlugin.getPlugin().storeSettings("SetupWizard"); - MainApp.bus().post(new EventConfigBuilderChange()); - MainApp.bus().post(new EventSWUpdate()); + RxBus.INSTANCE.send(new EventConfigBuilderChange()); + RxBus.INSTANCE.send(new EventSWUpdate(false)); }); layout.addView(radioGroup); super.generateDialog(layout); diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/events/EventSWUpdate.java b/app/src/main/java/info/nightscout/androidaps/setupwizard/events/EventSWUpdate.java deleted file mode 100644 index 181960ac40..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/setupwizard/events/EventSWUpdate.java +++ /dev/null @@ -1,14 +0,0 @@ -package info.nightscout.androidaps.setupwizard.events; - -import info.nightscout.androidaps.events.Event; - -public class EventSWUpdate extends Event { - public boolean redraw = false; - - public EventSWUpdate() { - } - - public EventSWUpdate(boolean redraw) { - this.redraw = redraw; - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/events/EventSWUpdate.kt b/app/src/main/java/info/nightscout/androidaps/setupwizard/events/EventSWUpdate.kt new file mode 100644 index 0000000000..3c700cda41 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/events/EventSWUpdate.kt @@ -0,0 +1,5 @@ +package info.nightscout.androidaps.setupwizard.events + +import info.nightscout.androidaps.events.Event + +class EventSWUpdate(var redraw: Boolean) : Event() \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/tabs/TabPageAdapter.java b/app/src/main/java/info/nightscout/androidaps/tabs/TabPageAdapter.java index 53b3700c72..c567df7b49 100644 --- a/app/src/main/java/info/nightscout/androidaps/tabs/TabPageAdapter.java +++ b/app/src/main/java/info/nightscout/androidaps/tabs/TabPageAdapter.java @@ -1,10 +1,10 @@ package info.nightscout.androidaps.tabs; import android.content.Context; -import android.support.annotation.Nullable; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentManager; -import android.support.v4.app.FragmentPagerAdapter; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentPagerAdapter; import android.view.ViewGroup; import org.slf4j.Logger; diff --git a/app/src/main/java/info/nightscout/androidaps/utils/AndroidPermission.java b/app/src/main/java/info/nightscout/androidaps/utils/AndroidPermission.java index 3f6c3a3ce0..9f7986cb97 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/AndroidPermission.java +++ b/app/src/main/java/info/nightscout/androidaps/utils/AndroidPermission.java @@ -1,15 +1,23 @@ package info.nightscout.androidaps.utils; import android.Manifest; +import android.annotation.SuppressLint; import android.app.Activity; +import android.content.ActivityNotFoundException; import android.content.Context; +import android.content.Intent; import android.content.pm.PackageManager; +import android.net.Uri; import android.os.Build; -import android.support.v4.app.ActivityCompat; -import android.support.v4.content.ContextCompat; +import android.os.PowerManager; +import android.provider.Settings; + +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification; import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification; import info.nightscout.androidaps.plugins.general.overview.notifications.Notification; @@ -21,85 +29,103 @@ public class AndroidPermission { public static final int CASE_SMS = 0x2; public static final int CASE_LOCATION = 0x3; public static final int CASE_BATTERY = 0x4; - public static final int CASE_PHONESTATE = 0x5; + public static final int CASE_PHONE_STATE = 0x5; - public static void askForPermission(Activity activity, String[] permission, Integer requestCode) { + private static boolean permission_battery_optimization_failed = false; + + @SuppressLint("BatteryLife") + private static void askForPermission(Activity activity, String[] permission, Integer requestCode) { boolean test = false; - for (int i = 0; i < permission.length; i++) { - test = test || (ContextCompat.checkSelfPermission(activity, permission[i]) != PackageManager.PERMISSION_GRANTED); + boolean testBattery = false; + for (String s : permission) { + test = test || (ContextCompat.checkSelfPermission(activity, s) != PackageManager.PERMISSION_GRANTED); + if (s.equals(Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS)) { + PowerManager powerManager = (PowerManager) activity.getSystemService(Context.POWER_SERVICE); + String packageName = activity.getPackageName(); + testBattery = testBattery || !powerManager.isIgnoringBatteryOptimizations(packageName); + } } if (test) { ActivityCompat.requestPermissions(activity, permission, requestCode); } + if (testBattery) { + try { + Intent i = new Intent(); + i.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS); + i.setData(Uri.parse("package:" + activity.getPackageName())); + activity.startActivityForResult(i, CASE_BATTERY); + } catch (ActivityNotFoundException e) { + permission_battery_optimization_failed = true; + OKDialog.show(activity, MainApp.gs(R.string.permission), MainApp.gs(R.string.alert_dialog_permission_battery_optimization_failed), activity::recreate); + } + } } public static void askForPermission(Activity activity, String permission, Integer requestCode) { String[] permissions = {permission}; - - if (ContextCompat.checkSelfPermission(activity, permission) != PackageManager.PERMISSION_GRANTED) - ActivityCompat.requestPermissions(activity, permissions, requestCode); + askForPermission(activity, permissions, requestCode); } - public static boolean checkForPermission(Context context, String permission) { - return ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED; + public static boolean permissionNotGranted(Context context, String permission) { + boolean selfCheck = ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED; + if (permission.equals(Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS)) { + if (!permission_battery_optimization_failed) { + PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); + String packageName = context.getPackageName(); + selfCheck = selfCheck && powerManager.isIgnoringBatteryOptimizations(packageName); + } + } + return !selfCheck; } public static synchronized void notifyForSMSPermissions(Activity activity) { if (SP.getBoolean(R.string.key_smscommunicator_remotecommandsallowed, false)) { - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) { - if (!checkForPermission(activity, Manifest.permission.RECEIVE_SMS)) { - NotificationWithAction notification = new NotificationWithAction(Notification.PERMISSION_SMS, MainApp.gs(R.string.smscommunicator_missingsmspermission), Notification.URGENT); - notification.action(MainApp.gs(R.string.request), () -> AndroidPermission.askForPermission(activity, new String[]{Manifest.permission.RECEIVE_SMS, - Manifest.permission.SEND_SMS, - Manifest.permission.RECEIVE_MMS}, AndroidPermission.CASE_SMS)); - MainApp.bus().post(new EventNewNotification(notification)); - } else - MainApp.bus().post(new EventDismissNotification(Notification.PERMISSION_SMS)); - } + if (permissionNotGranted(activity, Manifest.permission.RECEIVE_SMS)) { + NotificationWithAction notification = new NotificationWithAction(Notification.PERMISSION_SMS, MainApp.gs(R.string.smscommunicator_missingsmspermission), Notification.URGENT); + notification.action(R.string.request, () -> AndroidPermission.askForPermission(activity, new String[]{Manifest.permission.RECEIVE_SMS, + Manifest.permission.SEND_SMS, + Manifest.permission.RECEIVE_MMS}, AndroidPermission.CASE_SMS)); + RxBus.INSTANCE.send(new EventNewNotification(notification)); + } else + RxBus.INSTANCE.send(new EventDismissNotification(Notification.PERMISSION_SMS)); // Following is a bug in Android 8 if (Build.VERSION.SDK_INT == Build.VERSION_CODES.O) { - if (!checkForPermission(activity, Manifest.permission.READ_PHONE_STATE)) { + if (permissionNotGranted(activity, Manifest.permission.READ_PHONE_STATE)) { NotificationWithAction notification = new NotificationWithAction(Notification.PERMISSION_PHONESTATE, MainApp.gs(R.string.smscommunicator_missingphonestatepermission), Notification.URGENT); - notification.action(MainApp.gs(R.string.request), () -> - AndroidPermission.askForPermission(activity, new String[]{Manifest.permission.READ_PHONE_STATE}, AndroidPermission.CASE_PHONESTATE)); - MainApp.bus().post(new EventNewNotification(notification)); + notification.action(R.string.request, () -> + AndroidPermission.askForPermission(activity, new String[]{Manifest.permission.READ_PHONE_STATE}, AndroidPermission.CASE_PHONE_STATE)); + RxBus.INSTANCE.send(new EventNewNotification(notification)); } else - MainApp.bus().post(new EventDismissNotification(Notification.PERMISSION_PHONESTATE)); + RxBus.INSTANCE.send(new EventDismissNotification(Notification.PERMISSION_PHONESTATE)); } } } public static synchronized void notifyForBatteryOptimizationPermission(Activity activity) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - if (!checkForPermission(activity, Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS)) { - NotificationWithAction notification = new NotificationWithAction(Notification.PERMISSION_BATTERY, String.format(MainApp.gs(R.string.needwhitelisting), MainApp.gs(R.string.app_name)), Notification.URGENT); - notification.action(MainApp.gs(R.string.request), () -> AndroidPermission.askForPermission(activity, new String[]{Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS}, AndroidPermission.CASE_BATTERY)); - MainApp.bus().post(new EventNewNotification(notification)); - } else - MainApp.bus().post(new EventDismissNotification(Notification.PERMISSION_BATTERY)); - } + if (permissionNotGranted(activity, Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS)) { + NotificationWithAction notification = new NotificationWithAction(Notification.PERMISSION_BATTERY, String.format(MainApp.gs(R.string.needwhitelisting), MainApp.gs(R.string.app_name)), Notification.URGENT); + notification.action(R.string.request, () -> AndroidPermission.askForPermission(activity, new String[]{Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS}, AndroidPermission.CASE_BATTERY)); + RxBus.INSTANCE.send(new EventNewNotification(notification)); + } else + RxBus.INSTANCE.send(new EventDismissNotification(Notification.PERMISSION_BATTERY)); } public static synchronized void notifyForStoragePermission(Activity activity) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - if (!checkForPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { - NotificationWithAction notification = new NotificationWithAction(Notification.PERMISSION_STORAGE, MainApp.gs(R.string.needstoragepermission), Notification.URGENT); - notification.action(MainApp.gs(R.string.request), () -> AndroidPermission.askForPermission(activity, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, - Manifest.permission.WRITE_EXTERNAL_STORAGE}, AndroidPermission.CASE_STORAGE)); - MainApp.bus().post(new EventNewNotification(notification)); - } else - MainApp.bus().post(new EventDismissNotification(Notification.PERMISSION_STORAGE)); - } + if (permissionNotGranted(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { + NotificationWithAction notification = new NotificationWithAction(Notification.PERMISSION_STORAGE, MainApp.gs(R.string.needstoragepermission), Notification.URGENT); + notification.action(R.string.request, () -> AndroidPermission.askForPermission(activity, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.WRITE_EXTERNAL_STORAGE}, AndroidPermission.CASE_STORAGE)); + RxBus.INSTANCE.send(new EventNewNotification(notification)); + } else + RxBus.INSTANCE.send(new EventDismissNotification(Notification.PERMISSION_STORAGE)); } public static synchronized void notifyForLocationPermissions(Activity activity) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - if (!checkForPermission(activity, Manifest.permission.ACCESS_FINE_LOCATION)) { - NotificationWithAction notification = new NotificationWithAction(Notification.PERMISSION_LOCATION, MainApp.gs(R.string.needlocationpermission), Notification.URGENT); - notification.action(MainApp.gs(R.string.request), () -> AndroidPermission.askForPermission(activity, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, AndroidPermission.CASE_LOCATION)); - MainApp.bus().post(new EventNewNotification(notification)); - } else - MainApp.bus().post(new EventDismissNotification(Notification.PERMISSION_LOCATION)); - } + if (permissionNotGranted(activity, Manifest.permission.ACCESS_FINE_LOCATION)) { + NotificationWithAction notification = new NotificationWithAction(Notification.PERMISSION_LOCATION, MainApp.gs(R.string.needlocationpermission), Notification.URGENT); + notification.action(R.string.request, () -> AndroidPermission.askForPermission(activity, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, AndroidPermission.CASE_LOCATION)); + RxBus.INSTANCE.send(new EventNewNotification(notification)); + } else + RxBus.INSTANCE.send(new EventDismissNotification(Notification.PERMISSION_LOCATION)); } } diff --git a/app/src/main/java/info/nightscout/androidaps/utils/BolusWizard.java b/app/src/main/java/info/nightscout/androidaps/utils/BolusWizard.java deleted file mode 100644 index cd789ee225..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/utils/BolusWizard.java +++ /dev/null @@ -1,183 +0,0 @@ -package info.nightscout.androidaps.utils; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus; -import info.nightscout.androidaps.data.IobTotal; -import info.nightscout.androidaps.data.Profile; -import info.nightscout.androidaps.db.TempTarget; -import info.nightscout.androidaps.interfaces.TreatmentsInterface; -import info.nightscout.androidaps.logging.L; -import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; -import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; - -/** - * Created by mike on 11.10.2016. - */ - -public class BolusWizard { - private Logger log = LoggerFactory.getLogger(L.CORE); - // Inputs - private Profile specificProfile = null; - private TempTarget tempTarget; - public Integer carbs = 0; - private Double bg = 0d; - private Double cob = 0d; - private Double correction; - private Double percentageCorrection; - private Boolean includeBolusIOB = true; - private Boolean includeBasalIOB = true; - public Boolean superBolus = false; - private Boolean trend = false; - - // Intermediate - public double sens = 0d; - public double ic = 0d; - - public GlucoseStatus glucoseStatus; - - public double targetBGLow = 0d; - public double targetBGHigh = 0d; - public double bgDiff = 0d; - - public double insulinFromBG = 0d; - public double insulinFromCarbs = 0d; - public double insulingFromBolusIOB = 0d; - public double insulingFromBasalsIOB = 0d; - public double insulinFromCorrection = 0d; - public double insulinFromSuperBolus = 0d; - public double insulinFromCOB = 0d; - public double insulinFromTrend = 0d; - - // Result - public Double calculatedTotalInsulin = 0d; - public Double totalBeforePercentageAdjustment = 0d; - public Double carbsEquivalent = 0d; - - public Double doCalc(Profile specificProfile, TempTarget tempTarget, Integer carbs, Double cob, Double bg, Double correction, Boolean includeBolusIOB, Boolean includeBasalIOB, Boolean superBolus, Boolean trend) { - return doCalc(specificProfile, tempTarget, carbs, cob, bg, correction, 100d, includeBolusIOB, includeBasalIOB, superBolus, trend); - } - - public Double doCalc(Profile specificProfile, TempTarget tempTarget, Integer carbs, Double cob, Double bg, Double correction, double percentageCorrection, Boolean includeBolusIOB, Boolean includeBasalIOB, Boolean superBolus, Boolean trend) { - this.specificProfile = specificProfile; - this.tempTarget = tempTarget; - this.carbs = carbs; - this.bg = bg; - this.cob = cob; - this.correction = correction; - this.percentageCorrection = percentageCorrection; - this.includeBolusIOB = includeBolusIOB; - this.includeBasalIOB = includeBasalIOB; - this.superBolus = superBolus; - this.trend = trend; - - // Insulin from BG - sens = specificProfile.getIsf(); - targetBGLow = specificProfile.getTargetLow(); - targetBGHigh = specificProfile.getTargetHigh(); - if (tempTarget != null) { - targetBGLow = Profile.fromMgdlToUnits(tempTarget.low, specificProfile.getUnits()); - targetBGHigh = Profile.fromMgdlToUnits(tempTarget.high, specificProfile.getUnits()); - } - if (bg >= targetBGLow && bg <= targetBGHigh) { - bgDiff = 0d; - } else if (bg <= targetBGLow) { - bgDiff = bg - targetBGLow; - } else { - bgDiff = bg - targetBGHigh; - } - insulinFromBG = bg != 0d ? bgDiff / sens : 0d; - - // Insulin from 15 min trend - glucoseStatus = GlucoseStatus.getGlucoseStatusData(); - if (glucoseStatus != null && trend) { - insulinFromTrend = (Profile.fromMgdlToUnits(glucoseStatus.short_avgdelta, specificProfile.getUnits()) * 3) / sens; - } - - // Insuling from carbs - ic = specificProfile.getIc(); - insulinFromCarbs = carbs / ic; - insulinFromCOB = cob / ic; - - // Insulin from IOB - // IOB calculation - TreatmentsInterface treatments = TreatmentsPlugin.getPlugin(); - treatments.updateTotalIOBTreatments(); - IobTotal bolusIob = treatments.getLastCalculationTreatments().round(); - treatments.updateTotalIOBTempBasals(); - IobTotal basalIob = treatments.getLastCalculationTempBasals().round(); - - insulingFromBolusIOB = includeBolusIOB ? -bolusIob.iob : 0d; - insulingFromBasalsIOB = includeBasalIOB ? -basalIob.basaliob : 0d; - - // Insulin from correction - insulinFromCorrection = correction; - - // Insulin from superbolus for 2h. Get basal rate now and after 1h - if (superBolus) { - insulinFromSuperBolus = specificProfile.getBasal(); - long timeAfter1h = System.currentTimeMillis(); - timeAfter1h += T.hours(1).msecs(); - insulinFromSuperBolus += specificProfile.getBasal(timeAfter1h); - } - - // Total - calculatedTotalInsulin = insulinFromBG + insulinFromTrend + insulinFromCarbs + insulingFromBolusIOB + insulingFromBasalsIOB + insulinFromCorrection + insulinFromSuperBolus + insulinFromCOB; - - // Percentage adjustment - totalBeforePercentageAdjustment = calculatedTotalInsulin; - if (calculatedTotalInsulin > 0) { - calculatedTotalInsulin = calculatedTotalInsulin * percentageCorrection / 100d; - } - - if (calculatedTotalInsulin < 0) { - carbsEquivalent = -calculatedTotalInsulin * ic; - calculatedTotalInsulin = 0d; - } - - double bolusStep = ConfigBuilderPlugin.getPlugin().getActivePump().getPumpDescription().bolusStep; - calculatedTotalInsulin = Round.roundTo(calculatedTotalInsulin, bolusStep); - - log.debug(log()); - - return calculatedTotalInsulin; - } - - public String log() { - StringBuilder sb = new StringBuilder(); - - sb.append("TempTarget=").append(tempTarget != null ? tempTarget.toString() : "null").append("; "); - sb.append("Carbs=").append(carbs != null ? carbs : null).append("; "); - sb.append("Bg=").append(bg).append("; "); - sb.append("Cob=").append(cob).append("; "); - sb.append("Correction=").append(correction).append("; "); - sb.append("PercentageCorrection=").append(percentageCorrection).append("; "); - sb.append("IncludeBolusIOB=").append(includeBolusIOB).append("; "); - sb.append("IncludeBasalIOB=").append(includeBasalIOB).append("; "); - sb.append("Superbolus=").append(superBolus).append("; "); - sb.append("Trend=").append(trend).append("; "); - sb.append("Profile=").append(specificProfile != null && specificProfile.getData() != null ? specificProfile.getData().toString() : "null").append("; "); - sb.append("\n"); - - sb.append("targetBGLow=").append(targetBGLow).append("; "); - sb.append("targetBGHigh=").append(targetBGHigh).append("; "); - sb.append("bgDiff=").append(bgDiff).append("; "); - sb.append("insulinFromBG=").append(insulinFromBG).append("; "); - sb.append("insulinFromCarbs=").append(insulinFromCarbs).append("; "); - sb.append("insulingFromBolusIOB=").append(insulingFromBolusIOB).append("; "); - sb.append("insulingFromBasalsIOB=").append(insulingFromBasalsIOB).append("; "); - sb.append("insulinFromCorrection=").append(insulinFromCorrection).append("; "); - sb.append("insulinFromSuperBolus=").append(insulinFromSuperBolus).append("; "); - sb.append("insulinFromCOB=").append(insulinFromCOB).append("; "); - sb.append("insulinFromTrend=").append(insulinFromTrend).append("; "); - sb.append("\n"); - - - sb.append("calculatedTotalInsulin=").append(calculatedTotalInsulin).append("; "); - sb.append("totalBeforePercentageAdjustment=").append(totalBeforePercentageAdjustment).append("; "); - sb.append("carbsEquivalent=").append(carbsEquivalent).append("; "); - - return sb.toString(); - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/utils/BolusWizard.kt b/app/src/main/java/info/nightscout/androidaps/utils/BolusWizard.kt new file mode 100644 index 0000000000..ef4cae3f83 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/utils/BolusWizard.kt @@ -0,0 +1,356 @@ +package info.nightscout.androidaps.utils + +import android.content.Context +import android.content.Intent +import androidx.appcompat.app.AlertDialog +import info.nightscout.androidaps.MainApp +import info.nightscout.androidaps.R +import info.nightscout.androidaps.data.DetailedBolusInfo +import info.nightscout.androidaps.data.Profile +import info.nightscout.androidaps.db.CareportalEvent +import info.nightscout.androidaps.db.Source +import info.nightscout.androidaps.db.TempTarget +import info.nightscout.androidaps.events.EventRefreshOverview +import info.nightscout.androidaps.interfaces.Constraint +import info.nightscout.androidaps.interfaces.PluginType +import info.nightscout.androidaps.interfaces.PumpDescription +import info.nightscout.androidaps.interfaces.PumpInterface +import info.nightscout.androidaps.logging.L +import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin +import info.nightscout.androidaps.plugins.bus.RxBus +import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin +import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions +import info.nightscout.androidaps.plugins.general.overview.dialogs.ErrorHelperActivity +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin +import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin +import info.nightscout.androidaps.queue.Callback +import org.json.JSONException +import org.json.JSONObject +import org.slf4j.LoggerFactory +import java.util.* +import kotlin.math.abs + +class BolusWizard @JvmOverloads constructor(val profile: Profile, + val profileName: String, + val tempTarget: TempTarget?, + val carbs: Int, + val cob: Double, + val bg: Double, + val correction: Double, + private val percentageCorrection: Double = 100.0, + private val useBg: Boolean, + private val useCob: Boolean, + private val includeBolusIOB: Boolean, + private val includeBasalIOB: Boolean, + private val useSuperBolus: Boolean, + private val useTT: Boolean, + private val useTrend: Boolean, + val notes: String = "", + private val carbTime: Int = 0 +) { + + private val log = LoggerFactory.getLogger(L.CORE) + + // Intermediate + var sens = 0.0 + private set + + var ic = 0.0 + private set + + var glucoseStatus: GlucoseStatus? = null + private set + + private var targetBGLow = 0.0 + + private var targetBGHigh = 0.0 + + private var bgDiff = 0.0 + + var insulinFromBG = 0.0 + private set + + var insulinFromCarbs = 0.0 + private set + + var insulinFromBolusIOB = 0.0 + private set + + var insulinFromBasalsIOB = 0.0 + private set + + var insulinFromCorrection = 0.0 + private set + + var insulinFromSuperBolus = 0.0 + private set + + var insulinFromCOB = 0.0 + private set + + var insulinFromTrend = 0.0 + private set + + var trend = 0.0 + private set + + private var accepted = false + + // Result + var calculatedTotalInsulin: Double = 0.0 + private set + + var totalBeforePercentageAdjustment: Double = 0.0 + private set + + var carbsEquivalent: Double = 0.0 + private set + + var insulinAfterConstraints: Double = 0.0 + private set + + init { + doCalc() + } + + private fun doCalc() { + + // Insulin from BG + sens = profile.isf + targetBGLow = profile.targetLow + targetBGHigh = profile.targetHigh + if (useTT && tempTarget != null) { + targetBGLow = Profile.fromMgdlToUnits(tempTarget.low, profile.units) + targetBGHigh = Profile.fromMgdlToUnits(tempTarget.high, profile.units) + } + if (useBg && bg > 0) { + bgDiff = when { + bg in targetBGLow..targetBGHigh -> 0.0 + bg <= targetBGLow -> bg - targetBGLow + else -> bg - targetBGHigh + } + insulinFromBG = bgDiff / sens + } + + // Insulin from 15 min trend + glucoseStatus = GlucoseStatus.getGlucoseStatusData() + glucoseStatus?.let { + if (useTrend) { + trend = it.short_avgdelta + insulinFromTrend = Profile.fromMgdlToUnits(trend, profile.units) * 3 / sens + } + } + + + // Insulin from carbs + ic = profile.ic + insulinFromCarbs = carbs / ic + insulinFromCOB = if (useCob) (cob / ic) else 0.0 + + // Insulin from IOB + // IOB calculation + val treatments = TreatmentsPlugin.getPlugin() + treatments.updateTotalIOBTreatments() + val bolusIob = treatments.lastCalculationTreatments.round() + treatments.updateTotalIOBTempBasals() + val basalIob = treatments.lastCalculationTempBasals.round() + + insulinFromBolusIOB = if (includeBolusIOB) -bolusIob.iob else 0.0 + insulinFromBasalsIOB = if (includeBasalIOB) -basalIob.basaliob else 0.0 + + // Insulin from correction + insulinFromCorrection = correction + + // Insulin from superbolus for 2h. Get basal rate now and after 1h + if (useSuperBolus) { + insulinFromSuperBolus = profile.basal + var timeAfter1h = System.currentTimeMillis() + timeAfter1h += T.hours(1).msecs() + insulinFromSuperBolus += profile.getBasal(timeAfter1h) + } + + // Total + calculatedTotalInsulin = insulinFromBG + insulinFromTrend + insulinFromCarbs + insulinFromBolusIOB + insulinFromBasalsIOB + insulinFromCorrection + insulinFromSuperBolus + insulinFromCOB + + // Percentage adjustment + totalBeforePercentageAdjustment = calculatedTotalInsulin + if (calculatedTotalInsulin > 0) { + calculatedTotalInsulin = calculatedTotalInsulin * percentageCorrection / 100.0 + } + + if (calculatedTotalInsulin < 0) { + carbsEquivalent = (-calculatedTotalInsulin) * ic + calculatedTotalInsulin = 0.0 + } + + val bolusStep = ConfigBuilderPlugin.getPlugin().activePump?.pumpDescription?.bolusStep + ?: 0.1 + calculatedTotalInsulin = Round.roundTo(calculatedTotalInsulin, bolusStep) + + insulinAfterConstraints = MainApp.getConstraintChecker().applyBolusConstraints(Constraint(calculatedTotalInsulin)).value() + + log.debug(this.toString()) + } + + private fun nsJSON(): JSONObject { + val boluscalcJSON = JSONObject() + try { + boluscalcJSON.put("profile", profileName) + boluscalcJSON.put("notes", notes) + boluscalcJSON.put("eventTime", DateUtil.toISOString(Date())) + boluscalcJSON.put("targetBGLow", targetBGLow) + boluscalcJSON.put("targetBGHigh", targetBGHigh) + boluscalcJSON.put("isf", sens) + boluscalcJSON.put("ic", ic) + boluscalcJSON.put("iob", -(insulinFromBolusIOB + insulinFromBasalsIOB)) + boluscalcJSON.put("bolusiob", insulinFromBolusIOB) + boluscalcJSON.put("basaliob", insulinFromBasalsIOB) + boluscalcJSON.put("bolusiobused", includeBolusIOB) + boluscalcJSON.put("basaliobused", includeBasalIOB) + boluscalcJSON.put("bg", bg) + boluscalcJSON.put("insulinbg", insulinFromBG) + boluscalcJSON.put("insulinbgused", useBg) + boluscalcJSON.put("bgdiff", bgDiff) + boluscalcJSON.put("insulincarbs", insulinFromCarbs) + boluscalcJSON.put("carbs", carbs) + boluscalcJSON.put("cob", cob) + boluscalcJSON.put("cobused", useCob) + boluscalcJSON.put("insulincob", insulinFromCOB) + boluscalcJSON.put("othercorrection", correction) + boluscalcJSON.put("insulinsuperbolus", insulinFromSuperBolus) + boluscalcJSON.put("insulintrend", insulinFromTrend) + boluscalcJSON.put("insulin", calculatedTotalInsulin) + boluscalcJSON.put("superbolusused", useSuperBolus) + boluscalcJSON.put("insulinsuperbolus", insulinFromSuperBolus) + boluscalcJSON.put("trendused", useTrend) + boluscalcJSON.put("insulintrend", insulinFromTrend) + boluscalcJSON.put("trend", trend) + boluscalcJSON.put("ttused", useTT) + boluscalcJSON.put("percentageCorrection", percentageCorrection) + } catch (e: JSONException) { + log.error("Unhandled exception", e) + } + return boluscalcJSON + } + + private fun confirmMessageAfterConstraints(pump: PumpInterface): String { + + var confirmMessage = MainApp.gs(R.string.entertreatmentquestion) + if (insulinAfterConstraints > 0) { + val pct = if (percentageCorrection != 100.0) " (" + percentageCorrection.toInt() + "%)" else "" + confirmMessage += "
" + MainApp.gs(R.string.bolus) + ": " + "" + DecimalFormatter.toPumpSupportedBolus(insulinAfterConstraints) + "U" + pct + "" + } + if (carbs > 0) { + var timeShift = "" + if (carbTime > 0) { + timeShift += " ( +" + MainApp.gs(R.string.mins, carbTime) + " )" + } else if (carbTime < 0) { + timeShift += " ( -" + MainApp.gs(R.string.mins, carbTime) + " )" + } + confirmMessage += "
" + MainApp.gs(R.string.carbs) + ": " + "" + carbs + "g" + timeShift + "" + } + if (insulinFromCOB > 0) { + confirmMessage += "
" + MainApp.gs(R.string.insulinFromCob, MainApp.gc(R.color.cobAlert), insulinFromBolusIOB + insulinFromBasalsIOB + insulinFromCOB + insulinFromBG) + val absorptionRate = IobCobCalculatorPlugin.getPlugin().slowAbsorptionPercentage(60) + if (absorptionRate > .25) + confirmMessage += "
" + MainApp.gs(R.string.slowabsorptiondetected, MainApp.gc(R.color.cobAlert), (absorptionRate * 100).toInt()) + } + if (abs(insulinAfterConstraints - calculatedTotalInsulin) > pump.pumpDescription.pumpType.determineCorrectBolusStepSize(insulinAfterConstraints)) { + confirmMessage += "
" + MainApp.gs(R.string.bolusconstraintappliedwarning, MainApp.gc(R.color.warning), calculatedTotalInsulin, insulinAfterConstraints) + } + + return confirmMessage + } + + fun confirmAndExecute(context: Context) { + val profile = ProfileFunctions.getInstance().profile ?: return + val pump = ConfigBuilderPlugin.getPlugin().activePump ?: return + + if (calculatedTotalInsulin > 0.0 || carbs > 0.0) { + val confirmMessage = confirmMessageAfterConstraints(pump) + + val builder = AlertDialog.Builder(context) + builder.setTitle(MainApp.gs(R.string.confirmation)) + builder.setMessage(HtmlHelper.fromHtml(confirmMessage)) + builder.setPositiveButton(MainApp.gs(R.string.ok)) { _, _ -> + synchronized(builder) { + if (accepted) { + log.debug("guarding: already accepted") + return@setPositiveButton + } + accepted = true + if (insulinAfterConstraints > 0 || carbs > 0) { + if (useSuperBolus) { + val loopPlugin = LoopPlugin.getPlugin() + if (loopPlugin.isEnabled(PluginType.LOOP)) { + loopPlugin.superBolusTo(System.currentTimeMillis() + 2 * 60L * 60 * 1000) + RxBus.send(EventRefreshOverview("WizardDialog")) + } + + val pump1 = ConfigBuilderPlugin.getPlugin().activePump + + if (pump1?.pumpDescription?.tempBasalStyle == PumpDescription.ABSOLUTE) { + ConfigBuilderPlugin.getPlugin().commandQueue.tempBasalAbsolute(0.0, 120, true, profile, object : Callback() { + override fun run() { + if (!result.success) { + val i = Intent(MainApp.instance(), ErrorHelperActivity::class.java) + i.putExtra("soundid", R.raw.boluserror) + i.putExtra("status", result.comment) + i.putExtra("title", MainApp.gs(R.string.tempbasaldeliveryerror)) + i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + MainApp.instance().startActivity(i) + } + } + }) + } else { + + ConfigBuilderPlugin.getPlugin().commandQueue.tempBasalPercent(0, 120, true, profile, object : Callback() { + override fun run() { + if (!result.success) { + val i = Intent(MainApp.instance(), ErrorHelperActivity::class.java) + i.putExtra("soundid", R.raw.boluserror) + i.putExtra("status", result.comment) + i.putExtra("title", MainApp.gs(R.string.tempbasaldeliveryerror)) + i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + MainApp.instance().startActivity(i) + } + } + }) + } + } + val detailedBolusInfo = DetailedBolusInfo() + detailedBolusInfo.eventType = CareportalEvent.BOLUSWIZARD + detailedBolusInfo.insulin = insulinAfterConstraints + detailedBolusInfo.carbs = carbs.toDouble() + detailedBolusInfo.context = context + detailedBolusInfo.glucose = bg + detailedBolusInfo.glucoseType = "Manual" + detailedBolusInfo.carbTime = carbTime + detailedBolusInfo.boluscalc = nsJSON() + detailedBolusInfo.source = Source.USER + detailedBolusInfo.notes = notes + if (detailedBolusInfo.insulin > 0 || ConfigBuilderPlugin.getPlugin().activePump?.pumpDescription?.storesCarbInfo == true) { + ConfigBuilderPlugin.getPlugin().commandQueue.bolus(detailedBolusInfo, object : Callback() { + override fun run() { + if (!result.success) { + val i = Intent(MainApp.instance(), ErrorHelperActivity::class.java) + i.putExtra("soundid", R.raw.boluserror) + i.putExtra("status", result.comment) + i.putExtra("title", MainApp.gs(R.string.treatmentdeliveryerror)) + i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + MainApp.instance().startActivity(i) + } + } + }) + } else { + TreatmentsPlugin.getPlugin().addToHistoryTreatment(detailedBolusInfo, false) + } + } + } + } + builder.setNegativeButton(MainApp.gs(R.string.cancel), null) + builder.show() + } + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/utils/CompositeDisposablePlusAssign.kt b/app/src/main/java/info/nightscout/androidaps/utils/CompositeDisposablePlusAssign.kt new file mode 100644 index 0000000000..1f2fb892e5 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/utils/CompositeDisposablePlusAssign.kt @@ -0,0 +1,9 @@ +package info.nightscout.androidaps.utils + +import io.reactivex.disposables.CompositeDisposable +import io.reactivex.disposables.Disposable + +operator fun CompositeDisposable.plusAssign(disposable: Disposable) { + add(disposable) +} + diff --git a/app/src/main/java/info/nightscout/androidaps/utils/DateUtil.java b/app/src/main/java/info/nightscout/androidaps/utils/DateUtil.java index 7cc4424244..d592347d1b 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/DateUtil.java +++ b/app/src/main/java/info/nightscout/androidaps/utils/DateUtil.java @@ -1,6 +1,6 @@ package info.nightscout.androidaps.utils; -import android.support.v4.util.LongSparseArray; +import androidx.collection.LongSparseArray; import org.joda.time.DateTime; import org.joda.time.format.DateTimeFormat; @@ -8,6 +8,8 @@ import org.joda.time.format.DateTimeFormatter; import org.joda.time.format.ISODateTimeFormat; import java.text.DateFormat; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; @@ -37,10 +39,8 @@ public class DateUtil { * * @param isoDateString the iso date string * @return the date - * @throws Exception the exception */ - public static Date fromISODateString(String isoDateString) - throws Exception { + public static Date fromISODateString(String isoDateString) { DateTimeFormatter parser = ISODateTimeFormat.dateTimeParser(); DateTime dateTime = DateTime.parse(isoDateString, parser); @@ -71,6 +71,18 @@ public class DateUtil { return toISOString(new Date(date), FORMAT_DATE_ISO_OUT, TimeZone.getTimeZone("UTC")); } + public static String toISOAsUTC(final long timestamp) { + final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'0000Z'", Locale.US); + format.setTimeZone(TimeZone.getTimeZone("UTC")); + return format.format(timestamp); + } + + public static String toISONoZone(final long timestamp) { + final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.US); + format.setTimeZone(TimeZone.getDefault()); + return format.format(timestamp); + } + public static Date toDate(Integer seconds) { Calendar calendar = new GregorianCalendar(); calendar.set(Calendar.MONTH, 0); // Set january to be sure we miss DST changing @@ -106,11 +118,19 @@ public class DateUtil { } public static String timeString(Date date) { - return new DateTime(date).toString(DateTimeFormat.shortTime()); + String format = "hh:mma"; + if (android.text.format.DateFormat.is24HourFormat(MainApp.instance())) { + format = "HH:mm"; + } + return new DateTime(date).toString(DateTimeFormat.forPattern(format)); } public static String timeString(long mills) { - return new DateTime(mills).toString(DateTimeFormat.shortTime()); + String format = "hh:mma"; + if (android.text.format.DateFormat.is24HourFormat(MainApp.instance())) { + format = "HH:mm"; + } + return new DateTime(mills).toString(DateTimeFormat.forPattern(format)); } public static String timeFullString(long mills) { @@ -126,6 +146,7 @@ public class DateUtil { } public static String dateAndTimeString(long mills) { + if (mills == 0) return ""; return dateString(mills) + " " + timeString(mills); } @@ -187,4 +208,84 @@ public class DateUtil { long diff = Math.abs(date - now()); return diff < T.mins(2).msecs(); } + + public static GregorianCalendar gregorianCalendar() { + return new GregorianCalendar(); + } + + public static long getTimeZoneOffsetMs() { + return new GregorianCalendar().getTimeZone().getRawOffset(); + } + + public static int getTimeZoneOffsetMinutes(final long timestamp) { + return TimeZone.getDefault().getOffset(timestamp) / 60000; + } + + public static String niceTimeScalar(long t) { + String unit = MainApp.gs(R.string.unit_second); + t = t / 1000; + if (t != 1) unit = MainApp.gs(R.string.unit_seconds); + if (t > 59) { + unit = MainApp.gs(R.string.unit_minute); + t = t / 60; + if (t != 1) unit = MainApp.gs(R.string.unit_minutes); + if (t > 59) { + unit = MainApp.gs(R.string.unit_hour); + t = t / 60; + if (t != 1) unit = MainApp.gs(R.string.unit_hours); + if (t > 24) { + unit = MainApp.gs(R.string.unit_day) + "\""; + t = t / 24; + if (t != 1) unit = MainApp.gs(R.string.unit_days) + "\""; + if (t > 28) { + unit = MainApp.gs(R.string.unit_week) + "\""; + t = t / 7; + if (t != 1) unit = MainApp.gs(R.string.unit_weeks) + "\""; + } + } + } + } + //if (t != 1) unit = unit + "s"; //implemented plurality in every step, because in other languages plurality of time is not every time adding the same character + return qs((double) t, 0) + " " + unit; + } + + // singletons to avoid repeated allocation + private static DecimalFormatSymbols dfs; + private static DecimalFormat df; + public static String qs(double x, int digits) { + + if (digits == -1) { + digits = 0; + if (((int) x != x)) { + digits++; + if ((((int) x * 10) / 10 != x)) { + digits++; + if ((((int) x * 100) / 100 != x)) digits++; + } + } + } + + if (dfs == null) { + final DecimalFormatSymbols local_dfs = new DecimalFormatSymbols(); + local_dfs.setDecimalSeparator('.'); + dfs = local_dfs; // avoid race condition + } + + final DecimalFormat this_df; + // use singleton if on ui thread otherwise allocate new as DecimalFormat is not thread safe + if (Thread.currentThread().getId() == 1) { + if (df == null) { + final DecimalFormat local_df = new DecimalFormat("#", dfs); + local_df.setMinimumIntegerDigits(1); + df = local_df; // avoid race condition + } + this_df = df; + } else { + this_df = new DecimalFormat("#", dfs); + } + + this_df.setMaximumFractionDigits(digits); + return this_df.format(x); + } + } diff --git a/app/src/main/java/info/nightscout/androidaps/utils/DigitsKeyListenerWithComma.java b/app/src/main/java/info/nightscout/androidaps/utils/DigitsKeyListenerWithComma.java new file mode 100644 index 0000000000..a84ccd61a2 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/utils/DigitsKeyListenerWithComma.java @@ -0,0 +1,199 @@ +package info.nightscout.androidaps.utils; + +import android.text.InputType; +import android.text.SpannableStringBuilder; +import android.text.Spanned; +import android.text.method.NumberKeyListener; +import android.view.KeyEvent; + +class DigitsKeyListenerWithComma extends NumberKeyListener { + + /** + * The characters that are used. + * + * @see KeyEvent#getMatch + * @see #getAcceptedChars + */ + private static final char[][] CHARACTERS = new char[][]{ + new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}, + new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-'}, + new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', ','}, + new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '.', ','}, + }; + + private char[] mAccepted; + private boolean mSign; + private boolean mDecimal; + + private static final int SIGN = 1; + private static final int DECIMAL = 2; + + private static DigitsKeyListenerWithComma[] sInstance = new DigitsKeyListenerWithComma[4]; + + @Override + protected char[] getAcceptedChars() { + return mAccepted; + } + + /** + * Allocates a DigitsKeyListener that accepts the digits 0 through 9. + */ + public DigitsKeyListenerWithComma() { + this(false, false); + } + + /** + * Allocates a DigitsKeyListener that accepts the digits 0 through 9, + * plus the minus sign (only at the beginning) and/or decimal point + * (only one per field) if specified. + */ + public DigitsKeyListenerWithComma(boolean sign, boolean decimal) { + mSign = sign; + mDecimal = decimal; + + int kind = (sign ? SIGN : 0) | (decimal ? DECIMAL : 0); + mAccepted = CHARACTERS[kind]; + } + + /** + * Returns a DigitsKeyListener that accepts the digits 0 through 9. + */ + public static DigitsKeyListenerWithComma getInstance() { + return getInstance(false, false); + } + + /** + * Returns a DigitsKeyListener that accepts the digits 0 through 9, + * plus the minus sign (only at the beginning) and/or decimal point + * (only one per field) if specified. + */ + public static DigitsKeyListenerWithComma getInstance(boolean sign, boolean decimal) { + int kind = (sign ? SIGN : 0) | (decimal ? DECIMAL : 0); + + if (sInstance[kind] != null) + return sInstance[kind]; + + sInstance[kind] = new DigitsKeyListenerWithComma(sign, decimal); + return sInstance[kind]; + } + + /** + * Returns a DigitsKeyListener that accepts only the characters + * that appear in the specified String. Note that not all characters + * may be available on every keyboard. + */ + public static DigitsKeyListenerWithComma getInstance(String accepted) { + // TODO: do we need a cache of these to avoid allocating? + + DigitsKeyListenerWithComma dim = new DigitsKeyListenerWithComma(); + + dim.mAccepted = new char[accepted.length()]; + accepted.getChars(0, accepted.length(), dim.mAccepted, 0); + + return dim; + } + + public int getInputType() { + int contentType = InputType.TYPE_CLASS_NUMBER; + if (mSign) { + contentType |= InputType.TYPE_NUMBER_FLAG_SIGNED; + } + if (mDecimal) { + contentType |= InputType.TYPE_NUMBER_FLAG_DECIMAL; + } + return contentType; + } + + @Override + public CharSequence filter(CharSequence source, int start, int end, + Spanned dest, int dstart, int dend) { + CharSequence out = super.filter(source, start, end, dest, dstart, dend); + + if (mSign == false && mDecimal == false) { + return out; + } + + if (out != null) { + source = out; + start = 0; + end = out.length(); + } + + int sign = -1; + int decimal = -1; + int dlen = dest.length(); + + /* + * Find out if the existing text has '-' or '.' characters. + */ + + for (int i = 0; i < dstart; i++) { + char c = dest.charAt(i); + + if (c == '-') { + sign = i; + } else if (c == '.' || c == ',') { + decimal = i; + } + } + for (int i = dend; i < dlen; i++) { + char c = dest.charAt(i); + + if (c == '-') { + return ""; // Nothing can be inserted in front of a '-'. + } else if (c == '.' || c == ',') { + decimal = i; + } + } + + /* + * If it does, we must strip them out from the source. + * In addition, '-' must be the very first character, + * and nothing can be inserted before an existing '-'. + * Go in reverse order so the offsets are stable. + */ + + SpannableStringBuilder stripped = null; + + for (int i = end - 1; i >= start; i--) { + char c = source.charAt(i); + boolean strip = false; + + if (c == '-') { + if (i != start || dstart != 0) { + strip = true; + } else if (sign >= 0) { + strip = true; + } else { + sign = i; + } + } else if (c == '.' || c == ',') { + if (decimal >= 0) { + strip = true; + } else { + decimal = i; + } + } + + if (strip) { + if (end == start + 1) { + return ""; // Only one character, and it was stripped. + } + + if (stripped == null) { + stripped = new SpannableStringBuilder(source, start, end); + } + + stripped.delete(i - start, i + 1 - start); + } + } + + if (stripped != null) { + return stripped; + } else if (out != null) { + return out; + } else { + return null; + } + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/utils/FabricPrivacy.java b/app/src/main/java/info/nightscout/androidaps/utils/FabricPrivacy.java index 54f06bfaf0..355623f38d 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/FabricPrivacy.java +++ b/app/src/main/java/info/nightscout/androidaps/utils/FabricPrivacy.java @@ -8,10 +8,13 @@ import com.google.firebase.analytics.FirebaseAnalytics; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.List; + import info.nightscout.androidaps.BuildConfig; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.logging.L; import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; +import info.nightscout.androidaps.plugins.constraints.signatureVerifier.SignatureVerifierPlugin; /** * Created by jamorham on 21/02/2018. @@ -112,19 +115,23 @@ public class FabricPrivacy { String closedLoopEnabled = MainApp.getConstraintChecker().isClosedLoopAllowed().value() ? "CLOSED_LOOP_ENABLED" : "CLOSED_LOOP_DISABLED"; // Size is limited to 36 chars - String remote = BuildConfig.REMOTE - .replace("https://","") - .replace("http://","") + String remote = BuildConfig.REMOTE.toLowerCase() + .replace("https://", "") + .replace("http://", "") .replace(".git", "") .replace(".com/", ":") .replace(".org/", ":") .replace(".net/", ":"); MainApp.getFirebaseAnalytics().setUserProperty("Mode", BuildConfig.APPLICATION_ID + "-" + closedLoopEnabled); - MainApp.getFirebaseAnalytics().setUserProperty("Language", LocaleHelper.getLanguage(MainApp.instance())); + MainApp.getFirebaseAnalytics().setUserProperty("Language", LocaleHelper.INSTANCE.currentLanguage()); MainApp.getFirebaseAnalytics().setUserProperty("Version", BuildConfig.VERSION); MainApp.getFirebaseAnalytics().setUserProperty("HEAD", BuildConfig.HEAD); MainApp.getFirebaseAnalytics().setUserProperty("Remote", remote); + List hashes = SignatureVerifierPlugin.getPlugin().shortHashes(); + if (hashes.size() >= 1) + MainApp.getFirebaseAnalytics().setUserProperty("Hash", hashes.get(0)); + if (ConfigBuilderPlugin.getPlugin().getActivePump() != null) MainApp.getFirebaseAnalytics().setUserProperty("Pump", ConfigBuilderPlugin.getPlugin().getActivePump().getClass().getSimpleName()); if (ConfigBuilderPlugin.getPlugin().getActiveAPS() != null) @@ -137,7 +144,6 @@ public class FabricPrivacy { MainApp.getFirebaseAnalytics().setUserProperty("Sensitivity", ConfigBuilderPlugin.getPlugin().getActiveSensitivity().getClass().getSimpleName()); if (ConfigBuilderPlugin.getPlugin().getActiveInsulin() != null) MainApp.getFirebaseAnalytics().setUserProperty("Insulin", ConfigBuilderPlugin.getPlugin().getActiveInsulin().getClass().getSimpleName()); - } } diff --git a/app/src/main/java/info/nightscout/androidaps/utils/HtmlHelper.kt b/app/src/main/java/info/nightscout/androidaps/utils/HtmlHelper.kt new file mode 100644 index 0000000000..b79fe884b4 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/utils/HtmlHelper.kt @@ -0,0 +1,16 @@ +package info.nightscout.androidaps.utils + +import android.os.Build +import android.text.Html +import android.text.Spanned + +object HtmlHelper { + fun fromHtml(source: String): Spanned { + // API level 24 to replace call + @Suppress("DEPRECATION") + return when { + Build.VERSION.SDK_INT >= Build.VERSION_CODES.N -> Html.fromHtml(source, Html.FROM_HTML_MODE_LEGACY) + else -> Html.fromHtml(source) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/utils/InstanceId.kt b/app/src/main/java/info/nightscout/androidaps/utils/InstanceId.kt new file mode 100644 index 0000000000..64c84fcaa9 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/utils/InstanceId.kt @@ -0,0 +1,10 @@ +package info.nightscout.androidaps.utils + +import com.google.firebase.iid.FirebaseInstanceId + +object InstanceId { + fun instanceId(): String { + var id = FirebaseInstanceId.getInstance().id + return id + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/utils/JSONFormatter.java b/app/src/main/java/info/nightscout/androidaps/utils/JSONFormatter.java index 159e90fda7..6205beee21 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/JSONFormatter.java +++ b/app/src/main/java/info/nightscout/androidaps/utils/JSONFormatter.java @@ -1,5 +1,6 @@ package info.nightscout.androidaps.utils; +import android.os.Build; import android.text.Html; import android.text.Spanned; @@ -18,14 +19,23 @@ public class JSONFormatter { private static Logger log = LoggerFactory.getLogger(JSONFormatter.class); public static Spanned format(final String jsonString) { - final JsonVisitor visitor = new JsonVisitor(4, ' '); + final JsonVisitor visitor = new JsonVisitor(1, '\t'); try { if (jsonString.equals("undefined")) return Html.fromHtml("undefined"); else if (jsonString.getBytes()[0] == '[') - return Html.fromHtml(visitor.visit(new JSONArray(jsonString), 0)); - else - return Html.fromHtml(visitor.visit(new JSONObject(jsonString), 0)); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + return Html.fromHtml(visitor.visit(new JSONArray(jsonString), 0), Html.FROM_HTML_MODE_COMPACT); + } else { + return Html.fromHtml(visitor.visit(new JSONArray(jsonString), 0)); + } + else { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + return Html.fromHtml(visitor.visit(new JSONObject(jsonString), 0), Html.FROM_HTML_MODE_COMPACT); + } else { + return Html.fromHtml(visitor.visit(new JSONObject(jsonString), 0)); + } + } } catch (JSONException e) { log.error("Unhandled exception", e); return Html.fromHtml(""); @@ -33,7 +43,7 @@ public class JSONFormatter { } public static Spanned format(final JSONObject object) { - final JsonVisitor visitor = new JsonVisitor(4, ' '); + final JsonVisitor visitor = new JsonVisitor(1, '\t'); try { return Html.fromHtml(visitor.visit(object, 0)); } catch (JSONException e) { @@ -58,7 +68,7 @@ public class JSONFormatter { } else { ret += write("[", indent); for (int i = 0; i < length; i++) { - ret += visit(array.get(i), indent + 1); + ret += visit(array.get(i), indent); } ret += write("]", indent); } @@ -73,8 +83,8 @@ public class JSONFormatter { final Iterator keys = obj.keys(); while (keys.hasNext()) { final String key = keys.next(); - ret += write("" + key + ": ", indent + 1); - ret += visit(obj.get(key), 0); + ret += write("" + key + ": ", indent); + ret += visit(obj.get(key), indent + 1); ret += "
"; } } @@ -86,7 +96,7 @@ public class JSONFormatter { if (object instanceof JSONArray) { ret += visit((JSONArray) object, indent); } else if (object instanceof JSONObject) { - ret += visit((JSONObject) object, indent); + ret += "
" + visit((JSONObject) object, indent); } else { if (object instanceof String) { ret += write("\"" + ((String) object).replace("<", "<").replace(">", ">") + "\"", indent); diff --git a/app/src/main/java/info/nightscout/androidaps/utils/JsonHelper.java b/app/src/main/java/info/nightscout/androidaps/utils/JsonHelper.java index 8f7d9611b1..5308c62420 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/JsonHelper.java +++ b/app/src/main/java/info/nightscout/androidaps/utils/JsonHelper.java @@ -1,6 +1,6 @@ package info.nightscout.androidaps.utils; -import android.support.annotation.Nullable; +import androidx.annotation.Nullable; import org.json.JSONException; import org.json.JSONObject; @@ -17,7 +17,7 @@ public class JsonHelper { private static final Logger log = LoggerFactory.getLogger(JsonHelper.class); - private JsonHelper() {}; + private JsonHelper() {} public static Object safeGetObject(JSONObject json, String fieldName, Object defaultValue) { Object result = defaultValue; @@ -72,6 +72,19 @@ public class JsonHelper { return result; } + public static double safeGetDouble(JSONObject json, String fieldName, double defaultValue) { + double result = defaultValue; + + if (json != null && json.has(fieldName)) { + try { + result = json.getDouble(fieldName); + } catch (JSONException ignored) { + } + } + + return result; + } + public static int safeGetInt(JSONObject json, String fieldName) { int result = 0; diff --git a/app/src/main/java/info/nightscout/androidaps/utils/LocalAlertUtils.java b/app/src/main/java/info/nightscout/androidaps/utils/LocalAlertUtils.java index d23020a56c..282ad1aa55 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/LocalAlertUtils.java +++ b/app/src/main/java/info/nightscout/androidaps/utils/LocalAlertUtils.java @@ -10,9 +10,10 @@ import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.db.BgReading; import info.nightscout.androidaps.db.DatabaseHelper; import info.nightscout.androidaps.interfaces.PumpInterface; +import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions; -import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin; import info.nightscout.androidaps.plugins.general.nsclient.NSUpload; import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification; import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification; @@ -43,13 +44,13 @@ public class LocalAlertUtils { Notification n = new Notification(Notification.PUMP_UNREACHABLE, MainApp.gs(R.string.pump_unreachable), Notification.URGENT); n.soundId = R.raw.alarm; SP.putLong("nextPumpDisconnectedAlarm", System.currentTimeMillis() + pumpUnreachableThreshold()); - MainApp.bus().post(new EventNewNotification(n)); + RxBus.INSTANCE.send(new EventNewNotification(n)); if (SP.getBoolean(R.string.key_ns_create_announcements_from_errors, true)) { NSUpload.uploadError(n.text); } } if (!isStatusOutdated && !alarmTimeoutExpired) - MainApp.bus().post(new EventDismissNotification(Notification.PUMP_UNREACHABLE)); + RxBus.INSTANCE.send(new EventDismissNotification(Notification.PUMP_UNREACHABLE)); } /*Presnoozes the alarms with 5 minutes if no snooze exists. @@ -97,7 +98,7 @@ public class LocalAlertUtils { Notification n = new Notification(Notification.BG_READINGS_MISSED, MainApp.gs(R.string.missed_bg_readings), Notification.URGENT); n.soundId = R.raw.alarm; SP.putLong("nextMissedReadingsAlarm", System.currentTimeMillis() + missedReadingsThreshold()); - MainApp.bus().post(new EventNewNotification(n)); + RxBus.INSTANCE.send(new EventNewNotification(n)); if (SP.getBoolean(R.string.key_ns_create_announcements_from_errors, true)) { NSUpload.uploadError(n.text); } diff --git a/app/src/main/java/info/nightscout/androidaps/utils/LocaleHelper.java b/app/src/main/java/info/nightscout/androidaps/utils/LocaleHelper.java deleted file mode 100644 index bf0b2796ee..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/utils/LocaleHelper.java +++ /dev/null @@ -1,65 +0,0 @@ -package info.nightscout.androidaps.utils; - -import android.content.Context; -import android.content.SharedPreferences; -import android.content.res.Configuration; -import android.content.res.Resources; -import android.preference.PreferenceManager; - -import java.util.Locale; - -/** - * This class is used to change your application locale and persist this change for the next time - * that your app is going to be used. - *

- * You can also change the locale of your application on the fly by using the setLocale method. - *

- * Created by gunhansancar on 07/10/15. - */ -public class LocaleHelper { - - private static final String SELECTED_LANGUAGE = "Locale.Helper.Selected.Language"; - - public static void onCreate(Context context) { - String lang = getPersistedData(context, Locale.getDefault().getLanguage()); - setLocale(context, lang); - } - - public static void onCreate(Context context, String defaultLanguage) { - String lang = getPersistedData(context, defaultLanguage); - setLocale(context, lang); - } - - public static String getLanguage(Context context) { - return getPersistedData(context, Locale.getDefault().getLanguage()); - } - - public static void setLocale(Context context, String language) { - persist(context, language); - updateResources(context, language); - } - - private static String getPersistedData(Context context, String defaultLanguage) { - return SP.getString(SELECTED_LANGUAGE, defaultLanguage); - } - - private static void persist(Context context, String language) { - SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); - SharedPreferences.Editor editor = preferences.edit(); - - editor.putString(SELECTED_LANGUAGE, language); - editor.apply(); - } - - private static void updateResources(Context context, String language) { - Locale locale = new Locale(language); - Locale.setDefault(locale); - - Resources resources = context.getResources(); - - Configuration configuration = resources.getConfiguration(); - configuration.locale = locale; - - resources.updateConfiguration(configuration, resources.getDisplayMetrics()); - } -} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/utils/LocaleHelper.kt b/app/src/main/java/info/nightscout/androidaps/utils/LocaleHelper.kt new file mode 100644 index 0000000000..6052c5699a --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/utils/LocaleHelper.kt @@ -0,0 +1,32 @@ +package info.nightscout.androidaps.utils + +import android.content.Context +import info.nightscout.androidaps.R +import java.util.* + +object LocaleHelper { + fun update(context: Context) = + updateResources(context, currentLanguage()) + + fun currentLanguage(): String = + SP.getString(R.string.key_language, Locale.getDefault().language) + + fun currentLocale(): Locale = + Locale(SP.getString(R.string.key_language, Locale.getDefault().language)) + + @Suppress("DEPRECATION") + private fun updateResources(context: Context, language: String) { + var locale = Locale(language) + if (language.contains("_")) { + // language with country like pt_BR defined in arrays.xml + val lang = language.substring(0, 2) + val country = language.substring(3, 5) + locale = Locale(lang, country) + } + + Locale.setDefault(locale) + val resources = context.resources + resources.configuration.setLocale(locale) + resources.updateConfiguration(resources.configuration, resources.displayMetrics) + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/utils/NumberPicker.java b/app/src/main/java/info/nightscout/androidaps/utils/NumberPicker.java index 71c16442dc..e2a7489fc2 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/NumberPicker.java +++ b/app/src/main/java/info/nightscout/androidaps/utils/NumberPicker.java @@ -14,7 +14,6 @@ import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.LinearLayout; -import android.widget.TextView; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -34,7 +33,11 @@ public class NumberPicker extends LinearLayout implements View.OnKeyListener, View.OnTouchListener, View.OnClickListener { private static Logger log = LoggerFactory.getLogger(NumberPicker.class); - TextView editText; + public interface OnValueChangedListener { + void onValueChanged(double value); + } + + EditText editText; Button minusButton; Button plusButton; @@ -46,8 +49,11 @@ public class NumberPicker extends LinearLayout implements View.OnKeyListener, boolean allowZero = false; TextWatcher textWatcher = null; + Button okButton = null; + private Handler mHandler; private ScheduledExecutorService mUpdater; + private OnValueChangedListener mOnValueChangedListener; private class UpdateCounterTask implements Runnable { private boolean mInc; @@ -56,7 +62,7 @@ public class NumberPicker extends LinearLayout implements View.OnKeyListener, private final int doubleLimit = 5; - public UpdateCounterTask(boolean inc) { + UpdateCounterTask(boolean inc) { mInc = inc; } @@ -85,39 +91,32 @@ public class NumberPicker extends LinearLayout implements View.OnKeyListener, public NumberPicker(Context context, AttributeSet attrs) { super(context, attrs); - this.initialize(context, attrs); + this.initialize(context); } - public NumberPicker(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - } - - private void initialize(Context context, AttributeSet attrs) { + private void initialize(Context context) { // set layout view LayoutInflater.from(context).inflate(R.layout.number_picker_layout, this, true); // init ui components - minusButton = (Button) findViewById(R.id.decrement); + minusButton = findViewById(R.id.decrement); minusButton.setId(View.generateViewId()); - plusButton = (Button) findViewById(R.id.increment); + plusButton = findViewById(R.id.increment); plusButton.setId(View.generateViewId()); - editText = (EditText) findViewById(R.id.display); + editText = findViewById(R.id.display); editText.setId(View.generateViewId()); - mHandler = new Handler() { - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MSG_INC: - inc(msg.arg1); - return; - case MSG_DEC: - dec(msg.arg1); - return; - } - super.handleMessage(msg); + mHandler = new Handler(msg -> { + switch (msg.what) { + case MSG_INC: + inc(msg.arg1); + return true; + case MSG_DEC: + dec(msg.arg1); + return true; } - }; + return false; + }); minusButton.setOnTouchListener(this); minusButton.setOnKeyListener(this); @@ -139,33 +138,66 @@ public class NumberPicker extends LinearLayout implements View.OnKeyListener, @Override public void afterTextChanged(Editable s) { value = SafeParse.stringToDouble(editText.getText().toString()); + callValueChangedListener(); + if (okButton != null) { + if (value > maxValue || value < minValue) + okButton.setVisibility(INVISIBLE); + else + okButton.setVisibility(VISIBLE); + } } }); } + public void setOnValueChangedListener(OnValueChangedListener onValueChangedListener) { + mOnValueChangedListener = onValueChangedListener; + } + public void setTextWatcher(TextWatcher textWatcher) { this.textWatcher = textWatcher; editText.addTextChangedListener(textWatcher); + editText.setOnFocusChangeListener((v, hasFocus) -> { + if (!hasFocus) { + value = SafeParse.stringToDouble(editText.getText().toString()); + if (value > maxValue) { + value = maxValue; + ToastUtils.showToastInUiThread(MainApp.instance().getApplicationContext(), MainApp.gs(R.string.youareonallowedlimit)); + updateEditText(); + if (okButton != null) + okButton.setVisibility(VISIBLE); + } + if (value < minValue) { + value = minValue; + ToastUtils.showToastInUiThread(MainApp.instance().getApplicationContext(), MainApp.gs(R.string.youareonallowedlimit)); + updateEditText(); + if (okButton != null) + okButton.setVisibility(VISIBLE); + } + } + }); } - public void setParams(Double initValue, Double minValue, Double maxValue, Double step, NumberFormat formater, boolean allowZero, TextWatcher textWatcher) { + public void setParams(Double initValue, Double minValue, Double maxValue, Double step, NumberFormat formater, boolean allowZero, Button okButton, TextWatcher textWatcher) { if (this.textWatcher != null) { editText.removeTextChangedListener(this.textWatcher); } - setParams(initValue, minValue, maxValue, step, formater, allowZero); + setParams(initValue, minValue, maxValue, step, formater, allowZero, okButton); this.textWatcher = textWatcher; - editText.addTextChangedListener(textWatcher); + if (textWatcher != null) + editText.addTextChangedListener(textWatcher); } - public void setParams(Double initValue, Double minValue, Double maxValue, Double step, NumberFormat formater, boolean allowZero) { + public void setParams(Double initValue, Double minValue, Double maxValue, Double step, NumberFormat formater, boolean allowZero, Button okButton) { this.value = initValue; this.minValue = minValue; this.maxValue = maxValue; this.step = step; this.formater = formater; this.allowZero = allowZero; + callValueChangedListener(); + this.okButton = okButton; - editText.setKeyListener(DigitsKeyListener.getInstance(minValue < 0, step != Math.rint(step))); + editText.setKeyListener(DigitsKeyListenerWithComma.getInstance(minValue < 0, step != Math.rint(step))); if (textWatcher != null) editText.removeTextChangedListener(textWatcher); @@ -178,6 +210,7 @@ public class NumberPicker extends LinearLayout implements View.OnKeyListener, if (textWatcher != null) editText.removeTextChangedListener(textWatcher); this.value = value; + callValueChangedListener(); updateEditText(); if (textWatcher != null) editText.addTextChangedListener(textWatcher); @@ -199,6 +232,7 @@ public class NumberPicker extends LinearLayout implements View.OnKeyListener, value += step * multiplier; if (value > maxValue) { value = maxValue; + callValueChangedListener(); ToastUtils.showToastInUiThread(MainApp.instance().getApplicationContext(), MainApp.gs(R.string.youareonallowedlimit)); stopUpdating(); } @@ -209,6 +243,7 @@ public class NumberPicker extends LinearLayout implements View.OnKeyListener, value -= step * multiplier; if (value < minValue) { value = minValue; + callValueChangedListener(); ToastUtils.showToastInUiThread(MainApp.instance().getApplicationContext(), MainApp.gs(R.string.youareonallowedlimit)); stopUpdating(); } @@ -222,6 +257,11 @@ public class NumberPicker extends LinearLayout implements View.OnKeyListener, editText.setText(formater.format(value)); } + private void callValueChangedListener() { + if (mOnValueChangedListener != null) + mOnValueChangedListener.onValueChanged(value); + } + private void startUpdating(boolean inc) { if (mUpdater != null) { log.debug("Another executor is still active"); diff --git a/app/src/main/java/info/nightscout/androidaps/utils/OKDialog.java b/app/src/main/java/info/nightscout/androidaps/utils/OKDialog.java index 00b3c29692..a45d329d63 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/OKDialog.java +++ b/app/src/main/java/info/nightscout/androidaps/utils/OKDialog.java @@ -5,8 +5,8 @@ import android.content.Context; import android.content.DialogInterface; import android.os.Handler; import android.os.SystemClock; -import android.support.v7.app.AlertDialog; -import android.support.v7.view.ContextThemeWrapper; +import androidx.appcompat.app.AlertDialog; +import androidx.appcompat.view.ContextThemeWrapper; import android.text.Spanned; import org.slf4j.Logger; @@ -39,7 +39,7 @@ public class OKDialog { builder.create().show(); } catch (Exception e) { - log.debug("show_dialog exception: " + e); + log.debug("show_dialog exception: ", e); } } diff --git a/app/src/main/java/info/nightscout/androidaps/utils/SP.java b/app/src/main/java/info/nightscout/androidaps/utils/SP.java index 3b3a8e6907..1b46642751 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/SP.java +++ b/app/src/main/java/info/nightscout/androidaps/utils/SP.java @@ -102,12 +102,6 @@ public class SP { editor.apply(); } - static public void putDouble(int resourceID, double value) { - SharedPreferences.Editor editor = sharedPreferences.edit(); - editor.putString(MainApp.gs(resourceID), Double.toString(value)); - editor.apply(); - } - static public void putLong(String key, long value) { SharedPreferences.Editor editor = sharedPreferences.edit(); editor.putLong(key, value); @@ -132,6 +126,13 @@ public class SP { editor.apply(); } + static public void incInt(int resourceID) { + SharedPreferences.Editor editor = sharedPreferences.edit(); + int value = SP.getInt(resourceID, 0) + 1; + editor.putInt(MainApp.gs(resourceID), value); + editor.apply(); + } + static public void putString(int resourceID, String value) { SharedPreferences.Editor editor = sharedPreferences.edit(); editor.putString(MainApp.gs(resourceID), value); diff --git a/app/src/main/java/info/nightscout/androidaps/utils/SingleClickButton.java b/app/src/main/java/info/nightscout/androidaps/utils/SingleClickButton.java index eef4ca57fe..74500ebdbc 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/SingleClickButton.java +++ b/app/src/main/java/info/nightscout/androidaps/utils/SingleClickButton.java @@ -3,7 +3,7 @@ package info.nightscout.androidaps.utils; import android.app.Activity; import android.content.Context; import android.os.SystemClock; -import android.support.annotation.Nullable; +import androidx.annotation.Nullable; import android.util.AttributeSet; import android.view.View; @@ -14,7 +14,7 @@ import org.slf4j.LoggerFactory; * Created by mike on 22.12.2017. */ -public class SingleClickButton extends android.support.v7.widget.AppCompatButton implements View.OnClickListener { +public class SingleClickButton extends androidx.appcompat.widget.AppCompatButton implements View.OnClickListener { private static Logger log = LoggerFactory.getLogger(SingleClickButton.class); Context context; diff --git a/app/src/main/java/info/nightscout/androidaps/utils/SntpClient.java b/app/src/main/java/info/nightscout/androidaps/utils/SntpClient.java new file mode 100644 index 0000000000..fcee3638b9 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/utils/SntpClient.java @@ -0,0 +1,227 @@ +package info.nightscout.androidaps.utils; +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import android.os.SystemClock; +import android.util.Log; + +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; + +/** + * {@hide} + *

+ * Simple SNTP client class for retrieving network time. + *

+ * Sample usage: + *

SntpClient client = new SntpClient();
+ * if (client.requestTime("time.foo.com")) {
+ *     long now = client.getNtpTime() + SystemClock.elapsedRealtime() - client.getNtpTimeReference();
+ * }
+ * 
+ */ +public class SntpClient { + private static final String TAG = "SntpClient"; + + //private static final int REFERENCE_TIME_OFFSET = 16; + private static final int ORIGINATE_TIME_OFFSET = 24; + private static final int RECEIVE_TIME_OFFSET = 32; + private static final int TRANSMIT_TIME_OFFSET = 40; + private static final int NTP_PACKET_SIZE = 48; + + private static final int NTP_PORT = 123; + private static final int NTP_MODE_CLIENT = 3; + private static final int NTP_VERSION = 3; + + // Number of seconds between Jan 1, 1900 and Jan 1, 1970 + // 70 years plus 17 leap days + private static final long OFFSET_1900_TO_1970 = ((365L * 70L) + 17L) * 24L * 60L * 60L; + + // system time computed from NTP server response + private static long mNtpTime; + + // value of SystemClock.elapsedRealtime() corresponding to mNtpTime + private static long mNtpTimeReference; + + // round trip time in milliseconds + private static long mRoundTripTime; + + public static abstract class Callback implements Runnable { + public boolean networkConnected = false; + public boolean success = false; + public long time = 0; + } + + public static synchronized void ntpTime(final Callback callback, boolean isConnected) { + callback.networkConnected = isConnected; + if (callback.networkConnected) { + new Thread(() -> doNtpTime(callback)).start(); + } else { + callback.run(); + } + } + + static void doNtpTime(final Callback callback) { + callback.success = requestTime("time.google.com", 5000); + callback.time = getNtpTime() + SystemClock.elapsedRealtime() - getNtpTimeReference(); + callback.run(); + } + + /** + * Sends an SNTP request to the given host and processes the response. + * + * @param host host name of the server. + * @param timeout network timeout in milliseconds. + * @return true if the transaction was successful. + */ + private static synchronized boolean requestTime(String host, int timeout) { + try { + DatagramSocket socket = new DatagramSocket(); + socket.setSoTimeout(timeout); + InetAddress address = InetAddress.getByName(host); + byte[] buffer = new byte[NTP_PACKET_SIZE]; + DatagramPacket request = new DatagramPacket(buffer, buffer.length, address, NTP_PORT); + + // set mode = 3 (client) and version = 3 + // mode is in low 3 bits of first byte + // version is in bits 3-5 of first byte + buffer[0] = NTP_MODE_CLIENT | (NTP_VERSION << 3); + + // get current time and write it to the request packet + long requestTime = System.currentTimeMillis(); + long requestTicks = SystemClock.elapsedRealtime(); + writeTimeStamp(buffer, TRANSMIT_TIME_OFFSET, requestTime); + + socket.send(request); + + // read the response + DatagramPacket response = new DatagramPacket(buffer, buffer.length); + socket.receive(response); + long responseTicks = SystemClock.elapsedRealtime(); + long responseTime = requestTime + (responseTicks - requestTicks); + socket.close(); + + // extract the results + long originateTime = readTimeStamp(buffer, ORIGINATE_TIME_OFFSET); + long receiveTime = readTimeStamp(buffer, RECEIVE_TIME_OFFSET); + long transmitTime = readTimeStamp(buffer, TRANSMIT_TIME_OFFSET); + long roundTripTime = responseTicks - requestTicks - (transmitTime - receiveTime); + // receiveTime = originateTime + transit + skew + // responseTime = transmitTime + transit - skew + // clockOffset = ((receiveTime - originateTime) + (transmitTime - responseTime))/2 + // = ((originateTime + transit + skew - originateTime) + + // (transmitTime - (transmitTime + transit - skew)))/2 + // = ((transit + skew) + (transmitTime - transmitTime - transit + skew))/2 + // = (transit + skew - transit + skew)/2 + // = (2 * skew)/2 = skew + long clockOffset = ((receiveTime - originateTime) + (transmitTime - responseTime)) / 2; + // if (Config.LOGD) Log.d(TAG, "round trip: " + roundTripTime + " ms"); + // if (Config.LOGD) Log.d(TAG, "clock offset: " + clockOffset + " ms"); + + // save our results - use the times on this side of the network latency + // (response rather than request time) + mNtpTime = responseTime + clockOffset; + mNtpTimeReference = responseTicks; + mRoundTripTime = roundTripTime; + } catch (Exception e) { + Log.d(TAG, "request time failed: " + e); + return false; + } + + return true; + } + + /** + * Returns the time computed from the NTP transaction. + * + * @return time value computed from NTP server response. + */ + private static long getNtpTime() { + return mNtpTime; + } + + /** + * Returns the reference clock value (value of SystemClock.elapsedRealtime()) + * corresponding to the NTP time. + * + * @return reference clock corresponding to the NTP time. + */ + private static long getNtpTimeReference() { + return mNtpTimeReference; + } + + /** + * Returns the round trip time of the NTP transaction + * + * @return round trip time in milliseconds. + */ + public long getRoundTripTime() { + return mRoundTripTime; + } + + /** + * Reads an unsigned 32 bit big endian number from the given offset in the buffer. + */ + private static long read32(byte[] buffer, int offset) { + byte b0 = buffer[offset]; + byte b1 = buffer[offset + 1]; + byte b2 = buffer[offset + 2]; + byte b3 = buffer[offset + 3]; + + // convert signed bytes to unsigned values + int i0 = ((b0 & 0x80) == 0x80 ? (b0 & 0x7F) + 0x80 : b0); + int i1 = ((b1 & 0x80) == 0x80 ? (b1 & 0x7F) + 0x80 : b1); + int i2 = ((b2 & 0x80) == 0x80 ? (b2 & 0x7F) + 0x80 : b2); + int i3 = ((b3 & 0x80) == 0x80 ? (b3 & 0x7F) + 0x80 : b3); + + return ((long) i0 << 24) + ((long) i1 << 16) + ((long) i2 << 8) + (long) i3; + } + + /** + * Reads the NTP time stamp at the given offset in the buffer and returns + * it as a system time (milliseconds since January 1, 1970). + */ + private static long readTimeStamp(byte[] buffer, int offset) { + long seconds = read32(buffer, offset); + long fraction = read32(buffer, offset + 4); + return ((seconds - OFFSET_1900_TO_1970) * 1000) + ((fraction * 1000L) / 0x100000000L); + } + + /** + * Writes system time (milliseconds since January 1, 1970) as an NTP time stamp + * at the given offset in the buffer. + */ + private static void writeTimeStamp(byte[] buffer, int offset, long time) { + long seconds = time / 1000L; + long milliseconds = time - seconds * 1000L; + seconds += OFFSET_1900_TO_1970; + + // write seconds in big endian format + buffer[offset++] = (byte) (seconds >> 24); + buffer[offset++] = (byte) (seconds >> 16); + buffer[offset++] = (byte) (seconds >> 8); + buffer[offset++] = (byte) (seconds >> 0); + + long fraction = milliseconds * 0x100000000L / 1000L; + // write fraction in big endian format + buffer[offset++] = (byte) (fraction >> 24); + buffer[offset++] = (byte) (fraction >> 16); + buffer[offset++] = (byte) (fraction >> 8); + // low order bits should be random data + buffer[offset++] = (byte) (Math.random() * 255.0); + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/utils/StringUtils.java b/app/src/main/java/info/nightscout/androidaps/utils/StringUtils.java index cad2b76291..74e8da7c9d 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/StringUtils.java +++ b/app/src/main/java/info/nightscout/androidaps/utils/StringUtils.java @@ -1,5 +1,8 @@ package info.nightscout.androidaps.utils; +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.R; + /** * class contains useful String functions */ @@ -17,4 +20,12 @@ public class StringUtils { return string; } + + public static boolean emptyString(final String str) { + return str == null || str.length() == 0; + } + + public static String formatInsulin(double insulin) { + return String.format(MainApp.gs(R.string.formatinsulinunits), insulin); + } } diff --git a/app/src/main/java/info/nightscout/androidaps/utils/T.java b/app/src/main/java/info/nightscout/androidaps/utils/T.java index 2a9bcfc42c..5671f0725c 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/T.java +++ b/app/src/main/java/info/nightscout/androidaps/utils/T.java @@ -43,6 +43,12 @@ public class T { return t; } + public static T months(long month) { + T t = new T(); + t.time = month * 31 * 24 * 60 * 60 * 1000L; + return t; + } + public long msecs() { return time; } diff --git a/app/src/main/java/info/nightscout/androidaps/utils/TimeListEdit.java b/app/src/main/java/info/nightscout/androidaps/utils/TimeListEdit.java index d120e102d1..be8b89dff4 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/TimeListEdit.java +++ b/app/src/main/java/info/nightscout/androidaps/utils/TimeListEdit.java @@ -1,9 +1,6 @@ package info.nightscout.androidaps.utils; import android.content.Context; -import android.os.Handler; -import android.support.v4.content.ContextCompat; -import android.support.v4.widget.TextViewCompat; import android.text.Editable; import android.text.TextWatcher; import android.view.Gravity; @@ -15,6 +12,10 @@ import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.core.content.ContextCompat; +import androidx.core.widget.TextViewCompat; + import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; @@ -24,6 +25,7 @@ import org.slf4j.LoggerFactory; import java.text.DecimalFormat; import java.text.NumberFormat; import java.util.ArrayList; +import java.util.List; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; @@ -77,8 +79,8 @@ public class TimeListEdit { } private void buildView() { - layout = (LinearLayout) view.findViewById(resLayoutId); - layout.removeAllViews(); + layout = view.findViewById(resLayoutId); + layout.removeAllViewsInLayout(); textlabel = new TextView(context); textlabel.setText(label); @@ -96,72 +98,63 @@ public class TimeListEdit { } // last "plus" to append new interval + float factor = layout.getContext().getResources().getDisplayMetrics().density; finalAdd = new ImageView(context); finalAdd.setImageResource(R.drawable.add); - LinearLayout.LayoutParams illp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT); + LinearLayout.LayoutParams illp = new LinearLayout.LayoutParams((int) (35d * factor), (int) (35 * factor)); illp.setMargins(0, 25, 0, 25); // llp.setMargins(left, top, right, bottom); illp.gravity = Gravity.CENTER; layout.addView(finalAdd); finalAdd.setLayoutParams(illp); - finalAdd.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - addItem(itemsCount(), itemsCount() > 0 ? secondFromMidnight(itemsCount() - 1) + ONEHOURINSECONDS : 0, 0, 0); - callSave(); - log(); - fillView(); - } + finalAdd.setOnClickListener(view -> { + addItem(itemsCount(), itemsCount() > 0 ? secondFromMidnight(itemsCount() - 1) + ONEHOURINSECONDS : 0, 0, 0); + callSave(); + log(); + fillView(); }); fillView(); } - private void inflateRow(int i) { + private void inflateRow(final int position) { LayoutInflater inflater = LayoutInflater.from(context); - View childview = intervals[i] = inflater.inflate(R.layout.timelistedit_element, layout, false); - spinners[i] = new SpinnerHelper(childview.findViewById(R.id.timelistedit_time)); - numberPickers1[i] = (NumberPicker) childview.findViewById(R.id.timelistedit_edit1); - numberPickers2[i] = (NumberPicker) childview.findViewById(R.id.timelistedit_edit2); - addButtons[i] = (ImageView) childview.findViewById(R.id.timelistedit_add); - removeButtons[i] = (ImageView) childview.findViewById(R.id.timelistedit_remove); + View childView = intervals[position] = inflater.inflate(R.layout.timelistedit_element, layout, false); + spinners[position] = new SpinnerHelper(childView.findViewById(R.id.timelistedit_time)); + numberPickers1[position] = childView.findViewById(R.id.timelistedit_edit1); + numberPickers2[position] = childView.findViewById(R.id.timelistedit_edit2); + addButtons[position] = childView.findViewById(R.id.timelistedit_add); + removeButtons[position] = childView.findViewById(R.id.timelistedit_remove); - final int fixedPos = i; - addButtons[i].setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - int seconds = secondFromMidnight(fixedPos); - addItem(fixedPos, seconds, 0, 0); - // for here for the rest of values - for (int i = fixedPos + 1; i < itemsCount(); i++) { - if (secondFromMidnight(i - 1) >= secondFromMidnight(i)) { - editItem(i, secondFromMidnight(i - 1) + ONEHOURINSECONDS, value1(i), value2(i)); - } + addButtons[position].setOnClickListener(view -> { + int seconds = secondFromMidnight(position); + addItem(position, seconds, 0, 0); + // for here for the rest of values + for (int i = position + 1; i < itemsCount(); i++) { + if (secondFromMidnight(i - 1) >= secondFromMidnight(i)) { + editItem(i, secondFromMidnight(i - 1) + ONEHOURINSECONDS, value1(i), value2(i)); } - while (itemsCount() > 24 || secondFromMidnight(itemsCount() - 1) > 23 * ONEHOURINSECONDS) - removeItem(itemsCount() - 1); - callSave(); - log(); - fillView(); } + while (itemsCount() > 24 || secondFromMidnight(itemsCount() - 1) > 23 * ONEHOURINSECONDS) + removeItem(itemsCount() - 1); + callSave(); + log(); + fillView(); }); - removeButtons[i].setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - removeItem(fixedPos); - callSave(); - log(); - fillView(); - } + removeButtons[position].setOnClickListener(view -> { + removeItem(position); + callSave(); + log(); + fillView(); }); - spinners[i].setOnItemSelectedListener( + spinners[position].setOnItemSelectedListener( new AdapterView.OnItemSelectedListener() { @Override - public void onItemSelected(AdapterView parent, View view, int position, long id) { - int seconds = DateUtil.toSeconds(spinners[fixedPos].getSelectedItem().toString()); - editItem(fixedPos, seconds, value1(fixedPos), value2(fixedPos)); + public void onItemSelected(AdapterView parent, View view, int selected, long id) { + int seconds = ((SpinnerAdapter) spinners[position].getAdapter()).valueForPosition(selected); + editItem(position, seconds, value1(position), value2(position)); log(); callSave(); fillView(); @@ -173,30 +166,10 @@ public class TimeListEdit { } ); - numberPickers1[i].setTextWatcher(new TextWatcher() { - @Override - public void afterTextChanged(Editable s) { - editItem(fixedPos, secondFromMidnight(fixedPos), SafeParse.stringToDouble(numberPickers1[fixedPos].getText()), value2(fixedPos)); - callSave(); - log(); - } - - @Override - public void beforeTextChanged(CharSequence s, int start, - int count, int after) { - } - - @Override - public void onTextChanged(CharSequence s, int start, - int before, int count) { - } - }); - - - numberPickers2[i].setTextWatcher(new TextWatcher() { + numberPickers1[position].setTextWatcher(new TextWatcher() { @Override public void afterTextChanged(Editable s) { - editItem(fixedPos, secondFromMidnight(fixedPos), value1(fixedPos), SafeParse.stringToDouble(numberPickers2[fixedPos].getText())); + editItem(position, secondFromMidnight(position), SafeParse.stringToDouble(numberPickers1[position].getText()), value2(position)); callSave(); log(); } @@ -212,7 +185,27 @@ public class TimeListEdit { } }); - layout.addView(childview); + + numberPickers2[position].setTextWatcher(new TextWatcher() { + @Override + public void afterTextChanged(Editable s) { + editItem(position, secondFromMidnight(position), value1(position), SafeParse.stringToDouble(numberPickers2[position].getText())); + callSave(); + log(); + } + + @Override + public void beforeTextChanged(CharSequence s, int start, + int count, int after) { + } + + @Override + public void onTextChanged(CharSequence s, int start, + int before, int count) { + } + }); + + layout.addView(childView); } private void fillView() { @@ -220,7 +213,7 @@ public class TimeListEdit { if (i < itemsCount()) { intervals[i].setVisibility(View.VISIBLE); buildInterval(i); - } else if (i <= inflatedUntil){ + } else if (i <= inflatedUntil) { intervals[i].setVisibility(View.GONE); } } @@ -232,9 +225,8 @@ public class TimeListEdit { } } - private View buildInterval(int i) { + private void buildInterval(int i) { SpinnerHelper timeSpinner = spinners[i]; - View childview = intervals[i]; final NumberPicker editText1 = numberPickers1[i]; final NumberPicker editText2 = numberPickers2[i]; @@ -244,8 +236,8 @@ public class TimeListEdit { if (i == 0) next = ONEHOURINSECONDS; fillSpinner(timeSpinner, secondFromMidnight(i), previous, next); - editText1.setParams(value1(i), min, max, step, formatter, false); - editText2.setParams(value2(i), min, max, step, formatter, false); + editText1.setParams(value1(i), min, max, step, formatter, false,null); + editText2.setParams(value2(i), min, max, step, formatter, false, null); if (data2 == null) { editText2.setVisibility(View.GONE); @@ -263,29 +255,38 @@ public class TimeListEdit { addButtons[i].setVisibility(View.VISIBLE); } - return childview; + } + + class SpinnerAdapter extends ArrayAdapter { + List values; + + SpinnerAdapter(@NonNull Context context, int resource, final @NonNull List objects, final @NonNull List values) { + super(context, resource, objects); + this.values = values; + } + + int valueForPosition(int position) { + return values.get(position); + } } private void fillSpinner(final SpinnerHelper spinner, int secondsFromMidnight, int previous, int next) { int posInList = 0; ArrayList timeList = new ArrayList<>(); + ArrayList timeListValues = new ArrayList<>(); int pos = 0; for (int t = previous + ONEHOURINSECONDS; t < next; t += ONEHOURINSECONDS) { timeList.add(DateUtil.timeStringFromSeconds(t)); + timeListValues.add(t); if (secondsFromMidnight == t) posInList = pos; pos++; } - final ArrayAdapter adapter = new ArrayAdapter<>(context, - R.layout.spinner_centered, timeList); + final SpinnerAdapter adapter = new SpinnerAdapter(context, + R.layout.spinner_centered, timeList, timeListValues); spinner.setAdapter(adapter); - final int finalPosInList = posInList; - new Handler().postDelayed(new Runnable() { - public void run() { - spinner.setSelection(finalPosInList, false); - adapter.notifyDataSetChanged(); - } - }, 100); + spinner.setSelection(posInList, false); + adapter.notifyDataSetChanged(); } private int itemsCount() { @@ -350,7 +351,7 @@ public class TimeListEdit { data1.put(index, newObject1); if (data2 != null) { JSONObject newObject2 = new JSONObject(); - newObject1.put("time", time); + newObject2.put("time", time); newObject2.put("timeAsSeconds", timeAsSeconds); newObject2.put("value", value2); data2.put(index, newObject2); @@ -362,7 +363,7 @@ public class TimeListEdit { } private void addItem(int index, int timeAsSeconds, double value1, double value2) { - if(itemsCount()>inflatedUntil) { + if (itemsCount() > inflatedUntil) { layout.removeView(finalAdd); inflateRow(++inflatedUntil); layout.addView(finalAdd); @@ -389,10 +390,8 @@ public class TimeListEdit { } private void log() { - if (log.isDebugEnabled()) { - for (int i = 0; i < data1.length(); i++) { - log.debug(i + ": @" + DateUtil.timeStringFromSeconds(secondFromMidnight(i)) + " " + value1(i) + (data2 != null ? " " + value2(i) : "")); - } + for (int i = 0; i < data1.length(); i++) { + log.debug(i + ": @" + DateUtil.timeStringFromSeconds(secondFromMidnight(i)) + " " + value1(i) + (data2 != null ? " " + value2(i) : "")); } } @@ -400,9 +399,9 @@ public class TimeListEdit { if (save != null) save.run(); } - public void updateLabel(String txt){ + public void updateLabel(String txt) { this.label = txt; - if(textlabel!=null) + if (textlabel != null) textlabel.setText(txt); } } diff --git a/app/src/main/java/info/nightscout/androidaps/utils/ToastUtils.java b/app/src/main/java/info/nightscout/androidaps/utils/ToastUtils.java index d85b1510a0..fe596d8f08 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/ToastUtils.java +++ b/app/src/main/java/info/nightscout/androidaps/utils/ToastUtils.java @@ -7,21 +7,19 @@ import android.os.Looper; import android.widget.Toast; import info.nightscout.androidaps.MainApp; -import info.nightscout.androidaps.plugins.general.overview.notifications.Notification; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification; +import info.nightscout.androidaps.plugins.general.overview.notifications.Notification; public class ToastUtils { - public static void showToastInUiThread(final Context ctx, - final String string) { + public static void showToastInUiThread(final Context ctx, final int stringId) { + showToastInUiThread(ctx, MainApp.gs(stringId)); + } + public static void showToastInUiThread(final Context ctx, final String string) { Handler mainThread = new Handler(Looper.getMainLooper()); - mainThread.post(new Runnable() { - @Override - public void run() { - Toast.makeText(ctx, string, Toast.LENGTH_SHORT).show(); - } - }); + mainThread.post(() -> Toast.makeText(ctx, string, Toast.LENGTH_SHORT).show()); } public static void showToastInUiThread(final Context ctx, @@ -29,23 +27,13 @@ public class ToastUtils { showToastInUiThread(ctx, string); playSound(ctx, soundID); - new Thread(new Runnable() { - @Override - public void run() { - Notification notification = new Notification(Notification.TOAST_ALARM, string, Notification.URGENT); - MainApp.bus().post(new EventNewNotification(notification)); - } - }).start(); + Notification notification = new Notification(Notification.TOAST_ALARM, string, Notification.URGENT); + RxBus.INSTANCE.send(new EventNewNotification(notification)); } private static void playSound(final Context ctx, final int soundID) { final MediaPlayer soundMP = MediaPlayer.create(ctx, soundID); soundMP.start(); - soundMP.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { - @Override - public void onCompletion(MediaPlayer mp) { - mp.release(); - } - }); + soundMP.setOnCompletionListener(MediaPlayer::release); } } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/utils/Translator.java b/app/src/main/java/info/nightscout/androidaps/utils/Translator.java index ee83f856e4..e25c083a7b 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/Translator.java +++ b/app/src/main/java/info/nightscout/androidaps/utils/Translator.java @@ -33,6 +33,8 @@ public class Translator { return MainApp.gs(R.string.careportal_exercise); case "Site Change": return MainApp.gs(R.string.careportal_pumpsitechange); + case "Pump Battery Change": + return MainApp.gs(R.string.careportal_pumpbatterychange); case "Sensor Start": return MainApp.gs(R.string.careportal_cgmsensorstart); case "Sensor Change": diff --git a/app/src/main/java/info/nightscout/androidaps/utils/XdripCalibrations.java b/app/src/main/java/info/nightscout/androidaps/utils/XdripCalibrations.java index 99961a156b..f7b8761823 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/XdripCalibrations.java +++ b/app/src/main/java/info/nightscout/androidaps/utils/XdripCalibrations.java @@ -5,7 +5,7 @@ import android.content.DialogInterface; import android.content.Intent; import android.content.pm.ResolveInfo; import android.os.Bundle; -import android.support.v7.app.AlertDialog; +import androidx.appcompat.app.AlertDialog; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/app/src/main/res/drawable-anydpi/ic_keyboard_capslock.xml b/app/src/main/res/drawable-anydpi/ic_keyboard_capslock.xml new file mode 100644 index 0000000000..db4409f2b8 --- /dev/null +++ b/app/src/main/res/drawable-anydpi/ic_keyboard_capslock.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable-anydpi/ic_keyboard_tab.xml b/app/src/main/res/drawable-anydpi/ic_keyboard_tab.xml new file mode 100644 index 0000000000..03ec6ed69f --- /dev/null +++ b/app/src/main/res/drawable-anydpi/ic_keyboard_tab.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable-anydpi/ic_location_on.xml b/app/src/main/res/drawable-anydpi/ic_location_on.xml new file mode 100644 index 0000000000..1017cbc8d2 --- /dev/null +++ b/app/src/main/res/drawable-anydpi/ic_location_on.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable-anydpi/ic_network_wifi.xml b/app/src/main/res/drawable-anydpi/ic_network_wifi.xml new file mode 100644 index 0000000000..c5ae21ec89 --- /dev/null +++ b/app/src/main/res/drawable-anydpi/ic_network_wifi.xml @@ -0,0 +1,13 @@ + + + + diff --git a/app/src/main/res/drawable-anydpi/ic_notifications.xml b/app/src/main/res/drawable-anydpi/ic_notifications.xml new file mode 100644 index 0000000000..b655d741e6 --- /dev/null +++ b/app/src/main/res/drawable-anydpi/ic_notifications.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable-mdpi-v11/refresh.xml b/app/src/main/res/drawable-mdpi-v11/refresh.xml new file mode 100644 index 0000000000..677809a8c2 --- /dev/null +++ b/app/src/main/res/drawable-mdpi-v11/refresh.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/as.png b/app/src/main/res/drawable/as.png new file mode 100644 index 0000000000..a37e4c3465 Binary files /dev/null and b/app/src/main/res/drawable/as.png differ diff --git a/app/src/main/res/drawable/border_automation_unit.xml b/app/src/main/res/drawable/border_automation_unit.xml new file mode 100644 index 0000000000..eb85656295 --- /dev/null +++ b/app/src/main/res/drawable/border_automation_unit.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_access_alarm_24dp.xml b/app/src/main/res/drawable/ic_access_alarm_24dp.xml new file mode 100644 index 0000000000..3e1d84e037 --- /dev/null +++ b/app/src/main/res/drawable/ic_access_alarm_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_add_black_24dp.xml b/app/src/main/res/drawable/ic_add_black_24dp.xml new file mode 100644 index 0000000000..0258249cc4 --- /dev/null +++ b/app/src/main/res/drawable/ic_add_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_arrow_forward_white_24dp.xml b/app/src/main/res/drawable/ic_arrow_forward_white_24dp.xml new file mode 100644 index 0000000000..5304b93e18 --- /dev/null +++ b/app/src/main/res/drawable/ic_arrow_forward_white_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_pause_circle_outline_24dp.xml b/app/src/main/res/drawable/ic_pause_circle_outline_24dp.xml new file mode 100644 index 0000000000..4a469646bd --- /dev/null +++ b/app/src/main/res/drawable/ic_pause_circle_outline_24dp.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_play_circle_outline_24dp.xml b/app/src/main/res/drawable/ic_play_circle_outline_24dp.xml new file mode 100644 index 0000000000..9997bf2034 --- /dev/null +++ b/app/src/main/res/drawable/ic_play_circle_outline_24dp.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_refresh.xml b/app/src/main/res/drawable/ic_refresh.xml new file mode 100644 index 0000000000..8229a9a64c --- /dev/null +++ b/app/src/main/res/drawable/ic_refresh.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_replay_24dp.xml b/app/src/main/res/drawable/ic_replay_24dp.xml new file mode 100644 index 0000000000..32942990ac --- /dev/null +++ b/app/src/main/res/drawable/ic_replay_24dp.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_stop_24dp.xml b/app/src/main/res/drawable/ic_stop_24dp.xml new file mode 100644 index 0000000000..88299804a8 --- /dev/null +++ b/app/src/main/res/drawable/ic_stop_24dp.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_trash_outline.xml b/app/src/main/res/drawable/ic_trash_outline.xml new file mode 100644 index 0000000000..a411c394e0 --- /dev/null +++ b/app/src/main/res/drawable/ic_trash_outline.xml @@ -0,0 +1,7 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/icon_auto_delta.png b/app/src/main/res/drawable/icon_auto_delta.png new file mode 100644 index 0000000000..ddb0930b0b Binary files /dev/null and b/app/src/main/res/drawable/icon_auto_delta.png differ diff --git a/app/src/main/res/drawable/launch_screen.xml b/app/src/main/res/drawable/launch_screen.xml new file mode 100644 index 0000000000..543fd89929 --- /dev/null +++ b/app/src/main/res/drawable/launch_screen.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/splash_icon.png b/app/src/main/res/drawable/splash_icon.png new file mode 100644 index 0000000000..2c0ee32c1b Binary files /dev/null and b/app/src/main/res/drawable/splash_icon.png differ diff --git a/app/src/main/res/layout/activity_agreement.xml b/app/src/main/res/layout/activity_agreement.xml index 9a17432930..5c98f937ed 100644 --- a/app/src/main/res/layout/activity_agreement.xml +++ b/app/src/main/res/layout/activity_agreement.xml @@ -8,8 +8,7 @@ tools:context="info.nightscout.androidaps.activities.AgreementActivity"> + android:layout_height="fill_parent"> - - - - @@ -129,4 +128,4 @@ - + diff --git a/app/src/main/res/layout/activity_insight_pairing.xml b/app/src/main/res/layout/activity_insight_pairing.xml index d90b5f091d..83412a8645 100644 --- a/app/src/main/res/layout/activity_insight_pairing.xml +++ b/app/src/main/res/layout/activity_insight_pairing.xml @@ -18,7 +18,7 @@ android:text="@string/searching_for_devices" android:textSize="20sp" /> - - - - - + - - - - + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_setupwizard.xml b/app/src/main/res/layout/activity_setupwizard.xml index 8510abd33f..4bcc5b1221 100644 --- a/app/src/main/res/layout/activity_setupwizard.xml +++ b/app/src/main/res/layout/activity_setupwizard.xml @@ -23,7 +23,6 @@ app:srcCompat="@drawable/ic_exit_to_app" /> + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/automation_dialog_action.xml b/app/src/main/res/layout/automation_dialog_action.xml new file mode 100644 index 0000000000..5077dcd4bc --- /dev/null +++ b/app/src/main/res/layout/automation_dialog_action.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/automation_dialog_choose_action.xml b/app/src/main/res/layout/automation_dialog_choose_action.xml new file mode 100644 index 0000000000..8a2d714067 --- /dev/null +++ b/app/src/main/res/layout/automation_dialog_choose_action.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/automation_dialog_choose_trigger.xml b/app/src/main/res/layout/automation_dialog_choose_trigger.xml new file mode 100644 index 0000000000..8589096af7 --- /dev/null +++ b/app/src/main/res/layout/automation_dialog_choose_trigger.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/automation_dialog_edit_trigger.xml b/app/src/main/res/layout/automation_dialog_edit_trigger.xml new file mode 100644 index 0000000000..594a78055e --- /dev/null +++ b/app/src/main/res/layout/automation_dialog_edit_trigger.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/automation_dialog_event.xml b/app/src/main/res/layout/automation_dialog_event.xml new file mode 100644 index 0000000000..d440b8cf27 --- /dev/null +++ b/app/src/main/res/layout/automation_dialog_event.xml @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/automation_event_item.xml b/app/src/main/res/layout/automation_event_item.xml new file mode 100644 index 0000000000..6cbc138278 --- /dev/null +++ b/app/src/main/res/layout/automation_event_item.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/automation_fragment.xml b/app/src/main/res/layout/automation_fragment.xml new file mode 100644 index 0000000000..2f24e1b70c --- /dev/null +++ b/app/src/main/res/layout/automation_fragment.xml @@ -0,0 +1,36 @@ + + + + + + + + + + diff --git a/app/src/main/res/layout/bgsource_fragment.xml b/app/src/main/res/layout/bgsource_fragment.xml index 73829c12d4..6619b6bafa 100644 --- a/app/src/main/res/layout/bgsource_fragment.xml +++ b/app/src/main/res/layout/bgsource_fragment.xml @@ -10,12 +10,12 @@ android:layout_height="match_parent" android:orientation="vertical"> - - + diff --git a/app/src/main/res/layout/bgsource_item.xml b/app/src/main/res/layout/bgsource_item.xml index acd0454327..5d1842b8c6 100644 --- a/app/src/main/res/layout/bgsource_item.xml +++ b/app/src/main/res/layout/bgsource_item.xml @@ -1,7 +1,6 @@ - - + diff --git a/app/src/main/res/layout/careportal_fragment.xml b/app/src/main/res/layout/careportal_fragment.xml index 1e4c72dd72..66f79ef688 100644 --- a/app/src/main/res/layout/careportal_fragment.xml +++ b/app/src/main/res/layout/careportal_fragment.xml @@ -45,7 +45,7 @@ android:paddingStart="15dp" android:text="@string/careportal_activity_label" /> - - + - - + - - + - - + diff --git a/app/src/main/res/layout/careportal_newnstreatment_dialog.xml b/app/src/main/res/layout/careportal_newnstreatment_dialog.xml index 28172e63df..d27a5883b5 100644 --- a/app/src/main/res/layout/careportal_newnstreatment_dialog.xml +++ b/app/src/main/res/layout/careportal_newnstreatment_dialog.xml @@ -509,8 +509,7 @@ diff --git a/app/src/main/res/layout/close.xml b/app/src/main/res/layout/close.xml new file mode 100644 index 0000000000..a500295275 --- /dev/null +++ b/app/src/main/res/layout/close.xml @@ -0,0 +1,34 @@ + + + + +