Merge remote-tracking branch 'upstream/dev' into dash

This commit is contained in:
Bart Sopers 2021-02-20 18:48:06 +01:00
commit b56a3d8132
537 changed files with 14988 additions and 14263 deletions

8
.github/dependabot.yml vendored Normal file
View file

@ -0,0 +1,8 @@
version: 2
updates:
- package-ecosystem: gradle
directory: "/"
schedule:
interval: daily
open-pull-requests-limit: 10
target-branch: dev

1
.gitignore vendored
View file

@ -2,6 +2,7 @@
.gradle
/local.properties
.DS_Store
*/jacoco.exec
/build
/captures
*.apk

View file

@ -1,23 +1,18 @@
buildscript {
repositories {
maven { url "https://plugins.gradle.org/m2/" } // jacoco 0.2
}
dependencies {
//classpath 'com.dicedmelon.gradle:jacoco-android:0.1.4'
classpath 'com.hiya:jacoco-android:0.2'
}
}
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
apply plugin: 'com.google.gms.google-services'
//apply plugin: 'jacoco-android'
apply plugin: 'com.hiya.jacoco-android'
apply plugin: 'com.google.firebase.crashlytics'
apply from: "${project.rootDir}/gradle/android_dependencies.gradle"
jacoco {
toolVersion = "0.8.3"
toolVersion = "0.8.6"
}
tasks.withType(Test) {
jacoco.includeNoLocationClasses = true
}
repositories {
@ -109,51 +104,20 @@ tasks.matching { it instanceof Test }.all {
}
android {
compileSdkVersion 28
ndkVersion "21.1.6352462"
defaultConfig {
minSdkVersion 26
targetSdkVersion 28
multiDexEnabled true
versionCode 1500
version "2.8.2.1-dev"
version "2.8.2.1-dev-b"
buildConfigField "String", "VERSION", '"' + version + '"'
buildConfigField "String", "BUILDVERSION", '"' + generateGitBuild() + '-' + generateDate() + '"'
buildConfigField "String", "REMOTE", '"' + generateGitRemote() + '"'
buildConfigField "String", "HEAD", '"' + generateGitBuild() + '"'
buildConfigField "String", "COMMITTED", '"' + allCommitted() + '"'
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
// if you change minSdkVersion to less than 11, you need to change executeTask for wear
ndk {
moduleName "BleCommandUtil"
}
}
kotlinOptions {
jvmTarget = '1.8'
}
buildFeatures {
viewBinding true
}
lintOptions {
checkReleaseBuilds false
disable 'MissingTranslation'
disable 'ExtraTranslation'
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
testCoverageEnabled(project.hasProperty('coverage'))
}
firebaseDisable {
System.setProperty("disableFirebase", "true")
ext.enableCrashlytics = false
}
}
flavorDimensions "standard"
productFlavors {
full {
@ -197,28 +161,8 @@ android {
]
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
testOptions {
unitTests {
returnDefaultValues = true
includeAndroidResources = true
all {
maxParallelForks = 10
forkEvery = 20
}
}
}
useLibrary "org.apache.http.legacy"
configurations.all {
resolutionStrategy.force 'com.google.code.findbugs:jsr305:1.3.9'
}
}
allprojects {
@ -245,32 +189,9 @@ dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
// https://github.com/DavidProdinger/Weekdays-Selector (used outdated 'com.amulyakhare:com.amulyakhare.textdrawable:1.0.1')
implementation(name: 'weekdaysselector-1.1.1', ext: 'aar')
testImplementation "junit:junit:$junit_version"
testImplementation 'org.json:json:20201115'
testImplementation "org.mockito:mockito-core:${mockitoVersion}"
testImplementation "org.powermock:powermock-api-mockito2:${powermockVersion}"
testImplementation "org.powermock:powermock-module-junit4-rule-agent:${powermockVersion}"
testImplementation "org.powermock:powermock-module-junit4-rule:${powermockVersion}"
testImplementation "org.powermock:powermock-module-junit4:${powermockVersion}"
testImplementation "joda-time:joda-time:$jodatime_version"
testImplementation('com.google.truth:truth:1.1.2') {
exclude group: "com.google.guava", module: "guava"
exclude group: "com.google.code.findbugs", module: "jsr305"
}
testImplementation "org.skyscreamer:jsonassert:1.5.0"
testImplementation "org.hamcrest:hamcrest-all:1.3"
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0-alpha03'
androidTestImplementation "androidx.test.ext:junit:$androidx_junit"
androidTestImplementation "androidx.test:rules:$androidx_rules"
androidTestImplementation 'com.google.code.findbugs:jsr305:3.0.2'
/* Dagger2 - We are going to use dagger.android which includes
* support for Activity and fragment injection so we need to include
* the following dependencies */
* support for Activity and fragment injection so we need to include
* the following dependencies */
annotationProcessor "com.google.dagger:dagger-compiler:$dagger_version"
@ -279,10 +200,11 @@ dependencies {
/* Dagger2 - default dependency */
kapt "com.google.dagger:dagger-compiler:$dagger_version"
androidTestImplementation 'androidx.test.uiautomator:uiautomator:2.2.0'
}
apply from: "${project.rootDir}/gradle/test_dependencies.gradle"
/*
// Run 'adb' shell command to clear application data of main app for 'debug' variant
task clearMainAppData(type: Exec) {

Binary file not shown.

Binary file not shown.

View file

@ -25,6 +25,7 @@
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="com.dexcom.cgm.EXTERNAL_PERMISSION" />
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
<uses-permission android:name="com.android.alarm.permission.SET_ALARM" />
<uses-permission-sdk-23 android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
@ -74,8 +75,12 @@
<activity android:name=".activities.StatsActivity" />
<activity
android:name="com.google.firebase.auth.internal.FederatedSignInActivity"
tools:replace="android:launchMode"
android:launchMode="standard" />
android:excludeFromRecents="true"
android:exported="true"
android:launchMode="singleInstance"
android:permission="com.google.firebase.auth.api.gms.permission.LAUNCH_FEDERATED_SIGN_IN"
android:theme="@android:style/Theme.Translucent.NoTitleBar"
tools:replace="android:launchMode" />
<!-- Receive new BG readings from other local apps -->
<receiver

View file

@ -131,6 +131,7 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
// Default profile
binding.copytolocalprofile.setOnClickListener {
storeValues()
val age = ageUsed[tabSelected]
val weight = weightUsed[tabSelected]
val tdd = tddUsed[tabSelected]
@ -289,7 +290,7 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
}
private fun setBackgroundColorOnSelected(tab: Int) {
binding.menu1.setBackgroundColor(resourceHelper.gc(if (tab == 1) R.color.defaultbackground else R.color.tabBgColorSelected))
binding.menu1.setBackgroundColor(resourceHelper.gc(if (tab == 1) R.color.defaultbackground else R.color.tempbasal))
binding.menu2.setBackgroundColor(resourceHelper.gc(if (tab == 0) R.color.defaultbackground else R.color.examinedProfile))
}
}

View file

@ -3,6 +3,7 @@ package info.nightscout.androidaps.activities
import android.os.Bundle
import info.nightscout.androidaps.R
import info.nightscout.androidaps.databinding.ActivityStatsBinding
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.utils.ActivityMonitor
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.stats.TddCalculator
@ -14,6 +15,7 @@ class StatsActivity : NoSplashAppCompatActivity() {
@Inject lateinit var tddCalculator: TddCalculator
@Inject lateinit var tirCalculator: TirCalculator
@Inject lateinit var activityMonitor: ActivityMonitor
@Inject lateinit var uel: UserEntryLogger
private lateinit var binding: ActivityStatsBinding
@ -29,6 +31,7 @@ class StatsActivity : NoSplashAppCompatActivity() {
binding.ok.setOnClickListener { finish() }
binding.reset.setOnClickListener {
OKDialog.showConfirmation(this, resourceHelper.gs(R.string.doyouwantresetstats)) {
uel.log("STATS RESET")
activityMonitor.reset()
recreate()
}

View file

@ -77,18 +77,13 @@ abstract class FragmentsModule {
@ContributesAndroidInjector abstract fun contributesTidepoolFragment(): TidepoolFragment
@ContributesAndroidInjector abstract fun contributesTreatmentsFragment(): TreatmentsFragment
@ContributesAndroidInjector
abstract fun contributesTreatmentsBolusFragment(): TreatmentsBolusFragment
@ContributesAndroidInjector
abstract fun contributesTreatmentsTemporaryBasalsFragment(): TreatmentsTemporaryBasalsFragment
@ContributesAndroidInjector
abstract fun contributesTreatmentsTempTargetFragment(): TreatmentsTempTargetFragment
@ContributesAndroidInjector
abstract fun contributesTreatmentsExtendedBolusesFragment(): TreatmentsExtendedBolusesFragment
@ContributesAndroidInjector
abstract fun contributesTreatmentsCareportalFragment(): TreatmentsCareportalFragment
@ContributesAndroidInjector
abstract fun contributesTreatmentsProfileSwitchFragment(): TreatmentsProfileSwitchFragment
@ContributesAndroidInjector abstract fun contributesTreatmentsBolusFragment(): TreatmentsBolusFragment
@ContributesAndroidInjector abstract fun contributesTreatmentsTemporaryBasalsFragment(): TreatmentsTemporaryBasalsFragment
@ContributesAndroidInjector abstract fun contributesTreatmentsTempTargetFragment(): TreatmentsTempTargetFragment
@ContributesAndroidInjector abstract fun contributesTreatmentsExtendedBolusesFragment(): TreatmentsExtendedBolusesFragment
@ContributesAndroidInjector abstract fun contributesTreatmentsCareportalFragment(): TreatmentsCareportalFragment
@ContributesAndroidInjector abstract fun contributesTreatmentsProfileSwitchFragment(): TreatmentsProfileSwitchFragment
@ContributesAndroidInjector abstract fun contributesTreatmentsUserEntryFragment(): TreatmentsUserEntryFragment
@ContributesAndroidInjector abstract fun contributesVirtualPumpFragment(): VirtualPumpFragment

View file

@ -13,12 +13,13 @@ abstract class WizardModule {
@ContributesAndroidInjector abstract fun swBreakInjector(): SWBreak
@ContributesAndroidInjector abstract fun swButtonInjector(): SWButton
@ContributesAndroidInjector abstract fun swEditNumberWithUnitsInjector(): SWEditNumberWithUnits
@ContributesAndroidInjector abstract fun swEditNumberInjector(): SWEditNumber
@ContributesAndroidInjector abstract fun swEditStringInjector(): SWEditString
@ContributesAndroidInjector abstract fun swEditEncryptedPasswordInjector(): SWEditEncryptedPassword
@ContributesAndroidInjector abstract fun swEditUrlInjector(): SWEditUrl
@ContributesAndroidInjector abstract fun swFragmentInjector(): SWFragment
@ContributesAndroidInjector abstract fun swHtmlLinkInjector(): SWHtmlLink
@ContributesAndroidInjector abstract fun swInfotextInjector(): SWInfotext
@ContributesAndroidInjector abstract fun swInfotextInjector(): SWInfoText
@ContributesAndroidInjector abstract fun swItemInjector(): SWItem
@ContributesAndroidInjector abstract fun swPluginInjector(): SWPlugin
@ContributesAndroidInjector abstract fun swRadioButtonInjector(): SWRadioButton

View file

@ -11,6 +11,7 @@ import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.Profile
import info.nightscout.androidaps.databinding.DialogCalibrationBinding
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus
import info.nightscout.androidaps.utils.HtmlHelper
import info.nightscout.androidaps.utils.XdripCalibrations
@ -26,6 +27,7 @@ class CalibrationDialog : DialogFragmentWithDate() {
@Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var xdripCalibrations: XdripCalibrations
@Inject lateinit var uel: UserEntryLogger
private var _binding: DialogCalibrationBinding? = null
@ -75,7 +77,7 @@ class CalibrationDialog : DialogFragmentWithDate() {
if (bg > 0) {
activity?.let { activity ->
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.overview_calibration), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), {
aapsLogger.debug("USER ENTRY: CALIBRATION $bg")
uel.log("CALIBRATION", d1 = bg)
xdripCalibrations.sendIntent(bg)
})
}

View file

@ -17,16 +17,13 @@ import info.nightscout.androidaps.db.Source
import info.nightscout.androidaps.db.TempTarget
import info.nightscout.androidaps.interfaces.Constraint
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
import info.nightscout.androidaps.plugins.treatments.CarbsGenerator
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.DecimalFormatter
import info.nightscout.androidaps.utils.DefaultValueHelper
import info.nightscout.androidaps.utils.HtmlHelper
import info.nightscout.androidaps.utils.ToastUtils
import info.nightscout.androidaps.utils.*
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.extensions.formatColor
import info.nightscout.androidaps.utils.resources.ResourceHelper
@ -46,6 +43,8 @@ class CarbsDialog : DialogFragmentWithDate() {
@Inject lateinit var iobCobCalculatorPlugin: IobCobCalculatorPlugin
@Inject lateinit var nsUpload: NSUpload
@Inject lateinit var carbsGenerator: CarbsGenerator
@Inject lateinit var uel: UserEntryLogger
@Inject lateinit var carbTimer: CarbTimer
companion object {
@ -174,6 +173,7 @@ class CarbsDialog : DialogFragmentWithDate() {
val hypoTT = defaultValueHelper.determineHypoTT()
val actions: LinkedList<String?> = LinkedList()
val unitLabel = if (units == Constants.MMOL) resourceHelper.gs(R.string.mmol) else resourceHelper.gs(R.string.mgdl)
val useAlarm = binding.alarmCheckBox.isChecked
val activitySelected = binding.activityTt.isChecked
if (activitySelected)
@ -190,6 +190,8 @@ class CarbsDialog : DialogFragmentWithDate() {
val time = eventTime + timeOffset * 1000 * 60
if (timeOffset != 0)
actions.add(resourceHelper.gs(R.string.time) + ": " + dateUtil.dateAndTimeString(time))
if (useAlarm && carbs > 0 && timeOffset > 0)
actions.add(resourceHelper.gs(R.string.alarminxmin, timeOffset).formatColor(resourceHelper, R.color.info))
val duration = binding.duration.value.toInt()
if (duration > 0)
actions.add(resourceHelper.gs(R.string.duration) + ": " + duration + resourceHelper.gs(R.string.shorthour))
@ -210,7 +212,7 @@ class CarbsDialog : DialogFragmentWithDate() {
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.carbs), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), {
when {
activitySelected -> {
aapsLogger.debug("USER ENTRY: TEMPTARGET ACTIVITY $activityTT duration: $activityTTDuration")
uel.log("TT ACTIVITY", d1 = activityTT, i1 = activityTTDuration)
val tempTarget = TempTarget()
.date(System.currentTimeMillis())
.duration(activityTTDuration)
@ -222,7 +224,7 @@ class CarbsDialog : DialogFragmentWithDate() {
}
eatingSoonSelected -> {
aapsLogger.debug("USER ENTRY: TEMPTARGET EATING SOON $eatingSoonTT duration: $eatingSoonTTDuration")
uel.log("TT EATING SOON", d1 = eatingSoonTT, i1 = eatingSoonTTDuration)
val tempTarget = TempTarget()
.date(System.currentTimeMillis())
.duration(eatingSoonTTDuration)
@ -234,7 +236,7 @@ class CarbsDialog : DialogFragmentWithDate() {
}
hypoSelected -> {
aapsLogger.debug("USER ENTRY: TEMPTARGET HYPO $hypoTT duration: $hypoTTDuration")
uel.log("TT HYPO", d1 = hypoTT, i1 = hypoTTDuration)
val tempTarget = TempTarget()
.date(System.currentTimeMillis())
.duration(hypoTTDuration)
@ -247,14 +249,17 @@ class CarbsDialog : DialogFragmentWithDate() {
}
if (carbsAfterConstraints > 0) {
if (duration == 0) {
aapsLogger.debug("USER ENTRY: CARBS $carbsAfterConstraints time: $time")
uel.log("CARBS", d1 = carbsAfterConstraints.toDouble(), i1 = timeOffset)
carbsGenerator.createCarb(carbsAfterConstraints, time, CareportalEvent.CARBCORRECTION, notes)
} else {
aapsLogger.debug("USER ENTRY: CARBS $carbsAfterConstraints time: $time duration: $duration")
uel.log("CARBS", d1 = carbsAfterConstraints.toDouble(), i1 = timeOffset, i2 = duration)
carbsGenerator.generateCarbs(carbsAfterConstraints, time, duration, notes)
nsUpload.uploadEvent(CareportalEvent.NOTE, DateUtil.now() - 2000, resourceHelper.gs(R.string.generated_ecarbs_note, carbsAfterConstraints, duration, timeOffset))
}
}
if (useAlarm && carbs > 0 && timeOffset > 0) {
carbTimer.scheduleReminder(dateUtil._now() + T.mins(timeOffset.toLong()).msecs())
}
}, null)
}
} else

View file

@ -17,6 +17,7 @@ import info.nightscout.androidaps.databinding.DialogCareBinding
import info.nightscout.androidaps.db.CareportalEvent
import info.nightscout.androidaps.db.Source
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus
import info.nightscout.androidaps.utils.DateUtil
@ -37,6 +38,7 @@ class CareDialog : DialogFragmentWithDate() {
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var nsUpload: NSUpload
@Inject lateinit var translator: Translator
@Inject lateinit var uel: UserEntryLogger
enum class EventType {
BGCHECK,
@ -220,7 +222,7 @@ class CareDialog : DialogFragmentWithDate() {
EventType.ANNOUNCEMENT -> CareportalEvent.ANNOUNCEMENT
}
careportalEvent.json = json.toString()
aapsLogger.debug("USER ENTRY: CAREPORTAL ${careportalEvent.eventType} json: ${careportalEvent.json}")
uel.log("CAREPORTAL", careportalEvent.eventType)
MainApp.getDbHelper().createOrUpdate(careportalEvent)
nsUpload.uploadCareportalEntryToNS(json)
}, null)

View file

@ -1,7 +1,6 @@
package info.nightscout.androidaps.dialogs
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@ -13,6 +12,7 @@ import info.nightscout.androidaps.databinding.DialogExtendedbolusBinding
import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.CommandQueueProvider
import info.nightscout.androidaps.interfaces.Constraint
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.queue.Callback
import info.nightscout.androidaps.utils.HtmlHelper
@ -32,6 +32,7 @@ class ExtendedBolusDialog : DialogFragmentWithDate() {
@Inject lateinit var constraintChecker: ConstraintChecker
@Inject lateinit var commandQueue: CommandQueueProvider
@Inject lateinit var activePlugin: ActivePluginProvider
@Inject lateinit var uel: UserEntryLogger
private var _binding: DialogExtendedbolusBinding? = null
@ -86,16 +87,11 @@ class ExtendedBolusDialog : DialogFragmentWithDate() {
activity?.let { activity ->
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.extended_bolus), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), {
aapsLogger.debug("USER ENTRY: EXTENDED BOLUS $insulinAfterConstraint duration: $durationInMinutes")
uel.log("EXTENDED BOLUS", d1 = insulinAfterConstraint, i1 = durationInMinutes)
commandQueue.extendedBolus(insulinAfterConstraint, durationInMinutes, object : Callback() {
override fun run() {
if (!result.success) {
val i = Intent(ctx, ErrorHelperActivity::class.java)
i.putExtra("soundid", R.raw.boluserror)
i.putExtra("status", result.comment)
i.putExtra("title", resourceHelper.gs(R.string.treatmentdeliveryerror))
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
ctx.startActivity(i)
ErrorHelperActivity.runAlarm(ctx, result.comment, resourceHelper.gs(R.string.treatmentdeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror)
}
}
})

View file

@ -1,7 +1,6 @@
package info.nightscout.androidaps.dialogs
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@ -16,6 +15,7 @@ import info.nightscout.androidaps.db.Source
import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.CommandQueueProvider
import info.nightscout.androidaps.interfaces.Constraint
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload
import info.nightscout.androidaps.queue.Callback
@ -37,6 +37,7 @@ class FillDialog : DialogFragmentWithDate() {
@Inject lateinit var nsUpload: NSUpload
@Inject lateinit var commandQueue: CommandQueueProvider
@Inject lateinit var activePlugin: ActivePluginProvider
@Inject lateinit var uel: UserEntryLogger
private var _binding: DialogFillBinding? = null
@ -126,16 +127,16 @@ class FillDialog : DialogFragmentWithDate() {
activity?.let { activity ->
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.primefill), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), {
if (insulinAfterConstraints > 0) {
aapsLogger.debug("USER ENTRY: PRIME BOLUS $insulinAfterConstraints")
uel.log("PRIME BOLUS", d1 = insulinAfterConstraints)
requestPrimeBolus(insulinAfterConstraints, notes)
}
if (siteChange) {
aapsLogger.debug("USER ENTRY: SITE CHANGE")
uel.log("SITE CHANGE")
nsUpload.generateCareportalEvent(CareportalEvent.SITECHANGE, eventTime, notes)
}
if (insulinChange) {
// add a second for case of both checked
aapsLogger.debug("USER ENTRY: INSULIN CHANGE")
uel.log("INSULIN CHANGE")
nsUpload.generateCareportalEvent(CareportalEvent.INSULINCHANGE, eventTime + 1000, notes)
}
}, null)
@ -159,12 +160,7 @@ class FillDialog : DialogFragmentWithDate() {
commandQueue.bolus(detailedBolusInfo, object : Callback() {
override fun run() {
if (!result.success) {
val i = Intent(ctx, ErrorHelperActivity::class.java)
i.putExtra("soundid", R.raw.boluserror)
i.putExtra("status", result.comment)
i.putExtra("title", resourceHelper.gs(R.string.treatmentdeliveryerror))
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
ctx.startActivity(i)
ErrorHelperActivity.runAlarm(ctx, result.comment, resourceHelper.gs(R.string.treatmentdeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror)
}
}
})

View file

@ -1,7 +1,6 @@
package info.nightscout.androidaps.dialogs
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
@ -15,6 +14,7 @@ import info.nightscout.androidaps.R
import info.nightscout.androidaps.activities.ErrorHelperActivity
import info.nightscout.androidaps.data.DetailedBolusInfo
import info.nightscout.androidaps.data.Profile
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.databinding.DialogInsulinBinding
import info.nightscout.androidaps.db.CareportalEvent
import info.nightscout.androidaps.db.Source
@ -23,6 +23,7 @@ import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.CommandQueueProvider
import info.nightscout.androidaps.interfaces.Constraint
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.queue.Callback
import info.nightscout.androidaps.utils.*
@ -46,7 +47,9 @@ class InsulinDialog : DialogFragmentWithDate() {
@Inject lateinit var commandQueue: CommandQueueProvider
@Inject lateinit var activePlugin: ActivePluginProvider
@Inject lateinit var ctx: Context
@Inject lateinit var repository: AppRepository
@Inject lateinit var config: Config
@Inject lateinit var uel: UserEntryLogger
companion object {
@ -175,7 +178,7 @@ class InsulinDialog : DialogFragmentWithDate() {
activity?.let { activity ->
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.bolus), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), {
if (eatingSoonChecked) {
aapsLogger.debug("USER ENTRY: TEMPTARGET EATING SOON $eatingSoonTT duration: $eatingSoonTTDuration")
uel.log("TT EATING SOON", d1 = eatingSoonTT, i1 = eatingSoonTTDuration)
val tempTarget = TempTarget()
.date(System.currentTimeMillis())
.duration(eatingSoonTTDuration)
@ -193,21 +196,16 @@ class InsulinDialog : DialogFragmentWithDate() {
detailedBolusInfo.source = Source.USER
detailedBolusInfo.notes = notes
if (recordOnlyChecked) {
aapsLogger.debug("USER ENTRY: BOLUS RECORD ONLY $insulinAfterConstraints")
uel.log("BOLUS RECORD", d1 = insulinAfterConstraints, i1 = timeOffset)
detailedBolusInfo.date = time
activePlugin.activeTreatments.addToHistoryTreatment(detailedBolusInfo, false)
} else {
aapsLogger.debug("USER ENTRY: BOLUS $insulinAfterConstraints")
uel.log("BOLUS", d1 = insulinAfterConstraints)
detailedBolusInfo.date = DateUtil.now()
commandQueue.bolus(detailedBolusInfo, object : Callback() {
override fun run() {
if (!result.success) {
val i = Intent(ctx, ErrorHelperActivity::class.java)
i.putExtra("soundid", R.raw.boluserror)
i.putExtra("status", result.comment)
i.putExtra("title", resourceHelper.gs(R.string.treatmentdeliveryerror))
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
ctx.startActivity(i)
ErrorHelperActivity.runAlarm(ctx, result.comment, resourceHelper.gs(R.string.treatmentdeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror)
}
}
})

View file

@ -1,7 +1,6 @@
package info.nightscout.androidaps.dialogs
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.os.Handler
import android.view.LayoutInflater
@ -14,14 +13,15 @@ import dagger.android.support.DaggerDialogFragment
import info.nightscout.androidaps.R
import info.nightscout.androidaps.activities.ErrorHelperActivity
import info.nightscout.androidaps.databinding.DialogLoopBinding
import info.nightscout.androidaps.events.*
import info.nightscout.androidaps.events.EventPreferenceChange
import info.nightscout.androidaps.events.EventRefreshOverview
import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.plugins.constraints.objectives.ObjectivesPlugin
import info.nightscout.androidaps.queue.Callback
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.ToastUtils
@ -41,16 +41,17 @@ class LoopDialog : DaggerDialogFragment() {
@Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var loopPlugin: LoopPlugin
@Inject lateinit var objectivesPlugin: ObjectivesPlugin
@Inject lateinit var activePlugin: ActivePluginProvider
@Inject lateinit var constraintChecker: ConstraintChecker
@Inject lateinit var commandQueue: CommandQueueProvider
@Inject lateinit var configBuilderPlugin: ConfigBuilderPlugin
@Inject lateinit var uel: UserEntryLogger
private var showOkCancel: Boolean = true
private var _binding: DialogLoopBinding? = null
private var loopHandler = Handler()
private var refreshDialog: Runnable? = null
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!
@ -104,7 +105,7 @@ class LoopDialog : DaggerDialogFragment() {
binding.cancel.setOnClickListener { dismiss() }
refreshDialog = Runnable {
scheduleUpdateGUI("refreshDialog")
scheduleUpdateGUI()
loopHandler.postDelayed(refreshDialog, 15 * 1000L)
}
loopHandler.postDelayed(refreshDialog, 15 * 1000L)
@ -119,11 +120,11 @@ class LoopDialog : DaggerDialogFragment() {
var task: Runnable? = null
private fun scheduleUpdateGUI(from: String) {
private fun scheduleUpdateGUI() {
class UpdateRunnable : Runnable {
override fun run() {
updateGUI(from)
updateGUI("refreshDialog")
task = null
}
}
@ -137,8 +138,8 @@ class LoopDialog : DaggerDialogFragment() {
if (_binding == null) return
aapsLogger.debug("UpdateGUI from $from")
val pumpDescription: PumpDescription = activePlugin.activePump.pumpDescription
val closedLoopAllowed = objectivesPlugin.isClosedLoopAllowed(Constraint(true))
val lgsEnabled = objectivesPlugin.isLgsAllowed(Constraint(true))
val closedLoopAllowed = constraintChecker.isClosedLoopAllowed(Constraint(true))
val lgsEnabled = constraintChecker.isLgsAllowed(Constraint(true))
val apsMode = sp.getString(R.string.key_aps_mode, "open")
if (profileFunction.isProfileValid("LoopDialogUpdateGUI")) {
if (loopPlugin.isEnabled(PluginType.LOOP)) {
@ -237,28 +238,28 @@ class LoopDialog : DaggerDialogFragment() {
val profile = profileFunction.getProfile() ?: return true
when (v.id) {
R.id.overview_closeloop -> {
aapsLogger.debug("USER ENTRY: CLOSED LOOP MODE")
uel.log("CLOSED LOOP MODE")
sp.putString(R.string.key_aps_mode, "closed")
rxBus.send(EventPreferenceChange(resourceHelper.gs(R.string.closedloop)))
return true
}
R.id.overview_lgsloop -> {
aapsLogger.debug("USER ENTRY: LGS LOOP MODE")
uel.log("LGS LOOP MODE")
sp.putString(R.string.key_aps_mode, "lgs")
rxBus.send(EventPreferenceChange(resourceHelper.gs(R.string.lowglucosesuspend)))
return true
}
R.id.overview_openloop -> {
aapsLogger.debug("USER ENTRY: OPEN LOOP MODE")
uel.log("OPEN LOOP MODE")
sp.putString(R.string.key_aps_mode, "open")
rxBus.send(EventPreferenceChange(resourceHelper.gs(R.string.lowglucosesuspend)))
return true
}
R.id.overview_disable -> {
aapsLogger.debug("USER ENTRY: LOOP DISABLED")
uel.log("LOOP DISABLED")
loopPlugin.setPluginEnabled(PluginType.LOOP, false)
loopPlugin.setFragmentVisible(PluginType.LOOP, false)
configBuilderPlugin.storeSettings("DisablingLoop")
@ -275,7 +276,7 @@ class LoopDialog : DaggerDialogFragment() {
}
R.id.overview_enable -> {
aapsLogger.debug("USER ENTRY: LOOP ENABLED")
uel.log("LOOP ENABLED")
loopPlugin.setPluginEnabled(PluginType.LOOP, true)
loopPlugin.setFragmentVisible(PluginType.LOOP, true)
configBuilderPlugin.storeSettings("EnablingLoop")
@ -285,18 +286,13 @@ class LoopDialog : DaggerDialogFragment() {
}
R.id.overview_resume, R.id.overview_reconnect -> {
aapsLogger.debug("USER ENTRY: RESUME")
uel.log("RESUME")
loopPlugin.suspendTo(0L)
rxBus.send(EventRefreshOverview("suspendmenu"))
commandQueue.cancelTempBasal(true, object : Callback() {
override fun run() {
if (!result.success) {
val i = Intent(ctx, ErrorHelperActivity::class.java)
i.putExtra("soundid", R.raw.boluserror)
i.putExtra("status", result.comment)
i.putExtra("title", resourceHelper.gs(R.string.tempbasaldeliveryerror))
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
ctx.startActivity(i)
ErrorHelperActivity.runAlarm(ctx, result.comment, resourceHelper.gs(R.string.tempbasaldeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror)
}
}
})
@ -306,49 +302,49 @@ class LoopDialog : DaggerDialogFragment() {
}
R.id.overview_suspend_1h -> {
aapsLogger.debug("USER ENTRY: SUSPEND 1h")
uel.log("SUSPEND 1h")
loopPlugin.suspendLoop(60)
rxBus.send(EventRefreshOverview("suspendmenu"))
return true
}
R.id.overview_suspend_2h -> {
aapsLogger.debug("USER ENTRY: SUSPEND 2h")
uel.log("SUSPEND 2h")
loopPlugin.suspendLoop(120)
rxBus.send(EventRefreshOverview("suspendmenu"))
return true
}
R.id.overview_suspend_3h -> {
aapsLogger.debug("USER ENTRY: SUSPEND 3h")
uel.log("SUSPEND 3h")
loopPlugin.suspendLoop(180)
rxBus.send(EventRefreshOverview("suspendmenu"))
return true
}
R.id.overview_suspend_10h -> {
aapsLogger.debug("USER ENTRY: SUSPEND 10h")
uel.log("SUSPEND 10h")
loopPlugin.suspendLoop(600)
rxBus.send(EventRefreshOverview("suspendmenu"))
return true
}
R.id.overview_disconnect_15m -> {
aapsLogger.debug("USER ENTRY: DISCONNECT 15m")
uel.log("DISCONNECT 15m")
loopPlugin.disconnectPump(15, profile)
rxBus.send(EventRefreshOverview("suspendmenu"))
return true
}
R.id.overview_disconnect_30m -> {
aapsLogger.debug("USER ENTRY: DISCONNECT 30m")
uel.log("DISCONNECT 30m")
loopPlugin.disconnectPump(30, profile)
rxBus.send(EventRefreshOverview("suspendmenu"))
return true
}
R.id.overview_disconnect_1h -> {
aapsLogger.debug("USER ENTRY: DISCONNECT 1h")
uel.log("DISCONNECT 1h")
loopPlugin.disconnectPump(60, profile)
sp.putBoolean(R.string.key_objectiveusedisconnect, true)
rxBus.send(EventRefreshOverview("suspendmenu"))
@ -356,14 +352,14 @@ class LoopDialog : DaggerDialogFragment() {
}
R.id.overview_disconnect_2h -> {
aapsLogger.debug("USER ENTRY: DISCONNECT 2h")
uel.log("DISCONNECT 2h")
loopPlugin.disconnectPump(120, profile)
rxBus.send(EventRefreshOverview("suspendmenu"))
return true
}
R.id.overview_disconnect_3h -> {
aapsLogger.debug("USER ENTRY: DISCONNECT 3h")
uel.log("DISCONNECT 3h")
loopPlugin.disconnectPump(180, profile)
rxBus.send(EventRefreshOverview("suspendmenu"))
return true

View file

@ -11,6 +11,7 @@ import info.nightscout.androidaps.R
import info.nightscout.androidaps.databinding.DialogProfileswitchBinding
import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.HtmlHelper
@ -26,6 +27,7 @@ class ProfileSwitchDialog : DialogFragmentWithDate() {
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var treatmentsPlugin: TreatmentsPlugin
@Inject lateinit var activePlugin: ActivePluginProvider
@Inject lateinit var uel: UserEntryLogger
private var profileIndex: Int? = null
@ -122,7 +124,7 @@ class ProfileSwitchDialog : DialogFragmentWithDate() {
activity?.let { activity ->
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.careportal_profileswitch), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), {
aapsLogger.debug("USER ENTRY: PROFILE SWITCH $profile percent: $percent timeshift: $timeShift duration: $duration")
uel.log("PROFILE SWITCH", d1 = percent.toDouble(), i1 = timeShift, i2 = duration)
treatmentsPlugin.doProfileSwitch(profileStore, profile, duration, percent, timeShift, eventTime)
})
}

View file

@ -1,7 +1,6 @@
package info.nightscout.androidaps.dialogs
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@ -15,6 +14,7 @@ import info.nightscout.androidaps.interfaces.CommandQueueProvider
import info.nightscout.androidaps.interfaces.Constraint
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.interfaces.PumpDescription
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.queue.Callback
import info.nightscout.androidaps.utils.HtmlHelper
@ -35,6 +35,7 @@ class TempBasalDialog : DialogFragmentWithDate() {
@Inject lateinit var activePlugin: ActivePluginProvider
@Inject lateinit var commandQueue: CommandQueueProvider
@Inject lateinit var ctx: Context
@Inject lateinit var uel: UserEntryLogger
private var isPercentPump = true
@ -119,20 +120,15 @@ class TempBasalDialog : DialogFragmentWithDate() {
val callback: Callback = object : Callback() {
override fun run() {
if (!result.success) {
val i = Intent(ctx, ErrorHelperActivity::class.java)
i.putExtra("soundid", R.raw.boluserror)
i.putExtra("status", result.comment)
i.putExtra("title", resourceHelper.gs(R.string.tempbasaldeliveryerror))
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
ctx.startActivity(i)
ErrorHelperActivity.runAlarm(ctx, result.comment, resourceHelper.gs(R.string.tempbasaldeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror)
}
}
}
if (isPercentPump) {
aapsLogger.debug("USER ENTRY: TEMP BASAL $percent% duration: $durationInMinutes")
uel.log("TEMP BASAL", d1 = percent.toDouble(), i1 = durationInMinutes)
commandQueue.tempBasalPercent(percent, durationInMinutes, true, profile, callback)
} else {
aapsLogger.debug("USER ENTRY: TEMP BASAL $absolute duration: $durationInMinutes")
uel.log("TEMP BASAL", d1 = absolute, i1 = durationInMinutes)
commandQueue.tempBasalAbsolute(absolute, durationInMinutes, true, profile, callback)
}
})

View file

@ -15,6 +15,7 @@ import info.nightscout.androidaps.db.Source
import info.nightscout.androidaps.db.TempTarget
import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
import info.nightscout.androidaps.utils.DefaultValueHelper
@ -33,8 +34,9 @@ class TempTargetDialog : DialogFragmentWithDate() {
@Inject lateinit var defaultValueHelper: DefaultValueHelper
@Inject lateinit var treatmentsPlugin: TreatmentsPlugin
@Inject lateinit var activePlugin: ActivePluginProvider
@Inject lateinit var uel: UserEntryLogger
lateinit var reasonList: List<String>
private lateinit var reasonList: List<String>
private var _binding: DialogTemptargetBinding? = null
@ -162,7 +164,7 @@ class TempTargetDialog : DialogFragmentWithDate() {
activity?.let { activity ->
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.careportal_temporarytarget), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), {
aapsLogger.debug("USER ENTRY: TEMP TARGET $target duration: $duration")
uel.log("TT", d1 = target, i1 = duration)
if (target == 0.0 || duration == 0) {
val tempTarget = TempTarget()
.date(eventTime)

View file

@ -1,7 +1,6 @@
package info.nightscout.androidaps.dialogs
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
@ -19,6 +18,7 @@ import info.nightscout.androidaps.db.Source
import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.CommandQueueProvider
import info.nightscout.androidaps.interfaces.Constraint
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.queue.Callback
import info.nightscout.androidaps.utils.DecimalFormatter
@ -41,6 +41,7 @@ class TreatmentDialog : DialogFragmentWithDate() {
@Inject lateinit var commandQueue: CommandQueueProvider
@Inject lateinit var ctx: Context
@Inject lateinit var config: Config
@Inject lateinit var uel: UserEntryLogger
private val textWatcher: TextWatcher = object : TextWatcher {
override fun afterTextChanged(s: Editable) {}
@ -128,7 +129,7 @@ class TreatmentDialog : DialogFragmentWithDate() {
if (insulinAfterConstraints > 0 || carbsAfterConstraints > 0) {
activity?.let { activity ->
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.overview_treatment_label), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), {
aapsLogger.debug("USER ENTRY: BOLUS insulin $insulin carbs: $carbs")
uel.log("TREATMENT", d1 = insulin, i1 = carbs)
val detailedBolusInfo = DetailedBolusInfo()
if (insulinAfterConstraints == 0.0) detailedBolusInfo.eventType = CareportalEvent.CARBCORRECTION
if (carbsAfterConstraints == 0) detailedBolusInfo.eventType = CareportalEvent.CORRECTIONBOLUS
@ -140,12 +141,7 @@ class TreatmentDialog : DialogFragmentWithDate() {
commandQueue.bolus(detailedBolusInfo, object : Callback() {
override fun run() {
if (!result.success) {
val i = Intent(ctx, ErrorHelperActivity::class.java)
i.putExtra("soundid", R.raw.boluserror)
i.putExtra("status", result.comment)
i.putExtra("title", resourceHelper.gs(R.string.treatmentdeliveryerror))
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
ctx.startActivity(i)
ErrorHelperActivity.runAlarm(ctx, result.comment, resourceHelper.gs(R.string.treatmentdeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror)
}
}
})

View file

@ -270,7 +270,7 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
val end = start + T.hours(rangeToDisplay.toLong()).msecs()
iobCobCalculatorPluginHistory.stopCalculation(from)
iobCobCalculatorPluginHistory.clearCache()
iobCobCalculatorPluginHistory.runCalculation(from, end, true, false, eventCustomCalculationFinished)
iobCobCalculatorPluginHistory.runCalculation(from, end, bgDataReload = true, limitDataToOldestAvailable = false, cause = eventCustomCalculationFinished)
}
}
@ -333,24 +333,27 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
var useDevForScale = false
var useRatioForScale = false
var useDSForScale = false
var useIAForScale = false
var useBGIForScale = false
var useABSForScale = false
when {
menuChartSettings[g + 1][OverviewMenus.CharType.IOB.ordinal] -> useIobForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.COB.ordinal] -> useCobForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.DEV.ordinal] -> useDevForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.SEN.ordinal] -> useRatioForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.ACT.ordinal] -> useIAForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.BGI.ordinal] -> useBGIForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.ABS.ordinal] -> useABSForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.DEVSLOPE.ordinal] -> useDSForScale = true
}
if (menuChartSettings[g + 1][OverviewMenus.CharType.IOB.ordinal]) secondGraphData.addIob(fromTime, toTime, useIobForScale, 1.0, menuChartSettings[g + 1][OverviewMenus.CharType.PRE.ordinal])
if (menuChartSettings[g + 1][OverviewMenus.CharType.COB.ordinal]) secondGraphData.addCob(fromTime, toTime, useCobForScale, if (useCobForScale) 1.0 else 0.5)
if (menuChartSettings[g + 1][OverviewMenus.CharType.DEV.ordinal]) secondGraphData.addDeviations(fromTime, toTime, useDevForScale, 1.0)
if (menuChartSettings[g + 1][OverviewMenus.CharType.SEN.ordinal]) secondGraphData.addRatio(fromTime, toTime, useRatioForScale, 1.0)
if (menuChartSettings[g + 1][OverviewMenus.CharType.ACT.ordinal]) secondGraphData.addActivity(fromTime, toTime, useIAForScale, 0.8)
val alignIobScale = menuChartSettings[g + 1][OverviewMenus.CharType.ABS.ordinal] && menuChartSettings[g + 1][OverviewMenus.CharType.IOB.ordinal]
val alignDevBgiScale = menuChartSettings[g + 1][OverviewMenus.CharType.DEV.ordinal] && menuChartSettings[g + 1][OverviewMenus.CharType.BGI.ordinal]
if (menuChartSettings[g + 1][OverviewMenus.CharType.ABS.ordinal]) secondGraphData.addAbsIob(fromTime, toTime, useABSForScale, 1.0)
if (menuChartSettings[g + 1][OverviewMenus.CharType.IOB.ordinal]) secondGraphData.addIob(fromTime, toTime, useIobForScale, 1.0, menuChartSettings[g + 1][OverviewMenus.CharType.PRE.ordinal], alignIobScale)
if (menuChartSettings[g + 1][OverviewMenus.CharType.COB.ordinal]) secondGraphData.addCob(fromTime, toTime, useCobForScale, if (useCobForScale) 1.0 else 0.5)
if (menuChartSettings[g + 1][OverviewMenus.CharType.DEV.ordinal]) secondGraphData.addDeviations(fromTime, toTime, useDevForScale, 1.0, alignDevBgiScale)
if (menuChartSettings[g + 1][OverviewMenus.CharType.SEN.ordinal]) secondGraphData.addRatio(fromTime, toTime, useRatioForScale, 1.0)
if (menuChartSettings[g + 1][OverviewMenus.CharType.BGI.ordinal]) secondGraphData.addMinusBGI(fromTime, toTime, useBGIForScale, if (alignDevBgiScale) 1.0 else 0.8, alignDevBgiScale)
if (menuChartSettings[g + 1][OverviewMenus.CharType.DEVSLOPE.ordinal] && buildHelper.isDev()) secondGraphData.addDeviationSlope(fromTime, toTime, useDSForScale, 1.0)
// set manual x bounds to have nice steps
@ -362,7 +365,7 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
}
// set manual x bounds to have nice steps
graphData.setNumVerticalLables()
graphData.setNumVerticalLabels()
graphData.formatAxis(fromTime, toTime)
}
// finally enforce drawing of graphs in UI thread

View file

@ -1,63 +0,0 @@
package info.nightscout.androidaps.plugins.aps.logger;
import org.mozilla.javascript.ScriptableObject;
import javax.inject.Inject;
import info.nightscout.androidaps.db.StaticInjector;
import info.nightscout.androidaps.logging.AAPSLogger;
import info.nightscout.androidaps.logging.LTag;
/**
* Created by adrian on 15/10/17.
*/
public class LoggerCallback extends ScriptableObject {
@Inject
AAPSLogger aapsLogger;
private static StringBuffer errorBuffer = new StringBuffer();
private static StringBuffer logBuffer = new StringBuffer();
public LoggerCallback() {
//empty constructor needed for Rhino
errorBuffer = new StringBuffer();
logBuffer = new StringBuffer();
StaticInjector.Companion.getInstance().androidInjector().inject(this);
}
@Override
public String getClassName() {
return "LoggerCallback";
}
public void jsConstructor() {
//empty constructor on JS site; could work as setter
}
public void jsFunction_log(Object obj1) {
aapsLogger.debug(LTag.APS, obj1.toString().trim());
logBuffer.append(obj1.toString());
}
public void jsFunction_error(Object obj1) {
aapsLogger.error(LTag.APS, obj1.toString().trim());
errorBuffer.append(obj1.toString());
}
public static String getScriptDebug() {
String ret = "";
if (errorBuffer.length() > 0) {
ret += "e:\n" + errorBuffer.toString();
}
if (ret.length() > 0 && logBuffer.length() > 0) ret += '\n';
if (logBuffer.length() > 0) {
ret += "d:\n" + logBuffer.toString();
}
return ret;
}
}

View file

@ -0,0 +1,61 @@
package info.nightscout.androidaps.plugins.aps.logger
import info.nightscout.androidaps.db.StaticInjector
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import org.mozilla.javascript.ScriptableObject
import javax.inject.Inject
@Suppress("unused", "FunctionName")
class LoggerCallback : ScriptableObject() {
@Inject lateinit var aapsLogger: AAPSLogger
override fun getClassName(): String = "LoggerCallback"
fun jsConstructor() {
//empty constructor on JS site; could work as setter
}
fun jsFunction_log(obj1: Any) {
aapsLogger.debug(LTag.APS, obj1.toString().trim { it <= ' ' })
logBuffer.append(obj1.toString())
}
fun jsFunction_error(obj1: Any) {
aapsLogger.error(LTag.APS, obj1.toString().trim { it <= ' ' })
errorBuffer.append(obj1.toString())
}
companion object {
private var errorBuffer = StringBuffer()
private var logBuffer = StringBuffer()
val scriptDebug: String
get() {
var ret = ""
if (errorBuffer.isNotEmpty()) {
ret += """
e:
$errorBuffer
""".trimIndent()
}
if (ret.isNotEmpty() && logBuffer.isNotEmpty()) ret += '\n'
if (logBuffer.isNotEmpty()) {
ret += """
d:
$logBuffer
""".trimIndent()
}
return ret
}
}
init {
//empty constructor needed for Rhino
errorBuffer = StringBuffer()
logBuffer = StringBuffer()
@Suppress("DEPRECATION")
StaticInjector.Companion.getInstance().androidInjector().inject(this)
}
}

View file

@ -15,11 +15,11 @@ import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.HtmlHelper
import info.nightscout.androidaps.utils.extensions.plusAssign
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.androidaps.utils.sharedPreferences.SP
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign
import javax.inject.Inject
class LoopFragment : DaggerFragment() {

View file

@ -1,880 +0,0 @@
package info.nightscout.androidaps.plugins.aps.loop;
import android.annotation.SuppressLint;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.TaskStackBuilder;
import android.content.Context;
import android.content.Intent;
import android.os.SystemClock;
import androidx.core.app.NotificationCompat;
import org.jetbrains.annotations.Nullable;
import org.json.JSONException;
import org.json.JSONObject;
import javax.inject.Inject;
import javax.inject.Singleton;
import dagger.Lazy;
import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.BuildConfig;
import info.nightscout.androidaps.Config;
import info.nightscout.androidaps.Constants;
import info.nightscout.androidaps.MainActivity;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.activities.ErrorHelperActivity;
import info.nightscout.androidaps.data.DetailedBolusInfo;
import info.nightscout.androidaps.data.Profile;
import info.nightscout.androidaps.data.PumpEnactResult;
import info.nightscout.androidaps.database.entities.GlucoseValue;
import info.nightscout.androidaps.db.CareportalEvent;
import info.nightscout.androidaps.db.Source;
import info.nightscout.androidaps.db.TemporaryBasal;
import info.nightscout.androidaps.events.EventAcceptOpenLoopChange;
import info.nightscout.androidaps.events.EventNewBG;
import info.nightscout.androidaps.events.EventTempTargetChange;
import info.nightscout.androidaps.interfaces.APSInterface;
import info.nightscout.androidaps.interfaces.ActivePluginProvider;
import info.nightscout.androidaps.interfaces.CommandQueueProvider;
import info.nightscout.androidaps.interfaces.Constraint;
import info.nightscout.androidaps.interfaces.LoopInterface;
import info.nightscout.androidaps.interfaces.PluginBase;
import info.nightscout.androidaps.interfaces.PluginDescription;
import info.nightscout.androidaps.interfaces.PluginType;
import info.nightscout.androidaps.interfaces.ProfileFunction;
import info.nightscout.androidaps.interfaces.PumpDescription;
import info.nightscout.androidaps.interfaces.PumpInterface;
import info.nightscout.androidaps.logging.AAPSLogger;
import info.nightscout.androidaps.logging.LTag;
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.RxBusWrapper;
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker;
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;
import info.nightscout.androidaps.plugins.general.overview.notifications.Notification;
import info.nightscout.androidaps.plugins.general.wear.ActionStringHandler;
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin;
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventAutosensCalculationFinished;
import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin;
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin;
import info.nightscout.androidaps.queue.Callback;
import info.nightscout.androidaps.queue.commands.Command;
import info.nightscout.androidaps.receivers.ReceiverStatusStore;
import info.nightscout.androidaps.utils.DateUtil;
import info.nightscout.androidaps.utils.FabricPrivacy;
import info.nightscout.androidaps.utils.HardLimits;
import info.nightscout.androidaps.utils.T;
import info.nightscout.androidaps.utils.resources.ResourceHelper;
import info.nightscout.androidaps.utils.rx.AapsSchedulers;
import info.nightscout.androidaps.utils.sharedPreferences.SP;
import io.reactivex.disposables.CompositeDisposable;
@Singleton
public class LoopPlugin extends PluginBase implements LoopInterface {
private final HasAndroidInjector injector;
private final SP sp;
private final RxBusWrapper rxBus;
private final AapsSchedulers aapsSchedulers;
private final ConstraintChecker constraintChecker;
private final ResourceHelper resourceHelper;
private final ProfileFunction profileFunction;
private final Context context;
private final CommandQueueProvider commandQueue;
private final ActivePluginProvider activePlugin;
private final TreatmentsPlugin treatmentsPlugin;
private final VirtualPumpPlugin virtualPumpPlugin;
private final Lazy<ActionStringHandler> actionStringHandler;
private final IobCobCalculatorPlugin iobCobCalculatorPlugin;
private final ReceiverStatusStore receiverStatusStore;
private final FabricPrivacy fabricPrivacy;
private final NSUpload nsUpload;
private final HardLimits hardLimits;
private final CompositeDisposable disposable = new CompositeDisposable();
private static final String CHANNEL_ID = "AndroidAPS-Openloop";
private long lastBgTriggeredRun = 0;
private long loopSuspendedTill; // end of manual loop suspend
private boolean isSuperBolus;
private boolean isDisconnected;
private long carbsSuggestionsSuspendedUntil = 0;
private int prevCarbsreq = 0;
@Nullable private LastRun lastRun = null;
@Nullable @Override public LastRun getLastRun() {
return lastRun;
}
@Override public void setLastRun(@Nullable LastRun lastRun) {
this.lastRun = lastRun;
}
@Inject
public LoopPlugin(
HasAndroidInjector injector,
AAPSLogger aapsLogger,
AapsSchedulers aapsSchedulers,
RxBusWrapper rxBus,
SP sp,
Config config,
ConstraintChecker constraintChecker,
ResourceHelper resourceHelper,
ProfileFunction profileFunction,
Context context,
CommandQueueProvider commandQueue,
ActivePluginProvider activePlugin,
TreatmentsPlugin treatmentsPlugin,
VirtualPumpPlugin virtualPumpPlugin,
Lazy<ActionStringHandler> actionStringHandler, // TODO Adrian use RxBus instead of Lazy
IobCobCalculatorPlugin iobCobCalculatorPlugin,
ReceiverStatusStore receiverStatusStore,
FabricPrivacy fabricPrivacy,
NSUpload nsUpload,
HardLimits hardLimits
) {
super(new PluginDescription()
.mainType(PluginType.LOOP)
.fragmentClass(LoopFragment.class.getName())
.pluginIcon(R.drawable.ic_loop_closed_white)
.pluginName(R.string.loop)
.shortName(R.string.loop_shortname)
.preferencesId(R.xml.pref_loop)
.enableByDefault(config.getAPS())
.description(R.string.description_loop),
aapsLogger, resourceHelper, injector
);
this.injector = injector;
this.aapsSchedulers = aapsSchedulers;
this.sp = sp;
this.rxBus = rxBus;
this.constraintChecker = constraintChecker;
this.resourceHelper = resourceHelper;
this.profileFunction = profileFunction;
this.context = context;
this.activePlugin = activePlugin;
this.commandQueue = commandQueue;
this.treatmentsPlugin = treatmentsPlugin;
this.virtualPumpPlugin = virtualPumpPlugin;
this.actionStringHandler = actionStringHandler;
this.iobCobCalculatorPlugin = iobCobCalculatorPlugin;
this.receiverStatusStore = receiverStatusStore;
this.fabricPrivacy = fabricPrivacy;
this.nsUpload = nsUpload;
this.hardLimits = hardLimits;
loopSuspendedTill = sp.getLong("loopSuspendedTill", 0L);
isSuperBolus = sp.getBoolean("isSuperBolus", false);
isDisconnected = sp.getBoolean("isDisconnected", false);
}
@Override
protected void onStart() {
createNotificationChannel();
super.onStart();
disposable.add(rxBus
.toObservable(EventTempTargetChange.class)
.observeOn(aapsSchedulers.getIo())
.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.
<p>
*/
disposable.add(rxBus
.toObservable(EventAutosensCalculationFinished.class)
.observeOn(aapsSchedulers.getIo())
.subscribe(event -> {
// Autosens calculation not triggered by a new BG
if (!(event.getCause() instanceof EventNewBG)) return;
GlucoseValue glucoseValue = iobCobCalculatorPlugin.actualBg();
// BG outdated
if (glucoseValue == null) return;
// already looped with that value
if (glucoseValue.getTimestamp() <= lastBgTriggeredRun) return;
lastBgTriggeredRun = glucoseValue.getTimestamp();
invoke("AutosenseCalculation for " + glucoseValue, true);
}, fabricPrivacy::logException)
);
}
private void createNotificationChannel() {
NotificationManager mNotificationManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
@SuppressLint("WrongConstant") NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
CHANNEL_ID,
NotificationManager.IMPORTANCE_HIGH);
mNotificationManager.createNotificationChannel(channel);
}
@Override
protected void onStop() {
disposable.clear();
super.onStop();
}
@Override
public boolean specialEnableCondition() {
try {
PumpInterface pump = activePlugin.getActivePump();
return pump.getPumpDescription().isTempBasalCapable;
} catch (Exception ignored) {
// may fail during initialization
return true;
}
}
public void suspendTo(long endTime) {
loopSuspendedTill = endTime;
isSuperBolus = false;
isDisconnected = false;
sp.putLong("loopSuspendedTill", loopSuspendedTill);
sp.putBoolean("isSuperBolus", isSuperBolus);
sp.putBoolean("isDisconnected", isDisconnected);
}
public void superBolusTo(long endTime) {
loopSuspendedTill = endTime;
isSuperBolus = true;
isDisconnected = false;
sp.putLong("loopSuspendedTill", loopSuspendedTill);
sp.putBoolean("isSuperBolus", isSuperBolus);
sp.putBoolean("isDisconnected", isDisconnected);
}
private void disconnectTo(long endTime) {
loopSuspendedTill = endTime;
isSuperBolus = false;
isDisconnected = true;
sp.putLong("loopSuspendedTill", loopSuspendedTill);
sp.putBoolean("isSuperBolus", isSuperBolus);
sp.putBoolean("isDisconnected", isDisconnected);
}
public int minutesToEndOfSuspend() {
if (loopSuspendedTill == 0)
return 0;
long now = System.currentTimeMillis();
long msecDiff = loopSuspendedTill - now;
if (loopSuspendedTill <= now) { // time exceeded
suspendTo(0L);
return 0;
}
return (int) (msecDiff / 60d / 1000d);
}
public boolean isSuspended() {
if (loopSuspendedTill == 0)
return false;
long now = System.currentTimeMillis();
if (loopSuspendedTill <= now) { // time exceeded
suspendTo(0L);
return false;
}
return true;
}
public boolean isLGS() {
Constraint<Boolean> closedLoopEnabled = constraintChecker.isClosedLoopAllowed();
Double MaxIOBallowed = constraintChecker.getMaxIOBAllowed().value();
String APSmode = sp.getString(R.string.key_aps_mode, "open");
PumpInterface pump = activePlugin.getActivePump();
boolean isLGS = false;
if (!isSuspended() && !pump.isSuspended())
if (closedLoopEnabled.value())
if ((MaxIOBallowed.equals(hardLimits.getMAXIOB_LGS())) || (APSmode.equals("lgs")))
isLGS = true;
return isLGS;
}
public boolean isSuperBolus() {
if (loopSuspendedTill == 0)
return false;
long now = System.currentTimeMillis();
if (loopSuspendedTill <= now) { // time exceeded
suspendTo(0L);
return false;
}
return isSuperBolus;
}
public boolean isDisconnected() {
if (loopSuspendedTill == 0)
return false;
long now = System.currentTimeMillis();
if (loopSuspendedTill <= now) { // time exceeded
suspendTo(0L);
return false;
}
return isDisconnected;
}
public boolean treatmentTimethreshold(int duartionMinutes) {
long threshold = System.currentTimeMillis() + (duartionMinutes * 60 * 1000);
boolean bool = false;
if (treatmentsPlugin.getLastBolusTime() > threshold || treatmentsPlugin.getLastCarbTime() > threshold)
bool = true;
return bool;
}
public synchronized void invoke(String initiator, boolean allowNotification) {
invoke(initiator, allowNotification, false);
}
public synchronized void invoke(String initiator, boolean allowNotification, boolean tempBasalFallback) {
try {
getAapsLogger().debug(LTag.APS, "invoke from " + initiator);
Constraint<Boolean> loopEnabled = constraintChecker.isLoopInvocationAllowed();
if (!loopEnabled.value()) {
String message = resourceHelper.gs(R.string.loopdisabled) + "\n" + loopEnabled.getReasons(getAapsLogger());
getAapsLogger().debug(LTag.APS, message);
rxBus.send(new EventLoopSetLastRunGui(message));
return;
}
final PumpInterface pump = activePlugin.getActivePump();
APSResult result = null;
if (!isEnabled(PluginType.LOOP))
return;
Profile profile = profileFunction.getProfile();
if (profile == null || !profileFunction.isProfileValid("Loop")) {
getAapsLogger().debug(LTag.APS, resourceHelper.gs(R.string.noprofileselected));
rxBus.send(new EventLoopSetLastRunGui(resourceHelper.gs(R.string.noprofileselected)));
return;
}
// Check if pump info is loaded
if (pump.getBaseBasalRate() < 0.01d) return;
APSInterface usedAPS = activePlugin.getActiveAPS();
if (((PluginBase) usedAPS).isEnabled(PluginType.APS)) {
usedAPS.invoke(initiator, tempBasalFallback);
result = usedAPS.getLastAPSResult();
}
// Check if we have any result
if (result == null) {
rxBus.send(new EventLoopSetLastRunGui(resourceHelper.gs(R.string.noapsselected)));
return;
}
// Prepare for pumps using % basals
if (pump.getPumpDescription().tempBasalStyle == PumpDescription.PERCENT && allowPercentage()) {
result.setUsePercent(true);
}
result.setPercent((int) (result.getRate() / profile.getBasal() * 100));
// check rate for constraints
final APSResult resultAfterConstraints = result.newAndClone(injector);
resultAfterConstraints.setRateConstraint(new Constraint<>(resultAfterConstraints.getRate()));
resultAfterConstraints.setRate(constraintChecker.applyBasalConstraints(resultAfterConstraints.getRateConstraint(), profile).value());
resultAfterConstraints.setPercentConstraint(new Constraint<>(resultAfterConstraints.getPercent()));
resultAfterConstraints.setPercent(constraintChecker.applyBasalPercentConstraints(resultAfterConstraints.getPercentConstraint(), profile).value());
resultAfterConstraints.setSmbConstraint(new Constraint<>(resultAfterConstraints.getSmb()));
resultAfterConstraints.setSmb(constraintChecker.applyBolusConstraints(resultAfterConstraints.getSmbConstraint()).value());
// safety check for multiple SMBs
long lastBolusTime = treatmentsPlugin.getLastBolusTime();
if (lastBolusTime != 0 && lastBolusTime + T.mins(3).msecs() > System.currentTimeMillis()) {
getAapsLogger().debug(LTag.APS, "SMB requested but still in 3 min interval");
resultAfterConstraints.setSmb(0);
}
if (lastRun != null && lastRun.getConstraintsProcessed() != null) {
prevCarbsreq = lastRun.getConstraintsProcessed().getCarbsReq();
}
if (lastRun == null) lastRun = new LastRun();
lastRun.setRequest(result);
lastRun.setConstraintsProcessed(resultAfterConstraints);
lastRun.setLastAPSRun(DateUtil.now());
lastRun.setSource(((PluginBase) usedAPS).getName());
lastRun.setTbrSetByPump(null);
lastRun.setSmbSetByPump(null);
lastRun.setLastTBREnact(0);
lastRun.setLastTBRRequest(0);
lastRun.setLastSMBEnact(0);
lastRun.setLastSMBRequest(0);
nsUpload.uploadDeviceStatus(this, iobCobCalculatorPlugin, profileFunction, activePlugin.getActivePump(), receiverStatusStore, BuildConfig.VERSION_NAME + "-" + BuildConfig.BUILDVERSION);
if (isSuspended()) {
getAapsLogger().debug(LTag.APS, resourceHelper.gs(R.string.loopsuspended));
rxBus.send(new EventLoopSetLastRunGui(resourceHelper.gs(R.string.loopsuspended)));
return;
}
if (pump.isSuspended()) {
getAapsLogger().debug(LTag.APS, resourceHelper.gs(R.string.pumpsuspended));
rxBus.send(new EventLoopSetLastRunGui(resourceHelper.gs(R.string.pumpsuspended)));
return;
}
Constraint<Boolean> closedLoopEnabled = constraintChecker.isClosedLoopAllowed();
if (closedLoopEnabled.value()) {
if (allowNotification) {
if (resultAfterConstraints.isCarbsRequired()
&& resultAfterConstraints.getCarbsReq() >= sp.getInt(R.string.key_smb_enable_carbs_suggestions_threshold, 0)
&& carbsSuggestionsSuspendedUntil < System.currentTimeMillis() && !treatmentTimethreshold(-15)) {
if (sp.getBoolean(R.string.key_enable_carbs_required_alert_local, true) && !sp.getBoolean(R.string.key_raise_notifications_as_android_notifications, true)) {
Notification carbreqlocal = new Notification(Notification.CARBS_REQUIRED, resultAfterConstraints.getCarbsRequiredText(), Notification.NORMAL);
rxBus.send(new EventNewNotification(carbreqlocal));
}
if (sp.getBoolean(R.string.key_ns_create_announcements_from_carbs_req, false)) {
nsUpload.uploadError(resultAfterConstraints.getCarbsRequiredText());
}
if (sp.getBoolean(R.string.key_enable_carbs_required_alert_local, true) && sp.getBoolean(R.string.key_raise_notifications_as_android_notifications, true)) {
Intent intentAction5m = new Intent(context, CarbSuggestionReceiver.class);
intentAction5m.putExtra("ignoreDuration", 5);
PendingIntent pendingIntent5m = PendingIntent.getBroadcast(context, 1, intentAction5m, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Action actionIgnore5m = new
NotificationCompat.Action(R.drawable.ic_notif_aaps, resourceHelper.gs(R.string.ignore5m, "Ignore 5m"), pendingIntent5m);
Intent intentAction15m = new Intent(context, CarbSuggestionReceiver.class);
intentAction15m.putExtra("ignoreDuration", 15);
PendingIntent pendingIntent15m = PendingIntent.getBroadcast(context, 1, intentAction15m, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Action actionIgnore15m = new
NotificationCompat.Action(R.drawable.ic_notif_aaps, resourceHelper.gs(R.string.ignore15m, "Ignore 15m"), pendingIntent15m);
Intent intentAction30m = new Intent(context, CarbSuggestionReceiver.class);
intentAction30m.putExtra("ignoreDuration", 30);
PendingIntent pendingIntent30m = PendingIntent.getBroadcast(context, 1, intentAction30m, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Action actionIgnore30m = new
NotificationCompat.Action(R.drawable.ic_notif_aaps, resourceHelper.gs(R.string.ignore30m, "Ignore 30m"), pendingIntent30m);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, CHANNEL_ID);
builder.setSmallIcon(R.drawable.notif_icon)
.setContentTitle(resourceHelper.gs(R.string.carbssuggestion))
.setContentText(resultAfterConstraints.getCarbsRequiredText())
.setAutoCancel(true)
.setPriority(Notification.IMPORTANCE_HIGH)
.setCategory(Notification.CATEGORY_ALARM)
.addAction(actionIgnore5m)
.addAction(actionIgnore15m)
.addAction(actionIgnore30m)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setVibrate(new long[]{1000, 1000, 1000, 1000, 1000});
NotificationManager mNotificationManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
// mId allows you to update the notification later on.
mNotificationManager.notify(Constants.notificationID, builder.build());
rxBus.send(new EventNewOpenLoopNotification());
//only send to wear if Native notifications are turned off
if (!sp.getBoolean(R.string.key_raise_notifications_as_android_notifications, true)) {
// Send to Wear
actionStringHandler.get().handleInitiate("changeRequest");
}
}
} else {
//If carbs were required previously, but are no longer needed, dismiss notifications
if (prevCarbsreq > 0) {
dismissSuggestion();
rxBus.send(new EventDismissNotification(Notification.CARBS_REQUIRED));
}
}
}
if (resultAfterConstraints.isChangeRequested()
&& !commandQueue.bolusInQueue()
&& !commandQueue.isRunning(Command.CommandType.BOLUS)) {
final PumpEnactResult waiting = new PumpEnactResult(getInjector());
waiting.queued = true;
if (resultAfterConstraints.getTempBasalRequested())
lastRun.setTbrSetByPump(waiting);
if (resultAfterConstraints.getBolusRequested())
lastRun.setSmbSetByPump(waiting);
rxBus.send(new EventLoopUpdateGui());
fabricPrivacy.logCustom("APSRequest");
applyTBRRequest(resultAfterConstraints, profile, new Callback() {
@Override
public void run() {
if (result.enacted || result.success) {
lastRun.setTbrSetByPump(result);
lastRun.setLastTBRRequest(lastRun.getLastAPSRun());
lastRun.setLastTBREnact(DateUtil.now());
rxBus.send(new EventLoopUpdateGui());
applySMBRequest(resultAfterConstraints, new Callback() {
@Override
public void run() {
// Callback is only called if a bolus was actually requested
if (result.enacted || result.success) {
lastRun.setSmbSetByPump(result);
lastRun.setLastSMBRequest(lastRun.getLastAPSRun());
lastRun.setLastSMBEnact(DateUtil.now());
} else {
new Thread(() -> {
SystemClock.sleep(1000);
invoke("tempBasalFallback", allowNotification, true);
}).start();
}
rxBus.send(new EventLoopUpdateGui());
}
});
} else {
lastRun.setTbrSetByPump(result);
lastRun.setLastTBRRequest(lastRun.getLastAPSRun());
}
rxBus.send(new EventLoopUpdateGui());
}
});
} else {
lastRun.setTbrSetByPump(null);
lastRun.setSmbSetByPump(null);
}
} else {
if (resultAfterConstraints.isChangeRequested() && allowNotification) {
NotificationCompat.Builder builder =
new NotificationCompat.Builder(context, CHANNEL_ID);
builder.setSmallIcon(R.drawable.notif_icon)
.setContentTitle(resourceHelper.gs(R.string.openloop_newsuggestion))
.setContentText(resultAfterConstraints.toString())
.setAutoCancel(true)
.setPriority(Notification.IMPORTANCE_HIGH)
.setCategory(Notification.CATEGORY_ALARM)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
if (sp.getBoolean("wearcontrol", false)) {
builder.setLocalOnly(true);
}
presentSuggestion(builder);
} else if (allowNotification) {
dismissSuggestion();
}
}
rxBus.send(new EventLoopUpdateGui());
} finally {
getAapsLogger().debug(LTag.APS, "invoke end");
}
}
public void disableCarbSuggestions(int durationMinutes) {
carbsSuggestionsSuspendedUntil = System.currentTimeMillis() + (durationMinutes * 60 * 1000);
dismissSuggestion();
}
private void presentSuggestion(NotificationCompat.Builder builder) {
// Creates an explicit intent for an Activity in your app
Intent resultIntent = new Intent(context, MainActivity.class);
// The stack builder object will contain an artificial back stack for the
// started Activity.
// This ensures that navigating backward from the Activity leads out of
// your application to the Home screen.
TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
stackBuilder.addParentStack(MainActivity.class);
// Adds the Intent that starts the Activity to the top of the stack
stackBuilder.addNextIntent(resultIntent);
PendingIntent resultPendingIntent =
stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
builder.setContentIntent(resultPendingIntent);
builder.setVibrate(new long[]{1000, 1000, 1000, 1000, 1000});
NotificationManager mNotificationManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
// mId allows you to update the notification later on.
mNotificationManager.notify(Constants.notificationID, builder.build());
rxBus.send(new EventNewOpenLoopNotification());
// Send to Wear
actionStringHandler.get().handleInitiate("changeRequest");
}
private void dismissSuggestion() {
// dismiss notifications
NotificationManager notificationManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.cancel(Constants.notificationID);
actionStringHandler.get().handleInitiate("cancelChangeRequest");
}
public void acceptChangeRequest() {
Profile profile = profileFunction.getProfile();
final LoopPlugin lp = this;
applyTBRRequest(lastRun.getConstraintsProcessed(), profile, new Callback() {
@Override
public void run() {
if (result.enacted) {
lastRun.setTbrSetByPump(result);
lastRun.setLastTBRRequest(lastRun.getLastAPSRun());
lastRun.setLastTBREnact(DateUtil.now());
lastRun.setLastOpenModeAccept(DateUtil.now());
nsUpload.uploadDeviceStatus(lp, iobCobCalculatorPlugin, profileFunction, activePlugin.getActivePump(), receiverStatusStore, BuildConfig.VERSION_NAME + "-" + BuildConfig.BUILDVERSION);
sp.incInt(R.string.key_ObjectivesmanualEnacts);
}
rxBus.send(new EventAcceptOpenLoopChange());
}
});
fabricPrivacy.logCustom("AcceptTemp");
}
/**
* expect absolute request and allow both absolute and percent response based on pump capabilities
* TODO: update pump drivers to support APS request in %
*/
private void applyTBRRequest(APSResult request, Profile profile, Callback callback) {
if (!request.getTempBasalRequested()) {
if (callback != null) {
callback.result(new PumpEnactResult(getInjector()).enacted(false).success(true).comment(resourceHelper.gs(R.string.nochangerequested))).run();
}
return;
}
PumpInterface pump = activePlugin.getActivePump();
if (!pump.isInitialized()) {
getAapsLogger().debug(LTag.APS, "applyAPSRequest: " + resourceHelper.gs(R.string.pumpNotInitialized));
if (callback != null) {
callback.result(new PumpEnactResult(getInjector()).comment(resourceHelper.gs(R.string.pumpNotInitialized)).enacted(false).success(false)).run();
}
return;
}
if (pump.isSuspended()) {
getAapsLogger().debug(LTag.APS, "applyAPSRequest: " + resourceHelper.gs(R.string.pumpsuspended));
if (callback != null) {
callback.result(new PumpEnactResult(getInjector()).comment(resourceHelper.gs(R.string.pumpsuspended)).enacted(false).success(false)).run();
}
return;
}
getAapsLogger().debug(LTag.APS, "applyAPSRequest: " + request.toString());
long now = System.currentTimeMillis();
TemporaryBasal activeTemp = treatmentsPlugin.getTempBasalFromHistory(now);
if (request.getUsePercent() && allowPercentage()) {
if (request.getPercent() == 100 && request.getDuration() == 0) {
if (activeTemp != null) {
getAapsLogger().debug(LTag.APS, "applyAPSRequest: cancelTempBasal()");
commandQueue.cancelTempBasal(false, callback);
} else {
getAapsLogger().debug(LTag.APS, "applyAPSRequest: Basal set correctly");
if (callback != null) {
callback.result(new PumpEnactResult(getInjector()).percent(request.getPercent()).duration(0)
.enacted(false).success(true).comment(resourceHelper.gs(R.string.basal_set_correctly))).run();
}
}
} else if (activeTemp != null
&& activeTemp.getPlannedRemainingMinutes() > 5
&& request.getDuration() - activeTemp.getPlannedRemainingMinutes() < 30
&& request.getPercent() == activeTemp.percentRate) {
getAapsLogger().debug(LTag.APS, "applyAPSRequest: Temp basal set correctly");
if (callback != null) {
callback.result(new PumpEnactResult(getInjector()).percent(request.getPercent())
.enacted(false).success(true).duration(activeTemp.getPlannedRemainingMinutes())
.comment(resourceHelper.gs(R.string.let_temp_basal_run))).run();
}
} else {
getAapsLogger().debug(LTag.APS, "applyAPSRequest: tempBasalPercent()");
commandQueue.tempBasalPercent(request.getPercent(), request.getDuration(), false, profile, callback);
}
} else {
if ((request.getRate() == 0 && request.getDuration() == 0) || Math.abs(request.getRate() - pump.getBaseBasalRate()) < pump.getPumpDescription().basalStep) {
if (activeTemp != null) {
getAapsLogger().debug(LTag.APS, "applyAPSRequest: cancelTempBasal()");
commandQueue.cancelTempBasal(false, callback);
} else {
getAapsLogger().debug(LTag.APS, "applyAPSRequest: Basal set correctly");
if (callback != null) {
callback.result(new PumpEnactResult(getInjector()).absolute(request.getRate()).duration(0)
.enacted(false).success(true).comment(resourceHelper.gs(R.string.basal_set_correctly))).run();
}
}
} else if (activeTemp != null
&& activeTemp.getPlannedRemainingMinutes() > 5
&& request.getDuration() - activeTemp.getPlannedRemainingMinutes() < 30
&& Math.abs(request.getRate() - activeTemp.tempBasalConvertedToAbsolute(now, profile)) < pump.getPumpDescription().basalStep) {
getAapsLogger().debug(LTag.APS, "applyAPSRequest: Temp basal set correctly");
if (callback != null) {
callback.result(new PumpEnactResult(getInjector()).absolute(activeTemp.tempBasalConvertedToAbsolute(now, profile))
.enacted(false).success(true).duration(activeTemp.getPlannedRemainingMinutes())
.comment(resourceHelper.gs(R.string.let_temp_basal_run))).run();
}
} else {
getAapsLogger().debug(LTag.APS, "applyAPSRequest: setTempBasalAbsolute()");
commandQueue.tempBasalAbsolute(request.getRate(), request.getDuration(), false, profile, callback);
}
}
}
private void applySMBRequest(APSResult request, Callback callback) {
if (!request.getBolusRequested()) {
return;
}
PumpInterface pump = activePlugin.getActivePump();
long lastBolusTime = treatmentsPlugin.getLastBolusTime();
if (lastBolusTime != 0 && lastBolusTime + 3 * 60 * 1000 > System.currentTimeMillis()) {
getAapsLogger().debug(LTag.APS, "SMB requested but still in 3 min interval");
if (callback != null) {
callback.result(new PumpEnactResult(getInjector())
.comment(resourceHelper.gs(R.string.smb_frequency_exceeded))
.enacted(false).success(false)).run();
}
return;
}
if (!pump.isInitialized()) {
getAapsLogger().debug(LTag.APS, "applySMBRequest: " + resourceHelper.gs(R.string.pumpNotInitialized));
if (callback != null) {
callback.result(new PumpEnactResult(getInjector()).comment(resourceHelper.gs(R.string.pumpNotInitialized)).enacted(false).success(false)).run();
}
return;
}
if (pump.isSuspended()) {
getAapsLogger().debug(LTag.APS, "applySMBRequest: " + resourceHelper.gs(R.string.pumpsuspended));
if (callback != null) {
callback.result(new PumpEnactResult(getInjector()).comment(resourceHelper.gs(R.string.pumpsuspended)).enacted(false).success(false)).run();
}
return;
}
getAapsLogger().debug(LTag.APS, "applySMBRequest: " + request.toString());
// deliver SMB
DetailedBolusInfo detailedBolusInfo = new DetailedBolusInfo();
detailedBolusInfo.lastKnownBolusTime = treatmentsPlugin.getLastBolusTime();
detailedBolusInfo.eventType = CareportalEvent.CORRECTIONBOLUS;
detailedBolusInfo.insulin = request.getSmb();
detailedBolusInfo.isSMB = true;
detailedBolusInfo.source = Source.USER;
detailedBolusInfo.deliverAt = request.getDeliverAt();
getAapsLogger().debug(LTag.APS, "applyAPSRequest: bolus()");
commandQueue.bolus(detailedBolusInfo, callback);
}
private boolean allowPercentage() {
return virtualPumpPlugin.isEnabled(PluginType.PUMP);
}
public void disconnectPump(int durationInMinutes, Profile profile) {
PumpInterface pump = activePlugin.getActivePump();
disconnectTo(System.currentTimeMillis() + durationInMinutes * 60 * 1000L);
if (pump.getPumpDescription().tempBasalStyle == PumpDescription.ABSOLUTE) {
commandQueue.tempBasalAbsolute(0, durationInMinutes, true, profile, new Callback() {
@Override
public void run() {
if (!result.success) {
Intent i = new Intent(context, ErrorHelperActivity.class);
i.putExtra("soundid", R.raw.boluserror);
i.putExtra("status", result.comment);
i.putExtra("title", resourceHelper.gs(R.string.tempbasaldeliveryerror));
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
}
}
});
} else {
commandQueue.tempBasalPercent(0, durationInMinutes, true, profile, new Callback() {
@Override
public void run() {
if (!result.success) {
Intent i = new Intent(context, ErrorHelperActivity.class);
i.putExtra("soundid", R.raw.boluserror);
i.putExtra("status", result.comment);
i.putExtra("title", resourceHelper.gs(R.string.tempbasaldeliveryerror));
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
}
}
});
}
if (pump.getPumpDescription().isExtendedBolusCapable && treatmentsPlugin.isInHistoryExtendedBoluslInProgress()) {
commandQueue.cancelExtended(new Callback() {
@Override
public void run() {
if (!result.success) {
Intent i = new Intent(context, ErrorHelperActivity.class);
i.putExtra("soundid", R.raw.boluserror);
i.putExtra("status", result.comment);
i.putExtra("title", resourceHelper.gs(R.string.extendedbolusdeliveryerror));
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
}
}
});
}
createOfflineEvent(durationInMinutes);
}
public void suspendLoop(int durationInMinutes) {
suspendTo(System.currentTimeMillis() + durationInMinutes * 60 * 1000);
commandQueue.cancelTempBasal(true, new Callback() {
@Override
public void run() {
if (!result.success) {
Intent i = new Intent(context, ErrorHelperActivity.class);
i.putExtra("soundid", R.raw.boluserror);
i.putExtra("status", result.comment);
i.putExtra("title", resourceHelper.gs(R.string.tempbasaldeliveryerror));
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
}
}
});
createOfflineEvent(durationInMinutes);
}
public void createOfflineEvent(int durationInMinutes) {
JSONObject data = new JSONObject();
try {
data.put("eventType", CareportalEvent.OPENAPSOFFLINE);
data.put("duration", durationInMinutes);
} catch (JSONException e) {
getAapsLogger().error("Unhandled exception", e);
}
CareportalEvent event = new CareportalEvent(getInjector());
event.date = DateUtil.now();
event.source = Source.USER;
event.eventType = CareportalEvent.OPENAPSOFFLINE;
event.json = data.toString();
MainApp.getDbHelper().createOrUpdate(event);
nsUpload.uploadOpenAPSOffline(event);
}
}

View file

@ -0,0 +1,674 @@
package info.nightscout.androidaps.plugins.aps.loop
import android.annotation.SuppressLint
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.app.TaskStackBuilder
import android.content.Context
import android.content.Intent
import android.os.SystemClock
import androidx.core.app.NotificationCompat
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.*
import info.nightscout.androidaps.activities.ErrorHelperActivity
import info.nightscout.androidaps.data.DetailedBolusInfo
import info.nightscout.androidaps.data.Profile
import info.nightscout.androidaps.data.PumpEnactResult
import info.nightscout.androidaps.db.CareportalEvent
import info.nightscout.androidaps.db.Source
import info.nightscout.androidaps.events.EventAcceptOpenLoopChange
import info.nightscout.androidaps.events.EventNewBG
import info.nightscout.androidaps.events.EventTempTargetChange
import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.interfaces.LoopInterface.LastRun
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
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.RxBusWrapper
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
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
import info.nightscout.androidaps.plugins.general.overview.notifications.Notification
import info.nightscout.androidaps.plugins.general.wear.events.EventWearDoAction
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventAutosensCalculationFinished
import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
import info.nightscout.androidaps.queue.Callback
import info.nightscout.androidaps.queue.commands.Command
import info.nightscout.androidaps.receivers.ReceiverStatusStore
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.HardLimits
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.androidaps.utils.sharedPreferences.SP
import io.reactivex.disposables.CompositeDisposable
import org.json.JSONException
import org.json.JSONObject
import javax.inject.Inject
import javax.inject.Singleton
import kotlin.math.abs
@Singleton
open class LoopPlugin @Inject constructor(
injector: HasAndroidInjector,
aapsLogger: AAPSLogger?,
private val aapsSchedulers: AapsSchedulers,
private val rxBus: RxBusWrapper,
private val sp: SP,
config: Config,
private val constraintChecker: ConstraintChecker,
resourceHelper: ResourceHelper,
private val profileFunction: ProfileFunction,
private val context: Context,
private val commandQueue: CommandQueueProvider,
private val activePlugin: ActivePluginProvider,
private val treatmentsPlugin: TreatmentsPlugin,
private val virtualPumpPlugin: VirtualPumpPlugin,
private val iobCobCalculatorPlugin: IobCobCalculatorPlugin,
private val receiverStatusStore: ReceiverStatusStore,
private val fabricPrivacy: FabricPrivacy,
private val nsUpload: NSUpload,
private val hardLimits: HardLimits
) : PluginBase(PluginDescription()
.mainType(PluginType.LOOP)
.fragmentClass(LoopFragment::class.java.name)
.pluginIcon(R.drawable.ic_loop_closed_white)
.pluginName(R.string.loop)
.shortName(R.string.loop_shortname)
.preferencesId(R.xml.pref_loop)
.enableByDefault(config.APS)
.description(R.string.description_loop),
aapsLogger!!, resourceHelper, injector
), LoopInterface {
private val disposable = CompositeDisposable()
private var lastBgTriggeredRun: Long = 0
private var carbsSuggestionsSuspendedUntil: Long = 0
private var prevCarbsreq = 0
override var lastRun: LastRun? = null
override fun onStart() {
createNotificationChannel()
super.onStart()
disposable.add(rxBus
.toObservable(EventTempTargetChange::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ 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.
<p>
*/
disposable.add(rxBus
.toObservable(EventAutosensCalculationFinished::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ event: EventAutosensCalculationFinished ->
// Autosens calculation not triggered by a new BG
if (event.cause !is EventNewBG) return@subscribe
val glucoseValue = iobCobCalculatorPlugin.actualBg() ?: return@subscribe
// BG outdated
// already looped with that value
if (glucoseValue.timestamp <= lastBgTriggeredRun) return@subscribe
lastBgTriggeredRun = glucoseValue.timestamp
invoke("AutosenseCalculation for $glucoseValue", true)
}, fabricPrivacy::logException)
)
}
private fun createNotificationChannel() {
val mNotificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
@SuppressLint("WrongConstant") val channel = NotificationChannel(CHANNEL_ID,
CHANNEL_ID,
NotificationManager.IMPORTANCE_HIGH)
mNotificationManager.createNotificationChannel(channel)
}
override fun onStop() {
disposable.clear()
super.onStop()
}
override fun specialEnableCondition(): Boolean {
return try {
val pump = activePlugin.activePump
pump.pumpDescription.isTempBasalCapable
} catch (ignored: Exception) {
// may fail during initialization
true
}
}
fun suspendTo(endTime: Long) {
sp.putLong("loopSuspendedTill", endTime)
sp.putBoolean("isSuperBolus", false)
sp.putBoolean("isDisconnected", false)
}
fun superBolusTo(endTime: Long) {
sp.putLong("loopSuspendedTill", endTime)
sp.putBoolean("isSuperBolus", true)
sp.putBoolean("isDisconnected", false)
}
private fun disconnectTo(endTime: Long) {
sp.putLong("loopSuspendedTill", endTime)
sp.putBoolean("isSuperBolus", false)
sp.putBoolean("isDisconnected", true)
}
fun minutesToEndOfSuspend(): Int {
val loopSuspendedTill = sp.getLong("loopSuspendedTill", 0L)
if (loopSuspendedTill == 0L) return 0
val now = System.currentTimeMillis()
val millisDiff = loopSuspendedTill - now
if (loopSuspendedTill <= now) { // time exceeded
suspendTo(0L)
return 0
}
return (millisDiff / 60.0 / 1000.0).toInt()
}
// time exceeded
val isSuspended: Boolean
get() {
val loopSuspendedTill = sp.getLong("loopSuspendedTill", 0L)
if (loopSuspendedTill == 0L) return false
val now = System.currentTimeMillis()
if (loopSuspendedTill <= now) { // time exceeded
suspendTo(0L)
return false
}
return true
}
val isLGS: Boolean
get() {
val closedLoopEnabled = constraintChecker.isClosedLoopAllowed()
val maxIobAllowed = constraintChecker.getMaxIOBAllowed().value()
val apsMode = sp.getString(R.string.key_aps_mode, "open")
val pump = activePlugin.activePump
var isLGS = false
if (!isSuspended && !pump.isSuspended()) if (closedLoopEnabled.value()) if (maxIobAllowed == hardLimits.MAXIOB_LGS || apsMode == "lgs") isLGS = true
return isLGS
}
// time exceeded
val isSuperBolus: Boolean
get() {
val loopSuspendedTill = sp.getLong("loopSuspendedTill", 0L)
if (loopSuspendedTill == 0L) return false
val now = System.currentTimeMillis()
if (loopSuspendedTill <= now) { // time exceeded
suspendTo(0L)
return false
}
return sp.getBoolean("isSuperBolus", false)
}
// time exceeded
val isDisconnected: Boolean
get() {
val loopSuspendedTill = sp.getLong("loopSuspendedTill", 0L)
if (loopSuspendedTill == 0L) return false
val now = System.currentTimeMillis()
if (loopSuspendedTill <= now) { // time exceeded
suspendTo(0L)
return false
}
return sp.getBoolean("isDisconnected", false)
}
@Suppress("SameParameterValue")
private fun treatmentTimeThreshold(durationMinutes: Int): Boolean {
val threshold = System.currentTimeMillis() + durationMinutes * 60 * 1000
var bool = false
if (treatmentsPlugin.lastBolusTime > threshold || treatmentsPlugin.lastCarbTime > threshold) bool = true
return bool
}
@Synchronized operator fun invoke(initiator: String, allowNotification: Boolean) {
invoke(initiator, allowNotification, false)
}
@Synchronized
operator fun invoke(initiator: String, allowNotification: Boolean, tempBasalFallback: Boolean) {
try {
aapsLogger.debug(LTag.APS, "invoke from $initiator")
val loopEnabled = constraintChecker.isLoopInvocationAllowed()
if (!loopEnabled.value()) {
val message = """
${resourceHelper.gs(R.string.loopdisabled)}
${loopEnabled.getReasons(aapsLogger)}
""".trimIndent()
aapsLogger.debug(LTag.APS, message)
rxBus.send(EventLoopSetLastRunGui(message))
return
}
val pump = activePlugin.activePump
var apsResult: APSResult? = null
if (!isEnabled(PluginType.LOOP)) return
val profile = profileFunction.getProfile()
if (profile == null || !profileFunction.isProfileValid("Loop")) {
aapsLogger.debug(LTag.APS, resourceHelper.gs(R.string.noprofileselected))
rxBus.send(EventLoopSetLastRunGui(resourceHelper.gs(R.string.noprofileselected)))
return
}
// Check if pump info is loaded
if (pump.baseBasalRate < 0.01) return
val usedAPS = activePlugin.activeAPS
if ((usedAPS as PluginBase).isEnabled(PluginType.APS)) {
usedAPS.invoke(initiator, tempBasalFallback)
apsResult = usedAPS.lastAPSResult
}
// Check if we have any result
if (apsResult == null) {
rxBus.send(EventLoopSetLastRunGui(resourceHelper.gs(R.string.noapsselected)))
return
}
// Prepare for pumps using % basals
if (pump.pumpDescription.tempBasalStyle == PumpDescription.PERCENT && allowPercentage()) {
apsResult.usePercent = true
}
apsResult.percent = (apsResult.rate / profile.basal * 100).toInt()
// check rate for constraints
val resultAfterConstraints = apsResult.newAndClone(injector)
resultAfterConstraints.rateConstraint = Constraint(resultAfterConstraints.rate)
resultAfterConstraints.rate = constraintChecker.applyBasalConstraints(resultAfterConstraints.rateConstraint!!, profile).value()
resultAfterConstraints.percentConstraint = Constraint(resultAfterConstraints.percent)
resultAfterConstraints.percent = constraintChecker.applyBasalPercentConstraints(resultAfterConstraints.percentConstraint!!, profile).value()
resultAfterConstraints.smbConstraint = Constraint(resultAfterConstraints.smb)
resultAfterConstraints.smb = constraintChecker.applyBolusConstraints(resultAfterConstraints.smbConstraint!!).value()
// safety check for multiple SMBs
val lastBolusTime = treatmentsPlugin.lastBolusTime
if (lastBolusTime != 0L && lastBolusTime + T.mins(3).msecs() > System.currentTimeMillis()) {
aapsLogger.debug(LTag.APS, "SMB requested but still in 3 min interval")
resultAfterConstraints.smb = 0.0
}
if (lastRun != null && lastRun!!.constraintsProcessed != null) {
prevCarbsreq = lastRun!!.constraintsProcessed!!.carbsReq
}
if (lastRun == null) lastRun = LastRun()
lastRun!!.request = apsResult
lastRun!!.constraintsProcessed = resultAfterConstraints
lastRun!!.lastAPSRun = DateUtil.now()
lastRun!!.source = (usedAPS as PluginBase).name
lastRun!!.tbrSetByPump = null
lastRun!!.smbSetByPump = null
lastRun!!.lastTBREnact = 0
lastRun!!.lastTBRRequest = 0
lastRun!!.lastSMBEnact = 0
lastRun!!.lastSMBRequest = 0
nsUpload.uploadDeviceStatus(this, iobCobCalculatorPlugin, profileFunction, activePlugin.activePump, receiverStatusStore, BuildConfig.VERSION_NAME + "-" + BuildConfig.BUILDVERSION)
if (isSuspended) {
aapsLogger.debug(LTag.APS, resourceHelper.gs(R.string.loopsuspended))
rxBus.send(EventLoopSetLastRunGui(resourceHelper.gs(R.string.loopsuspended)))
return
}
if (pump.isSuspended()) {
aapsLogger.debug(LTag.APS, resourceHelper.gs(R.string.pumpsuspended))
rxBus.send(EventLoopSetLastRunGui(resourceHelper.gs(R.string.pumpsuspended)))
return
}
val closedLoopEnabled = constraintChecker.isClosedLoopAllowed()
if (closedLoopEnabled.value()) {
if (allowNotification) {
if (resultAfterConstraints.isCarbsRequired
&& resultAfterConstraints.carbsReq >= sp.getInt(R.string.key_smb_enable_carbs_suggestions_threshold, 0) && carbsSuggestionsSuspendedUntil < System.currentTimeMillis() && !treatmentTimeThreshold(-15)) {
if (sp.getBoolean(R.string.key_enable_carbs_required_alert_local, true) && !sp.getBoolean(R.string.key_raise_notifications_as_android_notifications, true)) {
val carbReqLocal = Notification(Notification.CARBS_REQUIRED, resultAfterConstraints.carbsRequiredText, Notification.NORMAL)
rxBus.send(EventNewNotification(carbReqLocal))
}
if (sp.getBoolean(R.string.key_ns_create_announcements_from_carbs_req, false)) {
nsUpload.uploadError(resultAfterConstraints.carbsRequiredText)
}
if (sp.getBoolean(R.string.key_enable_carbs_required_alert_local, true) && sp.getBoolean(R.string.key_raise_notifications_as_android_notifications, true)) {
val intentAction5m = Intent(context, CarbSuggestionReceiver::class.java)
intentAction5m.putExtra("ignoreDuration", 5)
val pendingIntent5m = PendingIntent.getBroadcast(context, 1, intentAction5m, PendingIntent.FLAG_UPDATE_CURRENT)
val actionIgnore5m = NotificationCompat.Action(R.drawable.ic_notif_aaps, resourceHelper.gs(R.string.ignore5m, "Ignore 5m"), pendingIntent5m)
val intentAction15m = Intent(context, CarbSuggestionReceiver::class.java)
intentAction15m.putExtra("ignoreDuration", 15)
val pendingIntent15m = PendingIntent.getBroadcast(context, 1, intentAction15m, PendingIntent.FLAG_UPDATE_CURRENT)
val actionIgnore15m = NotificationCompat.Action(R.drawable.ic_notif_aaps, resourceHelper.gs(R.string.ignore15m, "Ignore 15m"), pendingIntent15m)
val intentAction30m = Intent(context, CarbSuggestionReceiver::class.java)
intentAction30m.putExtra("ignoreDuration", 30)
val pendingIntent30m = PendingIntent.getBroadcast(context, 1, intentAction30m, PendingIntent.FLAG_UPDATE_CURRENT)
val actionIgnore30m = NotificationCompat.Action(R.drawable.ic_notif_aaps, resourceHelper.gs(R.string.ignore30m, "Ignore 30m"), pendingIntent30m)
val builder = NotificationCompat.Builder(context, CHANNEL_ID)
builder.setSmallIcon(R.drawable.notif_icon)
.setContentTitle(resourceHelper.gs(R.string.carbssuggestion))
.setContentText(resultAfterConstraints.carbsRequiredText)
.setAutoCancel(true)
.setPriority(Notification.IMPORTANCE_HIGH)
.setCategory(Notification.CATEGORY_ALARM)
.addAction(actionIgnore5m)
.addAction(actionIgnore15m)
.addAction(actionIgnore30m)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setVibrate(longArrayOf(1000, 1000, 1000, 1000, 1000))
val mNotificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
// mId allows you to update the notification later on.
mNotificationManager.notify(Constants.notificationID, builder.build())
rxBus.send(EventNewOpenLoopNotification())
//only send to wear if Native notifications are turned off
if (!sp.getBoolean(R.string.key_raise_notifications_as_android_notifications, true)) {
// Send to Wear
rxBus.send(EventWearDoAction("changeRequest"))
}
}
} else {
//If carbs were required previously, but are no longer needed, dismiss notifications
if (prevCarbsreq > 0) {
dismissSuggestion()
rxBus.send(EventDismissNotification(Notification.CARBS_REQUIRED))
}
}
}
if (resultAfterConstraints.isChangeRequested
&& !commandQueue.bolusInQueue()
&& !commandQueue.isRunning(Command.CommandType.BOLUS)) {
val waiting = PumpEnactResult(injector)
waiting.queued = true
if (resultAfterConstraints.tempBasalRequested) lastRun!!.tbrSetByPump = waiting
if (resultAfterConstraints.bolusRequested) lastRun!!.smbSetByPump = waiting
rxBus.send(EventLoopUpdateGui())
fabricPrivacy.logCustom("APSRequest")
applyTBRRequest(resultAfterConstraints, profile, object : Callback() {
override fun run() {
if (result.enacted || result.success) {
lastRun!!.tbrSetByPump = result
lastRun!!.lastTBRRequest = lastRun!!.lastAPSRun
lastRun!!.lastTBREnact = DateUtil.now()
rxBus.send(EventLoopUpdateGui())
applySMBRequest(resultAfterConstraints, object : Callback() {
override fun run() {
// Callback is only called if a bolus was actually requested
if (result.enacted || result.success) {
lastRun!!.smbSetByPump = result
lastRun!!.lastSMBRequest = lastRun!!.lastAPSRun
lastRun!!.lastSMBEnact = DateUtil.now()
} else {
Thread {
SystemClock.sleep(1000)
invoke("tempBasalFallback", allowNotification, true)
}.start()
}
rxBus.send(EventLoopUpdateGui())
}
})
} else {
lastRun!!.tbrSetByPump = result
lastRun!!.lastTBRRequest = lastRun!!.lastAPSRun
}
rxBus.send(EventLoopUpdateGui())
}
})
} else {
lastRun!!.tbrSetByPump = null
lastRun!!.smbSetByPump = null
}
} else {
if (resultAfterConstraints.isChangeRequested && allowNotification) {
val builder = NotificationCompat.Builder(context, CHANNEL_ID)
builder.setSmallIcon(R.drawable.notif_icon)
.setContentTitle(resourceHelper.gs(R.string.openloop_newsuggestion))
.setContentText(resultAfterConstraints.toString())
.setAutoCancel(true)
.setPriority(Notification.IMPORTANCE_HIGH)
.setCategory(Notification.CATEGORY_ALARM)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
if (sp.getBoolean(R.string.key_wear_control, false)) {
builder.setLocalOnly(true)
}
presentSuggestion(builder)
} else if (allowNotification) {
dismissSuggestion()
}
}
rxBus.send(EventLoopUpdateGui())
} finally {
aapsLogger.debug(LTag.APS, "invoke end")
}
}
fun disableCarbSuggestions(durationMinutes: Int) {
carbsSuggestionsSuspendedUntil = System.currentTimeMillis() + durationMinutes * 60 * 1000
dismissSuggestion()
}
private fun presentSuggestion(builder: NotificationCompat.Builder) {
// Creates an explicit intent for an Activity in your app
val resultIntent = Intent(context, MainActivity::class.java)
// The stack builder object will contain an artificial back stack for the
// started Activity.
// This ensures that navigating backward from the Activity leads out of
// your application to the Home screen.
val stackBuilder = TaskStackBuilder.create(context)
stackBuilder.addParentStack(MainActivity::class.java)
// Adds the Intent that starts the Activity to the top of the stack
stackBuilder.addNextIntent(resultIntent)
val resultPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT)
builder.setContentIntent(resultPendingIntent)
builder.setVibrate(longArrayOf(1000, 1000, 1000, 1000, 1000))
val mNotificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
// mId allows you to update the notification later on.
mNotificationManager.notify(Constants.notificationID, builder.build())
rxBus.send(EventNewOpenLoopNotification())
// Send to Wear
rxBus.send(EventWearDoAction("changeRequest"))
}
private fun dismissSuggestion() {
// dismiss notifications
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.cancel(Constants.notificationID)
rxBus.send(EventWearDoAction("cancelChangeRequest"))
}
fun acceptChangeRequest() {
val profile = profileFunction.getProfile()
val lp = this
applyTBRRequest(lastRun!!.constraintsProcessed, profile, object : Callback() {
override fun run() {
if (result.enacted) {
lastRun!!.tbrSetByPump = result
lastRun!!.lastTBRRequest = lastRun!!.lastAPSRun
lastRun!!.lastTBREnact = DateUtil.now()
lastRun!!.lastOpenModeAccept = DateUtil.now()
nsUpload.uploadDeviceStatus(lp, iobCobCalculatorPlugin, profileFunction, activePlugin.activePump, receiverStatusStore, BuildConfig.VERSION_NAME + "-" + BuildConfig.BUILDVERSION)
sp.incInt(R.string.key_ObjectivesmanualEnacts)
}
rxBus.send(EventAcceptOpenLoopChange())
}
})
fabricPrivacy.logCustom("AcceptTemp")
}
/**
* expect absolute request and allow both absolute and percent response based on pump capabilities
* TODO: update pump drivers to support APS request in %
*/
private fun applyTBRRequest(request: APSResult?, profile: Profile?, callback: Callback?) {
if (!request!!.tempBasalRequested) {
callback?.result(PumpEnactResult(injector).enacted(false).success(true).comment(resourceHelper.gs(R.string.nochangerequested)))?.run()
return
}
val pump = activePlugin.activePump
if (!pump.isInitialized()) {
aapsLogger.debug(LTag.APS, "applyAPSRequest: " + resourceHelper.gs(R.string.pumpNotInitialized))
callback?.result(PumpEnactResult(injector).comment(resourceHelper.gs(R.string.pumpNotInitialized)).enacted(false).success(false))?.run()
return
}
if (pump.isSuspended()) {
aapsLogger.debug(LTag.APS, "applyAPSRequest: " + resourceHelper.gs(R.string.pumpsuspended))
callback?.result(PumpEnactResult(injector).comment(resourceHelper.gs(R.string.pumpsuspended)).enacted(false).success(false))?.run()
return
}
aapsLogger.debug(LTag.APS, "applyAPSRequest: $request")
val now = System.currentTimeMillis()
val activeTemp = treatmentsPlugin.getTempBasalFromHistory(now)
if (request.usePercent && allowPercentage()) {
if (request.percent == 100 && request.duration == 0) {
if (activeTemp != null) {
aapsLogger.debug(LTag.APS, "applyAPSRequest: cancelTempBasal()")
commandQueue.cancelTempBasal(false, callback)
} else {
aapsLogger.debug(LTag.APS, "applyAPSRequest: Basal set correctly")
callback?.result(PumpEnactResult(injector).percent(request.percent).duration(0)
.enacted(false).success(true).comment(resourceHelper.gs(R.string.basal_set_correctly)))?.run()
}
} else if (activeTemp != null && activeTemp.plannedRemainingMinutes > 5 && request.duration - activeTemp.plannedRemainingMinutes < 30 && request.percent == activeTemp.percentRate) {
aapsLogger.debug(LTag.APS, "applyAPSRequest: Temp basal set correctly")
callback?.result(PumpEnactResult(injector).percent(request.percent)
.enacted(false).success(true).duration(activeTemp.plannedRemainingMinutes)
.comment(resourceHelper.gs(R.string.let_temp_basal_run)))?.run()
} else {
aapsLogger.debug(LTag.APS, "applyAPSRequest: tempBasalPercent()")
commandQueue.tempBasalPercent(request.percent, request.duration, false, profile!!, callback)
}
} else {
if (request.rate == 0.0 && request.duration == 0 || abs(request.rate - pump.baseBasalRate) < pump.pumpDescription.basalStep) {
if (activeTemp != null) {
aapsLogger.debug(LTag.APS, "applyAPSRequest: cancelTempBasal()")
commandQueue.cancelTempBasal(false, callback)
} else {
aapsLogger.debug(LTag.APS, "applyAPSRequest: Basal set correctly")
callback?.result(PumpEnactResult(injector).absolute(request.rate).duration(0)
.enacted(false).success(true).comment(resourceHelper.gs(R.string.basal_set_correctly)))?.run()
}
} else if (activeTemp != null && activeTemp.plannedRemainingMinutes > 5 && request.duration - activeTemp.plannedRemainingMinutes < 30 && abs(request.rate - activeTemp.tempBasalConvertedToAbsolute(now, profile)) < pump.pumpDescription.basalStep) {
aapsLogger.debug(LTag.APS, "applyAPSRequest: Temp basal set correctly")
callback?.result(PumpEnactResult(injector).absolute(activeTemp.tempBasalConvertedToAbsolute(now, profile))
.enacted(false).success(true).duration(activeTemp.plannedRemainingMinutes)
.comment(resourceHelper.gs(R.string.let_temp_basal_run)))?.run()
} else {
aapsLogger.debug(LTag.APS, "applyAPSRequest: setTempBasalAbsolute()")
commandQueue.tempBasalAbsolute(request.rate, request.duration, false, profile!!, callback)
}
}
}
private fun applySMBRequest(request: APSResult, callback: Callback?) {
if (!request.bolusRequested) {
return
}
val pump = activePlugin.activePump
val lastBolusTime = treatmentsPlugin.lastBolusTime
if (lastBolusTime != 0L && lastBolusTime + 3 * 60 * 1000 > System.currentTimeMillis()) {
aapsLogger.debug(LTag.APS, "SMB requested but still in 3 min interval")
callback?.result(PumpEnactResult(injector)
.comment(resourceHelper.gs(R.string.smb_frequency_exceeded))
.enacted(false).success(false))?.run()
return
}
if (!pump.isInitialized()) {
aapsLogger.debug(LTag.APS, "applySMBRequest: " + resourceHelper.gs(R.string.pumpNotInitialized))
callback?.result(PumpEnactResult(injector).comment(resourceHelper.gs(R.string.pumpNotInitialized)).enacted(false).success(false))?.run()
return
}
if (pump.isSuspended()) {
aapsLogger.debug(LTag.APS, "applySMBRequest: " + resourceHelper.gs(R.string.pumpsuspended))
callback?.result(PumpEnactResult(injector).comment(resourceHelper.gs(R.string.pumpsuspended)).enacted(false).success(false))?.run()
return
}
aapsLogger.debug(LTag.APS, "applySMBRequest: $request")
// deliver SMB
val detailedBolusInfo = DetailedBolusInfo()
detailedBolusInfo.lastKnownBolusTime = treatmentsPlugin.lastBolusTime
detailedBolusInfo.eventType = CareportalEvent.CORRECTIONBOLUS
detailedBolusInfo.insulin = request.smb
detailedBolusInfo.isSMB = true
detailedBolusInfo.source = Source.USER
detailedBolusInfo.deliverAt = request.deliverAt
aapsLogger.debug(LTag.APS, "applyAPSRequest: bolus()")
commandQueue.bolus(detailedBolusInfo, callback)
}
private fun allowPercentage(): Boolean {
return virtualPumpPlugin.isEnabled(PluginType.PUMP)
}
fun disconnectPump(durationInMinutes: Int, profile: Profile?) {
val pump = activePlugin.activePump
disconnectTo(System.currentTimeMillis() + durationInMinutes * 60 * 1000L)
if (pump.pumpDescription.tempBasalStyle == PumpDescription.ABSOLUTE) {
commandQueue.tempBasalAbsolute(0.0, durationInMinutes, true, profile!!, object : Callback() {
override fun run() {
if (!result.success) {
ErrorHelperActivity.runAlarm(context, result.comment, resourceHelper.gs(R.string.tempbasaldeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror)
}
}
})
} else {
commandQueue.tempBasalPercent(0, durationInMinutes, true, profile!!, object : Callback() {
override fun run() {
if (!result.success) {
ErrorHelperActivity.runAlarm(context, result.comment, resourceHelper.gs(R.string.tempbasaldeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror)
}
}
})
}
if (pump.pumpDescription.isExtendedBolusCapable && treatmentsPlugin.isInHistoryExtendedBoluslInProgress) {
commandQueue.cancelExtended(object : Callback() {
override fun run() {
if (!result.success) {
ErrorHelperActivity.runAlarm(context, result.comment, resourceHelper.gs(R.string.extendedbolusdeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror)
}
}
})
}
createOfflineEvent(durationInMinutes)
}
fun suspendLoop(durationInMinutes: Int) {
suspendTo(System.currentTimeMillis() + durationInMinutes * 60 * 1000)
commandQueue.cancelTempBasal(true, object : Callback() {
override fun run() {
if (!result.success) {
ErrorHelperActivity.runAlarm(context, result.comment, resourceHelper.gs(R.string.tempbasaldeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror)
}
}
})
createOfflineEvent(durationInMinutes)
}
fun createOfflineEvent(durationInMinutes: Int) {
val data = JSONObject()
try {
data.put("eventType", CareportalEvent.OPENAPSOFFLINE)
data.put("duration", durationInMinutes)
} catch (e: JSONException) {
aapsLogger.error("Unhandled exception", e)
}
val event = CareportalEvent(injector)
event.date = DateUtil.now()
event.source = Source.USER
event.eventType = CareportalEvent.OPENAPSOFFLINE
event.json = data.toString()
MainApp.getDbHelper().createOrUpdate(event)
nsUpload.uploadOpenAPSOffline(event)
}
companion object {
private const val CHANNEL_ID = "AndroidAPS-OpenLoop"
}
}

View file

@ -1,41 +0,0 @@
package info.nightscout.androidaps.plugins.aps.loop;
import android.content.Context;
import android.content.res.AssetManager;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
public class ScriptReader {
private final Context mContext;
public ScriptReader(Context context) {
mContext = context;
}
public byte[] readFile(String fileName) throws IOException {
AssetManager assetManager = mContext.getAssets();
InputStream is = assetManager.open(fileName);
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int nRead;
byte[] data = new byte[16384];
while ((nRead = is.read(data, 0, data.length)) != -1) {
buffer.write(data, 0, nRead);
}
buffer.flush();
byte[] bytes = buffer.toByteArray();
is.close();
buffer.close();
return bytes;
}
}

View file

@ -0,0 +1,25 @@
package info.nightscout.androidaps.plugins.aps.loop
import android.content.Context
import java.io.ByteArrayOutputStream
import java.io.IOException
class ScriptReader(private val context: Context) {
@Throws(IOException::class)
fun readFile(fileName: String): ByteArray {
val assetManager = context.assets
val `is` = assetManager.open(fileName)
val buffer = ByteArrayOutputStream()
var nRead: Int
val data = ByteArray(16384)
while (`is`.read(data, 0, data.size).also { nRead = it } != -1) {
buffer.write(data, 0, nRead)
}
buffer.flush()
val bytes = buffer.toByteArray()
`is`.close()
buffer.close()
return bytes
}
}

View file

@ -1,299 +0,0 @@
package info.nightscout.androidaps.plugins.aps.openAPSAMA;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.Function;
import org.mozilla.javascript.NativeJSON;
import org.mozilla.javascript.NativeObject;
import org.mozilla.javascript.RhinoException;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
import org.mozilla.javascript.Undefined;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.nio.charset.StandardCharsets;
import javax.annotation.Nullable;
import javax.inject.Inject;
import info.nightscout.androidaps.plugins.general.openhumans.OpenHumansUploader;
import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.Constants;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.data.IobTotal;
import info.nightscout.androidaps.data.MealData;
import info.nightscout.androidaps.data.Profile;
import info.nightscout.androidaps.db.TemporaryBasal;
import info.nightscout.androidaps.logging.AAPSLogger;
import info.nightscout.androidaps.logging.LTag;
import info.nightscout.androidaps.plugins.aps.loop.ScriptReader;
import info.nightscout.androidaps.plugins.aps.logger.LoggerCallback;
import info.nightscout.androidaps.plugins.aps.openAPSSMB.SMBDefaults;
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker;
import info.nightscout.androidaps.interfaces.ProfileFunction;
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.sharedPreferences.SP;
public class DetermineBasalAdapterAMAJS {
private final HasAndroidInjector injector;
@Inject AAPSLogger aapsLogger;
@Inject ConstraintChecker constraintChecker;
@Inject SP sp;
@Inject ProfileFunction profileFunction;
@Inject TreatmentsPlugin treatmentsPlugin;
@Inject OpenHumansUploader openHumansUploader;
private final ScriptReader mScriptReader;
private JSONObject mProfile;
private JSONObject mGlucoseStatus;
private JSONArray mIobData;
private JSONObject mMealData;
private JSONObject mCurrentTemp;
private JSONObject mAutosensData = null;
private String storedCurrentTemp = null;
private String storedIobData = null;
private String storedGlucoseStatus = null;
private String storedProfile = null;
private String storedMeal_data = null;
private String storedAutosens_data = null;
private String scriptDebug = "";
DetermineBasalAdapterAMAJS(ScriptReader scriptReader, HasAndroidInjector injector) {
injector.androidInjector().inject(this);
mScriptReader = scriptReader;
this.injector = injector;
}
@Nullable
public DetermineBasalResultAMA invoke() {
aapsLogger.debug(LTag.APS, ">>> Invoking detemine_basal <<<");
aapsLogger.debug(LTag.APS, "Glucose status: " + (storedGlucoseStatus = mGlucoseStatus.toString()));
aapsLogger.debug(LTag.APS, "IOB data: " + (storedIobData = mIobData.toString()));
aapsLogger.debug(LTag.APS, "Current temp: " + (storedCurrentTemp = mCurrentTemp.toString()));
aapsLogger.debug(LTag.APS, "Profile: " + (storedProfile = mProfile.toString()));
aapsLogger.debug(LTag.APS, "Meal data: " + (storedMeal_data = mMealData.toString()));
if (mAutosensData != null)
aapsLogger.debug(LTag.APS, "Autosens data: " + (storedAutosens_data = mAutosensData.toString()));
else
aapsLogger.debug(LTag.APS, "Autosens data: " + (storedAutosens_data = "undefined"));
DetermineBasalResultAMA determineBasalResultAMA = null;
Context rhino = Context.enter();
Scriptable scope = rhino.initStandardObjects();
// Turn off optimization to make Rhino Android compatible
rhino.setOptimizationLevel(-1);
try {
//register logger callback for console.log and console.error
ScriptableObject.defineClass(scope, LoggerCallback.class);
Scriptable myLogger = rhino.newObject(scope, "LoggerCallback", null);
scope.put("console2", scope, myLogger);
rhino.evaluateString(scope, readFile("OpenAPSAMA/loggerhelper.js"), "JavaScript", 0, null);
//set module parent
rhino.evaluateString(scope, "var module = {\"parent\":Boolean(1)};", "JavaScript", 0, null);
rhino.evaluateString(scope, "var round_basal = function round_basal(basal, profile) { return basal; };", "JavaScript", 0, null);
rhino.evaluateString(scope, "require = function() {return round_basal;};", "JavaScript", 0, null);
//generate functions "determine_basal" and "setTempBasal"
rhino.evaluateString(scope, readFile("OpenAPSAMA/determine-basal.js"), "JavaScript", 0, null);
rhino.evaluateString(scope, readFile("OpenAPSAMA/basal-set-temp.js"), "setTempBasal.js", 0, null);
Object determineBasalObj = scope.get("determine_basal", scope);
Object setTempBasalFunctionsObj = scope.get("tempBasalFunctions", scope);
//call determine-basal
if (determineBasalObj instanceof Function && setTempBasalFunctionsObj instanceof NativeObject) {
Function determineBasalJS = (Function) determineBasalObj;
//prepare parameters
Object[] params = new Object[]{
makeParam(mGlucoseStatus, rhino, scope),
makeParam(mCurrentTemp, rhino, scope),
makeParamArray(mIobData, rhino, scope),
makeParam(mProfile, rhino, scope),
makeParam(mAutosensData, rhino, scope),
makeParam(mMealData, rhino, scope),
setTempBasalFunctionsObj};
NativeObject jsResult = (NativeObject) determineBasalJS.call(rhino, scope, scope, params);
scriptDebug = LoggerCallback.getScriptDebug();
// Parse the jsResult object to a JSON-String
String result = NativeJSON.stringify(rhino, scope, jsResult, null, null).toString();
aapsLogger.debug(LTag.APS, "Result: " + result);
try {
JSONObject resultJson = new JSONObject(result);
openHumansUploader.enqueueAMAData(mProfile, mGlucoseStatus, mIobData, mMealData, mCurrentTemp, mAutosensData, resultJson);
determineBasalResultAMA = new DetermineBasalResultAMA(injector, jsResult, resultJson);
} catch (JSONException e) {
aapsLogger.error(LTag.APS, "Unhandled exception", e);
}
} else {
aapsLogger.error(LTag.APS, "Problem loading JS Functions");
}
} catch (IOException e) {
aapsLogger.error(LTag.APS, "IOException");
} catch (RhinoException e) {
aapsLogger.error(LTag.APS, "RhinoException: (" + e.lineNumber() + "," + e.columnNumber() + ") " + e.toString());
} catch (IllegalAccessException | InstantiationException | InvocationTargetException e) {
aapsLogger.error(LTag.APS, e.toString());
} finally {
Context.exit();
}
storedGlucoseStatus = mGlucoseStatus.toString();
storedIobData = mIobData.toString();
storedCurrentTemp = mCurrentTemp.toString();
storedProfile = mProfile.toString();
storedMeal_data = mMealData.toString();
return determineBasalResultAMA;
}
String getGlucoseStatusParam() {
return storedGlucoseStatus;
}
String getCurrentTempParam() {
return storedCurrentTemp;
}
String getIobDataParam() {
return storedIobData;
}
String getProfileParam() {
return storedProfile;
}
String getMealDataParam() {
return storedMeal_data;
}
String getAutosensDataParam() {
return storedAutosens_data;
}
String getScriptDebug() {
return scriptDebug;
}
public void setData(Profile profile,
double maxIob,
double maxBasal,
double minBg,
double maxBg,
double targetBg,
double basalrate,
IobTotal[] iobArray,
GlucoseStatus glucoseStatus,
MealData mealData,
double autosensDataRatio,
boolean tempTargetSet) throws JSONException {
mProfile = new JSONObject();
mProfile.put("max_iob", maxIob);
mProfile.put("dia", Math.min(profile.getDia(), 3d));
mProfile.put("type", "current");
mProfile.put("max_daily_basal", profile.getMaxDailyBasal());
mProfile.put("max_basal", maxBasal);
mProfile.put("min_bg", minBg);
mProfile.put("max_bg", maxBg);
mProfile.put("target_bg", targetBg);
mProfile.put("carb_ratio", profile.getIc());
mProfile.put("sens", profile.getIsfMgdl());
mProfile.put("max_daily_safety_multiplier", sp.getInt(R.string.key_openapsama_max_daily_safety_multiplier, 3));
mProfile.put("current_basal_safety_multiplier", sp.getDouble(R.string.key_openapsama_current_basal_safety_multiplier, 4d));
mProfile.put("skip_neutral_temps", true);
mProfile.put("current_basal", basalrate);
mProfile.put("temptargetSet", tempTargetSet);
mProfile.put("autosens_adjust_targets", sp.getBoolean(R.string.key_openapsama_autosens_adjusttargets, true));
//align with max-absorption model in AMA sensitivity
if (mealData.usedMinCarbsImpact > 0) {
mProfile.put("min_5m_carbimpact", mealData.usedMinCarbsImpact);
} else {
mProfile.put("min_5m_carbimpact", sp.getDouble(R.string.key_openapsama_min_5m_carbimpact, SMBDefaults.min_5m_carbimpact));
}
if (profileFunction.getUnits().equals(Constants.MMOL)) {
mProfile.put("out_units", "mmol/L");
}
long now = System.currentTimeMillis();
TemporaryBasal tb = treatmentsPlugin.getTempBasalFromHistory(now);
mCurrentTemp = new JSONObject();
mCurrentTemp.put("temp", "absolute");
mCurrentTemp.put("duration", tb != null ? tb.getPlannedRemainingMinutes() : 0);
mCurrentTemp.put("rate", tb != null ? tb.tempBasalConvertedToAbsolute(now, profile) : 0d);
// as we have non default temps longer than 30 mintues
TemporaryBasal tempBasal = treatmentsPlugin.getTempBasalFromHistory(System.currentTimeMillis());
if (tempBasal != null) {
mCurrentTemp.put("minutesrunning", tempBasal.getRealDuration());
}
mIobData = IobCobCalculatorPlugin.convertToJSONArray(iobArray);
mGlucoseStatus = new JSONObject();
mGlucoseStatus.put("glucose", glucoseStatus.glucose);
if (sp.getBoolean(R.string.key_always_use_shortavg, false)) {
mGlucoseStatus.put("delta", glucoseStatus.short_avgdelta);
} else {
mGlucoseStatus.put("delta", glucoseStatus.delta);
}
mGlucoseStatus.put("short_avgdelta", glucoseStatus.short_avgdelta);
mGlucoseStatus.put("long_avgdelta", glucoseStatus.long_avgdelta);
mMealData = new JSONObject();
mMealData.put("carbs", mealData.carbs);
mMealData.put("boluses", mealData.boluses);
mMealData.put("mealCOB", mealData.mealCOB);
if (constraintChecker.isAutosensModeEnabled().value()) {
mAutosensData = new JSONObject();
mAutosensData.put("ratio", autosensDataRatio);
} else {
mAutosensData = null;
}
}
private Object makeParam(JSONObject jsonObject, Context rhino, Scriptable scope) {
if (jsonObject == null) return Undefined.instance;
Object param = NativeJSON.parse(rhino, scope, jsonObject.toString(), (context, scriptable, scriptable1, objects) -> objects[1]);
return param;
}
private Object makeParamArray(JSONArray jsonArray, Context rhino, Scriptable scope) {
//Object param = NativeJSON.parse(rhino, scope, "{myarray: " + jsonArray.toString() + " }", new Callable() {
Object param = NativeJSON.parse(rhino, scope, jsonArray.toString(), (context, scriptable, scriptable1, objects) -> objects[1]);
return param;
}
private String readFile(String filename) throws IOException {
byte[] bytes = mScriptReader.readFile(filename);
String string = new String(bytes, StandardCharsets.UTF_8);
if (string.startsWith("#!/usr/bin/env node")) {
string = string.substring(20);
}
return string;
}
}

View file

@ -0,0 +1,240 @@
package info.nightscout.androidaps.plugins.aps.openAPSAMA
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.IobTotal
import info.nightscout.androidaps.data.MealData
import info.nightscout.androidaps.data.Profile
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.aps.logger.LoggerCallback
import info.nightscout.androidaps.plugins.aps.loop.ScriptReader
import info.nightscout.androidaps.plugins.aps.openAPSSMB.SMBDefaults
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.plugins.general.openhumans.OpenHumansUploader
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.sharedPreferences.SP
import org.json.JSONArray
import org.json.JSONException
import org.json.JSONObject
import org.mozilla.javascript.*
import org.mozilla.javascript.Function
import java.io.IOException
import java.lang.reflect.InvocationTargetException
import java.nio.charset.StandardCharsets
import javax.inject.Inject
import kotlin.math.min
class DetermineBasalAdapterAMAJS internal constructor(scriptReader: ScriptReader, injector: HasAndroidInjector) {
private val injector: HasAndroidInjector
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var constraintChecker: ConstraintChecker
@Inject lateinit var sp: SP
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var treatmentsPlugin: TreatmentsPlugin
@Inject lateinit var openHumansUploader: OpenHumansUploader
private val mScriptReader: ScriptReader
private var profile = JSONObject()
private var glucoseStatus = JSONObject()
private var iobData: JSONArray? = null
private var mealData = JSONObject()
private var currentTemp = JSONObject()
private var autosensData = JSONObject()
var currentTempParam: String? = null
private set
var iobDataParam: String? = null
private set
var glucoseStatusParam: String? = null
private set
var profileParam: String? = null
private set
var mealDataParam: String? = null
private set
var scriptDebug = ""
private set
@Suppress("SpellCheckingInspection")
operator fun invoke(): DetermineBasalResultAMA? {
aapsLogger.debug(LTag.APS, ">>> Invoking determine_basal <<<")
aapsLogger.debug(LTag.APS, "Glucose status: " + glucoseStatus.toString().also { glucoseStatusParam = it })
aapsLogger.debug(LTag.APS, "IOB data: " + iobData.toString().also { iobDataParam = it })
aapsLogger.debug(LTag.APS, "Current temp: " + currentTemp.toString().also { currentTempParam = it })
aapsLogger.debug(LTag.APS, "Profile: " + profile.toString().also { profileParam = it })
aapsLogger.debug(LTag.APS, "Meal data: " + mealData.toString().also { mealDataParam = it })
aapsLogger.debug(LTag.APS, "Autosens data: $autosensData")
var determineBasalResultAMA: DetermineBasalResultAMA? = null
val rhino = Context.enter()
val scope: Scriptable = rhino.initStandardObjects()
// Turn off optimization to make Rhino Android compatible
rhino.optimizationLevel = -1
try {
//register logger callback for console.log and console.error
ScriptableObject.defineClass(scope, LoggerCallback::class.java)
val myLogger = rhino.newObject(scope, "LoggerCallback", null)
scope.put("console2", scope, myLogger)
rhino.evaluateString(scope, readFile("OpenAPSAMA/loggerhelper.js"), "JavaScript", 0, null)
//set module parent
rhino.evaluateString(scope, "var module = {\"parent\":Boolean(1)};", "JavaScript", 0, null)
rhino.evaluateString(scope, "var round_basal = function round_basal(basal, profile) { return basal; };", "JavaScript", 0, null)
rhino.evaluateString(scope, "require = function() {return round_basal;};", "JavaScript", 0, null)
//generate functions "determine_basal" and "setTempBasal"
rhino.evaluateString(scope, readFile("OpenAPSAMA/determine-basal.js"), "JavaScript", 0, null)
rhino.evaluateString(scope, readFile("OpenAPSAMA/basal-set-temp.js"), "setTempBasal.js", 0, null)
val determineBasalObj = scope["determine_basal", scope]
val setTempBasalFunctionsObj = scope["tempBasalFunctions", scope]
//call determine-basal
if (determineBasalObj is Function && setTempBasalFunctionsObj is NativeObject) {
//prepare parameters
val params = arrayOf(
makeParam(glucoseStatus, rhino, scope),
makeParam(currentTemp, rhino, scope),
makeParamArray(iobData, rhino, scope),
makeParam(profile, rhino, scope),
makeParam(autosensData, rhino, scope),
makeParam(mealData, rhino, scope),
setTempBasalFunctionsObj)
val jsResult = determineBasalObj.call(rhino, scope, scope, params) as NativeObject
scriptDebug = LoggerCallback.scriptDebug
// Parse the jsResult object to a JSON-String
val result = NativeJSON.stringify(rhino, scope, jsResult, null, null).toString()
aapsLogger.debug(LTag.APS, "Result: $result")
try {
val resultJson = JSONObject(result)
openHumansUploader.enqueueAMAData(profile, glucoseStatus, iobData, mealData, currentTemp, autosensData, resultJson)
determineBasalResultAMA = DetermineBasalResultAMA(injector, jsResult, resultJson)
} catch (e: JSONException) {
aapsLogger.error(LTag.APS, "Unhandled exception", e)
}
} else {
aapsLogger.error(LTag.APS, "Problem loading JS Functions")
}
} catch (e: IOException) {
aapsLogger.error(LTag.APS, "IOException")
} catch (e: RhinoException) {
aapsLogger.error(LTag.APS, "RhinoException: (" + e.lineNumber() + "," + e.columnNumber() + ") " + e.toString())
} catch (e: IllegalAccessException) {
aapsLogger.error(LTag.APS, e.toString())
} catch (e: InstantiationException) {
aapsLogger.error(LTag.APS, e.toString())
} catch (e: InvocationTargetException) {
aapsLogger.error(LTag.APS, e.toString())
} finally {
Context.exit()
}
glucoseStatusParam = glucoseStatus.toString()
iobDataParam = iobData.toString()
currentTempParam = currentTemp.toString()
profileParam = profile.toString()
mealDataParam = mealData.toString()
return determineBasalResultAMA
}
@Suppress("SpellCheckingInspection")
@Throws(JSONException::class) fun setData(profile: Profile,
maxIob: Double,
maxBasal: Double,
minBg: Double,
maxBg: Double,
targetBg: Double,
basalRate: Double,
iobArray: Array<IobTotal>,
glucoseStatus: GlucoseStatus,
mealData: MealData,
autosensDataRatio: Double,
tempTargetSet: Boolean) {
this.profile = JSONObject()
this.profile.put("max_iob", maxIob)
this.profile.put("dia", min(profile.dia, 3.0))
this.profile.put("type", "current")
this.profile.put("max_daily_basal", profile.maxDailyBasal)
this.profile.put("max_basal", maxBasal)
this.profile.put("min_bg", minBg)
this.profile.put("max_bg", maxBg)
this.profile.put("target_bg", targetBg)
this.profile.put("carb_ratio", profile.ic)
this.profile.put("sens", profile.isfMgdl)
this.profile.put("max_daily_safety_multiplier", sp.getInt(R.string.key_openapsama_max_daily_safety_multiplier, 3))
this.profile.put("current_basal_safety_multiplier", sp.getDouble(R.string.key_openapsama_current_basal_safety_multiplier, 4.0))
this.profile.put("skip_neutral_temps", true)
this.profile.put("current_basal", basalRate)
this.profile.put("temptargetSet", tempTargetSet)
this.profile.put("autosens_adjust_targets", sp.getBoolean(R.string.key_openapsama_autosens_adjusttargets, true))
//align with max-absorption model in AMA sensitivity
if (mealData.usedMinCarbsImpact > 0) {
this.profile.put("min_5m_carbimpact", mealData.usedMinCarbsImpact)
} else {
this.profile.put("min_5m_carbimpact", sp.getDouble(R.string.key_openapsama_min_5m_carbimpact, SMBDefaults.min_5m_carbimpact))
}
if (profileFunction.getUnits() == Constants.MMOL) {
this.profile.put("out_units", "mmol/L")
}
val now = System.currentTimeMillis()
val tb = treatmentsPlugin.getTempBasalFromHistory(now)
currentTemp = JSONObject()
currentTemp.put("temp", "absolute")
currentTemp.put("duration", tb?.plannedRemainingMinutes ?: 0)
currentTemp.put("rate", tb?.tempBasalConvertedToAbsolute(now, profile) ?: 0.0)
// as we have non default temps longer than 30 minutes
val tempBasal = treatmentsPlugin.getTempBasalFromHistory(System.currentTimeMillis())
if (tempBasal != null) {
currentTemp.put("minutesrunning", tempBasal.realDuration)
}
iobData = IobCobCalculatorPlugin.convertToJSONArray(iobArray)
this.glucoseStatus = JSONObject()
this.glucoseStatus.put("glucose", glucoseStatus.glucose)
if (sp.getBoolean(R.string.key_always_use_shortavg, false)) {
this.glucoseStatus.put("delta", glucoseStatus.shortAvgDelta)
} else {
this.glucoseStatus.put("delta", glucoseStatus.delta)
}
this.glucoseStatus.put("short_avgdelta", glucoseStatus.shortAvgDelta)
this.glucoseStatus.put("long_avgdelta", glucoseStatus.longAvgDelta)
this.mealData = JSONObject()
this.mealData.put("carbs", mealData.carbs)
this.mealData.put("boluses", mealData.boluses)
this.mealData.put("mealCOB", mealData.mealCOB)
if (constraintChecker.isAutosensModeEnabled().value()) {
autosensData.put("ratio", autosensDataRatio)
} else {
autosensData.put("ratio", 1.0)
}
}
private fun makeParam(jsonObject: JSONObject?, rhino: Context, scope: Scriptable): Any {
return if (jsonObject == null) Undefined.instance else NativeJSON.parse(rhino, scope, jsonObject.toString()) { _: Context?, _: Scriptable?, _: Scriptable?, objects: Array<Any?> -> objects[1] }
}
private fun makeParamArray(jsonArray: JSONArray?, rhino: Context, scope: Scriptable): Any {
return NativeJSON.parse(rhino, scope, jsonArray.toString()) { _: Context?, _: Scriptable?, _: Scriptable?, objects: Array<Any?> -> objects[1] }
}
@Throws(IOException::class) private fun readFile(filename: String): String {
val bytes = mScriptReader.readFile(filename)
var string = String(bytes, StandardCharsets.UTF_8)
if (string.startsWith("#!/usr/bin/env node")) {
string = string.substring(20)
}
return string
}
init {
injector.androidInjector().inject(this)
mScriptReader = scriptReader
this.injector = injector
}
}

View file

@ -16,10 +16,10 @@ import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.JSONFormatter
import info.nightscout.androidaps.utils.extensions.plusAssign
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign
import org.json.JSONArray
import org.json.JSONException
import javax.inject.Inject
@ -114,7 +114,7 @@ class OpenAPSAMAFragment : DaggerFragment() {
if (openAPSAMAPlugin.lastAPSRun != 0L) {
binding.lastrun.text = dateUtil.dateAndTimeString(openAPSAMAPlugin.lastAPSRun)
}
openAPSAMAPlugin.lastAutosensResult?.let {
openAPSAMAPlugin.lastAutosensResult.let {
binding.autosensdata.text = JSONFormatter.format(it.json())
}
}

View file

@ -1,263 +0,0 @@
package info.nightscout.androidaps.plugins.aps.openAPSAMA;
import android.content.Context;
import org.json.JSONException;
import javax.inject.Inject;
import javax.inject.Singleton;
import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.data.IobTotal;
import info.nightscout.androidaps.data.MealData;
import info.nightscout.androidaps.data.Profile;
import info.nightscout.androidaps.db.TempTarget;
import info.nightscout.androidaps.interfaces.APSInterface;
import info.nightscout.androidaps.interfaces.ActivePluginProvider;
import info.nightscout.androidaps.interfaces.PluginBase;
import info.nightscout.androidaps.interfaces.PluginDescription;
import info.nightscout.androidaps.interfaces.PluginType;
import info.nightscout.androidaps.interfaces.ProfileFunction;
import info.nightscout.androidaps.interfaces.PumpInterface;
import info.nightscout.androidaps.logging.AAPSLogger;
import info.nightscout.androidaps.logging.LTag;
import info.nightscout.androidaps.plugins.aps.events.EventOpenAPSUpdateGui;
import info.nightscout.androidaps.plugins.aps.events.EventOpenAPSUpdateResultGui;
import info.nightscout.androidaps.plugins.aps.loop.APSResult;
import info.nightscout.androidaps.plugins.aps.loop.ScriptReader;
import info.nightscout.androidaps.plugins.bus.RxBusWrapper;
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker;
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.iob.iobCobCalculator.data.AutosensData;
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;
import info.nightscout.androidaps.utils.resources.ResourceHelper;
@Singleton
public class OpenAPSAMAPlugin extends PluginBase implements APSInterface {
private final AAPSLogger aapsLogger;
private final RxBusWrapper rxBus;
private final ConstraintChecker constraintChecker;
private final ResourceHelper resourceHelper;
private final ProfileFunction profileFunction;
private final Context context;
private final ActivePluginProvider activePlugin;
private final TreatmentsPlugin treatmentsPlugin;
private final IobCobCalculatorPlugin iobCobCalculatorPlugin;
private final HardLimits hardLimits;
private final Profiler profiler;
private final FabricPrivacy fabricPrivacy;
// last values
DetermineBasalAdapterAMAJS lastDetermineBasalAdapterAMAJS = null;
long lastAPSRun = 0;
DetermineBasalResultAMA lastAPSResult = null;
AutosensResult lastAutosensResult = null;
@Inject
public OpenAPSAMAPlugin(
HasAndroidInjector injector,
AAPSLogger aapsLogger,
RxBusWrapper rxBus,
ConstraintChecker constraintChecker,
ResourceHelper resourceHelper,
ProfileFunction profileFunction,
Context context,
ActivePluginProvider activePlugin,
TreatmentsPlugin treatmentsPlugin,
IobCobCalculatorPlugin iobCobCalculatorPlugin,
HardLimits hardLimits,
Profiler profiler,
FabricPrivacy fabricPrivacy
) {
super(new PluginDescription()
.mainType(PluginType.APS)
.fragmentClass(OpenAPSAMAFragment.class.getName())
.pluginIcon(R.drawable.ic_generic_icon)
.pluginName(R.string.openapsama)
.shortName(R.string.oaps_shortname)
.preferencesId(R.xml.pref_openapsama)
.description(R.string.description_ama),
aapsLogger, resourceHelper, injector
);
this.aapsLogger = aapsLogger;
this.rxBus = rxBus;
this.constraintChecker = constraintChecker;
this.resourceHelper = resourceHelper;
this.profileFunction = profileFunction;
this.context = context;
this.activePlugin = activePlugin;
this.treatmentsPlugin = treatmentsPlugin;
this.iobCobCalculatorPlugin = iobCobCalculatorPlugin;
this.hardLimits = hardLimits;
this.profiler = profiler;
this.fabricPrivacy = fabricPrivacy;
}
@Override
public boolean specialEnableCondition() {
try {
PumpInterface pump = activePlugin.getActivePump();
return pump.getPumpDescription().isTempBasalCapable;
} catch (Exception ignored) {
// may fail during initialization
return true;
}
}
@Override
public boolean specialShowInListCondition() {
PumpInterface pump = activePlugin.getActivePump();
return pump.getPumpDescription().isTempBasalCapable;
}
@Override
public APSResult getLastAPSResult() {
return lastAPSResult;
}
@Override
public long getLastAPSRun() {
return lastAPSRun;
}
@Override
public void invoke(String initiator, boolean tempBasalFallback) {
aapsLogger.debug(LTag.APS, "invoke from " + initiator + " tempBasalFallback: " + tempBasalFallback);
lastAPSResult = null;
DetermineBasalAdapterAMAJS determineBasalAdapterAMAJS;
determineBasalAdapterAMAJS = new DetermineBasalAdapterAMAJS(new ScriptReader(context), getInjector());
GlucoseStatus glucoseStatus = new GlucoseStatus(getInjector()).getGlucoseStatusData();
Profile profile = profileFunction.getProfile();
PumpInterface pump = activePlugin.getActivePump();
if (profile == null) {
rxBus.send(new EventOpenAPSUpdateResultGui(resourceHelper.gs(R.string.noprofileselected)));
aapsLogger.debug(LTag.APS, resourceHelper.gs(R.string.noprofileselected));
return;
}
if (!isEnabled(PluginType.APS)) {
rxBus.send(new EventOpenAPSUpdateResultGui(resourceHelper.gs(R.string.openapsma_disabled)));
aapsLogger.debug(LTag.APS, resourceHelper.gs(R.string.openapsma_disabled));
return;
}
if (glucoseStatus == null) {
rxBus.send(new EventOpenAPSUpdateResultGui(resourceHelper.gs(R.string.openapsma_noglucosedata)));
aapsLogger.debug(LTag.APS, resourceHelper.gs(R.string.openapsma_noglucosedata));
return;
}
double maxBasal = constraintChecker.getMaxBasalAllowed(profile).value();
double minBg = profile.getTargetLowMgdl();
double maxBg = profile.getTargetHighMgdl();
double targetBg = profile.getTargetMgdl();
minBg = Round.roundTo(minBg, 0.1d);
maxBg = Round.roundTo(maxBg, 0.1d);
long start = System.currentTimeMillis();
long startPart = System.currentTimeMillis();
IobTotal[] iobArray = iobCobCalculatorPlugin.calculateIobArrayInDia(profile);
profiler.log(LTag.APS, "calculateIobArrayInDia()", startPart);
startPart = System.currentTimeMillis();
MealData mealData = iobCobCalculatorPlugin.getMealData();
profiler.log(LTag.APS, "getMealData()", startPart);
double maxIob = constraintChecker.getMaxIOBAllowed().value();
minBg = hardLimits.verifyHardLimits(minBg, "minBg", hardLimits.getVERY_HARD_LIMIT_MIN_BG()[0], hardLimits.getVERY_HARD_LIMIT_MIN_BG()[1]);
maxBg = hardLimits.verifyHardLimits(maxBg, "maxBg", hardLimits.getVERY_HARD_LIMIT_MAX_BG()[0], hardLimits.getVERY_HARD_LIMIT_MAX_BG()[1]);
targetBg = hardLimits.verifyHardLimits(targetBg, "targetBg", hardLimits.getVERY_HARD_LIMIT_TARGET_BG()[0], hardLimits.getVERY_HARD_LIMIT_TARGET_BG()[1]);
boolean isTempTarget = false;
TempTarget tempTarget = treatmentsPlugin.getTempTargetFromHistory(System.currentTimeMillis());
if (tempTarget != null) {
isTempTarget = true;
minBg = hardLimits.verifyHardLimits(tempTarget.low, "minBg", hardLimits.getVERY_HARD_LIMIT_TEMP_MIN_BG()[0], hardLimits.getVERY_HARD_LIMIT_TEMP_MIN_BG()[1]);
maxBg = hardLimits.verifyHardLimits(tempTarget.high, "maxBg", hardLimits.getVERY_HARD_LIMIT_TEMP_MAX_BG()[0], hardLimits.getVERY_HARD_LIMIT_TEMP_MAX_BG()[1]);
targetBg = hardLimits.verifyHardLimits(tempTarget.target(), "targetBg", hardLimits.getVERY_HARD_LIMIT_TEMP_TARGET_BG()[0], hardLimits.getVERY_HARD_LIMIT_TEMP_TARGET_BG()[1]);
}
if (!hardLimits.checkOnlyHardLimits(profile.getDia(), "dia", hardLimits.minDia(), hardLimits.maxDia()))
return;
if (!hardLimits.checkOnlyHardLimits(profile.getIcTimeFromMidnight(Profile.secondsFromMidnight()), "carbratio", hardLimits.minIC(), hardLimits.maxIC()))
return;
if (!hardLimits.checkOnlyHardLimits(profile.getIsfMgdl(), "sens", hardLimits.getMINISF(), hardLimits.getMAXISF()))
return;
if (!hardLimits.checkOnlyHardLimits(profile.getMaxDailyBasal(), "max_daily_basal", 0.02, hardLimits.maxBasal()))
return;
if (!hardLimits.checkOnlyHardLimits(pump.getBaseBasalRate(), "current_basal", 0.01, hardLimits.maxBasal()))
return;
startPart = System.currentTimeMillis();
if (constraintChecker.isAutosensModeEnabled().value()) {
AutosensData autosensData = iobCobCalculatorPlugin.getLastAutosensDataSynchronized("OpenAPSPlugin");
if (autosensData == null) {
rxBus.send(new EventOpenAPSUpdateResultGui(resourceHelper.gs(R.string.openaps_noasdata)));
return;
}
lastAutosensResult = autosensData.autosensResult;
} else {
lastAutosensResult = new AutosensResult();
lastAutosensResult.sensResult = "autosens disabled";
}
profiler.log(LTag.APS, "detectSensitivityandCarbAbsorption()", startPart);
profiler.log(LTag.APS, "AMA data gathering", start);
start = System.currentTimeMillis();
try {
determineBasalAdapterAMAJS.setData(profile, maxIob, maxBasal, minBg, maxBg, targetBg, activePlugin.getActivePump().getBaseBasalRate(), iobArray, glucoseStatus, mealData,
lastAutosensResult.ratio, //autosensDataRatio
isTempTarget
);
} catch (JSONException e) {
fabricPrivacy.logException(e);
return;
}
DetermineBasalResultAMA determineBasalResultAMA = determineBasalAdapterAMAJS.invoke();
profiler.log(LTag.APS, "AMA calculation", start);
// Fix bug determine basal
if (determineBasalResultAMA == null) {
aapsLogger.error(LTag.APS, "SMB calculation returned null");
lastDetermineBasalAdapterAMAJS = null;
lastAPSResult = null;
lastAPSRun = 0;
} else {
if (determineBasalResultAMA.getRate() == 0d && determineBasalResultAMA.getDuration() == 0 && !treatmentsPlugin.isTempBasalInProgress())
determineBasalResultAMA.setTempBasalRequested(false);
determineBasalResultAMA.setIob(iobArray[0]);
long now = System.currentTimeMillis();
try {
determineBasalResultAMA.getJson().put("timestamp", DateUtil.toISOString(now));
} catch (JSONException e) {
aapsLogger.error(LTag.APS, "Unhandled exception", e);
}
lastDetermineBasalAdapterAMAJS = determineBasalAdapterAMAJS;
lastAPSResult = determineBasalResultAMA;
lastAPSRun = now;
}
rxBus.send(new EventOpenAPSUpdateGui());
//deviceStatus.suggested = determineBasalResultAMA.json;
}
}

View file

@ -0,0 +1,171 @@
package info.nightscout.androidaps.plugins.aps.openAPSAMA
import android.content.Context
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.Profile
import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.aps.events.EventOpenAPSUpdateGui
import info.nightscout.androidaps.plugins.aps.events.EventOpenAPSUpdateResultGui
import info.nightscout.androidaps.plugins.aps.loop.ScriptReader
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
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.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
import info.nightscout.androidaps.utils.resources.ResourceHelper
import org.json.JSONException
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
open class OpenAPSAMAPlugin @Inject constructor(
injector: HasAndroidInjector,
aapsLogger: AAPSLogger,
private val rxBus: RxBusWrapper,
private val constraintChecker: ConstraintChecker,
resourceHelper: ResourceHelper,
private val profileFunction: ProfileFunction,
private val context: Context,
private val activePlugin: ActivePluginProvider,
private val treatmentsPlugin: TreatmentsInterface,
private val iobCobCalculatorPlugin: IobCobCalculatorPlugin,
private val hardLimits: HardLimits,
private val profiler: Profiler,
private val fabricPrivacy: FabricPrivacy
) : PluginBase(PluginDescription()
.mainType(PluginType.APS)
.fragmentClass(OpenAPSAMAFragment::class.java.name)
.pluginIcon(R.drawable.ic_generic_icon)
.pluginName(R.string.openapsama)
.shortName(R.string.oaps_shortname)
.preferencesId(R.xml.pref_openapsama)
.description(R.string.description_ama),
aapsLogger, resourceHelper, injector
), APSInterface {
// last values
override var lastAPSRun: Long = 0
override var lastAPSResult: DetermineBasalResultAMA? = null
var lastDetermineBasalAdapterAMAJS: DetermineBasalAdapterAMAJS? = null
var lastAutosensResult: AutosensResult = AutosensResult()
override fun specialEnableCondition(): Boolean {
return try {
val pump = activePlugin.activePump
pump.pumpDescription.isTempBasalCapable
} catch (ignored: Exception) {
// may fail during initialization
true
}
}
override fun specialShowInListCondition(): Boolean {
val pump = activePlugin.activePump
return pump.pumpDescription.isTempBasalCapable
}
override fun invoke(initiator: String, tempBasalFallback: Boolean) {
aapsLogger.debug(LTag.APS, "invoke from $initiator tempBasalFallback: $tempBasalFallback")
lastAPSResult = null
val determineBasalAdapterAMAJS = DetermineBasalAdapterAMAJS(ScriptReader(context), injector)
val glucoseStatus = GlucoseStatus(injector).glucoseStatusData
val profile = profileFunction.getProfile()
val pump = activePlugin.activePump
if (profile == null) {
rxBus.send(EventOpenAPSUpdateResultGui(resourceHelper.gs(R.string.noprofileselected)))
aapsLogger.debug(LTag.APS, resourceHelper.gs(R.string.noprofileselected))
return
}
if (!isEnabled(PluginType.APS)) {
rxBus.send(EventOpenAPSUpdateResultGui(resourceHelper.gs(R.string.openapsma_disabled)))
aapsLogger.debug(LTag.APS, resourceHelper.gs(R.string.openapsma_disabled))
return
}
if (glucoseStatus == null) {
rxBus.send(EventOpenAPSUpdateResultGui(resourceHelper.gs(R.string.openapsma_noglucosedata)))
aapsLogger.debug(LTag.APS, resourceHelper.gs(R.string.openapsma_noglucosedata))
return
}
val inputConstraints = Constraint(0.0) // fake. only for collecting all results
val maxBasal = constraintChecker.getMaxBasalAllowed(profile).also {
inputConstraints.copyReasons(it)
}.value()
var start = System.currentTimeMillis()
var startPart = System.currentTimeMillis()
val iobArray = iobCobCalculatorPlugin.calculateIobArrayInDia(profile)
profiler.log(LTag.APS, "calculateIobArrayInDia()", startPart)
startPart = System.currentTimeMillis()
val mealData = iobCobCalculatorPlugin.mealData
profiler.log(LTag.APS, "getMealData()", startPart)
val maxIob = constraintChecker.getMaxIOBAllowed().also { maxIOBAllowedConstraint ->
inputConstraints.copyReasons(maxIOBAllowedConstraint)
}.value()
var minBg = hardLimits.verifyHardLimits(Round.roundTo(profile.targetLowMgdl, 0.1), "minBg", hardLimits.VERY_HARD_LIMIT_MIN_BG[0].toDouble(), hardLimits.VERY_HARD_LIMIT_MIN_BG[1].toDouble())
var maxBg = hardLimits.verifyHardLimits(Round.roundTo(profile.targetHighMgdl, 0.1), "maxBg", hardLimits.VERY_HARD_LIMIT_MAX_BG[0].toDouble(), hardLimits.VERY_HARD_LIMIT_MAX_BG[1].toDouble())
var targetBg = hardLimits.verifyHardLimits(profile.targetMgdl, "targetBg", hardLimits.VERY_HARD_LIMIT_TARGET_BG[0].toDouble(), hardLimits.VERY_HARD_LIMIT_TARGET_BG[1].toDouble())
var isTempTarget = false
treatmentsPlugin.getTempTargetFromHistory(System.currentTimeMillis())?.let { tempTarget ->
isTempTarget = true
minBg = hardLimits.verifyHardLimits(tempTarget.low, "minBg", hardLimits.VERY_HARD_LIMIT_TEMP_MIN_BG[0].toDouble(), hardLimits.VERY_HARD_LIMIT_TEMP_MIN_BG[1].toDouble())
maxBg = hardLimits.verifyHardLimits(tempTarget.high, "maxBg", hardLimits.VERY_HARD_LIMIT_TEMP_MAX_BG[0].toDouble(), hardLimits.VERY_HARD_LIMIT_TEMP_MAX_BG[1].toDouble())
targetBg = hardLimits.verifyHardLimits(tempTarget.target(), "targetBg", hardLimits.VERY_HARD_LIMIT_TEMP_TARGET_BG[0].toDouble(), hardLimits.VERY_HARD_LIMIT_TEMP_TARGET_BG[1].toDouble())
}
if (!hardLimits.checkOnlyHardLimits(profile.dia, "dia", hardLimits.minDia(), hardLimits.maxDia())) return
if (!hardLimits.checkOnlyHardLimits(profile.getIcTimeFromMidnight(Profile.secondsFromMidnight()), "carbratio", hardLimits.minIC(), hardLimits.maxIC())) return
if (!hardLimits.checkOnlyHardLimits(profile.isfMgdl, "sens", hardLimits.MINISF, hardLimits.MAXISF)) return
if (!hardLimits.checkOnlyHardLimits(profile.maxDailyBasal, "max_daily_basal", 0.02, hardLimits.maxBasal())) return
if (!hardLimits.checkOnlyHardLimits(pump.baseBasalRate, "current_basal", 0.01, hardLimits.maxBasal())) return
startPart = System.currentTimeMillis()
if (constraintChecker.isAutosensModeEnabled().value()) {
val autosensData = iobCobCalculatorPlugin.getLastAutosensDataSynchronized("OpenAPSPlugin")
if (autosensData == null) {
rxBus.send(EventOpenAPSUpdateResultGui(resourceHelper.gs(R.string.openaps_noasdata)))
return
}
lastAutosensResult = autosensData.autosensResult
} else {
lastAutosensResult.sensResult = "autosens disabled"
}
profiler.log(LTag.APS, "detectSensitivityAndCarbAbsorption()", startPart)
profiler.log(LTag.APS, "AMA data gathering", start)
start = System.currentTimeMillis()
try {
determineBasalAdapterAMAJS.setData(profile, maxIob, maxBasal, minBg, maxBg, targetBg, activePlugin.activePump.baseBasalRate, iobArray, glucoseStatus, mealData,
lastAutosensResult.ratio,
isTempTarget
)
} catch (e: JSONException) {
fabricPrivacy.logException(e)
return
}
val determineBasalResultAMA = determineBasalAdapterAMAJS.invoke()
profiler.log(LTag.APS, "AMA calculation", start)
// Fix bug determine basal
if (determineBasalResultAMA == null) {
aapsLogger.error(LTag.APS, "SMB calculation returned null")
lastDetermineBasalAdapterAMAJS = null
lastAPSResult = null
lastAPSRun = 0
} else {
if (determineBasalResultAMA.rate == 0.0 && determineBasalResultAMA.duration == 0 && !treatmentsPlugin.isTempBasalInProgress) determineBasalResultAMA.tempBasalRequested = false
determineBasalResultAMA.iob = iobArray[0]
val now = System.currentTimeMillis()
determineBasalResultAMA.json?.put("timestamp", DateUtil.toISOString(now))
determineBasalResultAMA.inputConstraints = inputConstraints
lastDetermineBasalAdapterAMAJS = determineBasalAdapterAMAJS
lastAPSResult = determineBasalResultAMA
lastAPSRun = now
}
rxBus.send(EventOpenAPSUpdateGui())
//deviceStatus.suggested = determineBasalResultAMA.json;
}
}

View file

@ -1,372 +0,0 @@
package info.nightscout.androidaps.plugins.aps.openAPSSMB;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.Function;
import org.mozilla.javascript.NativeJSON;
import org.mozilla.javascript.NativeObject;
import org.mozilla.javascript.RhinoException;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
import org.mozilla.javascript.Undefined;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.nio.charset.StandardCharsets;
import javax.annotation.Nullable;
import javax.inject.Inject;
import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.Constants;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.data.IobTotal;
import info.nightscout.androidaps.data.MealData;
import info.nightscout.androidaps.data.Profile;
import info.nightscout.androidaps.db.TemporaryBasal;
import info.nightscout.androidaps.interfaces.ActivePluginProvider;
import info.nightscout.androidaps.interfaces.ProfileFunction;
import info.nightscout.androidaps.interfaces.PumpInterface;
import info.nightscout.androidaps.logging.AAPSLogger;
import info.nightscout.androidaps.logging.LTag;
import info.nightscout.androidaps.plugins.aps.logger.LoggerCallback;
import info.nightscout.androidaps.plugins.aps.loop.ScriptReader;
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker;
import info.nightscout.androidaps.plugins.general.openhumans.OpenHumansUploader;
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.SafeParse;
import info.nightscout.androidaps.utils.resources.ResourceHelper;
import info.nightscout.androidaps.utils.sharedPreferences.SP;
public class DetermineBasalAdapterSMBJS {
private final HasAndroidInjector injector;
@Inject AAPSLogger aapsLogger;
@Inject ConstraintChecker constraintChecker;
@Inject SP sp;
@Inject ResourceHelper resourceHelper;
@Inject ProfileFunction profileFunction;
@Inject TreatmentsPlugin treatmentsPlugin;
@Inject ActivePluginProvider activePluginProvider;
@Inject OpenHumansUploader openHumansUploader;
private final ScriptReader mScriptReader;
private JSONObject mProfile;
private JSONObject mGlucoseStatus;
private JSONArray mIobData;
private JSONObject mMealData;
private JSONObject mCurrentTemp;
private JSONObject mAutosensData = null;
private boolean mMicrobolusAllowed;
private boolean mSMBAlwaysAllowed;
private long mCurrentTime;
private boolean mIsSaveCgmSource;
private String storedCurrentTemp = null;
private String storedIobData = null;
private String storedGlucoseStatus = null;
private String storedProfile = null;
private String storedMeal_data = null;
private String scriptDebug = "";
/**
* Main code
*/
DetermineBasalAdapterSMBJS(ScriptReader scriptReader, HasAndroidInjector injector) {
mScriptReader = scriptReader;
this.injector = injector;
injector.androidInjector().inject(this);
}
@Nullable
public DetermineBasalResultSMB invoke() {
aapsLogger.debug(LTag.APS, ">>> Invoking detemine_basal <<<");
aapsLogger.debug(LTag.APS, "Glucose status: " + (storedGlucoseStatus = mGlucoseStatus.toString()));
aapsLogger.debug(LTag.APS, "IOB data: " + (storedIobData = mIobData.toString()));
aapsLogger.debug(LTag.APS, "Current temp: " + (storedCurrentTemp = mCurrentTemp.toString()));
aapsLogger.debug(LTag.APS, "Profile: " + (storedProfile = mProfile.toString()));
aapsLogger.debug(LTag.APS, "Meal data: " + (storedMeal_data = mMealData.toString()));
if (mAutosensData != null)
aapsLogger.debug(LTag.APS, "Autosens data: " + mAutosensData.toString());
else
aapsLogger.debug(LTag.APS, "Autosens data: " + "undefined");
aapsLogger.debug(LTag.APS, "Reservoir data: " + "undefined");
aapsLogger.debug(LTag.APS, "MicroBolusAllowed: " + mMicrobolusAllowed);
aapsLogger.debug(LTag.APS, "SMBAlwaysAllowed: " + mSMBAlwaysAllowed);
aapsLogger.debug(LTag.APS, "CurrentTime: " + mCurrentTime);
aapsLogger.debug(LTag.APS, "isSaveCgmSource: " + mIsSaveCgmSource);
DetermineBasalResultSMB determineBasalResultSMB = null;
Context rhino = Context.enter();
Scriptable scope = rhino.initStandardObjects();
// Turn off optimization to make Rhino Android compatible
rhino.setOptimizationLevel(-1);
try {
//register logger callback for console.log and console.error
ScriptableObject.defineClass(scope, LoggerCallback.class);
Scriptable myLogger = rhino.newObject(scope, "LoggerCallback", null);
scope.put("console2", scope, myLogger);
rhino.evaluateString(scope, readFile("OpenAPSAMA/loggerhelper.js"), "JavaScript", 0, null);
//set module parent
rhino.evaluateString(scope, "var module = {\"parent\":Boolean(1)};", "JavaScript", 0, null);
rhino.evaluateString(scope, "var round_basal = function round_basal(basal, profile) { return basal; };", "JavaScript", 0, null);
rhino.evaluateString(scope, "require = function() {return round_basal;};", "JavaScript", 0, null);
//generate functions "determine_basal" and "setTempBasal"
rhino.evaluateString(scope, readFile("OpenAPSSMB/determine-basal.js"), "JavaScript", 0, null);
rhino.evaluateString(scope, readFile("OpenAPSSMB/basal-set-temp.js"), "setTempBasal.js", 0, null);
Object determineBasalObj = scope.get("determine_basal", scope);
Object setTempBasalFunctionsObj = scope.get("tempBasalFunctions", scope);
//call determine-basal
if (determineBasalObj instanceof Function && setTempBasalFunctionsObj instanceof NativeObject) {
Function determineBasalJS = (Function) determineBasalObj;
//prepare parameters
Object[] params = new Object[]{
makeParam(mGlucoseStatus, rhino, scope),
makeParam(mCurrentTemp, rhino, scope),
makeParamArray(mIobData, rhino, scope),
makeParam(mProfile, rhino, scope),
makeParam(mAutosensData, rhino, scope),
makeParam(mMealData, rhino, scope),
setTempBasalFunctionsObj,
Boolean.valueOf(mMicrobolusAllowed),
makeParam(null, rhino, scope), // reservoir data as undefined
Long.valueOf(mCurrentTime),
Boolean.valueOf(mIsSaveCgmSource)
};
NativeObject jsResult = (NativeObject) determineBasalJS.call(rhino, scope, scope, params);
scriptDebug = LoggerCallback.getScriptDebug();
// Parse the jsResult object to a JSON-String
String result = NativeJSON.stringify(rhino, scope, jsResult, null, null).toString();
aapsLogger.debug(LTag.APS, "Result: " + result);
try {
JSONObject resultJson = new JSONObject(result);
openHumansUploader.enqueueSMBData(mProfile, mGlucoseStatus, mIobData, mMealData, mCurrentTemp, mAutosensData, mMicrobolusAllowed, mSMBAlwaysAllowed, resultJson);
determineBasalResultSMB = new DetermineBasalResultSMB(injector, resultJson);
} catch (JSONException e) {
aapsLogger.error(LTag.APS, "Unhandled exception", e);
}
} else {
aapsLogger.error(LTag.APS, "Problem loading JS Functions");
}
} catch (IOException e) {
aapsLogger.error(LTag.APS, "IOException");
} catch (RhinoException e) {
aapsLogger.error(LTag.APS, "RhinoException: (" + e.lineNumber() + "," + e.columnNumber() + ") " + e.toString());
} catch (IllegalAccessException | InstantiationException | InvocationTargetException e) {
aapsLogger.error(LTag.APS, e.toString());
} finally {
Context.exit();
}
storedGlucoseStatus = mGlucoseStatus.toString();
storedIobData = mIobData.toString();
storedCurrentTemp = mCurrentTemp.toString();
storedProfile = mProfile.toString();
storedMeal_data = mMealData.toString();
return determineBasalResultSMB;
}
String getGlucoseStatusParam() {
return storedGlucoseStatus;
}
String getCurrentTempParam() {
return storedCurrentTemp;
}
String getIobDataParam() {
return storedIobData;
}
String getProfileParam() {
return storedProfile;
}
String getMealDataParam() {
return storedMeal_data;
}
String getScriptDebug() {
return scriptDebug;
}
public void setData(Profile profile,
double maxIob,
double maxBasal,
double minBg,
double maxBg,
double targetBg,
double basalrate,
IobTotal[] iobArray,
GlucoseStatus glucoseStatus,
MealData mealData,
double autosensDataRatio,
boolean tempTargetSet,
boolean microBolusAllowed,
boolean uamAllowed,
boolean advancedFiltering,
boolean isSaveCgmSource
) throws JSONException {
PumpInterface pump = activePluginProvider.getActivePump();
Double pumpbolusstep = pump.getPumpDescription().bolusStep;
mProfile = new JSONObject();
mProfile.put("max_iob", maxIob);
//mProfile.put("dia", profile.getDia());
mProfile.put("type", "current");
mProfile.put("max_daily_basal", profile.getMaxDailyBasal());
mProfile.put("max_basal", maxBasal);
mProfile.put("min_bg", minBg);
mProfile.put("max_bg", maxBg);
mProfile.put("target_bg", targetBg);
mProfile.put("carb_ratio", profile.getIc());
mProfile.put("sens", profile.getIsfMgdl());
mProfile.put("max_daily_safety_multiplier", sp.getInt(R.string.key_openapsama_max_daily_safety_multiplier, 3));
mProfile.put("current_basal_safety_multiplier", sp.getDouble(R.string.key_openapsama_current_basal_safety_multiplier, 4d));
//mProfile.put("high_temptarget_raises_sensitivity", SP.getBoolean(R.string.key_high_temptarget_raises_sensitivity, SMBDefaults.high_temptarget_raises_sensitivity));
mProfile.put("high_temptarget_raises_sensitivity", false);
//mProfile.put("low_temptarget_lowers_sensitivity", SP.getBoolean(R.string.key_low_temptarget_lowers_sensitivity, SMBDefaults.low_temptarget_lowers_sensitivity));
mProfile.put("low_temptarget_lowers_sensitivity", false);
mProfile.put("sensitivity_raises_target", sp.getBoolean(R.string.key_sensitivity_raises_target, SMBDefaults.sensitivity_raises_target));
mProfile.put("resistance_lowers_target", sp.getBoolean(R.string.key_resistance_lowers_target, SMBDefaults.resistance_lowers_target));
mProfile.put("adv_target_adjustments", SMBDefaults.adv_target_adjustments);
mProfile.put("exercise_mode", SMBDefaults.exercise_mode);
mProfile.put("half_basal_exercise_target", SMBDefaults.half_basal_exercise_target);
mProfile.put("maxCOB", SMBDefaults.maxCOB);
mProfile.put("skip_neutral_temps", pump.setNeutralTempAtFullHour());
// min_5m_carbimpact is not used within SMB determinebasal
//if (mealData.usedMinCarbsImpact > 0) {
// mProfile.put("min_5m_carbimpact", mealData.usedMinCarbsImpact);
//} else {
// mProfile.put("min_5m_carbimpact", SP.getDouble(R.string.key_openapsama_min_5m_carbimpact, SMBDefaults.min_5m_carbimpact));
//}
mProfile.put("remainingCarbsCap", SMBDefaults.remainingCarbsCap);
mProfile.put("enableUAM", uamAllowed);
mProfile.put("A52_risk_enable", SMBDefaults.A52_risk_enable);
boolean smbEnabled = sp.getBoolean(R.string.key_use_smb, false);
mProfile.put("SMBInterval", sp.getInt(R.string.key_smbinterval, SMBDefaults.SMBInterval));
mProfile.put("enableSMB_with_COB", smbEnabled && sp.getBoolean(R.string.key_enableSMB_with_COB, false));
mProfile.put("enableSMB_with_temptarget", smbEnabled && sp.getBoolean(R.string.key_enableSMB_with_temptarget, false));
mProfile.put("allowSMB_with_high_temptarget", smbEnabled && sp.getBoolean(R.string.key_allowSMB_with_high_temptarget, false));
mProfile.put("enableSMB_always", smbEnabled && sp.getBoolean(R.string.key_enableSMB_always, false) && advancedFiltering);
mProfile.put("enableSMB_after_carbs", smbEnabled && sp.getBoolean(R.string.key_enableSMB_after_carbs, false) && advancedFiltering);
mProfile.put("maxSMBBasalMinutes", sp.getInt(R.string.key_smbmaxminutes, SMBDefaults.maxSMBBasalMinutes));
mProfile.put("maxUAMSMBBasalMinutes", sp.getInt(R.string.key_uamsmbmaxminutes, SMBDefaults.maxUAMSMBBasalMinutes));
//set the min SMB amount to be the amount set by the pump.
mProfile.put("bolus_increment", pumpbolusstep);
mProfile.put("carbsReqThreshold", sp.getInt(R.string.key_carbsReqThreshold, SMBDefaults.carbsReqThreshold));
mProfile.put("current_basal", basalrate);
mProfile.put("temptargetSet", tempTargetSet);
mProfile.put("autosens_max", SafeParse.stringToDouble(sp.getString(R.string.key_openapsama_autosens_max, "1.2")));
if (profileFunction.getUnits().equals(Constants.MMOL)) {
mProfile.put("out_units", "mmol/L");
}
long now = System.currentTimeMillis();
TemporaryBasal tb = treatmentsPlugin.getTempBasalFromHistory(now);
mCurrentTemp = new JSONObject();
mCurrentTemp.put("temp", "absolute");
mCurrentTemp.put("duration", tb != null ? tb.getPlannedRemainingMinutes() : 0);
mCurrentTemp.put("rate", tb != null ? tb.tempBasalConvertedToAbsolute(now, profile) : 0d);
// as we have non default temps longer than 30 mintues
TemporaryBasal tempBasal = treatmentsPlugin.getTempBasalFromHistory(System.currentTimeMillis());
if (tempBasal != null) {
mCurrentTemp.put("minutesrunning", tempBasal.getRealDuration());
}
mIobData = IobCobCalculatorPlugin.convertToJSONArray(iobArray);
mGlucoseStatus = new JSONObject();
mGlucoseStatus.put("glucose", glucoseStatus.glucose);
mGlucoseStatus.put("noise", glucoseStatus.noise);
if (sp.getBoolean(R.string.key_always_use_shortavg, false)) {
mGlucoseStatus.put("delta", glucoseStatus.short_avgdelta);
} else {
mGlucoseStatus.put("delta", glucoseStatus.delta);
}
mGlucoseStatus.put("short_avgdelta", glucoseStatus.short_avgdelta);
mGlucoseStatus.put("long_avgdelta", glucoseStatus.long_avgdelta);
mGlucoseStatus.put("date", glucoseStatus.date);
mMealData = new JSONObject();
mMealData.put("carbs", mealData.carbs);
mMealData.put("boluses", mealData.boluses);
mMealData.put("mealCOB", mealData.mealCOB);
mMealData.put("slopeFromMaxDeviation", mealData.slopeFromMaxDeviation);
mMealData.put("slopeFromMinDeviation", mealData.slopeFromMinDeviation);
mMealData.put("lastBolusTime", mealData.lastBolusTime);
mMealData.put("lastCarbTime", mealData.lastCarbTime);
if (constraintChecker.isAutosensModeEnabled().value()) {
mAutosensData = new JSONObject();
mAutosensData.put("ratio", autosensDataRatio);
} else {
mAutosensData = new JSONObject();
mAutosensData.put("ratio", 1.0);
}
mMicrobolusAllowed = microBolusAllowed;
mSMBAlwaysAllowed = advancedFiltering;
mCurrentTime = now;
mIsSaveCgmSource = isSaveCgmSource;
}
private Object makeParam(JSONObject jsonObject, Context rhino, Scriptable scope) {
if (jsonObject == null) return Undefined.instance;
return NativeJSON.parse(rhino, scope, jsonObject.toString(), (context, scriptable, scriptable1, objects) -> objects[1]);
}
private Object makeParamArray(JSONArray jsonArray, Context rhino, Scriptable scope) {
//Object param = NativeJSON.parse(rhino, scope, "{myarray: " + jsonArray.toString() + " }", new Callable() {
return NativeJSON.parse(rhino, scope, jsonArray.toString(), (context, scriptable, scriptable1, objects) -> objects[1]);
}
private String readFile(String filename) throws IOException {
byte[] bytes = mScriptReader.readFile(filename);
String string = new String(bytes, StandardCharsets.UTF_8);
if (string.startsWith("#!/usr/bin/env node")) {
string = string.substring(20);
}
return string;
}
}

View file

@ -0,0 +1,290 @@
package info.nightscout.androidaps.plugins.aps.openAPSSMB
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.IobTotal
import info.nightscout.androidaps.data.MealData
import info.nightscout.androidaps.data.Profile
import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.aps.logger.LoggerCallback
import info.nightscout.androidaps.plugins.aps.loop.ScriptReader
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.plugins.general.openhumans.OpenHumansUploader
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.SafeParse
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP
import org.json.JSONArray
import org.json.JSONException
import org.json.JSONObject
import org.mozilla.javascript.*
import org.mozilla.javascript.Function
import java.io.IOException
import java.lang.reflect.InvocationTargetException
import java.nio.charset.StandardCharsets
import javax.inject.Inject
class DetermineBasalAdapterSMBJS internal constructor(private val scriptReader: ScriptReader, private val injector: HasAndroidInjector) {
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var constraintChecker: ConstraintChecker
@Inject lateinit var sp: SP
@Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var treatmentsPlugin: TreatmentsPlugin
@Inject lateinit var activePluginProvider: ActivePluginProvider
@Inject lateinit var openHumansUploader: OpenHumansUploader
private var profile = JSONObject()
private var mGlucoseStatus = JSONObject()
private var iobData: JSONArray? = null
private var mealData = JSONObject()
private var currentTemp = JSONObject()
private var autosensData = JSONObject()
private var microBolusAllowed = false
private var smbAlwaysAllowed = false
private var currentTime: Long = 0
private var saveCgmSource = false
var currentTempParam: String? = null
private set
var iobDataParam: String? = null
private set
var glucoseStatusParam: String? = null
private set
var profileParam: String? = null
private set
var mealDataParam: String? = null
private set
var scriptDebug = ""
private set
@Suppress("SpellCheckingInspection")
operator fun invoke(): DetermineBasalResultSMB? {
aapsLogger.debug(LTag.APS, ">>> Invoking determine_basal <<<")
aapsLogger.debug(LTag.APS, "Glucose status: " + mGlucoseStatus.toString().also { glucoseStatusParam = it })
aapsLogger.debug(LTag.APS, "IOB data: " + iobData.toString().also { iobDataParam = it })
aapsLogger.debug(LTag.APS, "Current temp: " + currentTemp.toString().also { currentTempParam = it })
aapsLogger.debug(LTag.APS, "Profile: " + profile.toString().also { profileParam = it })
aapsLogger.debug(LTag.APS, "Meal data: " + mealData.toString().also { mealDataParam = it })
aapsLogger.debug(LTag.APS, "Autosens data: $autosensData")
aapsLogger.debug(LTag.APS, "Reservoir data: " + "undefined")
aapsLogger.debug(LTag.APS, "MicroBolusAllowed: $microBolusAllowed")
aapsLogger.debug(LTag.APS, "SMBAlwaysAllowed: $smbAlwaysAllowed")
aapsLogger.debug(LTag.APS, "CurrentTime: $currentTime")
aapsLogger.debug(LTag.APS, "isSaveCgmSource: $saveCgmSource")
var determineBasalResultSMB: DetermineBasalResultSMB? = null
val rhino = Context.enter()
val scope: Scriptable = rhino.initStandardObjects()
// Turn off optimization to make Rhino Android compatible
rhino.optimizationLevel = -1
try {
//register logger callback for console.log and console.error
ScriptableObject.defineClass(scope, LoggerCallback::class.java)
val myLogger = rhino.newObject(scope, "LoggerCallback", null)
scope.put("console2", scope, myLogger)
rhino.evaluateString(scope, readFile("OpenAPSAMA/loggerhelper.js"), "JavaScript", 0, null)
//set module parent
rhino.evaluateString(scope, "var module = {\"parent\":Boolean(1)};", "JavaScript", 0, null)
rhino.evaluateString(scope, "var round_basal = function round_basal(basal, profile) { return basal; };", "JavaScript", 0, null)
rhino.evaluateString(scope, "require = function() {return round_basal;};", "JavaScript", 0, null)
//generate functions "determine_basal" and "setTempBasal"
rhino.evaluateString(scope, readFile("OpenAPSSMB/determine-basal.js"), "JavaScript", 0, null)
rhino.evaluateString(scope, readFile("OpenAPSSMB/basal-set-temp.js"), "setTempBasal.js", 0, null)
val determineBasalObj = scope["determine_basal", scope]
val setTempBasalFunctionsObj = scope["tempBasalFunctions", scope]
//call determine-basal
if (determineBasalObj is Function && setTempBasalFunctionsObj is NativeObject) {
//prepare parameters
val params = arrayOf(
makeParam(mGlucoseStatus, rhino, scope),
makeParam(currentTemp, rhino, scope),
makeParamArray(iobData, rhino, scope),
makeParam(profile, rhino, scope),
makeParam(autosensData, rhino, scope),
makeParam(mealData, rhino, scope),
setTempBasalFunctionsObj,
java.lang.Boolean.valueOf(microBolusAllowed),
makeParam(null, rhino, scope), // reservoir data as undefined
java.lang.Long.valueOf(currentTime),
java.lang.Boolean.valueOf(saveCgmSource)
)
val jsResult = determineBasalObj.call(rhino, scope, scope, params) as NativeObject
scriptDebug = LoggerCallback.scriptDebug
// Parse the jsResult object to a JSON-String
val result = NativeJSON.stringify(rhino, scope, jsResult, null, null).toString()
aapsLogger.debug(LTag.APS, "Result: $result")
try {
val resultJson = JSONObject(result)
openHumansUploader.enqueueSMBData(profile, mGlucoseStatus, iobData, mealData, currentTemp, autosensData, microBolusAllowed, smbAlwaysAllowed, resultJson)
determineBasalResultSMB = DetermineBasalResultSMB(injector, resultJson)
} catch (e: JSONException) {
aapsLogger.error(LTag.APS, "Unhandled exception", e)
}
} else {
aapsLogger.error(LTag.APS, "Problem loading JS Functions")
}
} catch (e: IOException) {
aapsLogger.error(LTag.APS, "IOException")
} catch (e: RhinoException) {
aapsLogger.error(LTag.APS, "RhinoException: (" + e.lineNumber() + "," + e.columnNumber() + ") " + e.toString())
} catch (e: IllegalAccessException) {
aapsLogger.error(LTag.APS, e.toString())
} catch (e: InstantiationException) {
aapsLogger.error(LTag.APS, e.toString())
} catch (e: InvocationTargetException) {
aapsLogger.error(LTag.APS, e.toString())
} finally {
Context.exit()
}
glucoseStatusParam = mGlucoseStatus.toString()
iobDataParam = iobData.toString()
currentTempParam = currentTemp.toString()
profileParam = profile.toString()
mealDataParam = mealData.toString()
return determineBasalResultSMB
}
@Suppress("SpellCheckingInspection") fun setData(profile: Profile,
maxIob: Double,
maxBasal: Double,
minBg: Double,
maxBg: Double,
targetBg: Double,
basalRate: Double,
iobArray: Array<IobTotal>,
glucoseStatus: GlucoseStatus,
mealData: MealData,
autosensDataRatio: Double,
tempTargetSet: Boolean,
microBolusAllowed: Boolean,
uamAllowed: Boolean,
advancedFiltering: Boolean,
isSaveCgmSource: Boolean
) {
val pump = activePluginProvider.activePump
val pumpBolusStep = pump.pumpDescription.bolusStep
this.profile.put("max_iob", maxIob)
//mProfile.put("dia", profile.getDia());
this.profile.put("type", "current")
this.profile.put("max_daily_basal", profile.maxDailyBasal)
this.profile.put("max_basal", maxBasal)
this.profile.put("min_bg", minBg)
this.profile.put("max_bg", maxBg)
this.profile.put("target_bg", targetBg)
this.profile.put("carb_ratio", profile.ic)
this.profile.put("sens", profile.isfMgdl)
this.profile.put("max_daily_safety_multiplier", sp.getInt(R.string.key_openapsama_max_daily_safety_multiplier, 3))
this.profile.put("current_basal_safety_multiplier", sp.getDouble(R.string.key_openapsama_current_basal_safety_multiplier, 4.0))
//mProfile.put("high_temptarget_raises_sensitivity", SP.getBoolean(R.string.key_high_temptarget_raises_sensitivity, SMBDefaults.high_temptarget_raises_sensitivity));
this.profile.put("high_temptarget_raises_sensitivity", false)
//mProfile.put("low_temptarget_lowers_sensitivity", SP.getBoolean(R.string.key_low_temptarget_lowers_sensitivity, SMBDefaults.low_temptarget_lowers_sensitivity));
this.profile.put("low_temptarget_lowers_sensitivity", false)
this.profile.put("sensitivity_raises_target", sp.getBoolean(R.string.key_sensitivity_raises_target, SMBDefaults.sensitivity_raises_target))
this.profile.put("resistance_lowers_target", sp.getBoolean(R.string.key_resistance_lowers_target, SMBDefaults.resistance_lowers_target))
this.profile.put("adv_target_adjustments", SMBDefaults.adv_target_adjustments)
this.profile.put("exercise_mode", SMBDefaults.exercise_mode)
this.profile.put("half_basal_exercise_target", SMBDefaults.half_basal_exercise_target)
this.profile.put("maxCOB", SMBDefaults.maxCOB)
this.profile.put("skip_neutral_temps", pump.setNeutralTempAtFullHour())
// min_5m_carbimpact is not used within SMB determinebasal
//if (mealData.usedMinCarbsImpact > 0) {
// mProfile.put("min_5m_carbimpact", mealData.usedMinCarbsImpact);
//} else {
// mProfile.put("min_5m_carbimpact", SP.getDouble(R.string.key_openapsama_min_5m_carbimpact, SMBDefaults.min_5m_carbimpact));
//}
this.profile.put("remainingCarbsCap", SMBDefaults.remainingCarbsCap)
this.profile.put("enableUAM", uamAllowed)
this.profile.put("A52_risk_enable", SMBDefaults.A52_risk_enable)
val smbEnabled = sp.getBoolean(R.string.key_use_smb, false)
this.profile.put("SMBInterval", sp.getInt(R.string.key_smbinterval, SMBDefaults.SMBInterval))
this.profile.put("enableSMB_with_COB", smbEnabled && sp.getBoolean(R.string.key_enableSMB_with_COB, false))
this.profile.put("enableSMB_with_temptarget", smbEnabled && sp.getBoolean(R.string.key_enableSMB_with_temptarget, false))
this.profile.put("allowSMB_with_high_temptarget", smbEnabled && sp.getBoolean(R.string.key_allowSMB_with_high_temptarget, false))
this.profile.put("enableSMB_always", smbEnabled && sp.getBoolean(R.string.key_enableSMB_always, false) && advancedFiltering)
this.profile.put("enableSMB_after_carbs", smbEnabled && sp.getBoolean(R.string.key_enableSMB_after_carbs, false) && advancedFiltering)
this.profile.put("maxSMBBasalMinutes", sp.getInt(R.string.key_smbmaxminutes, SMBDefaults.maxSMBBasalMinutes))
this.profile.put("maxUAMSMBBasalMinutes", sp.getInt(R.string.key_uamsmbmaxminutes, SMBDefaults.maxUAMSMBBasalMinutes))
//set the min SMB amount to be the amount set by the pump.
this.profile.put("bolus_increment", pumpBolusStep)
this.profile.put("carbsReqThreshold", sp.getInt(R.string.key_carbsReqThreshold, SMBDefaults.carbsReqThreshold))
this.profile.put("current_basal", basalRate)
this.profile.put("temptargetSet", tempTargetSet)
this.profile.put("autosens_max", SafeParse.stringToDouble(sp.getString(R.string.key_openapsama_autosens_max, "1.2")))
if (profileFunction.getUnits() == Constants.MMOL) {
this.profile.put("out_units", "mmol/L")
}
val now = System.currentTimeMillis()
val tb = treatmentsPlugin.getTempBasalFromHistory(now)
currentTemp.put("temp", "absolute")
currentTemp.put("duration", tb?.plannedRemainingMinutes ?: 0)
currentTemp.put("rate", tb?.tempBasalConvertedToAbsolute(now, profile) ?: 0.0)
// as we have non default temps longer than 30 mintues
val tempBasal = treatmentsPlugin.getTempBasalFromHistory(System.currentTimeMillis())
if (tempBasal != null) {
currentTemp.put("minutesrunning", tempBasal.realDuration)
}
iobData = IobCobCalculatorPlugin.convertToJSONArray(iobArray)
mGlucoseStatus.put("glucose", glucoseStatus.glucose)
mGlucoseStatus.put("noise", glucoseStatus.noise)
if (sp.getBoolean(R.string.key_always_use_shortavg, false)) {
mGlucoseStatus.put("delta", glucoseStatus.shortAvgDelta)
} else {
mGlucoseStatus.put("delta", glucoseStatus.delta)
}
mGlucoseStatus.put("short_avgdelta", glucoseStatus.shortAvgDelta)
mGlucoseStatus.put("long_avgdelta", glucoseStatus.longAvgDelta)
mGlucoseStatus.put("date", glucoseStatus.date)
this.mealData.put("carbs", mealData.carbs)
this.mealData.put("boluses", mealData.boluses)
this.mealData.put("mealCOB", mealData.mealCOB)
this.mealData.put("slopeFromMaxDeviation", mealData.slopeFromMaxDeviation)
this.mealData.put("slopeFromMinDeviation", mealData.slopeFromMinDeviation)
this.mealData.put("lastBolusTime", mealData.lastBolusTime)
this.mealData.put("lastCarbTime", mealData.lastCarbTime)
if (constraintChecker.isAutosensModeEnabled().value()) {
autosensData.put("ratio", autosensDataRatio)
} else {
autosensData.put("ratio", 1.0)
}
this.microBolusAllowed = microBolusAllowed
smbAlwaysAllowed = advancedFiltering
currentTime = now
saveCgmSource = isSaveCgmSource
}
private fun makeParam(jsonObject: JSONObject?, rhino: Context, scope: Scriptable): Any {
return if (jsonObject == null) Undefined.instance
else NativeJSON.parse(rhino, scope, jsonObject.toString()) { _: Context?, _: Scriptable?, _: Scriptable?, objects: Array<Any?> -> objects[1] }
}
private fun makeParamArray(jsonArray: JSONArray?, rhino: Context, scope: Scriptable): Any {
return NativeJSON.parse(rhino, scope, jsonArray.toString()) { _: Context?, _: Scriptable?, _: Scriptable?, objects: Array<Any?> -> objects[1] }
}
@Throws(IOException::class) private fun readFile(filename: String): String {
val bytes = scriptReader.readFile(filename)
var string = String(bytes, StandardCharsets.UTF_8)
if (string.startsWith("#!/usr/bin/env node")) {
string = string.substring(20)
}
return string
}
init {
injector.androidInjector().inject(this)
}
}

View file

@ -17,10 +17,10 @@ import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.JSONFormatter
import info.nightscout.androidaps.utils.extensions.plusAssign
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign
import org.json.JSONArray
import org.json.JSONException
import javax.inject.Inject
@ -117,7 +117,7 @@ class OpenAPSSMBFragment : DaggerFragment() {
if (openAPSSMBPlugin.lastAPSRun != 0L) {
binding.lastrun.text = dateUtil.dateAndTimeString(openAPSSMBPlugin.lastAPSRun)
}
openAPSSMBPlugin.lastAutosensResult?.let {
openAPSSMBPlugin.lastAutosensResult.let {
binding.autosensdata.text = JSONFormatter.format(it.json())
}
}

View file

@ -1,323 +0,0 @@
package info.nightscout.androidaps.plugins.aps.openAPSSMB;
import android.content.Context;
import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.SwitchPreference;
import org.jetbrains.annotations.NotNull;
import org.json.JSONException;
import javax.inject.Inject;
import javax.inject.Singleton;
import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.data.IobTotal;
import info.nightscout.androidaps.data.MealData;
import info.nightscout.androidaps.data.Profile;
import info.nightscout.androidaps.db.TempTarget;
import info.nightscout.androidaps.interfaces.APSInterface;
import info.nightscout.androidaps.interfaces.ActivePluginProvider;
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.ProfileFunction;
import info.nightscout.androidaps.interfaces.PumpInterface;
import info.nightscout.androidaps.logging.AAPSLogger;
import info.nightscout.androidaps.logging.LTag;
import info.nightscout.androidaps.plugins.aps.events.EventOpenAPSUpdateGui;
import info.nightscout.androidaps.plugins.aps.events.EventOpenAPSUpdateResultGui;
import info.nightscout.androidaps.plugins.aps.loop.APSResult;
import info.nightscout.androidaps.plugins.aps.loop.ScriptReader;
import info.nightscout.androidaps.plugins.bus.RxBusWrapper;
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker;
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.iob.iobCobCalculator.data.AutosensData;
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;
import info.nightscout.androidaps.utils.resources.ResourceHelper;
import info.nightscout.androidaps.utils.sharedPreferences.SP;
@Singleton
public class OpenAPSSMBPlugin extends PluginBase implements APSInterface, ConstraintsInterface {
private final ConstraintChecker constraintChecker;
private final ResourceHelper resourceHelper;
private final ProfileFunction profileFunction;
private final Context context;
private final RxBusWrapper rxBus;
private final ActivePluginProvider activePlugin;
private final TreatmentsPlugin treatmentsPlugin;
private final IobCobCalculatorPlugin iobCobCalculatorPlugin;
private final HardLimits hardLimits;
private final Profiler profiler;
private final FabricPrivacy fabricPrivacy;
private final SP sp;
// last values
DetermineBasalAdapterSMBJS lastDetermineBasalAdapterSMBJS = null;
long lastAPSRun = 0;
DetermineBasalResultSMB lastAPSResult = null;
AutosensResult lastAutosensResult = null;
@Inject
public OpenAPSSMBPlugin(
HasAndroidInjector injector,
AAPSLogger aapsLogger,
RxBusWrapper rxBus,
ConstraintChecker constraintChecker,
ResourceHelper resourceHelper,
ProfileFunction profileFunction,
Context context,
ActivePluginProvider activePlugin,
TreatmentsPlugin treatmentsPlugin,
IobCobCalculatorPlugin iobCobCalculatorPlugin,
HardLimits hardLimits,
Profiler profiler,
FabricPrivacy fabricPrivacy,
SP sp
) {
super(new PluginDescription()
.mainType(PluginType.APS)
.fragmentClass(OpenAPSSMBFragment.class.getName())
.pluginIcon(R.drawable.ic_generic_icon)
.pluginName(R.string.openapssmb)
.shortName(R.string.smb_shortname)
.preferencesId(R.xml.pref_openapssmb)
.description(R.string.description_smb)
.setDefault(),
aapsLogger, resourceHelper, injector
);
this.constraintChecker = constraintChecker;
this.resourceHelper = resourceHelper;
this.profileFunction = profileFunction;
this.rxBus = rxBus;
this.context = context;
this.activePlugin = activePlugin;
this.treatmentsPlugin = treatmentsPlugin;
this.iobCobCalculatorPlugin = iobCobCalculatorPlugin;
this.hardLimits = hardLimits;
this.profiler = profiler;
this.fabricPrivacy = fabricPrivacy;
this.sp = sp;
}
@Override
public boolean specialEnableCondition() {
try {
PumpInterface pump = activePlugin.getActivePump();
return pump.getPumpDescription().isTempBasalCapable;
} catch (Exception ignored) {
// may fail during initialization
return true;
}
}
@Override
public boolean specialShowInListCondition() {
PumpInterface pump = activePlugin.getActivePump();
return pump.getPumpDescription().isTempBasalCapable;
}
@Override
public void preprocessPreferences(@NotNull PreferenceFragmentCompat preferenceFragment) {
super.preprocessPreferences(preferenceFragment);
boolean smbAlwaysEnabled = sp.getBoolean(R.string.key_enableSMB_always, false);
SwitchPreference withCOB = preferenceFragment.findPreference(resourceHelper.gs(R.string.key_enableSMB_with_COB));
if (withCOB != null) {
withCOB.setVisible(!smbAlwaysEnabled);
}
SwitchPreference withTempTarget = preferenceFragment.findPreference(resourceHelper.gs(R.string.key_enableSMB_with_temptarget));
if (withTempTarget != null) {
withTempTarget.setVisible(!smbAlwaysEnabled);
}
SwitchPreference afterCarbs = preferenceFragment.findPreference(resourceHelper.gs(R.string.key_enableSMB_after_carbs));
if (afterCarbs != null) {
afterCarbs.setVisible(!smbAlwaysEnabled);
}
}
@Override
public APSResult getLastAPSResult() {
return lastAPSResult;
}
@Override
public long getLastAPSRun() {
return lastAPSRun;
}
@Override
public void invoke(String initiator, boolean tempBasalFallback) {
getAapsLogger().debug(LTag.APS, "invoke from " + initiator + " tempBasalFallback: " + tempBasalFallback);
lastAPSResult = null;
DetermineBasalAdapterSMBJS determineBasalAdapterSMBJS;
determineBasalAdapterSMBJS = new DetermineBasalAdapterSMBJS(new ScriptReader(context), getInjector());
GlucoseStatus glucoseStatus = new GlucoseStatus(getInjector()).getGlucoseStatusData();
Profile profile = profileFunction.getProfile();
PumpInterface pump = activePlugin.getActivePump();
if (profile == null) {
rxBus.send(new EventOpenAPSUpdateResultGui(resourceHelper.gs(R.string.noprofileselected)));
getAapsLogger().debug(LTag.APS, resourceHelper.gs(R.string.noprofileselected));
return;
}
if (!isEnabled(PluginType.APS)) {
rxBus.send(new EventOpenAPSUpdateResultGui(resourceHelper.gs(R.string.openapsma_disabled)));
getAapsLogger().debug(LTag.APS, resourceHelper.gs(R.string.openapsma_disabled));
return;
}
if (glucoseStatus == null) {
rxBus.send(new EventOpenAPSUpdateResultGui(resourceHelper.gs(R.string.openapsma_noglucosedata)));
getAapsLogger().debug(LTag.APS, resourceHelper.gs(R.string.openapsma_noglucosedata));
return;
}
Constraint<Double> inputConstraints = new Constraint<>(0d); // fake. only for collecting all results
Constraint<Double> maxBasalConstraint = constraintChecker.getMaxBasalAllowed(profile);
inputConstraints.copyReasons(maxBasalConstraint);
double maxBasal = maxBasalConstraint.value();
double minBg = profile.getTargetLowMgdl();
double maxBg = profile.getTargetHighMgdl();
double targetBg = profile.getTargetMgdl();
minBg = Round.roundTo(minBg, 0.1d);
maxBg = Round.roundTo(maxBg, 0.1d);
long start = System.currentTimeMillis();
long startPart = System.currentTimeMillis();
MealData mealData = iobCobCalculatorPlugin.getMealData();
profiler.log(LTag.APS, "getMealData()", startPart);
Constraint<Double> maxIOBAllowedConstraint = constraintChecker.getMaxIOBAllowed();
inputConstraints.copyReasons(maxIOBAllowedConstraint);
double maxIob = maxIOBAllowedConstraint.value();
minBg = hardLimits.verifyHardLimits(minBg, "minBg", hardLimits.getVERY_HARD_LIMIT_MIN_BG()[0], hardLimits.getVERY_HARD_LIMIT_MIN_BG()[1]);
maxBg = hardLimits.verifyHardLimits(maxBg, "maxBg", hardLimits.getVERY_HARD_LIMIT_MAX_BG()[0], hardLimits.getVERY_HARD_LIMIT_MAX_BG()[1]);
targetBg = hardLimits.verifyHardLimits(targetBg, "targetBg", hardLimits.getVERY_HARD_LIMIT_TARGET_BG()[0], hardLimits.getVERY_HARD_LIMIT_TARGET_BG()[1]);
boolean isTempTarget = false;
TempTarget tempTarget = treatmentsPlugin.getTempTargetFromHistory(System.currentTimeMillis());
if (tempTarget != null) {
isTempTarget = true;
minBg = hardLimits.verifyHardLimits(tempTarget.low, "minBg", hardLimits.getVERY_HARD_LIMIT_TEMP_MIN_BG()[0], hardLimits.getVERY_HARD_LIMIT_TEMP_MIN_BG()[1]);
maxBg = hardLimits.verifyHardLimits(tempTarget.high, "maxBg", hardLimits.getVERY_HARD_LIMIT_TEMP_MAX_BG()[0], hardLimits.getVERY_HARD_LIMIT_TEMP_MAX_BG()[1]);
targetBg = hardLimits.verifyHardLimits(tempTarget.target(), "targetBg", hardLimits.getVERY_HARD_LIMIT_TEMP_TARGET_BG()[0], hardLimits.getVERY_HARD_LIMIT_TEMP_TARGET_BG()[1]);
}
if (!hardLimits.checkOnlyHardLimits(profile.getDia(), "dia", hardLimits.minDia(), hardLimits.maxDia()))
return;
if (!hardLimits.checkOnlyHardLimits(profile.getIcTimeFromMidnight(Profile.secondsFromMidnight()), "carbratio", hardLimits.minIC(), hardLimits.maxIC()))
return;
if (!hardLimits.checkOnlyHardLimits(profile.getIsfMgdl(), "sens", hardLimits.getMINISF(), hardLimits.getMAXISF()))
return;
if (!hardLimits.checkOnlyHardLimits(profile.getMaxDailyBasal(), "max_daily_basal", 0.02, hardLimits.maxBasal()))
return;
if (!hardLimits.checkOnlyHardLimits(pump.getBaseBasalRate(), "current_basal", 0.01, hardLimits.maxBasal()))
return;
startPart = System.currentTimeMillis();
if (constraintChecker.isAutosensModeEnabled().value()) {
AutosensData autosensData = iobCobCalculatorPlugin.getLastAutosensDataSynchronized("OpenAPSPlugin");
if (autosensData == null) {
rxBus.send(new EventOpenAPSUpdateResultGui(resourceHelper.gs(R.string.openaps_noasdata)));
return;
}
lastAutosensResult = autosensData.autosensResult;
} else {
lastAutosensResult = new AutosensResult();
lastAutosensResult.sensResult = "autosens disabled";
}
IobTotal[] iobArray = iobCobCalculatorPlugin.calculateIobArrayForSMB(lastAutosensResult, SMBDefaults.exercise_mode, SMBDefaults.half_basal_exercise_target, isTempTarget);
profiler.log(LTag.APS, "calculateIobArrayInDia()", startPart);
startPart = System.currentTimeMillis();
Constraint<Boolean> smbAllowed = new Constraint<>(!tempBasalFallback);
constraintChecker.isSMBModeEnabled(smbAllowed);
inputConstraints.copyReasons(smbAllowed);
Constraint<Boolean> advancedFiltering = new Constraint<>(!tempBasalFallback);
constraintChecker.isAdvancedFilteringEnabled(advancedFiltering);
inputConstraints.copyReasons(advancedFiltering);
Constraint<Boolean> uam = new Constraint<>(true);
constraintChecker.isUAMEnabled(uam);
inputConstraints.copyReasons(uam);
profiler.log(LTag.APS, "detectSensitivityandCarbAbsorption()", startPart);
profiler.log(LTag.APS, "SMB data gathering", start);
start = System.currentTimeMillis();
try {
determineBasalAdapterSMBJS.setData(profile, maxIob, maxBasal, minBg, maxBg, targetBg, activePlugin.getActivePump().getBaseBasalRate(), iobArray, glucoseStatus, mealData,
lastAutosensResult.ratio, //autosensDataRatio
isTempTarget,
smbAllowed.value(),
uam.value(),
advancedFiltering.value(),
activePlugin.getActiveBgSource().getClass().getSimpleName().equals("DexcomPlugin")
);
} catch (JSONException e) {
fabricPrivacy.logException(e);
return;
}
long now = System.currentTimeMillis();
DetermineBasalResultSMB determineBasalResultSMB = determineBasalAdapterSMBJS.invoke();
profiler.log(LTag.APS, "SMB calculation", start);
if (determineBasalResultSMB == null) {
getAapsLogger().error(LTag.APS, "SMB calculation returned null");
lastDetermineBasalAdapterSMBJS = null;
lastAPSResult = null;
lastAPSRun = 0;
} else {
// TODO still needed with oref1?
// Fix bug determine basal
if (determineBasalResultSMB.getRate() == 0d && determineBasalResultSMB.getDuration() == 0 && !treatmentsPlugin.isTempBasalInProgress())
determineBasalResultSMB.setTempBasalRequested(false);
determineBasalResultSMB.setIob(iobArray[0]);
try {
determineBasalResultSMB.getJson().put("timestamp", DateUtil.toISOString(now));
} catch (JSONException e) {
getAapsLogger().error(LTag.APS, "Unhandled exception", e);
}
determineBasalResultSMB.setInputConstraints(inputConstraints);
lastDetermineBasalAdapterSMBJS = determineBasalAdapterSMBJS;
lastAPSResult = determineBasalResultSMB;
lastAPSRun = now;
}
rxBus.send(new EventOpenAPSUpdateGui());
//deviceStatus.suggested = determineBasalResultAMA.json;
}
@NotNull
@Override
public Constraint<Boolean> isSuperBolusEnabled(Constraint<Boolean> value) {
value.set(getAapsLogger(), false);
return value;
}
}

View file

@ -0,0 +1,201 @@
package info.nightscout.androidaps.plugins.aps.openAPSSMB
import android.content.Context
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.SwitchPreference
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.Profile
import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.aps.events.EventOpenAPSUpdateGui
import info.nightscout.androidaps.plugins.aps.events.EventOpenAPSUpdateResultGui
import info.nightscout.androidaps.plugins.aps.loop.ScriptReader
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
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.utils.DateUtil
import info.nightscout.androidaps.utils.HardLimits
import info.nightscout.androidaps.utils.Profiler
import info.nightscout.androidaps.utils.Round
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
open class OpenAPSSMBPlugin @Inject constructor(
injector: HasAndroidInjector,
aapsLogger: AAPSLogger,
private val rxBus: RxBusWrapper,
private val constraintChecker: ConstraintChecker,
resourceHelper: ResourceHelper,
private val profileFunction: ProfileFunction,
private val context: Context,
private val activePlugin: ActivePluginProvider,
private val treatmentsPlugin: TreatmentsInterface,
private val iobCobCalculatorPlugin: IobCobCalculatorPlugin,
private val hardLimits: HardLimits,
private val profiler: Profiler,
private val sp: SP
) : PluginBase(PluginDescription()
.mainType(PluginType.APS)
.fragmentClass(OpenAPSSMBFragment::class.java.name)
.pluginIcon(R.drawable.ic_generic_icon)
.pluginName(R.string.openapssmb)
.shortName(R.string.smb_shortname)
.preferencesId(R.xml.pref_openapssmb)
.description(R.string.description_smb)
.setDefault(),
aapsLogger, resourceHelper, injector
), APSInterface, ConstraintsInterface {
// last values
override var lastAPSRun: Long = 0
override var lastAPSResult: DetermineBasalResultSMB? = null
var lastDetermineBasalAdapterSMBJS: DetermineBasalAdapterSMBJS? = null
var lastAutosensResult = AutosensResult()
override fun specialEnableCondition(): Boolean {
return try {
activePlugin.activePump.pumpDescription.isTempBasalCapable
} catch (ignored: Exception) {
// may fail during initialization
true
}
}
override fun specialShowInListCondition(): Boolean {
val pump = activePlugin.activePump
return pump.pumpDescription.isTempBasalCapable
}
override fun preprocessPreferences(preferenceFragment: PreferenceFragmentCompat) {
super.preprocessPreferences(preferenceFragment)
val smbAlwaysEnabled = sp.getBoolean(R.string.key_enableSMB_always, false)
preferenceFragment.findPreference<SwitchPreference>(resourceHelper.gs(R.string.key_enableSMB_with_COB))?.isVisible = !smbAlwaysEnabled
preferenceFragment.findPreference<SwitchPreference>(resourceHelper.gs(R.string.key_enableSMB_with_temptarget))?.isVisible = !smbAlwaysEnabled
preferenceFragment.findPreference<SwitchPreference>(resourceHelper.gs(R.string.key_enableSMB_after_carbs))?.isVisible = !smbAlwaysEnabled
}
override fun invoke(initiator: String, tempBasalFallback: Boolean) {
aapsLogger.debug(LTag.APS, "invoke from $initiator tempBasalFallback: $tempBasalFallback")
lastAPSResult = null
val glucoseStatus = GlucoseStatus(injector).glucoseStatusData
val profile = profileFunction.getProfile()
val pump = activePlugin.activePump
if (profile == null) {
rxBus.send(EventOpenAPSUpdateResultGui(resourceHelper.gs(R.string.noprofileselected)))
aapsLogger.debug(LTag.APS, resourceHelper.gs(R.string.noprofileselected))
return
}
if (!isEnabled(PluginType.APS)) {
rxBus.send(EventOpenAPSUpdateResultGui(resourceHelper.gs(R.string.openapsma_disabled)))
aapsLogger.debug(LTag.APS, resourceHelper.gs(R.string.openapsma_disabled))
return
}
if (glucoseStatus == null) {
rxBus.send(EventOpenAPSUpdateResultGui(resourceHelper.gs(R.string.openapsma_noglucosedata)))
aapsLogger.debug(LTag.APS, resourceHelper.gs(R.string.openapsma_noglucosedata))
return
}
val inputConstraints = Constraint(0.0) // fake. only for collecting all results
val maxBasal = constraintChecker.getMaxBasalAllowed(profile).also {
inputConstraints.copyReasons(it)
}.value()
var start = System.currentTimeMillis()
var startPart = System.currentTimeMillis()
profiler.log(LTag.APS, "getMealData()", startPart)
val maxIob = constraintChecker.getMaxIOBAllowed().also { maxIOBAllowedConstraint ->
inputConstraints.copyReasons(maxIOBAllowedConstraint)
}.value()
var minBg = hardLimits.verifyHardLimits(Round.roundTo(profile.targetLowMgdl, 0.1), "minBg", hardLimits.VERY_HARD_LIMIT_MIN_BG[0].toDouble(), hardLimits.VERY_HARD_LIMIT_MIN_BG[1].toDouble())
var maxBg = hardLimits.verifyHardLimits(Round.roundTo(profile.targetHighMgdl, 0.1), "maxBg", hardLimits.VERY_HARD_LIMIT_MAX_BG[0].toDouble(), hardLimits.VERY_HARD_LIMIT_MAX_BG[1].toDouble())
var targetBg = hardLimits.verifyHardLimits(profile.targetMgdl, "targetBg", hardLimits.VERY_HARD_LIMIT_TARGET_BG[0].toDouble(), hardLimits.VERY_HARD_LIMIT_TARGET_BG[1].toDouble())
var isTempTarget = false
treatmentsPlugin.getTempTargetFromHistory(System.currentTimeMillis())?.let { tempTarget ->
isTempTarget = true
minBg = hardLimits.verifyHardLimits(tempTarget.low, "minBg", hardLimits.VERY_HARD_LIMIT_TEMP_MIN_BG[0].toDouble(), hardLimits.VERY_HARD_LIMIT_TEMP_MIN_BG[1].toDouble())
maxBg = hardLimits.verifyHardLimits(tempTarget.high, "maxBg", hardLimits.VERY_HARD_LIMIT_TEMP_MAX_BG[0].toDouble(), hardLimits.VERY_HARD_LIMIT_TEMP_MAX_BG[1].toDouble())
targetBg = hardLimits.verifyHardLimits(tempTarget.target(), "targetBg", hardLimits.VERY_HARD_LIMIT_TEMP_TARGET_BG[0].toDouble(), hardLimits.VERY_HARD_LIMIT_TEMP_TARGET_BG[1].toDouble())
}
if (!hardLimits.checkOnlyHardLimits(profile.dia, "dia", hardLimits.minDia(), hardLimits.maxDia())) return
if (!hardLimits.checkOnlyHardLimits(profile.getIcTimeFromMidnight(Profile.secondsFromMidnight()), "carbratio", hardLimits.minIC(), hardLimits.maxIC())) return
if (!hardLimits.checkOnlyHardLimits(profile.isfMgdl, "sens", hardLimits.MINISF, hardLimits.MAXISF)) return
if (!hardLimits.checkOnlyHardLimits(profile.maxDailyBasal, "max_daily_basal", 0.02, hardLimits.maxBasal())) return
if (!hardLimits.checkOnlyHardLimits(pump.baseBasalRate, "current_basal", 0.01, hardLimits.maxBasal())) return
startPart = System.currentTimeMillis()
if (constraintChecker.isAutosensModeEnabled().value()) {
val autosensData = iobCobCalculatorPlugin.getLastAutosensDataSynchronized("OpenAPSPlugin")
if (autosensData == null) {
rxBus.send(EventOpenAPSUpdateResultGui(resourceHelper.gs(R.string.openaps_noasdata)))
return
}
lastAutosensResult = autosensData.autosensResult
} else {
lastAutosensResult.sensResult = "autosens disabled"
}
val iobArray = iobCobCalculatorPlugin.calculateIobArrayForSMB(lastAutosensResult, SMBDefaults.exercise_mode, SMBDefaults.half_basal_exercise_target, isTempTarget)
profiler.log(LTag.APS, "calculateIobArrayInDia()", startPart)
startPart = System.currentTimeMillis()
val smbAllowed = Constraint(!tempBasalFallback).also {
constraintChecker.isSMBModeEnabled(it)
inputConstraints.copyReasons(it)
}
val advancedFiltering = Constraint(!tempBasalFallback).also {
constraintChecker.isAdvancedFilteringEnabled(it)
inputConstraints.copyReasons(it)
}
val uam = Constraint(true).also {
constraintChecker.isUAMEnabled(it)
inputConstraints.copyReasons(it)
}
profiler.log(LTag.APS, "detectSensitivityAndCarbAbsorption()", startPart)
profiler.log(LTag.APS, "SMB data gathering", start)
start = System.currentTimeMillis()
DetermineBasalAdapterSMBJS(ScriptReader(context), injector).also { determineBasalAdapterSMBJS ->
determineBasalAdapterSMBJS.setData(profile, maxIob, maxBasal, minBg, maxBg, targetBg,
activePlugin.activePump.baseBasalRate,
iobArray,
glucoseStatus,
iobCobCalculatorPlugin.mealData,
lastAutosensResult.ratio,
isTempTarget,
smbAllowed.value(),
uam.value(),
advancedFiltering.value(),
activePlugin.activeBgSource.javaClass.simpleName == "DexcomPlugin")
val now = System.currentTimeMillis()
val determineBasalResultSMB = determineBasalAdapterSMBJS.invoke()
profiler.log(LTag.APS, "SMB calculation", start)
if (determineBasalResultSMB == null) {
aapsLogger.error(LTag.APS, "SMB calculation returned null")
lastDetermineBasalAdapterSMBJS = null
lastAPSResult = null
lastAPSRun = 0
} else {
// TODO still needed with oref1?
// Fix bug determine basal
if (determineBasalResultSMB.rate == 0.0 && determineBasalResultSMB.duration == 0 && !treatmentsPlugin.isTempBasalInProgress) determineBasalResultSMB.tempBasalRequested = false
determineBasalResultSMB.iob = iobArray[0]
determineBasalResultSMB.json?.put("timestamp", DateUtil.toISOString(now))
determineBasalResultSMB.inputConstraints = inputConstraints
lastDetermineBasalAdapterSMBJS = determineBasalAdapterSMBJS
lastAPSResult = determineBasalResultSMB
lastAPSRun = now
}
}
rxBus.send(EventOpenAPSUpdateGui())
}
override fun isSuperBolusEnabled(value: Constraint<Boolean>): Constraint<Boolean> {
value[aapsLogger] = false
return value
}
}

View file

@ -18,7 +18,7 @@ import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.configBuilder.events.EventConfigBuilderUpdateGui
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.extensions.plusAssign
import io.reactivex.rxkotlin.plusAssign
import info.nightscout.androidaps.utils.extensions.toVisibility
import info.nightscout.androidaps.utils.protection.ProtectionCheck
import info.nightscout.androidaps.utils.resources.ResourceHelper

View file

@ -9,9 +9,10 @@ import info.nightscout.androidaps.events.EventRebuildTabs
import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.configBuilder.events.EventConfigBuilderUpdateGui
import info.nightscout.androidaps.utils.alertDialogs.OKDialog.showConfirmation
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP
import java.util.*
@ -25,7 +26,8 @@ class ConfigBuilderPlugin @Inject constructor(
resourceHelper: ResourceHelper,
private val sp: SP,
private val rxBus: RxBusWrapper,
private val activePlugin: ActivePluginProvider
private val activePlugin: ActivePluginProvider,
private val uel: UserEntryLogger
) : PluginBase(PluginDescription()
.mainType(PluginType.GENERAL)
.fragmentClass(ConfigBuilderFragment::class.java.name)
@ -137,9 +139,10 @@ class ConfigBuilderPlugin @Inject constructor(
if (allowHardwarePump || activity == null) {
performPluginSwitch(changedPlugin, newState, type)
} else {
showConfirmation(activity, resourceHelper.gs(R.string.allow_hardware_pump_text), Runnable {
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.allow_hardware_pump_text), Runnable {
performPluginSwitch(changedPlugin, newState, type)
sp.putBoolean("allow_hardware_pump", true)
uel.log("HW PUMP ALLOWED")
aapsLogger.debug(LTag.PUMP, "First time HW pump allowed!")
}, Runnable {
rxBus.send(EventConfigBuilderUpdateGui())

View file

@ -22,6 +22,7 @@ import info.nightscout.androidaps.databinding.ObjectivesItemBinding
import info.nightscout.androidaps.dialogs.NtpProgressDialog
import info.nightscout.androidaps.events.EventNtpStatus
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.constraints.objectives.activities.ObjectivesExamDialog
import info.nightscout.androidaps.plugins.constraints.objectives.events.EventObjectivesUpdateGui
@ -33,7 +34,7 @@ import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.HtmlHelper
import info.nightscout.androidaps.utils.SntpClient
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.extensions.plusAssign
import io.reactivex.rxkotlin.plusAssign
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.androidaps.utils.sharedPreferences.SP
@ -52,6 +53,7 @@ class ObjectivesFragment : DaggerFragment() {
@Inject lateinit var receiverStatusStore: ReceiverStatusStore
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var sntpClient: SntpClient
@Inject lateinit var uel: UserEntryLogger
private val objectivesAdapter = ObjectivesAdapter()
private val handler = Handler(Looper.getMainLooper())
@ -194,19 +196,19 @@ class ObjectivesFragment : DaggerFragment() {
if (task.shouldBeIgnored()) continue
// name
val name = TextView(holder.binding.progress.context)
name.text = resourceHelper.gs(task.task) + ":"
name.text = "${resourceHelper.gs(task.task)}:"
name.setTextColor(-0x1)
holder.binding.progress.addView(name, LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT)
// hint
task.hints.forEach { h ->
if (!task.isCompleted)
holder.binding.progress.addView(h.generate(context))
if (!task.isCompleted())
context?.let { holder.binding.progress.addView(h.generate(it)) }
}
// state
val state = TextView(holder.binding.progress.context)
state.setTextColor(-0x1)
val basicHTML = "<font color=\"%1\$s\"><b>%2\$s</b></font>"
val formattedHTML = String.format(basicHTML, if (task.isCompleted) "#4CAF50" else "#FF9800", task.progress)
val formattedHTML = String.format(basicHTML, if (task.isCompleted()) "#4CAF50" else "#FF9800", task.progress)
state.text = HtmlHelper.fromHtml(formattedHTML)
state.gravity = Gravity.END
holder.binding.progress.addView(state, LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT)
@ -305,6 +307,7 @@ class ObjectivesFragment : DaggerFragment() {
holder.binding.unstart.setOnClickListener {
activity?.let { activity ->
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.objectives), resourceHelper.gs(R.string.doyouwantresetstart), Runnable {
uel.log("OBJECTIVE UNSTARTED", i1 = position + 1)
objective.startedOn = 0
scrollToCurrentObjective()
rxBus.send(EventObjectivesUpdateGui())
@ -329,7 +332,7 @@ class ObjectivesFragment : DaggerFragment() {
holder.binding.inputhint.visibility = View.VISIBLE
holder.binding.enterbutton.setOnClickListener {
val input = holder.binding.input.text.toString()
objective.specialAction(activity, input)
activity?.let { activity -> objective.specialAction(activity, input) }
rxBus.send(EventObjectivesUpdateGui())
}
} else {

View file

@ -9,6 +9,7 @@ import info.nightscout.androidaps.Config
import info.nightscout.androidaps.R
import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.constraints.objectives.objectives.*
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
@ -25,8 +26,8 @@ class ObjectivesPlugin @Inject constructor(
resourceHelper: ResourceHelper,
private val activePlugin: ActivePluginProvider,
private val sp: SP,
private val config: Config
config: Config,
private val uel: UserEntryLogger
) : PluginBase(PluginDescription()
.mainType(PluginType.CONSTRAINTS)
.fragmentClass(ObjectivesFragment::class.qualifiedName)
@ -141,6 +142,7 @@ class ObjectivesPlugin @Inject constructor(
sp.putLong("Objectives_" + "auto" + "_accomplished", DateUtil.now())
setupObjectives()
OKDialog.show(activity, resourceHelper.gs(R.string.objectives), resourceHelper.gs(R.string.codeaccepted))
uel.log("OBJECTIVES SKIPPED")
} else {
OKDialog.show(activity, resourceHelper.gs(R.string.objectives), resourceHelper.gs(R.string.codeinvalid))
}
@ -163,7 +165,7 @@ class ObjectivesPlugin @Inject constructor(
return value
}
fun isLgsAllowed(value: Constraint<Boolean>): Constraint<Boolean> {
override fun isLgsAllowed(value: Constraint<Boolean>): Constraint<Boolean> {
if (!objectives[MAXBASAL_OBJECTIVE].isStarted)
value.set(aapsLogger, false, String.format(resourceHelper.gs(R.string.objectivenotstarted), MAXBASAL_OBJECTIVE + 1), this)
return value

View file

@ -80,28 +80,30 @@ class ObjectivesExamDialog : DaggerDialogFragment() {
// Options
binding.examOptions.removeAllViews()
task.options.forEach {
val cb = it.generate(context)
if (task.answered) {
cb.isEnabled = false
if (it.isCorrect)
cb.isChecked = true
context?.let { context ->
val cb = it.generate(context)
if (task.answered) {
cb.isEnabled = false
if (it.isCorrect)
cb.isChecked = true
}
binding.examOptions.addView(cb)
}
binding.examOptions.addView(cb)
}
// Hints
binding.examHints.removeAllViews()
for (h in task.hints) {
binding.examHints.addView(h.generate(context))
context?.let { binding.examHints.addView(h.generate(it)) }
}
// Disabled to
binding.examDisabledto.text = resourceHelper.gs(R.string.answerdisabledto, dateUtil.timeString(task.disabledTo))
binding.examDisabledto.visibility = if (task.isEnabledAnswer) View.GONE else View.VISIBLE
binding.examDisabledto.visibility = if (task.isEnabledAnswer()) View.GONE else View.VISIBLE
// Buttons
binding.examVerify.isEnabled = !task.answered && task.isEnabledAnswer
binding.examVerify.isEnabled = !task.answered && task.isEnabledAnswer()
binding.examVerify.setOnClickListener {
var result = true
for (o in task.options) {
val option: Option = o as Option
val option: Option = o
result = result && option.evaluate()
}
task.answered = result
@ -133,14 +135,14 @@ class ObjectivesExamDialog : DaggerDialogFragment() {
binding.nextUnansweredButton.isEnabled = !objective.isCompleted
binding.nextUnansweredButton.setOnClickListener {
for (i in (currentTask + 1) until objective.tasks.size) {
if (!objective.tasks[i].isCompleted) {
if (!objective.tasks[i].isCompleted()) {
currentTask = i
updateGui()
return@setOnClickListener
}
}
for (i in 0..currentTask) {
if (!objective.tasks[i].isCompleted) {
if (!objective.tasks[i].isCompleted()) {
currentTask = i
updateGui()
return@setOnClickListener

View file

@ -1,295 +0,0 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives;
import android.content.Context;
import android.graphics.Color;
import android.text.util.Linkify;
import android.widget.CheckBox;
import android.widget.TextView;
import androidx.annotation.StringRes;
import androidx.fragment.app.FragmentActivity;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.utils.DateUtil;
import info.nightscout.androidaps.utils.T;
import info.nightscout.androidaps.utils.resources.ResourceHelper;
import info.nightscout.androidaps.utils.sharedPreferences.SP;
public abstract class Objective {
@Inject public SP sp;
@Inject public ResourceHelper resourceHelper;
private final String spName;
@StringRes private final int objective;
@StringRes private final int gate;
private long startedOn;
private long accomplishedOn;
List<Task> tasks = new ArrayList<>();
public boolean hasSpecialInput = false;
public Objective(HasAndroidInjector injector, String spName, @StringRes int objective, @StringRes int gate) {
injector.androidInjector().inject(this);
this.spName = spName;
this.objective = objective;
this.gate = gate;
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;
}
public boolean isCompleted() {
for (Task task : tasks) {
if (!task.shouldBeIgnored() && !task.isCompleted())
return false;
}
return true;
}
public boolean isCompleted(long trueTime) {
for (Task task : tasks) {
if (!task.shouldBeIgnored() && !task.isCompleted(trueTime))
return false;
}
return true;
}
public boolean isAccomplished() {
return accomplishedOn != 0 && accomplishedOn < DateUtil.now();
}
public boolean isStarted() {
return startedOn != 0;
}
public long getStartedOn() {
return startedOn;
}
public int getObjective() {
return objective;
}
public int getGate() {
return gate;
}
public void setStartedOn(long startedOn) {
this.startedOn = startedOn;
sp.putLong("Objectives_" + spName + "_started", startedOn);
}
public void setAccomplishedOn(long accomplishedOn) {
this.accomplishedOn = accomplishedOn;
sp.putLong("Objectives_" + spName + "_accomplished", accomplishedOn);
}
public long getAccomplishedOn() {
return accomplishedOn;
}
protected void setupTasks(List<Task> tasks) {
}
public List<Task> getTasks() {
return tasks;
}
public boolean specialActionEnabled() {
return true;
}
public void specialAction(FragmentActivity activity, String input) {
}
public abstract class Task {
@StringRes
private final int task;
private Objective objective;
ArrayList<Hint> hints = new ArrayList<>();
public Task(@StringRes int task) {
this.task = task;
}
public @StringRes int getTask() {
return task;
}
protected Objective getObjective() {
return objective;
}
public abstract boolean isCompleted();
public boolean isCompleted(long trueTime) {
return isCompleted();
}
public String getProgress() {
return resourceHelper.gs(isCompleted() ? R.string.completed_well_done : R.string.not_completed_yet);
}
Task hint(Hint hint) {
hints.add(hint);
return this;
}
public ArrayList<Hint> getHints() {
return hints;
}
public boolean shouldBeIgnored() {
return false;
}
}
public class MinimumDurationTask extends Task {
private final long minimumDuration;
MinimumDurationTask(long minimumDuration) {
super(R.string.time_elapsed);
this.minimumDuration = minimumDuration;
}
@Override
public boolean isCompleted() {
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())
+ " / " + getDurationText(minimumDuration);
}
private String getDurationText(long duration) {
int days = (int) Math.floor((double) duration / T.days(1).msecs());
int hours = (int) Math.floor((double) duration / T.hours(1).msecs());
int minutes = (int) Math.floor((double) duration / T.mins(1).msecs());
if (days > 0) return resourceHelper.gq(R.plurals.days, days, days);
else if (hours > 0) return resourceHelper.gq(R.plurals.hours, hours, hours);
else return resourceHelper.gq(R.plurals.minutes, minutes, minutes);
}
}
public class ExamTask extends Task {
@StringRes
int question;
ArrayList<Option> options = new ArrayList<>();
private final String spIdentifier;
private boolean answered;
private long disabledTo;
ExamTask(@StringRes int task, @StringRes int question, String spIdentifier) {
super(task);
this.question = question;
this.spIdentifier = spIdentifier;
answered = sp.getBoolean("ExamTask_" + spIdentifier, false);
disabledTo = sp.getLong("DisabledTo_" + spIdentifier, 0L);
}
public void setDisabledTo(long newState) {
disabledTo = newState;
sp.putLong("DisabledTo_" + spIdentifier, disabledTo);
}
public long getDisabledTo() {
return disabledTo;
}
public boolean isEnabledAnswer() {
return disabledTo < DateUtil.now();
}
public void setAnswered(boolean newState) {
answered = newState;
sp.putBoolean("ExamTask_" + spIdentifier, answered);
}
public boolean getAnswered() {
return answered;
}
ExamTask option(Option option) {
options.add(option);
return this;
}
public @StringRes int getQuestion() {
return question;
}
public List<Objective.Option> getOptions() {
return options;
}
@Override
public boolean isCompleted() {
return answered;
}
}
public class Option {
@StringRes int option;
boolean isCorrect;
CheckBox cb; // TODO: change it, this will block releasing memeory
Option(@StringRes int option, boolean isCorrect) {
this.option = option;
this.isCorrect = isCorrect;
}
public boolean isCorrect() {
return isCorrect;
}
public CheckBox generate(Context context) {
cb = new CheckBox(context);
cb.setText(option);
return cb;
}
public boolean evaluate() {
boolean selection = cb.isChecked();
if (selection && isCorrect) return true;
return !selection && !isCorrect;
}
}
public class Hint {
@StringRes int hint;
Hint(@StringRes int hint) {
this.hint = hint;
}
public TextView generate(Context context) {
TextView textView = new TextView(context);
textView.setText(hint);
textView.setAutoLinkMask(Linkify.WEB_URLS);
textView.setLinksClickable(true);
textView.setLinkTextColor(Color.YELLOW);
Linkify.addLinks(textView, Linkify.WEB_URLS);
return textView;
}
}
}

View file

@ -0,0 +1,183 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives
import android.content.Context
import android.graphics.Color
import android.text.util.Linkify
import android.widget.CheckBox
import android.widget.TextView
import androidx.annotation.StringRes
import androidx.fragment.app.FragmentActivity
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP
import java.util.*
import javax.inject.Inject
import kotlin.math.floor
abstract class Objective(injector: HasAndroidInjector, spName: String, @StringRes objective: Int, @StringRes gate: Int) {
@Inject lateinit var sp: SP
@Inject lateinit var resourceHelper: ResourceHelper
private val spName: String
@StringRes val objective: Int
@StringRes val gate: Int
var startedOn: Long = 0
set(value) {
field = value
sp.putLong("Objectives_" + spName + "_started", startedOn)
}
var accomplishedOn: Long = 0
set(value) {
field = value
sp.putLong("Objectives_" + spName + "_accomplished", value)
}
var tasks: MutableList<Task> = ArrayList()
var hasSpecialInput = false
val isCompleted: Boolean
get() {
for (task in tasks) {
if (!task.shouldBeIgnored() && !task.isCompleted()) return false
}
return true
}
init {
injector.androidInjector().inject(this)
this.spName = spName
this.objective = objective
this.gate = gate
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
}
}
fun isCompleted(trueTime: Long): Boolean {
for (task in tasks) {
if (!task.shouldBeIgnored() && !task.isCompleted(trueTime)) return false
}
return true
}
val isAccomplished: Boolean
get() = accomplishedOn != 0L && accomplishedOn < DateUtil.now()
val isStarted: Boolean
get() = startedOn != 0L
open fun specialActionEnabled(): Boolean {
return true
}
open fun specialAction(activity: FragmentActivity, input: String) {}
abstract inner class Task(var objective: Objective, @StringRes val task: Int) {
var hints = ArrayList<Hint>()
abstract fun isCompleted(): Boolean
open fun isCompleted(trueTime: Long): Boolean = isCompleted
open val progress: String
get() = resourceHelper.gs(if (isCompleted) R.string.completed_well_done else R.string.not_completed_yet)
fun hint(hint: Hint): Task {
hints.add(hint)
return this
}
open fun shouldBeIgnored(): Boolean = false
}
inner class MinimumDurationTask internal constructor(objective: Objective, private val minimumDuration: Long) : Task(objective, R.string.time_elapsed) {
override fun isCompleted(): Boolean =
objective.isStarted && System.currentTimeMillis() - objective.startedOn >= minimumDuration
override fun isCompleted(trueTime: Long): Boolean {
return objective.isStarted && trueTime - objective.startedOn >= minimumDuration
}
override val progress: String
get() = (getDurationText(System.currentTimeMillis() - objective.startedOn)
+ " / " + getDurationText(minimumDuration))
private fun getDurationText(duration: Long): String {
val days = floor(duration.toDouble() / T.days(1).msecs()).toInt()
val hours = floor(duration.toDouble() / T.hours(1).msecs()).toInt()
val minutes = floor(duration.toDouble() / T.mins(1).msecs()).toInt()
return when {
days > 0 -> resourceHelper.gq(R.plurals.days, days, days)
hours > 0 -> resourceHelper.gq(R.plurals.hours, hours, hours)
else -> resourceHelper.gq(R.plurals.minutes, minutes, minutes)
}
}
}
inner class ExamTask internal constructor(objective: Objective, @StringRes task: Int, @StringRes val question: Int, private val spIdentifier: String) : Task(objective, task) {
var options = ArrayList<Option>()
var answered: Boolean = false
set(value) {
field = value
sp.putBoolean("ExamTask_$spIdentifier", value)
}
var disabledTo: Long = 0
set(value) {
field = value
sp.putLong("DisabledTo_$spIdentifier", value)
}
init {
answered = sp.getBoolean("ExamTask_$spIdentifier", false)
disabledTo = sp.getLong("DisabledTo_$spIdentifier", 0L)
}
override fun isCompleted(): Boolean = answered
fun isEnabledAnswer(): Boolean = disabledTo < DateUtil.now()
fun option(option: Option): ExamTask {
options.add(option)
return this
}
}
inner class Option internal constructor(@StringRes var option: Int, var isCorrect: Boolean) {
var cb: CheckBox? = null // TODO: change it, this will block releasing memory
fun generate(context: Context): CheckBox {
cb = CheckBox(context)
cb?.setText(option)
return cb!!
}
fun evaluate(): Boolean {
val selection = cb!!.isChecked
return if (selection && isCorrect) true else !selection && !isCorrect
}
}
inner class Hint internal constructor(@StringRes var hint: Int) {
fun generate(context: Context): TextView {
val textView = TextView(context)
textView.setText(hint)
textView.autoLinkMask = Linkify.WEB_URLS
textView.linksClickable = true
textView.setLinkTextColor(Color.YELLOW)
Linkify.addLinks(textView, Linkify.WEB_URLS)
return textView
}
}
}

View file

@ -1,91 +0,0 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives;
import java.util.List;
import javax.inject.Inject;
import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.interfaces.APSInterface;
import info.nightscout.androidaps.interfaces.ActivePluginProvider;
import info.nightscout.androidaps.interfaces.PluginBase;
import info.nightscout.androidaps.interfaces.PluginType;
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin;
import info.nightscout.androidaps.plugins.general.nsclient.NSClientPlugin;
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin;
import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin;
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin;
import info.nightscout.androidaps.utils.DateUtil;
import info.nightscout.androidaps.utils.sharedPreferences.SP;
public class Objective0 extends Objective {
@Inject SP sp;
@Inject ActivePluginProvider activePlugin;
@Inject VirtualPumpPlugin virtualPumpPlugin;
@Inject TreatmentsPlugin treatmentsPlugin;
@Inject LoopPlugin loopPlugin;
@Inject NSClientPlugin nsClientPlugin;
@Inject IobCobCalculatorPlugin iobCobCalculatorPlugin;
public Objective0(HasAndroidInjector injector) {
super(injector, "config", R.string.objectives_0_objective, R.string.objectives_0_gate);
}
@Override
protected void setupTasks(List<Task> tasks) {
tasks.add(new Task(R.string.objectives_bgavailableinns) {
@Override
public boolean isCompleted() {
return sp.getBoolean(R.string.key_ObjectivesbgIsAvailableInNS, false);
}
});
tasks.add(new Task(R.string.nsclienthaswritepermission) {
@Override
public boolean isCompleted() {
return nsClientPlugin.hasWritePermission();
}
});
tasks.add(new Task(R.string.virtualpump_uploadstatus_title) {
@Override
public boolean isCompleted() {
return sp.getBoolean(R.string.key_virtualpump_uploadstatus, false);
}
@Override
public boolean shouldBeIgnored() {
return !virtualPumpPlugin.isEnabled(PluginType.PUMP);
}
});
tasks.add(new Task(R.string.objectives_pumpstatusavailableinns) {
@Override
public boolean isCompleted() {
return sp.getBoolean(R.string.key_ObjectivespumpStatusIsAvailableInNS, false);
}
});
tasks.add(new Task(R.string.hasbgdata) {
@Override
public boolean isCompleted() {
return iobCobCalculatorPlugin.lastBg() != null;
}
});
tasks.add(new Task(R.string.loopenabled) {
@Override
public boolean isCompleted() {
return loopPlugin.isEnabled(PluginType.LOOP);
}
});
tasks.add(new Task(R.string.apsselected) {
@Override
public boolean isCompleted() {
APSInterface usedAPS = activePlugin.getActiveAPS();
return ((PluginBase) usedAPS).isEnabled(PluginType.APS);
}
});
tasks.add(new Task(R.string.activate_profile) {
@Override
public boolean isCompleted() {
return treatmentsPlugin.getProfileSwitchFromHistory(DateUtil.now()) != null;
}
});
}
}

View file

@ -0,0 +1,72 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.PluginBase
import info.nightscout.androidaps.interfaces.PluginType
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin
import info.nightscout.androidaps.plugins.general.nsclient.NSClientPlugin
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
import info.nightscout.androidaps.utils.DateUtil
import javax.inject.Inject
class Objective0(injector: HasAndroidInjector) : Objective(injector, "config", R.string.objectives_0_objective, R.string.objectives_0_gate) {
@Inject lateinit var activePlugin: ActivePluginProvider
@Inject lateinit var virtualPumpPlugin: VirtualPumpPlugin
@Inject lateinit var treatmentsPlugin: TreatmentsPlugin
@Inject lateinit var loopPlugin: LoopPlugin
@Inject lateinit var nsClientPlugin: NSClientPlugin
@Inject lateinit var iobCobCalculatorPlugin: IobCobCalculatorPlugin
init {
tasks.add(object : Task(this, R.string.objectives_bgavailableinns) {
override fun isCompleted(): Boolean {
return sp.getBoolean(R.string.key_ObjectivesbgIsAvailableInNS, false)
}
})
tasks.add(object : Task(this, R.string.nsclienthaswritepermission) {
override fun isCompleted(): Boolean {
return nsClientPlugin.hasWritePermission()
}
})
tasks.add(object : Task(this, R.string.virtualpump_uploadstatus_title) {
override fun isCompleted(): Boolean {
return sp.getBoolean(R.string.key_virtualpump_uploadstatus, false)
}
override fun shouldBeIgnored(): Boolean {
return !virtualPumpPlugin.isEnabled(PluginType.PUMP)
}
})
tasks.add(object : Task(this, R.string.objectives_pumpstatusavailableinns) {
override fun isCompleted(): Boolean {
return sp.getBoolean(R.string.key_ObjectivespumpStatusIsAvailableInNS, false)
}
})
tasks.add(object : Task(this, R.string.hasbgdata) {
override fun isCompleted(): Boolean {
return iobCobCalculatorPlugin.lastBg() != null
}
})
tasks.add(object : Task(this, R.string.loopenabled) {
override fun isCompleted(): Boolean {
return loopPlugin.isEnabled(PluginType.LOOP)
}
})
tasks.add(object : Task(this, R.string.apsselected) {
override fun isCompleted(): Boolean {
val usedAPS = activePlugin.activeAPS
return (usedAPS as PluginBase).isEnabled(PluginType.APS)
}
})
tasks.add(object : Task(this, R.string.activate_profile) {
override fun isCompleted(): Boolean {
return treatmentsPlugin.getProfileSwitchFromHistory(DateUtil.now()) != null
}
})
}
}

View file

@ -1,67 +0,0 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives;
import java.util.List;
import javax.inject.Inject;
import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.interfaces.PluginType;
import info.nightscout.androidaps.plugins.general.actions.ActionsPlugin;
import info.nightscout.androidaps.utils.sharedPreferences.SP;
public class Objective1 extends Objective {
@Inject SP sp;
@Inject ActionsPlugin actionsPlugin;
@Inject
public Objective1(HasAndroidInjector injector) {
super(injector, "usage", R.string.objectives_usage_objective, R.string.objectives_usage_gate);
}
@Override
protected void setupTasks(List<Task> tasks) {
tasks.add(new Task(R.string.objectives_useprofileswitch) {
@Override
public boolean isCompleted() {
return sp.getBoolean(R.string.key_objectiveuseprofileswitch, false);
}
});
tasks.add(new Task(R.string.objectives_usedisconnectpump) {
@Override
public boolean isCompleted() {
return sp.getBoolean(R.string.key_objectiveusedisconnect, false);
}
}.hint(new Hint(R.string.disconnectpump_hint)));
tasks.add(new Task(R.string.objectives_usereconnectpump) {
@Override
public boolean isCompleted() {
return sp.getBoolean(R.string.key_objectiveusereconnect, false);
}
}.hint(new Hint(R.string.disconnectpump_hint)));
tasks.add(new Task(R.string.objectives_usetemptarget) {
@Override
public boolean isCompleted() {
return sp.getBoolean(R.string.key_objectiveusetemptarget, false);
}
}.hint(new Hint(R.string.usetemptarget_hint)));
tasks.add(new Task(R.string.objectives_useactions) {
@Override
public boolean isCompleted() {
return sp.getBoolean(R.string.key_objectiveuseactions, false) && actionsPlugin.isEnabled(PluginType.GENERAL) && actionsPlugin.isFragmentVisible();
}
}.hint(new Hint(R.string.useaction_hint)));
tasks.add(new Task(R.string.objectives_useloop) {
@Override
public boolean isCompleted() {
return sp.getBoolean(R.string.key_objectiveuseloop, false);
}
}.hint(new Hint(R.string.useaction_hint)));
tasks.add(new Task(R.string.objectives_usescale) {
@Override
public boolean isCompleted() {
return sp.getBoolean(R.string.key_objectiveusescale, false);
}
}.hint(new Hint(R.string.usescale_hint)));
}
}

View file

@ -0,0 +1,50 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.interfaces.PluginType
import info.nightscout.androidaps.plugins.general.actions.ActionsPlugin
import javax.inject.Inject
class Objective1 @Inject constructor(injector: HasAndroidInjector) : Objective(injector, "usage", R.string.objectives_usage_objective, R.string.objectives_usage_gate) {
@Inject lateinit var actionsPlugin: ActionsPlugin
init {
tasks.add(object : Task(this, R.string.objectives_useprofileswitch) {
override fun isCompleted(): Boolean {
return sp.getBoolean(R.string.key_objectiveuseprofileswitch, false)
}
})
tasks.add(object : Task(this, R.string.objectives_usedisconnectpump) {
override fun isCompleted(): Boolean {
return sp.getBoolean(R.string.key_objectiveusedisconnect, false)
}
}.hint(Hint(R.string.disconnectpump_hint)))
tasks.add(object : Task(this, R.string.objectives_usereconnectpump) {
override fun isCompleted(): Boolean {
return sp.getBoolean(R.string.key_objectiveusereconnect, false)
}
}.hint(Hint(R.string.disconnectpump_hint)))
tasks.add(object : Task(this, R.string.objectives_usetemptarget) {
override fun isCompleted(): Boolean {
return sp.getBoolean(R.string.key_objectiveusetemptarget, false)
}
}.hint(Hint(R.string.usetemptarget_hint)))
tasks.add(object : Task(this, R.string.objectives_useactions) {
override fun isCompleted(): Boolean {
return sp.getBoolean(R.string.key_objectiveuseactions, false) && actionsPlugin.isEnabled(PluginType.GENERAL) && actionsPlugin.isFragmentVisible()
}
}.hint(Hint(R.string.useaction_hint)))
tasks.add(object : Task(this, R.string.objectives_useloop) {
override fun isCompleted(): Boolean {
return sp.getBoolean(R.string.key_objectiveuseloop, false)
}
}.hint(Hint(R.string.useaction_hint)))
tasks.add(object : Task(this, R.string.objectives_usescale) {
override fun isCompleted(): Boolean {
return sp.getBoolean(R.string.key_objectiveusescale, false)
}
}.hint(Hint(R.string.usescale_hint)))
}
}

View file

@ -1,19 +0,0 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives;
import java.util.List;
import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.utils.T;
public class Objective10 extends Objective {
public Objective10(HasAndroidInjector injector) {
super(injector, "auto", R.string.objectives_auto_objective, R.string.objectives_auto_gate);
}
@Override
protected void setupTasks(List<Task> tasks) {
tasks.add(new MinimumDurationTask(T.days(28).msecs()));
}
}

View file

@ -0,0 +1,12 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.utils.T
class Objective10(injector: HasAndroidInjector) : Objective(injector, "auto", R.string.objectives_auto_objective, R.string.objectives_auto_gate) {
init {
tasks.add(MinimumDurationTask(this, T.days(28).msecs()))
}
}

View file

@ -1,222 +0,0 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives;
import java.util.Collections;
import java.util.List;
import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
public class Objective2 extends Objective {
public Objective2(HasAndroidInjector injector) {
super(injector, "exam", R.string.objectives_exam_objective, R.string.objectives_exam_gate);
for (Task task : tasks) {
if (!task.isCompleted()) setAccomplishedOn(0);
}
}
@Override
protected void setupTasks(List<Task> tasks) {
tasks.add(new ExamTask(R.string.prerequisites_label, R.string.prerequisites_what, "prerequisites")
.option(new Option(R.string.prerequisites_nightscout, true))
.option(new Option(R.string.prerequisites_computer, true))
.option(new Option(R.string.prerequisites_pump, true))
.option(new Option(R.string.prerequisites_beanandroiddeveloper, false))
.hint(new Hint(R.string.prerequisites_hint1))
);
tasks.add(new ExamTask(R.string.prerequisites2_label, R.string.prerequisites2_what, "prerequisites2")
.option(new Option(R.string.prerequisites2_profile, true))
.option(new Option(R.string.prerequisites2_device, true))
.option(new Option(R.string.prerequisites2_internet, false))
.option(new Option(R.string.prerequisites2_supportedcgm, true))
.hint(new Hint(R.string.prerequisites2_hint1))
);
tasks.add(new ExamTask(R.string.basaltest_label, R.string.basaltest_when,"basaltest")
.option(new Option(R.string.basaltest_fixed, false))
.option(new Option(R.string.basaltest_havingregularhighlow, true))
.option(new Option(R.string.basaltest_weekly, false))
.option(new Option(R.string.basaltest_beforeloop, true))
.hint(new Hint(R.string.basaltest_hint1))
);
tasks.add(new ExamTask(R.string.dia_label_exam, R.string.dia_whatmeansdia,"dia")
.option(new Option(R.string.dia_profile, true))
.option(new Option(R.string.dia_minimumis5h, true))
.option(new Option(R.string.dia_meaningisequaltodiapump, false))
.option(new Option(R.string.dia_valuemustbedetermined, true))
.hint(new Hint(R.string.dia_hint1))
);
tasks.add(new ExamTask(R.string.isf_label_exam, R.string.blank,"isf")
.option(new Option(R.string.isf_decreasingvalue, true))
.option(new Option(R.string.isf_preferences, false))
.option(new Option(R.string.isf_increasingvalue, false))
.option(new Option(R.string.isf_noeffect, false))
.hint(new Hint(R.string.isf_hint1))
.hint(new Hint(R.string.isf_hint2))
);
tasks.add(new ExamTask(R.string.ic_label_exam, R.string.blank,"ic")
.option(new Option(R.string.ic_increasingvalue, true))
.option(new Option(R.string.ic_decreasingvalue, false))
.option(new Option(R.string.ic_multiple, true))
.option(new Option(R.string.ic_isf, false))
.hint(new Hint(R.string.ic_hint1))
);
tasks.add(new ExamTask(R.string.hypott_label, R.string.hypott_whenhypott,"hypott")
.option(new Option(R.string.hypott_preventoversmb, true))
.option(new Option(R.string.hypott_exercise, false))
.option(new Option(R.string.hypott_wrongbasal, false))
.option(new Option(R.string.hypott_0basal, false))
.hint(new Hint(R.string.hypott_hint1))
);
tasks.add(new ExamTask(R.string.profileswitch_label, R.string.profileswitch_pctwillchange,"profileswitch")
.option(new Option(R.string.profileswitch_basallower, true))
.option(new Option(R.string.profileswitch_isfhigher, true))
.option(new Option(R.string.profileswitch_iclower, false))
.option(new Option(R.string.profileswitch_unchanged, false))
.hint(new Hint(R.string.profileswitch_hint1))
);
tasks.add(new ExamTask(R.string.profileswitch2_label, R.string.profileswitch2_pctwillchange,"profileswitch2")
.option(new Option(R.string.profileswitch2_bghigher, false))
.option(new Option(R.string.profileswitch2_basalhigher, true))
.option(new Option(R.string.profileswitch2_bgunchanged, true))
.option(new Option(R.string.profileswitch2_isfhigher, false))
.hint(new Hint(R.string.profileswitch_hint1))
);
tasks.add(new ExamTask(R.string.profileswitchtime_label, R.string.profileswitchtime_iwant,"profileswitchtime")
.option(new Option(R.string.profileswitchtime_2, false))
.option(new Option(R.string.profileswitchtime__2, true))
.option(new Option(R.string.profileswitchtime_tt, false))
.option(new Option(R.string.profileswitchtime_100, false))
.hint(new Hint(R.string.profileswitchtime_hint1))
);
tasks.add(new ExamTask(R.string.profileswitch4_label, R.string.blank,"profileswitch4")
.option(new Option(R.string.profileswitch4_rates, true))
.option(new Option(R.string.profileswitch4_internet, true))
.option(new Option(R.string.profileswitch4_sufficient, false))
.option(new Option(R.string.profileswitch4_multi, true))
.hint(new Hint(R.string.profileswitch_hint1))
);
tasks.add(new ExamTask(R.string.exerciseprofile_label, R.string.exerciseprofile_whattodo,"exercise")
.option(new Option(R.string.exerciseprofile_switchprofileabove100, false))
.option(new Option(R.string.exerciseprofile_switchprofilebelow100, true))
.option(new Option(R.string.exerciseprofile_suspendloop, false))
.option(new Option(R.string.exerciseprofile_leaveat100, false))
.hint(new Hint(R.string.exerciseprofile_hint1))
);
tasks.add(new ExamTask(R.string.exercise_label, R.string.exercise_whattodo,"exercise2")
.option(new Option(R.string.exercise_settt, true))
.option(new Option(R.string.exercise_setfinished, false))
.option(new Option(R.string.exercise_setunchanged, false))
.option(new Option(R.string.exercise_15g, false))
.hint(new Hint(R.string.exercise_hint1))
);
tasks.add(new ExamTask(R.string.noisycgm_label, R.string.noisycgm_whattodo,"noisycgm")
.option(new Option(R.string.noisycgm_nothing, false))
.option(new Option(R.string.noisycgm_pause, true))
.option(new Option(R.string.noisycgm_replacesensor, true))
.option(new Option(R.string.noisycgm_checksmoothing, true))
.hint(new Hint(R.string.noisycgm_hint1))
);
tasks.add(new ExamTask(R.string.pumpdisconnect_label, R.string.blank,"pumpdisconnect")
.option(new Option(R.string.pumpdisconnect_unnecessary, false))
.option(new Option(R.string.pumpdisconnect_missinginsulin, true))
.option(new Option(R.string.pumpdisconnect_notstop, false))
.option(new Option(R.string.pumpdisconnect_openloop, false))
.hint(new Hint(R.string.pumpdisconnect_hint1))
);
tasks.add(new ExamTask(R.string.insulin_label, R.string.insulin_ultrarapid,"insulin")
.option(new Option(R.string.insulin_novorapid, false))
.option(new Option(R.string.insulin_humalog, false))
.option(new Option(R.string.insulin_actrapid, false))
.option(new Option(R.string.insulin_fiasp, true))
.hint(new Hint(R.string.insulin_hint1))
);
tasks.add(new ExamTask(R.string.sensitivity_label, R.string.blank,"sensitivity")
.option(new Option(R.string.sensitivity_adjust, true))
.option(new Option(R.string.sensitivity_edit, false))
.option(new Option(R.string.sensitivity_cannula, true))
.option(new Option(R.string.sensitivity_time, true))
.hint(new Hint(R.string.sensitivity_hint1))
);
tasks.add(new ExamTask(R.string.objectives_label, R.string.objectives_howtosave,"objectives")
.option(new Option(R.string.objectives_notesettings, false))
.option(new Option(R.string.objectives_afterobjective, true))
.option(new Option(R.string.objectives_afterchange, true))
.option(new Option(R.string.objectives_afterinitialsetup, true))
.hint(new Hint(R.string.objectives_hint1))
.hint(new Hint(R.string.objectives_hint2))
);
tasks.add(new ExamTask(R.string.objectives2_label, R.string.objectives_howtosave,"objectives2")
.option(new Option(R.string.objectives2_maintenance, true))
.option(new Option(R.string.objectives2_internalstorage, true))
.option(new Option(R.string.objectives2_cloud, true))
.option(new Option(R.string.objectives2_easyrestore, false))
.hint(new Hint(R.string.objectives_hint1))
.hint(new Hint(R.string.objectives_hint2))
);
tasks.add(new ExamTask(R.string.update_label, R.string.blank,"update")
.option(new Option(R.string.update_git, true))
.option(new Option(R.string.update_askfriend, false))
.option(new Option(R.string.update_keys, true))
.option(new Option(R.string.update_asap, true))
.hint(new Hint(R.string.update_hint1))
);
tasks.add(new ExamTask(R.string.troubleshooting_label, R.string.troubleshooting_wheretoask,"troubleshooting")
.option(new Option(R.string.troubleshooting_fb, true))
.option(new Option(R.string.troubleshooting_wiki, true))
.option(new Option(R.string.troubleshooting_gitter, true))
.option(new Option(R.string.troubleshooting_yourendo, false))
.hint(new Hint(R.string.troubleshooting_hint1))
.hint(new Hint(R.string.troubleshooting_hint2))
.hint(new Hint(R.string.troubleshooting_hint3))
);
tasks.add(new ExamTask(R.string.wrongcarbs_label, R.string.wrongcarbs_whattodo,"wrongcarbs")
.option(new Option(R.string.wrongcarbs_addinsulin, false))
.option(new Option(R.string.wrongcarbs_treatmentstab, true))
.option(new Option(R.string.wrongcarbs_donothing, false))
.option(new Option(R.string.wrongcarbs_bolus, false))
);
tasks.add(new ExamTask(R.string.wronginsulin_label, R.string.wronginsulin_whattodo,"wronginsulin")
.option(new Option(R.string.wronginsulin_careportal, false))
.option(new Option(R.string.wronginsulin_compare, true))
.option(new Option(R.string.wronginsulin_prime, true))
.option(new Option(R.string.wrongcarbs_donothing, false))
);
tasks.add(new ExamTask(R.string.iob_label, R.string.blank,"iob")
.option(new Option(R.string.iob_value, true))
.option(new Option(R.string.iob_hightemp, false))
.option(new Option(R.string.iob_negiob, true))
.option(new Option(R.string.iob_posiob, true))
);
tasks.add(new ExamTask(R.string.breadgrams_label, R.string.blank,"breadgrams")
.option(new Option(R.string.breadgrams_grams, true))
.option(new Option(R.string.breadgrams_exchange, false))
.option(new Option(R.string.breadgrams_decay, true))
.option(new Option(R.string.breadgrams_calc, true))
.hint(new Hint(R.string.breadgrams_hint1))
);
tasks.add(new ExamTask(R.string.extendedcarbs_label, R.string.extendedcarbs_handling,"extendedcarbs")
.option(new Option(R.string.extendedcarbs_future, true))
.option(new Option(R.string.extendedcarbs_free, false))
.option(new Option(R.string.extendedcarbs_fat, true))
.option(new Option(R.string.extendedcarbs_rescue, false))
.hint(new Hint(R.string.extendedcarbs_hint1))
);
tasks.add(new ExamTask(R.string.nsclient_label, R.string.nsclient_howcanyou,"nsclient")
.option(new Option(R.string.nsclient_nightscout, true))
.option(new Option(R.string.nsclient_dexcomfollow, true))
.option(new Option(R.string.nsclient_data, true))
.option(new Option(R.string.nsclient_fullcontrol, false))
.hint(new Hint(R.string.nsclient_hint1))
);
tasks.add(new ExamTask(R.string.other_medication_label, R.string.other_medication_text,"otherMedicationWarning")
.option(new Option(R.string.yes, true))
.option(new Option(R.string.no, false))
);
for (Task task : tasks)
Collections.shuffle(((ExamTask)task).options);
}
}

View file

@ -0,0 +1,211 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
@Suppress("SpellCheckingInspection")
class Objective2(injector: HasAndroidInjector) : Objective(injector, "exam", R.string.objectives_exam_objective, R.string.objectives_exam_gate) {
init {
tasks.add(ExamTask(this, R.string.prerequisites_label, R.string.prerequisites_what, "prerequisites")
.option(Option(R.string.prerequisites_nightscout, true))
.option(Option(R.string.prerequisites_computer, true))
.option(Option(R.string.prerequisites_pump, true))
.option(Option(R.string.prerequisites_beanandroiddeveloper, false))
.hint(Hint(R.string.prerequisites_hint1))
)
tasks.add(ExamTask(this, R.string.prerequisites2_label, R.string.prerequisites2_what, "prerequisites2")
.option(Option(R.string.prerequisites2_profile, true))
.option(Option(R.string.prerequisites2_device, true))
.option(Option(R.string.prerequisites2_internet, false))
.option(Option(R.string.prerequisites2_supportedcgm, true))
.hint(Hint(R.string.prerequisites2_hint1))
)
tasks.add(ExamTask(this, R.string.basaltest_label, R.string.basaltest_when, "basaltest")
.option(Option(R.string.basaltest_fixed, false))
.option(Option(R.string.basaltest_havingregularhighlow, true))
.option(Option(R.string.basaltest_weekly, false))
.option(Option(R.string.basaltest_beforeloop, true))
.hint(Hint(R.string.basaltest_hint1))
)
tasks.add(ExamTask(this, R.string.dia_label_exam, R.string.dia_whatmeansdia, "dia")
.option(Option(R.string.dia_profile, true))
.option(Option(R.string.dia_minimumis5h, true))
.option(Option(R.string.dia_meaningisequaltodiapump, false))
.option(Option(R.string.dia_valuemustbedetermined, true))
.hint(Hint(R.string.dia_hint1))
)
tasks.add(ExamTask(this, R.string.isf_label_exam, R.string.blank, "isf")
.option(Option(R.string.isf_decreasingvalue, true))
.option(Option(R.string.isf_preferences, false))
.option(Option(R.string.isf_increasingvalue, false))
.option(Option(R.string.isf_noeffect, false))
.hint(Hint(R.string.isf_hint1))
.hint(Hint(R.string.isf_hint2))
)
tasks.add(ExamTask(this, R.string.ic_label_exam, R.string.blank, "ic")
.option(Option(R.string.ic_increasingvalue, true))
.option(Option(R.string.ic_decreasingvalue, false))
.option(Option(R.string.ic_multiple, true))
.option(Option(R.string.ic_isf, false))
.hint(Hint(R.string.ic_hint1))
)
tasks.add(ExamTask(this, R.string.hypott_label, R.string.hypott_whenhypott, "hypott")
.option(Option(R.string.hypott_preventoversmb, true))
.option(Option(R.string.hypott_exercise, false))
.option(Option(R.string.hypott_wrongbasal, false))
.option(Option(R.string.hypott_0basal, false))
.hint(Hint(R.string.hypott_hint1))
)
tasks.add(ExamTask(this, R.string.profileswitch_label, R.string.profileswitch_pctwillchange, "profileswitch")
.option(Option(R.string.profileswitch_basallower, true))
.option(Option(R.string.profileswitch_isfhigher, true))
.option(Option(R.string.profileswitch_iclower, false))
.option(Option(R.string.profileswitch_unchanged, false))
.hint(Hint(R.string.profileswitch_hint1))
)
tasks.add(ExamTask(this, R.string.profileswitch2_label, R.string.profileswitch2_pctwillchange, "profileswitch2")
.option(Option(R.string.profileswitch2_bghigher, false))
.option(Option(R.string.profileswitch2_basalhigher, true))
.option(Option(R.string.profileswitch2_bgunchanged, true))
.option(Option(R.string.profileswitch2_isfhigher, false))
.hint(Hint(R.string.profileswitch_hint1))
)
tasks.add(ExamTask(this, R.string.profileswitchtime_label, R.string.profileswitchtime_iwant, "profileswitchtime")
.option(Option(R.string.profileswitchtime_2, false))
.option(Option(R.string.profileswitchtime__2, true))
.option(Option(R.string.profileswitchtime_tt, false))
.option(Option(R.string.profileswitchtime_100, false))
.hint(Hint(R.string.profileswitchtime_hint1))
)
tasks.add(ExamTask(this, R.string.profileswitch4_label, R.string.blank, "profileswitch4")
.option(Option(R.string.profileswitch4_rates, true))
.option(Option(R.string.profileswitch4_internet, true))
.option(Option(R.string.profileswitch4_sufficient, false))
.option(Option(R.string.profileswitch4_multi, true))
.hint(Hint(R.string.profileswitch_hint1))
)
tasks.add(ExamTask(this, R.string.exerciseprofile_label, R.string.exerciseprofile_whattodo, "exercise")
.option(Option(R.string.exerciseprofile_switchprofileabove100, false))
.option(Option(R.string.exerciseprofile_switchprofilebelow100, true))
.option(Option(R.string.exerciseprofile_suspendloop, false))
.option(Option(R.string.exerciseprofile_leaveat100, false))
.hint(Hint(R.string.exerciseprofile_hint1))
)
tasks.add(ExamTask(this, R.string.exercise_label, R.string.exercise_whattodo, "exercise2")
.option(Option(R.string.exercise_settt, true))
.option(Option(R.string.exercise_setfinished, false))
.option(Option(R.string.exercise_setunchanged, false))
.option(Option(R.string.exercise_15g, false))
.hint(Hint(R.string.exercise_hint1))
)
tasks.add(ExamTask(this, R.string.noisycgm_label, R.string.noisycgm_whattodo, "noisycgm")
.option(Option(R.string.noisycgm_nothing, false))
.option(Option(R.string.noisycgm_pause, true))
.option(Option(R.string.noisycgm_replacesensor, true))
.option(Option(R.string.noisycgm_checksmoothing, true))
.hint(Hint(R.string.noisycgm_hint1))
)
tasks.add(ExamTask(this, R.string.pumpdisconnect_label, R.string.blank, "pumpdisconnect")
.option(Option(R.string.pumpdisconnect_unnecessary, false))
.option(Option(R.string.pumpdisconnect_missinginsulin, true))
.option(Option(R.string.pumpdisconnect_notstop, false))
.option(Option(R.string.pumpdisconnect_openloop, false))
.hint(Hint(R.string.pumpdisconnect_hint1))
)
tasks.add(ExamTask(this, R.string.insulin_label, R.string.insulin_ultrarapid, "insulin")
.option(Option(R.string.insulin_novorapid, false))
.option(Option(R.string.insulin_humalog, false))
.option(Option(R.string.insulin_actrapid, false))
.option(Option(R.string.insulin_fiasp, true))
.hint(Hint(R.string.insulin_hint1))
)
tasks.add(ExamTask(this, R.string.sensitivity_label, R.string.blank, "sensitivity")
.option(Option(R.string.sensitivity_adjust, true))
.option(Option(R.string.sensitivity_edit, false))
.option(Option(R.string.sensitivity_cannula, true))
.option(Option(R.string.sensitivity_time, true))
.hint(Hint(R.string.sensitivity_hint1))
)
tasks.add(ExamTask(this, R.string.objectives_label, R.string.objectives_howtosave, "objectives")
.option(Option(R.string.objectives_notesettings, false))
.option(Option(R.string.objectives_afterobjective, true))
.option(Option(R.string.objectives_afterchange, true))
.option(Option(R.string.objectives_afterinitialsetup, true))
.hint(Hint(R.string.objectives_hint1))
.hint(Hint(R.string.objectives_hint2))
)
tasks.add(ExamTask(this, R.string.objectives2_label, R.string.objectives_howtosave, "objectives2")
.option(Option(R.string.objectives2_maintenance, true))
.option(Option(R.string.objectives2_internalstorage, true))
.option(Option(R.string.objectives2_cloud, true))
.option(Option(R.string.objectives2_easyrestore, false))
.hint(Hint(R.string.objectives_hint1))
.hint(Hint(R.string.objectives_hint2))
)
tasks.add(ExamTask(this, R.string.update_label, R.string.blank, "update")
.option(Option(R.string.update_git, true))
.option(Option(R.string.update_askfriend, false))
.option(Option(R.string.update_keys, true))
.option(Option(R.string.update_asap, true))
.hint(Hint(R.string.update_hint1))
)
tasks.add(ExamTask(this, R.string.troubleshooting_label, R.string.troubleshooting_wheretoask, "troubleshooting")
.option(Option(R.string.troubleshooting_fb, true))
.option(Option(R.string.troubleshooting_wiki, true))
.option(Option(R.string.troubleshooting_gitter, true))
.option(Option(R.string.troubleshooting_yourendo, false))
.hint(Hint(R.string.troubleshooting_hint1))
.hint(Hint(R.string.troubleshooting_hint2))
.hint(Hint(R.string.troubleshooting_hint3))
)
tasks.add(ExamTask(this, R.string.wrongcarbs_label, R.string.wrongcarbs_whattodo, "wrongcarbs")
.option(Option(R.string.wrongcarbs_addinsulin, false))
.option(Option(R.string.wrongcarbs_treatmentstab, true))
.option(Option(R.string.wrongcarbs_donothing, false))
.option(Option(R.string.wrongcarbs_bolus, false))
)
tasks.add(ExamTask(this, R.string.wronginsulin_label, R.string.wronginsulin_whattodo, "wronginsulin")
.option(Option(R.string.wronginsulin_careportal, false))
.option(Option(R.string.wronginsulin_compare, true))
.option(Option(R.string.wronginsulin_prime, true))
.option(Option(R.string.wrongcarbs_donothing, false))
)
tasks.add(ExamTask(this, R.string.iob_label, R.string.blank, "iob")
.option(Option(R.string.iob_value, true))
.option(Option(R.string.iob_hightemp, false))
.option(Option(R.string.iob_negiob, true))
.option(Option(R.string.iob_posiob, true))
)
tasks.add(ExamTask(this, R.string.breadgrams_label, R.string.blank, "breadgrams")
.option(Option(R.string.breadgrams_grams, true))
.option(Option(R.string.breadgrams_exchange, false))
.option(Option(R.string.breadgrams_decay, true))
.option(Option(R.string.breadgrams_calc, true))
.hint(Hint(R.string.breadgrams_hint1))
)
tasks.add(ExamTask(this, R.string.extendedcarbs_label, R.string.extendedcarbs_handling, "extendedcarbs")
.option(Option(R.string.extendedcarbs_future, true))
.option(Option(R.string.extendedcarbs_free, false))
.option(Option(R.string.extendedcarbs_fat, true))
.option(Option(R.string.extendedcarbs_rescue, false))
.hint(Hint(R.string.extendedcarbs_hint1))
)
tasks.add(ExamTask(this, R.string.nsclient_label, R.string.nsclient_howcanyou, "nsclient")
.option(Option(R.string.nsclient_nightscout, true))
.option(Option(R.string.nsclient_dexcomfollow, true))
.option(Option(R.string.nsclient_data, true))
.option(Option(R.string.nsclient_fullcontrol, false))
.hint(Hint(R.string.nsclient_hint1))
)
tasks.add(ExamTask(this, R.string.other_medication_label, R.string.other_medication_text, "otherMedicationWarning")
.option(Option(R.string.yes, true))
.option(Option(R.string.no, false))
)
for (task in tasks) (task as ExamTask).options.shuffle()
for (task in tasks) {
if (!task.isCompleted()) accomplishedOn = 0
}
}
}

View file

@ -1,61 +0,0 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives;
import androidx.fragment.app.FragmentActivity;
import java.util.List;
import javax.inject.Inject;
import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.plugins.constraints.objectives.ObjectivesPlugin;
import info.nightscout.androidaps.plugins.general.nsclient.NSClientPlugin;
import info.nightscout.androidaps.plugins.general.nsclient.services.NSClientService;
import info.nightscout.androidaps.utils.T;
import info.nightscout.androidaps.utils.resources.ResourceHelper;
import info.nightscout.androidaps.utils.sharedPreferences.SP;
public class Objective3 extends Objective {
@Inject SP sp;
@Inject ObjectivesPlugin objectivesPlugin;
@Inject ResourceHelper resourceHelper;
@Inject NSClientPlugin nsClientPlugin;
private final int MANUAL_ENACTS_NEEDED = 20;
@Inject
public Objective3(HasAndroidInjector injector) {
super(injector, "openloop", R.string.objectives_openloop_objective, R.string.objectives_openloop_gate);
// disable option for skipping objectives for now
// hasSpecialInput = true;
}
@Override
protected void setupTasks(List<Task> tasks) {
tasks.add(new MinimumDurationTask(T.days(7).msecs()));
tasks.add(new Task(R.string.objectives_manualenacts) {
@Override
public boolean isCompleted() {
return sp.getInt(R.string.key_ObjectivesmanualEnacts, 0) >= MANUAL_ENACTS_NEEDED;
}
@Override
public String getProgress() {
if (sp.getInt(R.string.key_ObjectivesmanualEnacts, 0) >= MANUAL_ENACTS_NEEDED)
return resourceHelper.gs(R.string.completed_well_done);
else
return sp.getInt(R.string.key_ObjectivesmanualEnacts, 0) + " / " + MANUAL_ENACTS_NEEDED;
}
});
}
@Override
public boolean specialActionEnabled() {
return NSClientService.isConnected && NSClientService.hasWriteAuth;
}
@Override
public void specialAction(FragmentActivity activity, String input) {
objectivesPlugin.completeObjectives(activity, input);
}
}

View file

@ -0,0 +1,41 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives
import androidx.fragment.app.FragmentActivity
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.plugins.constraints.objectives.ObjectivesPlugin
import info.nightscout.androidaps.plugins.general.nsclient.NSClientPlugin
import info.nightscout.androidaps.plugins.general.nsclient.services.NSClientService
import info.nightscout.androidaps.utils.T
import javax.inject.Inject
@Suppress("SpellCheckingInspection")
class Objective3 @Inject constructor(injector: HasAndroidInjector) : Objective(injector, "openloop", R.string.objectives_openloop_objective, R.string.objectives_openloop_gate) {
@Inject lateinit var objectivesPlugin: ObjectivesPlugin
@Inject lateinit var nsClientPlugin: NSClientPlugin
init {
tasks.add(MinimumDurationTask(this, T.days(7).msecs()))
tasks.add(object : Task(this, R.string.objectives_manualenacts) {
override fun isCompleted(): Boolean {
return sp.getInt(R.string.key_ObjectivesmanualEnacts, 0) >= MANUAL_ENACTS_NEEDED
}
override val progress: String
get() = if (sp.getInt(R.string.key_ObjectivesmanualEnacts, 0) >= MANUAL_ENACTS_NEEDED) resourceHelper.gs(R.string.completed_well_done) else sp.getInt(R.string.key_ObjectivesmanualEnacts, 0).toString() + " / " + MANUAL_ENACTS_NEEDED
})
}
override fun specialActionEnabled(): Boolean =
NSClientService.isConnected && NSClientService.hasWriteAuth
override fun specialAction(activity: FragmentActivity, input: String) {
objectivesPlugin.completeObjectives(activity, input)
}
companion object {
private const val MANUAL_ENACTS_NEEDED = 20
}
}

View file

@ -1,11 +0,0 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives;
import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.R;
public class Objective4 extends Objective {
public Objective4(HasAndroidInjector injector) {
super(injector, "maxbasal", R.string.objectives_maxbasal_objective, R.string.objectives_maxbasal_gate);
}
}

View file

@ -0,0 +1,7 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
@Suppress("SpellCheckingInspection")
class Objective4(injector: HasAndroidInjector) : Objective(injector, "maxbasal", R.string.objectives_maxbasal_objective, R.string.objectives_maxbasal_gate)

View file

@ -1,32 +0,0 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives;
import java.util.List;
import javax.inject.Inject;
import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.interfaces.Constraint;
import info.nightscout.androidaps.plugins.constraints.safety.SafetyPlugin;
import info.nightscout.androidaps.utils.T;
public class Objective5 extends Objective {
@Inject SafetyPlugin safetyPlugin;
public Objective5(HasAndroidInjector injector) {
super(injector, "maxiobzero", R.string.objectives_maxiobzero_objective, R.string.objectives_maxiobzero_gate);
}
@Override
protected void setupTasks(List<Task> tasks) {
tasks.add(new MinimumDurationTask(T.days(5).msecs()));
tasks.add(new Task(R.string.closedmodeenabled) {
@Override
public boolean isCompleted() {
Constraint<Boolean> closedLoopEnabled = new Constraint<>(true);
safetyPlugin.isClosedLoopAllowed(closedLoopEnabled);
return closedLoopEnabled.value();
}
});
}
}

View file

@ -0,0 +1,25 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.interfaces.Constraint
import info.nightscout.androidaps.plugins.constraints.safety.SafetyPlugin
import info.nightscout.androidaps.utils.T
import javax.inject.Inject
@Suppress("SpellCheckingInspection")
class Objective5(injector: HasAndroidInjector) : Objective(injector, "maxiobzero", R.string.objectives_maxiobzero_objective, R.string.objectives_maxiobzero_gate) {
@Inject lateinit var safetyPlugin: SafetyPlugin
init {
tasks.add(MinimumDurationTask(this, T.days(5).msecs()))
tasks.add(object : Task(this, R.string.closedmodeenabled) {
override fun isCompleted(): Boolean {
val closedLoopEnabled = Constraint(true)
safetyPlugin.isClosedLoopAllowed(closedLoopEnabled)
return closedLoopEnabled.value()
}
})
}
}

View file

@ -1,30 +0,0 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives;
import java.util.List;
import javax.inject.Inject;
import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker;
import info.nightscout.androidaps.utils.T;
public class Objective6 extends Objective {
@Inject ConstraintChecker constraintChecker;
public Objective6(HasAndroidInjector injector) {
super(injector, "maxiob", R.string.objectives_maxiob_objective, R.string.objectives_maxiob_gate);
}
@Override
protected void setupTasks(List<Task> tasks) {
tasks.add(new MinimumDurationTask(T.days(1).msecs()));
tasks.add(new Task(R.string.maxiobset) {
@Override
public boolean isCompleted() {
double maxIOB = constraintChecker.getMaxIOBAllowed().value();
return maxIOB > 0;
}
});
}
}

View file

@ -0,0 +1,23 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.utils.T
import javax.inject.Inject
@Suppress("SpellCheckingInspection")
class Objective6(injector: HasAndroidInjector) : Objective(injector, "maxiob", R.string.objectives_maxiob_objective, R.string.objectives_maxiob_gate) {
@Inject lateinit var constraintChecker: ConstraintChecker
init {
tasks.add(MinimumDurationTask(this, T.days(1).msecs()))
tasks.add(object : Task(this, R.string.maxiobset) {
override fun isCompleted(): Boolean {
val maxIOB = constraintChecker.getMaxIOBAllowed().value()
return maxIOB > 0
}
})
}
}

View file

@ -1,19 +0,0 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives;
import java.util.List;
import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.utils.T;
public class Objective7 extends Objective {
public Objective7(HasAndroidInjector injector) {
super(injector, "autosens", R.string.objectives_autosens_objective, R.string.objectives_autosens_gate);
}
@Override
protected void setupTasks(List<Task> tasks) {
tasks.add(new MinimumDurationTask(T.days(7).msecs()));
}
}

View file

@ -0,0 +1,12 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.utils.T
class Objective7(injector: HasAndroidInjector) : Objective(injector, "autosens", R.string.objectives_autosens_objective, R.string.objectives_autosens_gate) {
init {
tasks.add(MinimumDurationTask(this, T.days(7).msecs()))
}
}

View file

@ -1,19 +0,0 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives;
import java.util.List;
import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.utils.T;
public class Objective8 extends Objective {
public Objective8(HasAndroidInjector injector) {
super(injector, "ama", R.string.objectives_ama_objective, 0);
}
@Override
protected void setupTasks(List<Task> tasks) {
tasks.add(new MinimumDurationTask(T.days(28).msecs()));
}
}

View file

@ -0,0 +1,12 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.utils.T
class Objective8(injector: HasAndroidInjector) : Objective(injector, "ama", R.string.objectives_ama_objective, 0) {
init {
tasks.add(MinimumDurationTask(this, T.days(28).msecs()))
}
}

View file

@ -1,19 +0,0 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives;
import java.util.List;
import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.utils.T;
public class Objective9 extends Objective {
public Objective9(HasAndroidInjector injector) {
super(injector, "smb", R.string.objectives_smb_objective, R.string.objectives_smb_gate);
}
@Override
protected void setupTasks(List<Task> tasks) {
tasks.add(new MinimumDurationTask(T.days(28).msecs()));
}
}

View file

@ -0,0 +1,12 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.utils.T
class Objective9(injector: HasAndroidInjector) : Objective(injector, "smb", R.string.objectives_smb_objective, R.string.objectives_smb_gate) {
init {
tasks.add(MinimumDurationTask(this, T.days(28).msecs()))
}
}

View file

@ -1,289 +0,0 @@
package info.nightscout.androidaps.plugins.constraints.safety;
import androidx.annotation.NonNull;
import javax.inject.Inject;
import javax.inject.Singleton;
import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.Config;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.data.Profile;
import info.nightscout.androidaps.interfaces.ActivePluginProvider;
import info.nightscout.androidaps.interfaces.BgSourceInterface;
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.PumpDescription;
import info.nightscout.androidaps.interfaces.PumpInterface;
import info.nightscout.androidaps.logging.AAPSLogger;
import info.nightscout.androidaps.plugins.aps.openAPSAMA.OpenAPSAMAPlugin;
import info.nightscout.androidaps.plugins.aps.openAPSSMB.OpenAPSSMBPlugin;
import info.nightscout.androidaps.plugins.bus.RxBusWrapper;
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker;
import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification;
import info.nightscout.androidaps.plugins.general.overview.notifications.Notification;
import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref1Plugin;
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin;
import info.nightscout.androidaps.utils.DecimalFormatter;
import info.nightscout.androidaps.utils.HardLimits;
import info.nightscout.androidaps.utils.Round;
import info.nightscout.androidaps.utils.buildHelper.BuildHelper;
import info.nightscout.androidaps.utils.resources.ResourceHelper;
import info.nightscout.androidaps.utils.sharedPreferences.SP;
@Singleton
public class SafetyPlugin extends PluginBase implements ConstraintsInterface {
private final SP sp;
private final RxBusWrapper rxBus;
private final ConstraintChecker constraintChecker;
private final OpenAPSAMAPlugin openAPSAMAPlugin;
private final OpenAPSSMBPlugin openAPSSMBPlugin;
private final SensitivityOref1Plugin sensitivityOref1Plugin;
private final ActivePluginProvider activePlugin;
private final HardLimits hardLimits;
private final BuildHelper buildHelper;
private final TreatmentsPlugin treatmentsPlugin;
private final Config config;
@Inject
public SafetyPlugin(
HasAndroidInjector injector,
AAPSLogger aapsLogger,
ResourceHelper resourceHelper,
SP sp,
RxBusWrapper rxBus,
ConstraintChecker constraintChecker,
OpenAPSAMAPlugin openAPSAMAPlugin,
OpenAPSSMBPlugin openAPSSMBPlugin,
SensitivityOref1Plugin sensitivityOref1Plugin,
ActivePluginProvider activePlugin,
HardLimits hardLimits,
BuildHelper buildHelper,
TreatmentsPlugin treatmentsPlugin,
Config config
) {
super(new PluginDescription()
.mainType(PluginType.CONSTRAINTS)
.neverVisible(true)
.alwaysEnabled(true)
.showInList(false)
.pluginName(R.string.safety)
.preferencesId(R.xml.pref_safety),
aapsLogger, resourceHelper, injector
);
this.sp = sp;
this.rxBus = rxBus;
this.constraintChecker = constraintChecker;
this.openAPSAMAPlugin = openAPSAMAPlugin;
this.openAPSSMBPlugin = openAPSSMBPlugin;
this.sensitivityOref1Plugin = sensitivityOref1Plugin;
this.activePlugin = activePlugin;
this.hardLimits = hardLimits;
this.buildHelper = buildHelper;
this.treatmentsPlugin = treatmentsPlugin;
this.config = config;
}
/**
* Constraints interface
**/
@NonNull @Override
public Constraint<Boolean> isLoopInvocationAllowed(@NonNull Constraint<Boolean> value) {
if (!activePlugin.getActivePump().getPumpDescription().isTempBasalCapable)
value.set(getAapsLogger(), false, getResourceHelper().gs(R.string.pumpisnottempbasalcapable), this);
return value;
}
@NonNull @Override
public Constraint<Boolean> isClosedLoopAllowed(@NonNull Constraint<Boolean> value) {
String mode = sp.getString(R.string.key_aps_mode, "open");
if ((mode.equals("open")))
value.set(getAapsLogger(), false, getResourceHelper().gs(R.string.closedmodedisabledinpreferences), this);
if (!buildHelper.isEngineeringModeOrRelease()) {
if (value.value()) {
Notification n = new Notification(Notification.TOAST_ALARM, getResourceHelper().gs(R.string.closed_loop_disabled_on_dev_branch), Notification.NORMAL);
rxBus.send(new EventNewNotification(n));
}
value.set(getAapsLogger(), false, getResourceHelper().gs(R.string.closed_loop_disabled_on_dev_branch), this);
}
PumpInterface pump = activePlugin.getActivePump();
if (!pump.isFakingTempsByExtendedBoluses() && treatmentsPlugin.isInHistoryExtendedBoluslInProgress()) {
value.set(getAapsLogger(), false, getResourceHelper().gs(R.string.closed_loop_disabled_with_eb), this);
}
return value;
}
@NonNull @Override
public Constraint<Boolean> isAutosensModeEnabled(@NonNull Constraint<Boolean> value) {
boolean enabled = sp.getBoolean(R.string.key_openapsama_useautosens, false);
if (!enabled)
value.set(getAapsLogger(), false, getResourceHelper().gs(R.string.autosensdisabledinpreferences), this);
return value;
}
@NonNull @Override
public Constraint<Boolean> isSMBModeEnabled(@NonNull Constraint<Boolean> value) {
boolean enabled = sp.getBoolean(R.string.key_use_smb, false);
if (!enabled)
value.set(getAapsLogger(), false, getResourceHelper().gs(R.string.smbdisabledinpreferences), this);
Constraint<Boolean> closedLoop = constraintChecker.isClosedLoopAllowed();
if (!closedLoop.value())
value.set(getAapsLogger(), false, getResourceHelper().gs(R.string.smbnotallowedinopenloopmode), this);
return value;
}
@NonNull @Override
public Constraint<Boolean> isUAMEnabled(@NonNull Constraint<Boolean> value) {
boolean enabled = sp.getBoolean(R.string.key_use_uam, false);
if (!enabled)
value.set(getAapsLogger(), false, getResourceHelper().gs(R.string.uamdisabledinpreferences), this);
boolean oref1Enabled = sensitivityOref1Plugin.isEnabled(PluginType.SENSITIVITY);
if (!oref1Enabled)
value.set(getAapsLogger(), false, getResourceHelper().gs(R.string.uamdisabledoref1notselected), this);
return value;
}
@NonNull @Override
public Constraint<Boolean> isAdvancedFilteringEnabled(@NonNull Constraint<Boolean> value) {
BgSourceInterface bgSource = activePlugin.getActiveBgSource();
if (!bgSource.advancedFilteringSupported())
value.set(getAapsLogger(), false, getResourceHelper().gs(R.string.smbalwaysdisabled), this);
return value;
}
@NonNull @Override
public Constraint<Double> applyBasalConstraints(Constraint<Double> absoluteRate, @NonNull Profile profile) {
absoluteRate.setIfGreater(getAapsLogger(), 0d, String.format(getResourceHelper().gs(R.string.limitingbasalratio), 0d, getResourceHelper().gs(R.string.itmustbepositivevalue)), this);
if (config.getAPS()) {
double maxBasal = sp.getDouble(R.string.key_openapsma_max_basal, 1d);
if (maxBasal < profile.getMaxDailyBasal()) {
maxBasal = profile.getMaxDailyBasal();
absoluteRate.addReason(getResourceHelper().gs(R.string.increasingmaxbasal), this);
}
absoluteRate.setIfSmaller(getAapsLogger(), maxBasal, String.format(getResourceHelper().gs(R.string.limitingbasalratio), maxBasal, getResourceHelper().gs(R.string.maxvalueinpreferences)), this);
// Check percentRate but absolute rate too, because we know real current basal in pump
double maxBasalMult = sp.getDouble(R.string.key_openapsama_current_basal_safety_multiplier, 4d);
double maxFromBasalMult = Math.floor(maxBasalMult * profile.getBasal() * 100) / 100;
absoluteRate.setIfSmaller(getAapsLogger(), maxFromBasalMult, String.format(getResourceHelper().gs(R.string.limitingbasalratio), maxFromBasalMult, getResourceHelper().gs(R.string.maxbasalmultiplier)), this);
double maxBasalFromDaily = sp.getDouble(R.string.key_openapsama_max_daily_safety_multiplier, 3d);
double maxFromDaily = Math.floor(profile.getMaxDailyBasal() * maxBasalFromDaily * 100) / 100;
absoluteRate.setIfSmaller(getAapsLogger(), maxFromDaily, String.format(getResourceHelper().gs(R.string.limitingbasalratio), maxFromDaily, getResourceHelper().gs(R.string.maxdailybasalmultiplier)), this);
}
absoluteRate.setIfSmaller(getAapsLogger(), hardLimits.maxBasal(), String.format(getResourceHelper().gs(R.string.limitingbasalratio), hardLimits.maxBasal(), getResourceHelper().gs(R.string.hardlimit)), this);
PumpInterface pump = activePlugin.getActivePump();
// check for pump max
if (pump.getPumpDescription().tempBasalStyle == PumpDescription.ABSOLUTE) {
double pumpLimit = pump.getPumpDescription().pumpType.getTbrSettings().getMaxDose();
absoluteRate.setIfSmaller(getAapsLogger(), pumpLimit, String.format(getResourceHelper().gs(R.string.limitingbasalratio), pumpLimit, getResourceHelper().gs(R.string.pumplimit)), this);
}
// do rounding
if (pump.getPumpDescription().tempBasalStyle == PumpDescription.ABSOLUTE) {
absoluteRate.set(getAapsLogger(), Round.roundTo(absoluteRate.value(), pump.getPumpDescription().tempAbsoluteStep));
}
return absoluteRate;
}
@NonNull @Override
public Constraint<Integer> applyBasalPercentConstraints(Constraint<Integer> percentRate, Profile profile) {
Double currentBasal = profile.getBasal();
double absoluteRate = currentBasal * ((double) percentRate.originalValue() / 100);
percentRate.addReason("Percent rate " + percentRate.originalValue() + "% recalculated to " + DecimalFormatter.to2Decimal(absoluteRate) + " U/h with current basal " + DecimalFormatter.to2Decimal(currentBasal) + " U/h", this);
Constraint<Double> absoluteConstraint = new Constraint<>(absoluteRate);
applyBasalConstraints(absoluteConstraint, profile);
percentRate.copyReasons(absoluteConstraint);
PumpInterface pump = activePlugin.getActivePump();
int percentRateAfterConst = Double.valueOf(absoluteConstraint.value() / currentBasal * 100).intValue();
if (percentRateAfterConst < 100)
percentRateAfterConst = Round.ceilTo((double) percentRateAfterConst, (double) pump.getPumpDescription().tempPercentStep).intValue();
else
percentRateAfterConst = Round.floorTo((double) percentRateAfterConst, (double) pump.getPumpDescription().tempPercentStep).intValue();
percentRate.set(getAapsLogger(), percentRateAfterConst, String.format(getResourceHelper().gs(R.string.limitingpercentrate), percentRateAfterConst, getResourceHelper().gs(R.string.pumplimit)), this);
if (pump.getPumpDescription().tempBasalStyle == PumpDescription.PERCENT) {
double pumpLimit = pump.getPumpDescription().pumpType.getTbrSettings().getMaxDose();
percentRate.setIfSmaller(getAapsLogger(), (int) pumpLimit, String.format(getResourceHelper().gs(R.string.limitingbasalratio), pumpLimit, getResourceHelper().gs(R.string.pumplimit)), this);
}
return percentRate;
}
@NonNull @Override
public Constraint<Double> applyBolusConstraints(Constraint<Double> insulin) {
insulin.setIfGreater(getAapsLogger(), 0d, String.format(getResourceHelper().gs(R.string.limitingbolus), 0d, getResourceHelper().gs(R.string.itmustbepositivevalue)), this);
Double maxBolus = sp.getDouble(R.string.key_treatmentssafety_maxbolus, 3d);
insulin.setIfSmaller(getAapsLogger(), maxBolus, String.format(getResourceHelper().gs(R.string.limitingbolus), maxBolus, getResourceHelper().gs(R.string.maxvalueinpreferences)), this);
insulin.setIfSmaller(getAapsLogger(), hardLimits.maxBolus(), String.format(getResourceHelper().gs(R.string.limitingbolus), hardLimits.maxBolus(), getResourceHelper().gs(R.string.hardlimit)), this);
PumpInterface pump = activePlugin.getActivePump();
double rounded = pump.getPumpDescription().pumpType.determineCorrectBolusSize(insulin.value());
insulin.setIfDifferent(getAapsLogger(), rounded, getResourceHelper().gs(R.string.pumplimit), this);
return insulin;
}
@NonNull @Override
public Constraint<Double> applyExtendedBolusConstraints(Constraint<Double> insulin) {
insulin.setIfGreater(getAapsLogger(), 0d, String.format(getResourceHelper().gs(R.string.limitingextendedbolus), 0d, getResourceHelper().gs(R.string.itmustbepositivevalue)), this);
Double maxBolus = sp.getDouble(R.string.key_treatmentssafety_maxbolus, 3d);
insulin.setIfSmaller(getAapsLogger(), maxBolus, String.format(getResourceHelper().gs(R.string.limitingextendedbolus), maxBolus, getResourceHelper().gs(R.string.maxvalueinpreferences)), this);
insulin.setIfSmaller(getAapsLogger(), hardLimits.maxBolus(), String.format(getResourceHelper().gs(R.string.limitingextendedbolus), hardLimits.maxBolus(), getResourceHelper().gs(R.string.hardlimit)), this);
PumpInterface pump = activePlugin.getActivePump();
double rounded = pump.getPumpDescription().pumpType.determineCorrectExtendedBolusSize(insulin.value());
insulin.setIfDifferent(getAapsLogger(), rounded, getResourceHelper().gs(R.string.pumplimit), this);
return insulin;
}
@NonNull @Override
public Constraint<Integer> applyCarbsConstraints(Constraint<Integer> carbs) {
carbs.setIfGreater(getAapsLogger(), 0, String.format(getResourceHelper().gs(R.string.limitingcarbs), 0, getResourceHelper().gs(R.string.itmustbepositivevalue)), this);
Integer maxCarbs = sp.getInt(R.string.key_treatmentssafety_maxcarbs, 48);
carbs.setIfSmaller(getAapsLogger(), maxCarbs, String.format(getResourceHelper().gs(R.string.limitingcarbs), maxCarbs, getResourceHelper().gs(R.string.maxvalueinpreferences)), this);
return carbs;
}
@NonNull @Override
public Constraint<Double> applyMaxIOBConstraints(@NonNull Constraint<Double> maxIob) {
double maxIobPref;
String apsmode = sp.getString(R.string.key_aps_mode, "open");
if (openAPSSMBPlugin.isEnabled(PluginType.APS))
maxIobPref = sp.getDouble(R.string.key_openapssmb_max_iob, 3d);
else
maxIobPref = sp.getDouble(R.string.key_openapsma_max_iob, 1.5d);
maxIob.setIfSmaller(getAapsLogger(), maxIobPref, String.format(getResourceHelper().gs(R.string.limitingiob), maxIobPref, getResourceHelper().gs(R.string.maxvalueinpreferences)), this);
if (openAPSAMAPlugin.isEnabled(PluginType.APS))
maxIob.setIfSmaller(getAapsLogger(), hardLimits.maxIobAMA(), String.format(getResourceHelper().gs(R.string.limitingiob), hardLimits.maxIobAMA(), getResourceHelper().gs(R.string.hardlimit)), this);
if (openAPSSMBPlugin.isEnabled(PluginType.APS))
maxIob.setIfSmaller(getAapsLogger(), hardLimits.maxIobSMB(), String.format(getResourceHelper().gs(R.string.limitingiob), hardLimits.maxIobSMB(), getResourceHelper().gs(R.string.hardlimit)), this);
if ((apsmode.equals("lgs")))
maxIob.setIfSmaller(getAapsLogger(), hardLimits.getMAXIOB_LGS(), String.format(getResourceHelper().gs(R.string.limitingiob), hardLimits.getMAXIOB_LGS(), getResourceHelper().gs(R.string.lowglucosesuspend)), this);
return maxIob;
}
}

View file

@ -0,0 +1,194 @@
package info.nightscout.androidaps.plugins.constraints.safety
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.Config
import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.Profile
import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.plugins.aps.openAPSAMA.OpenAPSAMAPlugin
import info.nightscout.androidaps.plugins.aps.openAPSSMB.OpenAPSSMBPlugin
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification
import info.nightscout.androidaps.plugins.general.overview.notifications.Notification
import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref1Plugin
import info.nightscout.androidaps.utils.DecimalFormatter
import info.nightscout.androidaps.utils.HardLimits
import info.nightscout.androidaps.utils.Round
import info.nightscout.androidaps.utils.buildHelper.BuildHelper
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP
import javax.inject.Inject
import javax.inject.Singleton
import kotlin.math.floor
@Singleton
class SafetyPlugin @Inject constructor(
injector: HasAndroidInjector,
aapsLogger: AAPSLogger,
resourceHelper: ResourceHelper,
private val sp: SP,
private val rxBus: RxBusWrapper,
private val constraintChecker: ConstraintChecker,
private val openAPSAMAPlugin: OpenAPSAMAPlugin,
private val openAPSSMBPlugin: OpenAPSSMBPlugin,
private val sensitivityOref1Plugin: SensitivityOref1Plugin,
private val activePlugin: ActivePluginProvider,
private val hardLimits: HardLimits,
private val buildHelper: BuildHelper,
private val treatmentsPlugin: TreatmentsInterface,
private val config: Config
) : PluginBase(PluginDescription()
.mainType(PluginType.CONSTRAINTS)
.neverVisible(true)
.alwaysEnabled(true)
.showInList(false)
.pluginName(R.string.safety)
.preferencesId(R.xml.pref_safety),
aapsLogger, resourceHelper, injector
), ConstraintsInterface {
/**
* Constraints interface
*/
override fun isLoopInvocationAllowed(value: Constraint<Boolean>): Constraint<Boolean> {
if (!activePlugin.activePump.pumpDescription.isTempBasalCapable) value[aapsLogger, false, resourceHelper.gs(R.string.pumpisnottempbasalcapable)] = this
return value
}
override fun isClosedLoopAllowed(value: Constraint<Boolean>): Constraint<Boolean> {
val mode = sp.getString(R.string.key_aps_mode, "open")
if (mode == "open") value[aapsLogger, false, resourceHelper.gs(R.string.closedmodedisabledinpreferences)] = this
if (!buildHelper.isEngineeringModeOrRelease()) {
if (value.value()) {
val n = Notification(Notification.TOAST_ALARM, resourceHelper.gs(R.string.closed_loop_disabled_on_dev_branch), Notification.NORMAL)
rxBus.send(EventNewNotification(n))
}
value[aapsLogger, false, resourceHelper.gs(R.string.closed_loop_disabled_on_dev_branch)] = this
}
val pump = activePlugin.activePump
if (!pump.isFakingTempsByExtendedBoluses && treatmentsPlugin.isInHistoryExtendedBoluslInProgress) {
value[aapsLogger, false, resourceHelper.gs(R.string.closed_loop_disabled_with_eb)] = this
}
return value
}
override fun isAutosensModeEnabled(value: Constraint<Boolean>): Constraint<Boolean> {
val enabled = sp.getBoolean(R.string.key_openapsama_useautosens, false)
if (!enabled) value[aapsLogger, false, resourceHelper.gs(R.string.autosensdisabledinpreferences)] = this
return value
}
override fun isSMBModeEnabled(value: Constraint<Boolean>): Constraint<Boolean> {
val enabled = sp.getBoolean(R.string.key_use_smb, false)
if (!enabled) value[aapsLogger, false, resourceHelper.gs(R.string.smbdisabledinpreferences)] = this
val closedLoop = constraintChecker.isClosedLoopAllowed()
if (!closedLoop.value()) value[aapsLogger, false, resourceHelper.gs(R.string.smbnotallowedinopenloopmode)] = this
return value
}
override fun isUAMEnabled(value: Constraint<Boolean>): Constraint<Boolean> {
val enabled = sp.getBoolean(R.string.key_use_uam, false)
if (!enabled) value[aapsLogger, false, resourceHelper.gs(R.string.uamdisabledinpreferences)] = this
val oref1Enabled = sensitivityOref1Plugin.isEnabled(PluginType.SENSITIVITY)
if (!oref1Enabled) value[aapsLogger, false, resourceHelper.gs(R.string.uamdisabledoref1notselected)] = this
return value
}
override fun isAdvancedFilteringEnabled(value: Constraint<Boolean>): Constraint<Boolean> {
val bgSource = activePlugin.activeBgSource
if (!bgSource.advancedFilteringSupported()) value[aapsLogger, false, resourceHelper.gs(R.string.smbalwaysdisabled)] = this
return value
}
override fun applyBasalConstraints(absoluteRate: Constraint<Double>, profile: Profile): Constraint<Double> {
absoluteRate.setIfGreater(aapsLogger, 0.0, String.format(resourceHelper.gs(R.string.limitingbasalratio), 0.0, resourceHelper.gs(R.string.itmustbepositivevalue)), this)
if (config.APS) {
var maxBasal = sp.getDouble(R.string.key_openapsma_max_basal, 1.0)
if (maxBasal < profile.maxDailyBasal) {
maxBasal = profile.maxDailyBasal
absoluteRate.addReason(resourceHelper.gs(R.string.increasingmaxbasal), this)
}
absoluteRate.setIfSmaller(aapsLogger, maxBasal, String.format(resourceHelper.gs(R.string.limitingbasalratio), maxBasal, resourceHelper.gs(R.string.maxvalueinpreferences)), this)
// Check percentRate but absolute rate too, because we know real current basal in pump
val maxBasalMultiplier = sp.getDouble(R.string.key_openapsama_current_basal_safety_multiplier, 4.0)
val maxFromBasalMultiplier = floor(maxBasalMultiplier * profile.basal * 100) / 100
absoluteRate.setIfSmaller(aapsLogger, maxFromBasalMultiplier, String.format(resourceHelper.gs(R.string.limitingbasalratio), maxFromBasalMultiplier, resourceHelper.gs(R.string.maxbasalmultiplier)), this)
val maxBasalFromDaily = sp.getDouble(R.string.key_openapsama_max_daily_safety_multiplier, 3.0)
val maxFromDaily = floor(profile.maxDailyBasal * maxBasalFromDaily * 100) / 100
absoluteRate.setIfSmaller(aapsLogger, maxFromDaily, String.format(resourceHelper.gs(R.string.limitingbasalratio), maxFromDaily, resourceHelper.gs(R.string.maxdailybasalmultiplier)), this)
}
absoluteRate.setIfSmaller(aapsLogger, hardLimits.maxBasal(), String.format(resourceHelper.gs(R.string.limitingbasalratio), hardLimits.maxBasal(), resourceHelper.gs(R.string.hardlimit)), this)
val pump = activePlugin.activePump
// check for pump max
if (pump.pumpDescription.tempBasalStyle == PumpDescription.ABSOLUTE) {
val pumpLimit = pump.pumpDescription.pumpType.tbrSettings.maxDose
absoluteRate.setIfSmaller(aapsLogger, pumpLimit, String.format(resourceHelper.gs(R.string.limitingbasalratio), pumpLimit, resourceHelper.gs(R.string.pumplimit)), this)
}
// do rounding
if (pump.pumpDescription.tempBasalStyle == PumpDescription.ABSOLUTE) {
absoluteRate[aapsLogger] = Round.roundTo(absoluteRate.value(), pump.pumpDescription.tempAbsoluteStep)
}
return absoluteRate
}
override fun applyBasalPercentConstraints(percentRate: Constraint<Int>, profile: Profile): Constraint<Int> {
val currentBasal = profile.basal
val absoluteRate = currentBasal * (percentRate.originalValue().toDouble() / 100)
percentRate.addReason("Percent rate " + percentRate.originalValue() + "% recalculated to " + DecimalFormatter.to2Decimal(absoluteRate) + " U/h with current basal " + DecimalFormatter.to2Decimal(currentBasal) + " U/h", this)
val absoluteConstraint = Constraint(absoluteRate)
applyBasalConstraints(absoluteConstraint, profile)
percentRate.copyReasons(absoluteConstraint)
val pump = activePlugin.activePump
var percentRateAfterConst = java.lang.Double.valueOf(absoluteConstraint.value() / currentBasal * 100).toInt()
percentRateAfterConst = if (percentRateAfterConst < 100) Round.ceilTo(percentRateAfterConst.toDouble(), pump.pumpDescription.tempPercentStep.toDouble()).toInt() else Round.floorTo(percentRateAfterConst.toDouble(), pump.pumpDescription.tempPercentStep.toDouble()).toInt()
percentRate[aapsLogger, percentRateAfterConst, String.format(resourceHelper.gs(R.string.limitingpercentrate), percentRateAfterConst, resourceHelper.gs(R.string.pumplimit))] = this
if (pump.pumpDescription.tempBasalStyle == PumpDescription.PERCENT) {
val pumpLimit = pump.pumpDescription.pumpType.tbrSettings.maxDose
percentRate.setIfSmaller(aapsLogger, pumpLimit.toInt(), String.format(resourceHelper.gs(R.string.limitingbasalratio), pumpLimit, resourceHelper.gs(R.string.pumplimit)), this)
}
return percentRate
}
override fun applyBolusConstraints(insulin: Constraint<Double>): Constraint<Double> {
insulin.setIfGreater(aapsLogger, 0.0, String.format(resourceHelper.gs(R.string.limitingbolus), 0.0, resourceHelper.gs(R.string.itmustbepositivevalue)), this)
val maxBolus = sp.getDouble(R.string.key_treatmentssafety_maxbolus, 3.0)
insulin.setIfSmaller(aapsLogger, maxBolus, String.format(resourceHelper.gs(R.string.limitingbolus), maxBolus, resourceHelper.gs(R.string.maxvalueinpreferences)), this)
insulin.setIfSmaller(aapsLogger, hardLimits.maxBolus(), String.format(resourceHelper.gs(R.string.limitingbolus), hardLimits.maxBolus(), resourceHelper.gs(R.string.hardlimit)), this)
val pump = activePlugin.activePump
val rounded = pump.pumpDescription.pumpType.determineCorrectBolusSize(insulin.value())
insulin.setIfDifferent(aapsLogger, rounded, resourceHelper.gs(R.string.pumplimit), this)
return insulin
}
override fun applyExtendedBolusConstraints(insulin: Constraint<Double>): Constraint<Double> {
insulin.setIfGreater(aapsLogger, 0.0, String.format(resourceHelper.gs(R.string.limitingextendedbolus), 0.0, resourceHelper.gs(R.string.itmustbepositivevalue)), this)
val maxBolus = sp.getDouble(R.string.key_treatmentssafety_maxbolus, 3.0)
insulin.setIfSmaller(aapsLogger, maxBolus, String.format(resourceHelper.gs(R.string.limitingextendedbolus), maxBolus, resourceHelper.gs(R.string.maxvalueinpreferences)), this)
insulin.setIfSmaller(aapsLogger, hardLimits.maxBolus(), String.format(resourceHelper.gs(R.string.limitingextendedbolus), hardLimits.maxBolus(), resourceHelper.gs(R.string.hardlimit)), this)
val pump = activePlugin.activePump
val rounded = pump.pumpDescription.pumpType.determineCorrectExtendedBolusSize(insulin.value())
insulin.setIfDifferent(aapsLogger, rounded, resourceHelper.gs(R.string.pumplimit), this)
return insulin
}
override fun applyCarbsConstraints(carbs: Constraint<Int>): Constraint<Int> {
carbs.setIfGreater(aapsLogger, 0, String.format(resourceHelper.gs(R.string.limitingcarbs), 0, resourceHelper.gs(R.string.itmustbepositivevalue)), this)
val maxCarbs = sp.getInt(R.string.key_treatmentssafety_maxcarbs, 48)
carbs.setIfSmaller(aapsLogger, maxCarbs, String.format(resourceHelper.gs(R.string.limitingcarbs), maxCarbs, resourceHelper.gs(R.string.maxvalueinpreferences)), this)
return carbs
}
override fun applyMaxIOBConstraints(maxIob: Constraint<Double>): Constraint<Double> {
val apsMode = sp.getString(R.string.key_aps_mode, "open")
val maxIobPref: Double = if (openAPSSMBPlugin.isEnabled(PluginType.APS)) sp.getDouble(R.string.key_openapssmb_max_iob, 3.0) else sp.getDouble(R.string.key_openapsma_max_iob, 1.5)
maxIob.setIfSmaller(aapsLogger, maxIobPref, String.format(resourceHelper.gs(R.string.limitingiob), maxIobPref, resourceHelper.gs(R.string.maxvalueinpreferences)), this)
if (openAPSAMAPlugin.isEnabled(PluginType.APS)) maxIob.setIfSmaller(aapsLogger, hardLimits.maxIobAMA(), String.format(resourceHelper.gs(R.string.limitingiob), hardLimits.maxIobAMA(), resourceHelper.gs(R.string.hardlimit)), this)
if (openAPSSMBPlugin.isEnabled(PluginType.APS)) maxIob.setIfSmaller(aapsLogger, hardLimits.maxIobSMB(), String.format(resourceHelper.gs(R.string.limitingiob), hardLimits.maxIobSMB(), resourceHelper.gs(R.string.hardlimit)), this)
if (apsMode == "lgs") maxIob.setIfSmaller(aapsLogger, hardLimits.MAXIOB_LGS, String.format(resourceHelper.gs(R.string.limitingiob), hardLimits.MAXIOB_LGS, resourceHelper.gs(R.string.lowglucosesuspend)), this)
return maxIob
}
}

View file

@ -23,6 +23,7 @@ import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.CommandQueueProvider
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.general.actions.defs.CustomAction
import info.nightscout.androidaps.plugins.general.overview.StatusLightHandler
@ -32,7 +33,6 @@ import info.nightscout.androidaps.skins.SkinProvider
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.buildHelper.BuildHelper
import info.nightscout.androidaps.utils.extensions.plusAssign
import info.nightscout.androidaps.utils.extensions.toVisibility
import info.nightscout.androidaps.utils.protection.ProtectionCheck
import info.nightscout.androidaps.utils.resources.ResourceHelper
@ -41,6 +41,7 @@ import info.nightscout.androidaps.utils.sharedPreferences.SP
import info.nightscout.androidaps.utils.ui.SingleClickButton
import info.nightscout.androidaps.utils.ui.UIRunnable
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign
import java.util.*
import javax.inject.Inject
@ -61,6 +62,7 @@ class ActionsFragment : DaggerFragment() {
@Inject lateinit var protectionCheck: ProtectionCheck
@Inject lateinit var skinProvider: SkinProvider
@Inject lateinit var config: Config
@Inject lateinit var uel: UserEntryLogger
private var disposable: CompositeDisposable = CompositeDisposable()
@ -152,16 +154,11 @@ class ActionsFragment : DaggerFragment() {
}
extendedBolusCancel?.setOnClickListener {
if (activePlugin.activeTreatments.isInHistoryExtendedBoluslInProgress) {
aapsLogger.debug("USER ENTRY: CANCEL EXTENDED BOLUS")
uel.log("CANCEL EXTENDED BOLUS")
commandQueue.cancelExtended(object : Callback() {
override fun run() {
if (!result.success) {
val i = Intent(ctx, ErrorHelperActivity::class.java)
i.putExtra("soundid", R.raw.boluserror)
i.putExtra("status", result.comment)
i.putExtra("title", resourceHelper.gs(R.string.extendedbolusdeliveryerror))
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
ctx.startActivity(i)
ErrorHelperActivity.runAlarm(ctx, result.comment, resourceHelper.gs(R.string.extendedbolusdeliveryerror), R.raw.boluserror)
}
}
})
@ -172,16 +169,11 @@ class ActionsFragment : DaggerFragment() {
}
cancelTempBasal?.setOnClickListener {
if (activePlugin.activeTreatments.isTempBasalInProgress) {
aapsLogger.debug("USER ENTRY: CANCEL TEMP BASAL")
uel.log("CANCEL TEMP BASAL")
commandQueue.cancelTempBasal(true, object : Callback() {
override fun run() {
if (!result.success) {
val i = Intent(ctx, ErrorHelperActivity::class.java)
i.putExtra("soundid", R.raw.boluserror)
i.putExtra("status", result.comment)
i.putExtra("title", resourceHelper.gs(R.string.tempbasaldeliveryerror))
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
ctx.startActivity(i)
ErrorHelperActivity.runAlarm(ctx, result.comment, resourceHelper.gs(R.string.tempbasaldeliveryerror), R.raw.boluserror)
}
}
})
@ -264,10 +256,10 @@ class ActionsFragment : DaggerFragment() {
profileSwitch?.visibility = (
activePlugin.activeProfileInterface.profile != null &&
pump.pumpDescription.isSetBasalProfileCapable &&
pump.isInitialized &&
!pump.isSuspended).toVisibility()
pump.isInitialized() &&
!pump.isSuspended()).toVisibility()
if (!pump.pumpDescription.isExtendedBolusCapable || !pump.isInitialized || pump.isSuspended || pump.isFakingTempsByExtendedBoluses) {
if (!pump.pumpDescription.isExtendedBolusCapable || !pump.isInitialized() || pump.isSuspended() || pump.isFakingTempsByExtendedBoluses) {
extendedBolus?.visibility = View.GONE
extendedBolusCancel?.visibility = View.GONE
} else {
@ -283,7 +275,7 @@ class ActionsFragment : DaggerFragment() {
}
}
if (!pump.pumpDescription.isTempBasalCapable || !pump.isInitialized || pump.isSuspended) {
if (!pump.pumpDescription.isTempBasalCapable || !pump.isInitialized() || pump.isSuspended()) {
setTempBasal?.visibility = View.GONE
cancelTempBasal?.visibility = View.GONE
} else {
@ -300,7 +292,7 @@ class ActionsFragment : DaggerFragment() {
}
val activeBgSource = activePlugin.activeBgSource
historyBrowser?.visibility = (profile != null).toVisibility()
fill?.visibility = (pump.pumpDescription.isRefillingCapable && pump.isInitialized && !pump.isSuspended).toVisibility()
fill?.visibility = (pump.pumpDescription.isRefillingCapable && pump.isInitialized() && !pump.isSuspended()).toVisibility()
pumpBatteryChange?.visibility = (pump.pumpDescription.isBatteryReplaceable || (pump is OmnipodErosPumpPlugin && pump.isUseRileyLinkBatteryLevel && pump.isBatteryChangeLoggingEnabled)).toVisibility()
tempTarget?.visibility = (profile != null && config.APS).toVisibility()
tddStats?.visibility = pump.pumpDescription.supportsTDDs.toVisibility()
@ -320,7 +312,7 @@ class ActionsFragment : DaggerFragment() {
private fun checkPumpCustomActions() {
val activePump = activePlugin.activePump
val customActions = activePump.customActions ?: return
val customActions = activePump.getCustomActions() ?: return
val currentContext = context ?: return
removePumpCustomActions()

View file

@ -18,7 +18,7 @@ class AutomationEvent(private val injector: HasAndroidInjector) {
@Inject lateinit var aapsLogger: AAPSLogger
var title: String? = null
var title: String = ""
var isEnabled = true
var systemAction: Boolean = false // true = generated by AAPS, false = entered by user
var readOnly: Boolean = false // removing, editing disabled

View file

@ -20,6 +20,7 @@ import dagger.android.support.DaggerFragment
import info.nightscout.androidaps.R
import info.nightscout.androidaps.databinding.AutomationEventItemBinding
import info.nightscout.androidaps.databinding.AutomationFragmentBinding
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.general.automation.dialogs.EditEventDialog
import info.nightscout.androidaps.plugins.general.automation.dragHelpers.ItemTouchHelperAdapter
@ -31,8 +32,8 @@ import info.nightscout.androidaps.plugins.general.automation.events.EventAutomat
import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerConnector
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.HtmlHelper
import info.nightscout.androidaps.utils.alertDialogs.OKDialog.showConfirmation
import info.nightscout.androidaps.utils.extensions.plusAssign
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import io.reactivex.rxkotlin.plusAssign
import info.nightscout.androidaps.utils.extensions.toVisibility
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers
@ -48,6 +49,7 @@ class AutomationFragment : DaggerFragment(), OnStartDragListener {
@Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var automationPlugin: AutomationPlugin
@Inject lateinit var injector: HasAndroidInjector
@Inject lateinit var uel: UserEntryLogger
private var disposable: CompositeDisposable = CompositeDisposable()
private lateinit var eventListAdapter: EventListAdapter
@ -212,11 +214,12 @@ class AutomationFragment : DaggerFragment(), OnStartDragListener {
}
// remove event
holder.binding.iconTrash.setOnClickListener {
showConfirmation(requireContext(), resourceHelper.gs(R.string.removerecord) + " " + automationPlugin.at(position).title,
Runnable {
OKDialog.showConfirmation(requireContext(), resourceHelper.gs(R.string.removerecord) + " " + automationPlugin.at(position).title,
{
uel.log("AUTOM REMOVED", automationPlugin.at(position).title)
automationPlugin.removeAt(position)
notifyItemRemoved(position)
}, Runnable {
}, {
rxBus.send(EventAutomationUpdateGui())
})
}
@ -234,8 +237,9 @@ class AutomationFragment : DaggerFragment(), OnStartDragListener {
override fun onItemDismiss(position: Int) {
activity?.let { activity ->
showConfirmation(activity, resourceHelper.gs(R.string.removerecord) + " " + automationPlugin.at(position).title,
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.removerecord) + " " + automationPlugin.at(position).title,
Runnable {
uel.log("AUTOM REMOVED", automationPlugin.at(position).title)
automationPlugin.removeAt(position)
notifyItemRemoved(position)
rxBus.send(EventAutomationDataChanged())

View file

@ -30,7 +30,7 @@ import info.nightscout.androidaps.services.LocationServiceHelper
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.extensions.plusAssign
import io.reactivex.rxkotlin.plusAssign
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.androidaps.utils.sharedPreferences.SP

View file

@ -1,25 +1,20 @@
package info.nightscout.androidaps.plugins.general.automation.actions
import android.content.Context
import android.content.Intent
import android.widget.LinearLayout
import androidx.annotation.DrawableRes
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.activities.ErrorHelperActivity
import info.nightscout.androidaps.activities.PreferencesActivity
import info.nightscout.androidaps.data.PumpEnactResult
import info.nightscout.androidaps.events.EventRefreshOverview
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.general.automation.elements.InputString
import info.nightscout.androidaps.plugins.general.automation.elements.LabelWithElement
import info.nightscout.androidaps.plugins.general.automation.elements.LayoutBuilder
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.NotificationUserMessage
import info.nightscout.androidaps.queue.Callback
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.JsonHelper
import info.nightscout.androidaps.utils.alertDialogs.WarningDialog
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.TimerUtil
import info.nightscout.androidaps.utils.resources.ResourceHelper
import org.json.JSONObject
import javax.inject.Inject
@ -28,28 +23,24 @@ class ActionAlarm(injector: HasAndroidInjector) : Action(injector) {
@Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var nsUpload: NSUpload
@Inject lateinit var context: Context
@Inject lateinit var timerUtil: TimerUtil
var text = InputString(injector)
constructor(injector: HasAndroidInjector, text: String) : this(injector) {
this.text = InputString(injector, text)
}
override fun friendlyName(): Int = R.string.alarm
override fun shortDescription(): String = resourceHelper.gs(R.string.alarm_message, text.value)
@DrawableRes override fun icon(): Int = R.drawable.ic_access_alarm_24dp
override fun isValid(): Boolean = text.value.isNotEmpty()
override fun isValid(): Boolean = true // empty alarm will show app name
override fun doAction(callback: Callback) {
val i = Intent(context, ErrorHelperActivity::class.java)
i.putExtra("soundid", R.raw.modern_alarm)
i.putExtra("status", text.value)
i.putExtra("title", resourceHelper.gs(R.string.alarm))
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
context.startActivity(i)
timerUtil.scheduleReminder(DateUtil.now() + T.secs(10L).msecs(), text.value.takeIf { it.isNotBlank() }
?: resourceHelper.gs(R.string.app_name))
callback.result(PumpEnactResult(injector).success(true).comment(R.string.ok))?.run()
}

View file

@ -25,7 +25,7 @@ import info.nightscout.androidaps.plugins.general.automation.events.EventAutomat
import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerConnector
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.ToastUtils
import info.nightscout.androidaps.utils.extensions.plusAssign
import io.reactivex.rxkotlin.plusAssign
import info.nightscout.androidaps.utils.extensions.toVisibility
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import io.reactivex.disposables.CompositeDisposable

View file

@ -16,7 +16,7 @@ import info.nightscout.androidaps.plugins.general.automation.triggers.Trigger
import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerConnector
import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerDummy
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.extensions.plusAssign
import io.reactivex.rxkotlin.plusAssign
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import io.reactivex.disposables.CompositeDisposable
import org.json.JSONObject

View file

@ -1,12 +1,10 @@
package info.nightscout.androidaps.plugins.general.automation.elements
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import androidx.annotation.StringRes
import com.dpro.widgets.WeekdaysPicker
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.utils.ui.WeekdayPicker
import java.util.*
class InputWeekDay(injector: HasAndroidInjector) : Element(injector) {
@ -56,7 +54,7 @@ class InputWeekDay(injector: HasAndroidInjector) : Element(injector) {
for (day in DayOfWeek.values()) set(day, false)
}
fun setAll(value:Boolean) {
fun setAll(value: Boolean) {
for (day in DayOfWeek.values()) set(day, value)
}
@ -78,13 +76,10 @@ class InputWeekDay(injector: HasAndroidInjector) : Element(injector) {
}
override fun addToLayout(root: LinearLayout) {
val weekdaysPicker = WeekdaysPicker(root.context)
weekdaysPicker.setEditable(true)
weekdaysPicker.selectedDays = getSelectedDays()
weekdaysPicker.setOnWeekdaysChangeListener { _: View?, i: Int, list: List<Int?> -> set(DayOfWeek.fromCalendarInt(i), list.contains(i)) }
weekdaysPicker.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
weekdaysPicker.sundayFirstDay = Calendar.getInstance().firstDayOfWeek == Calendar.SUNDAY
weekdaysPicker.redrawDays()
root.addView(weekdaysPicker)
WeekdayPicker(root.context).apply {
setSelectedDays(getSelectedDays())
setOnWeekdaysChangeListener { i: Int, selected: Boolean -> set(DayOfWeek.fromCalendarInt(i), selected) }
root.addView(this)
}
}
}

View file

@ -11,15 +11,15 @@ import com.google.common.base.Optional
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.interfaces.TreatmentsInterface
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.plugins.general.automation.dialogs.ChooseTriggerDialog
import info.nightscout.androidaps.plugins.general.automation.events.EventTriggerChanged
import info.nightscout.androidaps.plugins.general.automation.events.EventTriggerClone
import info.nightscout.androidaps.plugins.general.automation.events.EventTriggerRemove
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
import info.nightscout.androidaps.services.LastLocationDataContainer
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP
@ -29,13 +29,14 @@ import javax.inject.Inject
import kotlin.reflect.full.primaryConstructor
abstract class Trigger(val injector: HasAndroidInjector) {
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var sp: SP
@Inject lateinit var locationDataContainer: LastLocationDataContainer
@Inject lateinit var treatmentsPlugin: TreatmentsPlugin
@Inject lateinit var treatmentsInterface: TreatmentsInterface
@Inject lateinit var activePlugin: ActivePluginProvider
@Inject lateinit var iobCobCalculatorPlugin: IobCobCalculatorPlugin
@ -53,12 +54,13 @@ abstract class Trigger(val injector: HasAndroidInjector) {
abstract fun duplicate(): Trigger
companion object {
@JvmStatic
fun scanForActivity(cont: Context?): AppCompatActivity? {
when (cont) {
null -> return null
null -> return null
is AppCompatActivity -> return cont
is ContextWrapper -> return scanForActivity(cont.baseContext)
is ContextWrapper -> return scanForActivity(cont.baseContext)
else -> return null
}
}

View file

@ -35,7 +35,7 @@ class TriggerBolusAgo(injector: HasAndroidInjector) : Trigger(injector) {
}
override fun shouldRun(): Boolean {
val lastBolusTime = treatmentsPlugin.getLastBolusTime(true)
val lastBolusTime = treatmentsInterface.getLastBolusTime(true)
if (lastBolusTime == 0L)
return if (comparator.value == Comparator.Compare.IS_NOT_AVAILABLE) {
aapsLogger.debug(LTag.AUTOMATION, "Ready for execution: " + friendlyDescription())

View file

@ -73,8 +73,8 @@ class TriggerDelta(injector: HasAndroidInjector) : Trigger(injector) {
false
}
val calculatedDelta = when (delta.deltaType) {
DeltaType.SHORT_AVERAGE -> glucoseStatus.short_avgdelta
DeltaType.LONG_AVERAGE -> glucoseStatus.long_avgdelta
DeltaType.SHORT_AVERAGE -> glucoseStatus.shortAvgDelta
DeltaType.LONG_AVERAGE -> glucoseStatus.longAvgDelta
else -> glucoseStatus.delta
}
if (comparator.value.check(calculatedDelta, Profile.toMgdl(delta.value, units))) {

View file

@ -28,7 +28,7 @@ class TriggerTempTarget(injector: HasAndroidInjector) : Trigger(injector) {
}
override fun shouldRun(): Boolean {
val tt = treatmentsPlugin.tempTargetFromHistory
val tt = treatmentsInterface.tempTargetFromHistory
if (tt == null && comparator.value == ComparatorExists.Compare.NOT_EXISTS) {
aapsLogger.debug(LTag.AUTOMATION, "Ready for execution: " + friendlyDescription())
return true

View file

@ -130,7 +130,7 @@ class DataBroadcastPlugin @Inject constructor(
bundle.putString("units", profileFunction.getUnits()) // units used in AAPS "mg/dl" or "mmol"
bundle.putString("slopeArrow", lastBG.trendArrow.text) // direction arrow as string
bundle.putDouble("deltaMgdl", glucoseStatus.delta) // bg delta in mgdl
bundle.putDouble("avgDeltaMgdl", glucoseStatus.avgdelta) // average bg delta
bundle.putDouble("avgDeltaMgdl", glucoseStatus.avgDelta) // average bg delta
bundle.putDouble("high", defaultValueHelper.determineHighLine()) // predefined top value of in range (green area)
bundle.putDouble("low", defaultValueHelper.determineLowLine()) // predefined bottom value of in range
}

View file

@ -18,11 +18,12 @@ import info.nightscout.androidaps.R
import info.nightscout.androidaps.databinding.FoodFragmentBinding
import info.nightscout.androidaps.databinding.FoodItemBinding
import info.nightscout.androidaps.events.EventFoodDatabaseChanged
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.general.food.FoodFragment.RecyclerViewAdapter.FoodsViewHolder
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.alertDialogs.OKDialog.showConfirmation
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import io.reactivex.disposables.CompositeDisposable
@ -38,6 +39,7 @@ class FoodFragment : DaggerFragment() {
@Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var foodPlugin: FoodPlugin
@Inject lateinit var nsUpload: NSUpload
@Inject lateinit var uel: UserEntryLogger
private val disposable = CompositeDisposable()
private lateinit var unfiltered: List<Food>
@ -213,7 +215,8 @@ class FoodFragment : DaggerFragment() {
binding.remove.setOnClickListener { v: View ->
val food = v.tag as Food
activity?.let { activity ->
showConfirmation(activity, resourceHelper.gs(R.string.confirmation), resourceHelper.gs(R.string.removerecord) + "\n" + food.name, DialogInterface.OnClickListener { _: DialogInterface?, _: Int ->
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.confirmation), resourceHelper.gs(R.string.removerecord) + "\n" + food.name, DialogInterface.OnClickListener { _: DialogInterface?, _: Int ->
uel.log("FOOD REMOVED", food.name)
if (food._id != null && food._id != "") {
nsUpload.removeFoodFromNS(food._id)
}

View file

@ -20,13 +20,13 @@ import info.nightscout.androidaps.interfaces.ConfigInterface
import info.nightscout.androidaps.interfaces.ImportExportPrefsInterface
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.general.maintenance.formats.*
import info.nightscout.androidaps.utils.AndroidPermission
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.ToastUtils
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.alertDialogs.OKDialog.show
import info.nightscout.androidaps.utils.alertDialogs.PrefImportSummaryDialog
import info.nightscout.androidaps.utils.alertDialogs.TwoMessagesAlertDialog
import info.nightscout.androidaps.utils.alertDialogs.WarningDialog
@ -58,7 +58,8 @@ class ImportExportPrefs @Inject constructor(
private val androidPermission: AndroidPermission,
private val classicPrefsFormat: ClassicPrefsFormat,
private val encryptedPrefsFormat: EncryptedPrefsFormat,
private val prefFileList: PrefFileListProvider
private val prefFileList: PrefFileListProvider,
private val uel: UserEntryLogger
) : ImportExportPrefsInterface {
override fun prefsFileExists(): Boolean {
@ -342,7 +343,8 @@ class ImportExportPrefs @Inject constructor(
private fun restartAppAfterImport(context: Context) {
sp.putBoolean(R.string.key_setupwizard_processed, true)
show(context, resourceHelper.gs(R.string.setting_imported), resourceHelper.gs(R.string.restartingapp), Runnable {
OKDialog.show(context, resourceHelper.gs(R.string.setting_imported), resourceHelper.gs(R.string.restartingapp), Runnable {
uel.log("IMPORT")
log.debug(LTag.CORE, "Exiting")
rxBus.send(EventAppExit())
if (context is AppCompatActivity) {

View file

@ -1,25 +0,0 @@
package info.nightscout.androidaps.plugins.general.maintenance;
import org.slf4j.LoggerFactory;
import ch.qos.logback.classic.LoggerContext;
/**
* This class provides serveral methods for log-handling (eg. sending logs as emails).
*/
public class LoggerUtils {
public static String SUFFIX = ".log.zip";
/**
* Returns the directory, in which the logs are stored on the system. This is configured in the
* logback.xml file.
*
* @return
*/
public static String getLogDirectory() {
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
return lc.getProperty("EXT_FILES_DIR");
}
}

View file

@ -0,0 +1,27 @@
package info.nightscout.androidaps.plugins.general.maintenance
import ch.qos.logback.classic.LoggerContext
import org.slf4j.LoggerFactory
import javax.inject.Inject
import javax.inject.Singleton
/**
* This class provides several methods for log-handling (eg. sending logs as emails).
*/
@Singleton
class LoggerUtils @Inject constructor() {
var suffix = ".log.zip"
/**
* Returns the directory, in which the logs are stored on the system. This is configured in the
* logback.xml file.
*
* @return
*/
val logDirectory: String
get() {
val lc = LoggerFactory.getILoggerFactory() as LoggerContext
return lc.getProperty("EXT_FILES_DIR")
}
}

View file

@ -13,6 +13,7 @@ import info.nightscout.androidaps.databinding.MaintenanceFragmentBinding
import info.nightscout.androidaps.events.EventNewBG
import info.nightscout.androidaps.interfaces.ImportExportPrefsInterface
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.general.food.FoodPlugin
import info.nightscout.androidaps.plugins.general.maintenance.activities.LogSettingActivity
@ -36,6 +37,7 @@ class MaintenanceFragment : DaggerFragment() {
@Inject lateinit var importExportPrefs: ImportExportPrefsInterface
@Inject lateinit var aapsSchedulers: AapsSchedulers
@Inject lateinit var repository: AppRepository
@Inject lateinit var uel: UserEntryLogger
private val compositeDisposable = CompositeDisposable()
@ -54,13 +56,13 @@ class MaintenanceFragment : DaggerFragment() {
super.onViewCreated(view, savedInstanceState)
binding.logSend.setOnClickListener { maintenancePlugin.sendLogs() }
binding.logDelete.setOnClickListener {
aapsLogger.debug("USER ENTRY: DELETE LOGS")
uel.log("DELETE LOGS")
maintenancePlugin.deleteLogs()
}
binding.navResetdb.setOnClickListener {
activity?.let { activity ->
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.maintenance), resourceHelper.gs(R.string.reset_db_confirm), Runnable {
aapsLogger.debug("USER ENTRY: RESET DATABASES")
uel.log("RESET DATABASES")
compositeDisposable.add(
fromAction {
MainApp.getDbHelper().resetDatabases()
@ -81,14 +83,14 @@ class MaintenanceFragment : DaggerFragment() {
}
}
binding.navExport.setOnClickListener {
aapsLogger.debug("USER ENTRY: EXPORT SETTINGS")
uel.log("EXPORT SETTINGS")
// start activity for checking permissions...
importExportPrefs.verifyStoragePermissions(this) {
importExportPrefs.exportSharedPreferences(this)
}
}
binding.navImport.setOnClickListener {
aapsLogger.debug("USER ENTRY: IMPORT SETTINGS")
uel.log("IMPORT SETTINGS")
// start activity for checking permissions...
importExportPrefs.verifyStoragePermissions(this) {
importExportPrefs.importSharedPreferences(this)

View file

@ -34,7 +34,8 @@ class MaintenancePlugin @Inject constructor(
private val nsSettingsStatus: NSSettingsStatus,
aapsLogger: AAPSLogger,
private val buildHelper: BuildHelper,
private val config: Config
private val config: Config,
private val loggerUtils: LoggerUtils
) : PluginBase(PluginDescription()
.mainType(PluginType.GENERAL)
.fragmentClass(MaintenanceFragment::class.java.name)
@ -51,8 +52,7 @@ class MaintenancePlugin @Inject constructor(
fun sendLogs() {
val recipient = sp.getString(R.string.key_maintenance_logs_email, "logs@androidaps.org")
val amount = sp.getInt(R.string.key_maintenance_logs_amount, 2)
val logDirectory = LoggerUtils.getLogDirectory()
val logs = getLogFiles(logDirectory, amount)
val logs = getLogFiles(amount)
val zipDir = context.getExternalFilesDir("exports")
val zipFile = File(zipDir, constructName())
aapsLogger.debug("zipFile: ${zipFile.absolutePath}")
@ -66,29 +66,27 @@ class MaintenancePlugin @Inject constructor(
//todo replace this with a call on startup of the application, specifically to remove
// unnecessary garbage from the log exports
fun deleteLogs() {
LoggerUtils.getLogDirectory()?.let { logDirectory ->
val logDir = File(logDirectory)
val files = logDir.listFiles { _: File?, name: String ->
(name.startsWith("AndroidAPS") && name.endsWith(".zip"))
val logDir = File(loggerUtils.logDirectory)
val files = logDir.listFiles { _: File?, name: String ->
(name.startsWith("AndroidAPS") && name.endsWith(".zip"))
}
Arrays.sort(files) { f1: File, f2: File -> f1.name.compareTo(f2.name) }
var delFiles = listOf(*files)
val amount = sp.getInt(R.string.key_logshipper_amount, 2)
val keepIndex = amount - 1
if (keepIndex < delFiles.size) {
delFiles = delFiles.subList(keepIndex, delFiles.size)
for (file in delFiles) {
file.delete()
}
Arrays.sort(files) { f1: File, f2: File -> f1.name.compareTo(f2.name) }
var delFiles = listOf(*files)
val amount = sp.getInt(R.string.key_logshipper_amount, 2)
val keepIndex = amount - 1
if (keepIndex < delFiles.size) {
delFiles = delFiles.subList(keepIndex, delFiles.size)
for (file in delFiles) {
file.delete()
}
}
val exportDir = File(logDirectory, "exports")
if (exportDir.exists()) {
val expFiles = exportDir.listFiles()
for (file in expFiles) {
file.delete()
}
exportDir.delete()
}
val exportDir = File(loggerUtils.logDirectory, "exports")
if (exportDir.exists()) {
val expFiles = exportDir.listFiles()
for (file in expFiles) {
file.delete()
}
exportDir.delete()
}
}
@ -98,17 +96,16 @@ class MaintenancePlugin @Inject constructor(
*
* The log files are sorted by the name descending.
*
* @param directory
* @param amount
* @return
*/
fun getLogFiles(directory: String, amount: Int): List<File> {
aapsLogger.debug("getting $amount logs from directory $directory")
val logDir = File(directory)
fun getLogFiles(amount: Int): List<File> {
aapsLogger.debug("getting $amount logs from directory ${loggerUtils.logDirectory}")
val logDir = File(loggerUtils.logDirectory)
val files = logDir.listFiles { _: File?, name: String ->
(name.startsWith("AndroidAPS")
&& (name.endsWith(".log")
|| name.endsWith(".zip") && !name.endsWith(LoggerUtils.SUFFIX)))
|| name.endsWith(".zip") && !name.endsWith(loggerUtils.suffix)))
}
Arrays.sort(files) { f1: File, f2: File -> f2.name.compareTo(f1.name) }
val result = listOf(*files)
@ -139,7 +136,7 @@ class MaintenancePlugin @Inject constructor(
* @return
*/
private fun constructName(): String {
return "AndroidAPS_LOG_" + Date().time + LoggerUtils.SUFFIX
return "AndroidAPS_LOG_" + Date().time + loggerUtils.suffix
}
private fun zip(zipFile: File?, files: List<File>) {

View file

@ -16,6 +16,7 @@ import javax.inject.Inject;
import dagger.android.support.DaggerFragment;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.logging.UserEntryLogger;
import info.nightscout.androidaps.plugins.bus.RxBusWrapper;
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientNewLog;
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart;
@ -36,6 +37,7 @@ public class NSClientFragment extends DaggerFragment implements View.OnClickList
@Inject UploadQueue uploadQueue;
@Inject FabricPrivacy fabricPrivacy;
@Inject AapsSchedulers aapsSchedulers;
@Inject UserEntryLogger uel;
private final CompositeDisposable disposable = new CompositeDisposable();
@ -121,6 +123,7 @@ public class NSClientFragment extends DaggerFragment implements View.OnClickList
break;
case R.id.nsclientinternal_clearqueue:
OKDialog.showConfirmation(getContext(), resourceHelper.gs(R.string.nsclientinternal), resourceHelper.gs(R.string.clearqueueconfirm), () -> {
uel.log("NS QUEUE CLEARED", "", 0.0, 0.0, 0, 0);
uploadQueue.clearQueue();
updateGui();
fabricPrivacy.logCustom("NSClientClearQueue");
@ -136,6 +139,7 @@ public class NSClientFragment extends DaggerFragment implements View.OnClickList
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
switch (buttonView.getId()) {
case R.id.nsclientinternal_paused:
uel.log("NS PAUSED", "", 0.0, 0.0, isChecked ? 1 : 0, 0);
nsClientPlugin.pause(isChecked);
updateGui();
fabricPrivacy.logCustom("NSClientPause");

View file

@ -5,6 +5,7 @@ import info.nightscout.androidaps.Config
import info.nightscout.androidaps.R
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification
import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification
@ -116,7 +117,8 @@ class NSSettingsStatus @Inject constructor(
private val rxBus: RxBusWrapper,
private val defaultValueHelper: DefaultValueHelper,
private val sp: SP,
private val config: Config
private val config: Config,
private val uel: UserEntryLogger
) {
var nightscoutVersionName = ""
@ -127,7 +129,7 @@ class NSSettingsStatus @Inject constructor(
fun handleNewData(nightscoutVersionName: String, nightscoutVersionCode: Int, status: JSONObject) {
this.nightscoutVersionName = nightscoutVersionName
aapsLogger.debug(LTag.NSCLIENT, "Got versions: Nightscout: $nightscoutVersionName")
if (nightscoutVersionCode < config.SUPPORTEDNSVERSION) {
if (nightscoutVersionCode != 0 && nightscoutVersionCode < config.SUPPORTEDNSVERSION) {
val notification = Notification(Notification.OLD_NS, resourceHelper.gs(R.string.unsupportednsversion), Notification.NORMAL)
rxBus.send(EventNewNotification(notification))
} else {
@ -233,6 +235,7 @@ class NSSettingsStatus @Inject constructor(
getExtendedWarnValue("sage", "urgent")?.let { sp.putDouble(R.string.key_statuslights_sage_critical, it) }
getExtendedWarnValue("bage", "warn")?.let { sp.putDouble(R.string.key_statuslights_bage_warning, it) }
getExtendedWarnValue("bage", "urgent")?.let { sp.putDouble(R.string.key_statuslights_bage_critical, it) }
uel.log("NS SETTINGS COPIED")
}
if (context != null) OKDialog.showConfirmation(context, resourceHelper.gs(R.string.statuslights), resourceHelper.gs(R.string.copyexistingvalues), action)

View file

@ -16,7 +16,7 @@ import info.nightscout.androidaps.R
import info.nightscout.androidaps.events.Event
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.extensions.plusAssign
import io.reactivex.rxkotlin.plusAssign
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import io.reactivex.BackpressureStrategy

View file

@ -27,7 +27,7 @@ import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
import info.nightscout.androidaps.utils.extensions.plusAssign
import io.reactivex.rxkotlin.plusAssign
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.androidaps.utils.sharedPreferences.SP
@ -363,7 +363,7 @@ class OpenHumansUploader @Inject constructor(
copyDisposable = Completable.fromCallable { MainApp.getDbHelper().clearOpenHumansQueue() }
.andThen(Single.defer { Single.just(MainApp.getDbHelper().countOfAllRows + treatmentsPlugin.service.count()) })
.doOnSuccess { maxProgress = it }
.flatMapObservable { Observable.defer { Observable.fromIterable(treatmentsPlugin.service.treatmentData) } }
.flatMapObservable { Observable.defer { Observable.fromIterable(treatmentsPlugin.service.getTreatmentData()) } }
.map { enqueueTreatment(it); increaseCounter() }
.ignoreElements()
.andThen(Observable.defer { Observable.fromIterable(repository.compatGetBgReadingsDataFromTime(0, true).blockingGet()) })

View file

@ -33,6 +33,7 @@ import info.nightscout.androidaps.dialogs.*
import info.nightscout.androidaps.events.*
import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin
import info.nightscout.androidaps.plugins.aps.loop.events.EventNewOpenLoopNotification
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
@ -42,7 +43,7 @@ import info.nightscout.androidaps.plugins.general.nsclient.data.NSDeviceStatus
import info.nightscout.androidaps.plugins.general.overview.activities.QuickWizardListActivity
import info.nightscout.androidaps.plugins.general.overview.graphData.GraphData
import info.nightscout.androidaps.plugins.general.overview.notifications.NotificationStore
import info.nightscout.androidaps.plugins.general.wear.ActionStringHandler
import info.nightscout.androidaps.plugins.general.wear.events.EventWearDoAction
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventAutosensCalculationFinished
@ -98,7 +99,6 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
@Inject lateinit var dexcomMediator: DexcomPlugin.DexcomMediator
@Inject lateinit var xdripPlugin: XdripPlugin
@Inject lateinit var notificationStore: NotificationStore
@Inject lateinit var actionStringHandler: ActionStringHandler
@Inject lateinit var quickWizard: QuickWizard
@Inject lateinit var buildHelper: BuildHelper
@Inject lateinit var commandQueue: CommandQueue
@ -109,6 +109,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
@Inject lateinit var config: Config
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var databaseHelper: DatabaseHelperInterface
@Inject lateinit var uel: UserEntryLogger
private val disposable = CompositeDisposable()
@ -341,10 +342,10 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, UIRunnable {
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.tempbasal_label), lastRun.constraintsProcessed?.toSpanned()
?: "".toSpanned(), {
aapsLogger.debug("USER ENTRY: ACCEPT TEMP BASAL")
uel.log("ACCEPT TEMP BASAL")
binding.buttonsLayout.acceptTempButton.visibility = View.GONE
(context?.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager).cancel(Constants.notificationID)
actionStringHandler.handleInitiate("cancelChangeRequest")
rxBus.send(EventWearDoAction("cancelChangeRequest"))
loopPlugin.acceptChangeRequest()
})
})
@ -445,7 +446,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
// QuickWizard button
val quickWizardEntry = quickWizard.getActive()
if (quickWizardEntry != null && lastBG != null && profile != null && pump.isInitialized && !pump.isSuspended) {
if (quickWizardEntry != null && lastBG != null && profile != null && pump.isInitialized() && !pump.isSuspended()) {
binding.buttonsLayout.quickWizardButton.visibility = View.VISIBLE
val wizard = quickWizardEntry.doCalc(profile, profileName, lastBG, false)
binding.buttonsLayout.quickWizardButton.text = quickWizardEntry.buttonText() + "\n" + resourceHelper.gs(R.string.format_carbs, quickWizardEntry.carbs()) +
@ -462,7 +463,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
(lastRun.lastOpenModeAccept == 0L || lastRun.lastOpenModeAccept < lastRun.lastAPSRun) &&// never accepted or before last result
lastRun.constraintsProcessed?.isChangeRequested == true // change is requested
if (showAcceptButton && pump.isInitialized && !pump.isSuspended && loopPlugin.isEnabled(PluginType.LOOP)) {
if (showAcceptButton && pump.isInitialized() && !pump.isSuspended() && loopPlugin.isEnabled(PluginType.LOOP)) {
binding.buttonsLayout.acceptTempButton.visibility = View.VISIBLE
binding.buttonsLayout.acceptTempButton.text = "${resourceHelper.gs(R.string.setbasalquestion)}\n${lastRun!!.constraintsProcessed}"
} else {
@ -470,10 +471,10 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
}
// **** Various treatment buttons ****
binding.buttonsLayout.carbsButton.visibility = ((!activePlugin.activePump.pumpDescription.storesCarbInfo || pump.isInitialized && !pump.isSuspended) && profile != null && sp.getBoolean(R.string.key_show_carbs_button, true)).toVisibility()
binding.buttonsLayout.treatmentButton.visibility = (pump.isInitialized && !pump.isSuspended && profile != null && sp.getBoolean(R.string.key_show_treatment_button, false)).toVisibility()
binding.buttonsLayout.wizardButton.visibility = (pump.isInitialized && !pump.isSuspended && profile != null && sp.getBoolean(R.string.key_show_wizard_button, true)).toVisibility()
binding.buttonsLayout.insulinButton.visibility = (pump.isInitialized && !pump.isSuspended && profile != null && sp.getBoolean(R.string.key_show_insulin_button, true)).toVisibility()
binding.buttonsLayout.carbsButton.visibility = ((!activePlugin.activePump.pumpDescription.storesCarbInfo || pump.isInitialized() && !pump.isSuspended()) && profile != null && sp.getBoolean(R.string.key_show_carbs_button, true)).toVisibility()
binding.buttonsLayout.treatmentButton.visibility = (pump.isInitialized() && !pump.isSuspended() && profile != null && sp.getBoolean(R.string.key_show_treatment_button, false)).toVisibility()
binding.buttonsLayout.wizardButton.visibility = (pump.isInitialized() && !pump.isSuspended() && profile != null && sp.getBoolean(R.string.key_show_wizard_button, true)).toVisibility()
binding.buttonsLayout.insulinButton.visibility = (pump.isInitialized() && !pump.isSuspended() && profile != null && sp.getBoolean(R.string.key_show_insulin_button, true)).toVisibility()
// **** Calibration & CGM buttons ****
val xDripIsBgSource = xdripPlugin.isEnabled(PluginType.BGSOURCE)
@ -587,8 +588,8 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
binding.infoLayout.deltaLarge.text = Profile.toSignedUnitsString(glucoseStatus.delta, glucoseStatus.delta * Constants.MGDL_TO_MMOLL, units)
binding.infoLayout.deltaLarge.setTextColor(color)
binding.infoLayout.delta.text = Profile.toSignedUnitsString(glucoseStatus.delta, glucoseStatus.delta * Constants.MGDL_TO_MMOLL, units)
binding.infoLayout.avgDelta.text = Profile.toSignedUnitsString(glucoseStatus.short_avgdelta, glucoseStatus.short_avgdelta * Constants.MGDL_TO_MMOLL, units)
binding.infoLayout.longAvgDelta.text = Profile.toSignedUnitsString(glucoseStatus.long_avgdelta, glucoseStatus.long_avgdelta * Constants.MGDL_TO_MMOLL, units)
binding.infoLayout.avgDelta.text = Profile.toSignedUnitsString(glucoseStatus.shortAvgDelta, glucoseStatus.shortAvgDelta * Constants.MGDL_TO_MMOLL, units)
binding.infoLayout.longAvgDelta.text = Profile.toSignedUnitsString(glucoseStatus.longAvgDelta, glucoseStatus.longAvgDelta * Constants.MGDL_TO_MMOLL, units)
} else {
binding.infoLayout.delta.text = "Δ " + resourceHelper.gs(R.string.notavailable)
binding.infoLayout.avgDelta.text = ""
@ -632,7 +633,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
binding.infoLayout.apsModeText.visibility = View.VISIBLE
}
pump.isSuspended -> {
pump.isSuspended() -> {
binding.infoLayout.apsMode.setImageResource(if (pump.pumpDescription.pumpType == PumpType.Omnipod_Eros) {
// For Omnipod, indicate the pump as disconnected when it's suspended.
// The only way to 'reconnect' it, is through the Omnipod tab
@ -860,7 +861,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
graphData.addTreatments(fromTime, endTime)
// set manual x bounds to have nice steps
graphData.setNumVerticalLables()
graphData.setNumVerticalLabels()
graphData.formatAxis(fromTime, endTime)
@ -887,23 +888,25 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
var useDevForScale = false
var useRatioForScale = false
var useDSForScale = false
var useIAForScale = false
var useBGIForScale = false
when {
menuChartSettings[g + 1][OverviewMenus.CharType.ABS.ordinal] -> useABSForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.IOB.ordinal] -> useIobForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.COB.ordinal] -> useCobForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.DEV.ordinal] -> useDevForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.SEN.ordinal] -> useRatioForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.ACT.ordinal] -> useIAForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.BGI.ordinal] -> useBGIForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.DEVSLOPE.ordinal] -> useDSForScale = true
}
val alignIobScale = menuChartSettings[g + 1][OverviewMenus.CharType.ABS.ordinal] && menuChartSettings[g + 1][OverviewMenus.CharType.IOB.ordinal]
val alignDevBgiScale = menuChartSettings[g + 1][OverviewMenus.CharType.DEV.ordinal] && menuChartSettings[g + 1][OverviewMenus.CharType.BGI.ordinal]
if (menuChartSettings[g + 1][OverviewMenus.CharType.ABS.ordinal]) secondGraphData.addAbsIob(fromTime, now, useABSForScale, 1.0)
if (menuChartSettings[g + 1][OverviewMenus.CharType.IOB.ordinal]) secondGraphData.addIob(fromTime, now, useIobForScale, 1.0, menuChartSettings[g + 1][OverviewMenus.CharType.PRE.ordinal])
if (menuChartSettings[g + 1][OverviewMenus.CharType.IOB.ordinal]) secondGraphData.addIob(fromTime, now, useIobForScale, 1.0, menuChartSettings[g + 1][OverviewMenus.CharType.PRE.ordinal], alignIobScale)
if (menuChartSettings[g + 1][OverviewMenus.CharType.COB.ordinal]) secondGraphData.addCob(fromTime, now, useCobForScale, if (useCobForScale) 1.0 else 0.5)
if (menuChartSettings[g + 1][OverviewMenus.CharType.DEV.ordinal]) secondGraphData.addDeviations(fromTime, now, useDevForScale, 1.0)
if (menuChartSettings[g + 1][OverviewMenus.CharType.DEV.ordinal]) secondGraphData.addDeviations(fromTime, now, useDevForScale, 1.0, alignDevBgiScale)
if (menuChartSettings[g + 1][OverviewMenus.CharType.SEN.ordinal]) secondGraphData.addRatio(fromTime, now, useRatioForScale, 1.0)
if (menuChartSettings[g + 1][OverviewMenus.CharType.ACT.ordinal]) secondGraphData.addActivity(fromTime, endTime, useIAForScale, 0.8)
if (menuChartSettings[g + 1][OverviewMenus.CharType.BGI.ordinal]) secondGraphData.addMinusBGI(fromTime, endTime, useBGIForScale, if (alignDevBgiScale) 1.0 else 0.8, alignDevBgiScale)
if (menuChartSettings[g + 1][OverviewMenus.CharType.DEVSLOPE.ordinal] && buildHelper.isDev()) secondGraphData.addDeviationSlope(fromTime, now, useDSForScale, 1.0)
// set manual x bounds to have nice steps
@ -924,7 +927,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
menuChartSettings[g + 1][OverviewMenus.CharType.COB.ordinal] ||
menuChartSettings[g + 1][OverviewMenus.CharType.DEV.ordinal] ||
menuChartSettings[g + 1][OverviewMenus.CharType.SEN.ordinal] ||
menuChartSettings[g + 1][OverviewMenus.CharType.ACT.ordinal] ||
menuChartSettings[g + 1][OverviewMenus.CharType.BGI.ordinal] ||
menuChartSettings[g + 1][OverviewMenus.CharType.DEVSLOPE.ordinal]
).toVisibility()
secondaryGraphsData[g].performUpdate()

View file

@ -40,7 +40,8 @@ class OverviewMenus @Inject constructor(
COB(R.string.overview_show_cob, R.color.cob, primary = false, secondary = true,shortnameId = R.string.cob),
DEV(R.string.overview_show_deviations, R.color.deviations, primary = false, secondary = true,shortnameId = R.string.deviation_shortname),
SEN(R.string.overview_show_sensitivity, R.color.ratio, primary = false, secondary = true,shortnameId = R.string.sensitivity_shortname),
ACT(R.string.overview_show_activity, R.color.activity, primary = true, secondary = true,shortnameId = R.string.activity_shortname),
ACT(R.string.overview_show_activity, R.color.activity, primary = true, secondary = false,shortnameId = R.string.activity_shortname),
BGI(R.string.overview_show_bgi, R.color.bgi, primary = false, secondary = true,shortnameId = R.string.bgi_shortname),
DEVSLOPE(R.string.overview_show_deviationslope, R.color.devslopepos, primary = false, secondary = true,shortnameId = R.string.devslope_shortname)
}

View file

@ -21,6 +21,7 @@ import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.androidaps.utils.sharedPreferences.SP
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign
import org.json.JSONObject
import javax.inject.Inject
import javax.inject.Singleton

View file

@ -17,7 +17,7 @@ import info.nightscout.androidaps.plugins.general.overview.dialogs.EditQuickWiza
import info.nightscout.androidaps.plugins.general.overview.events.EventQuickWizardChange
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.extensions.plusAssign
import io.reactivex.rxkotlin.plusAssign
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.androidaps.utils.wizard.QuickWizard
import io.reactivex.disposables.CompositeDisposable

View file

@ -66,7 +66,7 @@ class GraphData(
bgReadingsArray = iobCobCalculatorPlugin.bgReadings
if (bgReadingsArray?.isEmpty() != false) {
aapsLogger.debug("No BG data.")
maxY = 10.0
maxY = if (units == Constants.MGDL) 180.0 else 10.0
minY = 0.0
return
}
@ -88,7 +88,7 @@ class GraphData(
addSeries(PointsWithLabelGraphSeries(Array(bgListArray.size) { i -> bgListArray[i] }))
}
internal fun setNumVerticalLables() {
internal fun setNumVerticalLabels() {
graph.gridLabelRenderer.numVerticalLabels = if (units == Constants.MGDL) (maxY / 40 + 1).toInt() else (maxY / 2 + 1).toInt()
}
@ -291,7 +291,7 @@ class GraphData(
fun addActivity(fromTime: Long, toTime: Long, useForScale: Boolean, scale: Double) {
val actArrayHist: MutableList<ScaledDataPoint> = ArrayList()
val actArrayPred: MutableList<ScaledDataPoint> = ArrayList()
val actArrayPrediction: MutableList<ScaledDataPoint> = ArrayList()
val now = System.currentTimeMillis().toDouble()
val actScale = Scale()
var total: IobTotal
@ -305,7 +305,7 @@ class GraphData(
}
total = iobCobCalculatorPlugin.calculateFromTreatmentsAndTempsSynchronized(time, profile)
val act: Double = total.activity
if (time <= now) actArrayHist.add(ScaledDataPoint(time, act, actScale)) else actArrayPred.add(ScaledDataPoint(time, act, actScale))
if (time <= now) actArrayHist.add(ScaledDataPoint(time, act, actScale)) else actArrayPrediction.add(ScaledDataPoint(time, act, actScale))
maxIAValue = max(maxIAValue, abs(act))
time += 5 * 60 * 1000L
}
@ -314,7 +314,7 @@ class GraphData(
it.color = resourceHelper.gc(R.color.activity)
it.thickness = 3
})
addSeries(FixedLineGraphSeries(Array(actArrayPred.size) { i -> actArrayPred[i] }).also {
addSeries(FixedLineGraphSeries(Array(actArrayPrediction.size) { i -> actArrayPrediction[i] }).also {
it.setCustomPaint(Paint().also { paint ->
paint.style = Paint.Style.STROKE
paint.strokeWidth = 3f
@ -329,8 +329,51 @@ class GraphData(
actScale.setMultiplier(maxY * scale / maxIAValue)
}
//Function below show -BGI to be able to compare curves with deviations
fun addMinusBGI(fromTime: Long, toTime: Long, useForScale: Boolean, scale: Double, devBgiScale: Boolean) {
val bgiArrayHist: MutableList<ScaledDataPoint> = ArrayList()
val bgiArrayPrediction: MutableList<ScaledDataPoint> = ArrayList()
val now = System.currentTimeMillis().toDouble()
val bgiScale = Scale()
var total: IobTotal
var maxBGIValue = 0.0
var time = fromTime
while (time <= toTime) {
val profile = profileFunction.getProfile(time)
if (profile == null) {
time += 5 * 60 * 1000L
continue
}
val deviation = if (devBgiScale) iobCobCalculatorPlugin.getAutosensData(time)?.deviation ?:0.0 else 0.0
total = iobCobCalculatorPlugin.calculateFromTreatmentsAndTempsSynchronized(time, profile)
val bgi: Double = total.activity * profile.getIsfMgdl(time) * 5.0
if (time <= now) bgiArrayHist.add(ScaledDataPoint(time, bgi, bgiScale)) else bgiArrayPrediction.add(ScaledDataPoint(time, bgi, bgiScale))
maxBGIValue = max(maxBGIValue, max(abs(bgi), deviation))
time += 5 * 60 * 1000L
}
addSeries(FixedLineGraphSeries(Array(bgiArrayHist.size) { i -> bgiArrayHist[i] }).also {
it.isDrawBackground = false
it.color = resourceHelper.gc(R.color.bgi)
it.thickness = 3
})
addSeries(FixedLineGraphSeries(Array(bgiArrayPrediction.size) { i -> bgiArrayPrediction[i] }).also {
it.setCustomPaint(Paint().also { paint ->
paint.style = Paint.Style.STROKE
paint.strokeWidth = 3f
paint.pathEffect = DashPathEffect(floatArrayOf(4f, 4f), 0f)
paint.color = resourceHelper.gc(R.color.bgi)
})
})
if (useForScale) {
maxY = maxBGIValue
minY = -maxBGIValue
}
bgiScale.setMultiplier(maxY * scale / maxBGIValue)
}
// scale in % of vertical size (like 0.3)
fun addIob(fromTime: Long, toTime: Long, useForScale: Boolean, scale: Double, showPrediction: Boolean) {
fun addIob(fromTime: Long, toTime: Long, useForScale: Boolean, scale: Double, showPrediction: Boolean, absScale: Boolean) {
val iobSeries: FixedLineGraphSeries<ScaledDataPoint?>
val iobArray: MutableList<ScaledDataPoint> = ArrayList()
var maxIobValueFound = Double.MIN_VALUE
@ -340,11 +383,15 @@ class GraphData(
while (time <= toTime) {
val profile = profileFunction.getProfile(time)
var iob = 0.0
if (profile != null) iob = iobCobCalculatorPlugin.calculateFromTreatmentsAndTempsSynchronized(time, profile).iob
var absIob = 0.0
if (profile != null) {
iob = iobCobCalculatorPlugin.calculateFromTreatmentsAndTempsSynchronized(time, profile).iob
if (absScale) absIob = iobCobCalculatorPlugin.calculateAbsInsulinFromTreatmentsAndTempsSynchronized(time).iob
}
if (abs(lastIob - iob) > 0.02) {
if (abs(lastIob - iob) > 0.2) iobArray.add(ScaledDataPoint(time, lastIob, iobScale))
iobArray.add(ScaledDataPoint(time, iob, iobScale))
maxIobValueFound = max(maxIobValueFound, abs(iob))
maxIobValueFound = if (absScale) max(maxIobValueFound, abs(absIob)) else max(maxIobValueFound, abs(iob))
lastIob = iob
}
time += 5 * 60 * 1000L
@ -359,22 +406,22 @@ class GraphData(
val autosensData = iobCobCalculatorPlugin.getLastAutosensDataSynchronized("GraphData")
val lastAutosensResult = autosensData?.autosensResult ?: AutosensResult()
val isTempTarget = treatmentsPlugin.getTempTargetFromHistory(System.currentTimeMillis()) != null
val iobPred: MutableList<DataPointWithLabelInterface> = ArrayList()
val iobPredArray = iobCobCalculatorPlugin.calculateIobArrayForSMB(lastAutosensResult, SMBDefaults.exercise_mode, SMBDefaults.half_basal_exercise_target, isTempTarget)
for (i in iobPredArray) {
iobPred.add(i.setColor(resourceHelper.gc(R.color.iobPredAS)))
val iobPrediction: MutableList<DataPointWithLabelInterface> = ArrayList()
val iobPredictionArray = iobCobCalculatorPlugin.calculateIobArrayForSMB(lastAutosensResult, SMBDefaults.exercise_mode, SMBDefaults.half_basal_exercise_target, isTempTarget)
for (i in iobPredictionArray) {
iobPrediction.add(i.setColor(resourceHelper.gc(R.color.iobPredAS)))
maxIobValueFound = max(maxIobValueFound, abs(i.iob))
}
addSeries(PointsWithLabelGraphSeries(Array(iobPred.size) { i -> iobPred[i] }))
val iobPred2: MutableList<DataPointWithLabelInterface> = ArrayList()
val iobPredArray2 = iobCobCalculatorPlugin.calculateIobArrayForSMB(AutosensResult(), SMBDefaults.exercise_mode, SMBDefaults.half_basal_exercise_target, isTempTarget)
for (i in iobPredArray2) {
iobPred2.add(i.setColor(resourceHelper.gc(R.color.iobPred)))
addSeries(PointsWithLabelGraphSeries(Array(iobPrediction.size) { i -> iobPrediction[i] }))
val iobPrediction2: MutableList<DataPointWithLabelInterface> = ArrayList()
val iobPredictionArray2 = iobCobCalculatorPlugin.calculateIobArrayForSMB(AutosensResult(), SMBDefaults.exercise_mode, SMBDefaults.half_basal_exercise_target, isTempTarget)
for (i in iobPredictionArray2) {
iobPrediction2.add(i.setColor(resourceHelper.gc(R.color.iobPred)))
maxIobValueFound = max(maxIobValueFound, abs(i.iob))
}
addSeries(PointsWithLabelGraphSeries(Array(iobPred2.size) { i -> iobPred2[i] }))
aapsLogger.debug(LTag.AUTOSENS, "IOB pred for AS=" + DecimalFormatter.to2Decimal(lastAutosensResult.ratio) + ": " + iobCobCalculatorPlugin.iobArrayToString(iobPredArray))
aapsLogger.debug(LTag.AUTOSENS, "IOB pred for AS=" + DecimalFormatter.to2Decimal(1.0) + ": " + iobCobCalculatorPlugin.iobArrayToString(iobPredArray2))
addSeries(PointsWithLabelGraphSeries(Array(iobPrediction2.size) { i -> iobPrediction2[i] }))
aapsLogger.debug(LTag.AUTOSENS, "IOB prediction for AS=" + DecimalFormatter.to2Decimal(lastAutosensResult.ratio) + ": " + iobCobCalculatorPlugin.iobArrayToString(iobPredictionArray))
aapsLogger.debug(LTag.AUTOSENS, "IOB prediction for AS=" + DecimalFormatter.to2Decimal(1.0) + ": " + iobCobCalculatorPlugin.iobArrayToString(iobPredictionArray2))
}
if (useForScale) {
maxY = maxIobValueFound
@ -395,7 +442,7 @@ class GraphData(
while (time <= toTime) {
val profile = profileFunction.getProfile(time)
var iob = 0.0
if (profile != null) iob = iobCobCalculatorPlugin.calculateAbsInsulinFromTreatmentsAndTempsSynchronized(time, profile).iob
if (profile != null) iob = iobCobCalculatorPlugin.calculateAbsInsulinFromTreatmentsAndTempsSynchronized(time).iob
if (abs(lastIob - iob) > 0.02) {
if (abs(lastIob - iob) > 0.2) iobArray.add(ScaledDataPoint(time, lastIob, iobScale))
iobArray.add(ScaledDataPoint(time, iob, iobScale))
@ -460,14 +507,23 @@ class GraphData(
}
// scale in % of vertical size (like 0.3)
fun addDeviations(fromTime: Long, toTime: Long, useForScale: Boolean, scale: Double) {
fun addDeviations(fromTime: Long, toTime: Long, useForScale: Boolean, scale: Double, devBgiScale: Boolean) {
class DeviationDataPoint(x: Double, y: Double, var color: Int, scale: Scale) : ScaledDataPoint(x, y, scale)
val devArray: MutableList<DeviationDataPoint> = ArrayList()
var maxDevValueFound = 0.0
val devScale = Scale()
var time = fromTime
var total: IobTotal
while (time <= toTime) {
// if align Dev Scale with BGI scale, then calculate BGI value, else bgi = 0.0
val bgi: Double = if (devBgiScale) {
val profile = profileFunction.getProfile(time)
total = iobCobCalculatorPlugin.calculateFromTreatmentsAndTempsSynchronized(time, profile)
total.activity * (profile?.getIsfMgdl(time) ?: 0.0) * 5.0
} else 0.0
iobCobCalculatorPlugin.getAutosensData(time)?.let { autosensData ->
var color = resourceHelper.gc(R.color.deviationblack) // "="
if (autosensData.type == "" || autosensData.type == "non-meal") {
@ -480,7 +536,7 @@ class GraphData(
color = resourceHelper.gc(R.color.deviationgrey)
}
devArray.add(DeviationDataPoint(time.toDouble(), autosensData.deviation, color, devScale))
maxDevValueFound = max(maxDevValueFound, abs(autosensData.deviation))
maxDevValueFound = max(maxDevValueFound, max(abs(autosensData.deviation), abs(bgi)))
}
time += 5 * 60 * 1000L
}

View file

@ -140,7 +140,7 @@ class PersistentNotificationPlugin @Inject constructor(
line1 = line1aa
if (glucoseStatus != null) {
line1 += (" Δ" + Profile.toSignedUnitsString(glucoseStatus.delta, glucoseStatus.delta * Constants.MGDL_TO_MMOLL, units)
+ " avgΔ" + Profile.toSignedUnitsString(glucoseStatus.avgdelta, glucoseStatus.avgdelta * Constants.MGDL_TO_MMOLL, units))
+ " avgΔ" + Profile.toSignedUnitsString(glucoseStatus.avgDelta, glucoseStatus.avgDelta * Constants.MGDL_TO_MMOLL, units))
line1aa += " " + lastBG.trendArrow.symbol
} else {
line1 += " " +
@ -207,7 +207,7 @@ class PersistentNotificationPlugin @Inject constructor(
builder.setCategory(NotificationCompat.CATEGORY_STATUS)
builder.setSmallIcon(iconsProvider.getNotificationIcon())
builder.setLargeIcon(resourceHelper.decodeResource(iconsProvider.getIcon()))
if (line1 != null) builder.setContentTitle(line1)
builder.setContentTitle(line1)
if (line2 != null) builder.setContentText(line2)
if (line3 != null) builder.setSubText(line3)
/// Android Auto

View file

@ -11,7 +11,7 @@ import info.nightscout.androidaps.plugins.general.smsCommunicator.events.EventSm
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.HtmlHelper
import info.nightscout.androidaps.utils.extensions.plusAssign
import io.reactivex.rxkotlin.plusAssign
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import io.reactivex.disposables.CompositeDisposable
import java.util.*

Some files were not shown because too many files have changed in this diff Show more