diff --git a/.idea/dictionaries/project_dictionary.xml b/.idea/dictionaries/project_dictionary.xml index eb2cb8b9f3..3639be9c59 100644 --- a/.idea/dictionaries/project_dictionary.xml +++ b/.idea/dictionaries/project_dictionary.xml @@ -2,19 +2,24 @@ aaps + acked actionstring allowednumbers androidaps autosens autosensdata + autosense bage + basaliob basals bgcheck + bgsource bolusing carb carbs carbsreq careportal + cellnovo crashlytics danar danars @@ -23,6 +28,7 @@ dexcom dexdrip enteredby + enteredinsulin eveningoutpost eversense extendedbolus @@ -32,12 +38,15 @@ gson hmac iage + insulet iobtotal + libre listdelimiter localprofile medtronic mgdl mmol + multiwave netinsulin netratio nightscout @@ -45,6 +54,7 @@ nsclient okcancel omnipod + openaps oref passcode poctech @@ -56,7 +66,9 @@ refresheventsfromnightscout rileylink roboelectric + sitechange smscommunicator + sooil soundid splitted superbolus @@ -73,6 +85,9 @@ uart wizzardpage xdrip + ypso + ypsomed + ypsopump \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index d08c3bf279..57f0a8f3ee 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ language: android -jdk: oraclejdk8 +jdk: openjdk11 dist: trusty env: matrix: diff --git a/app/build.gradle b/app/build.gradle index 31d430687c..63b7948b91 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -111,7 +111,7 @@ android { defaultConfig { multiDexEnabled true versionCode 1500 - version "2.8.2.1-dev-e" + version "2.8.2.1-dev-e4" buildConfigField "String", "VERSION", '"' + version + '"' buildConfigField "String", "BUILDVERSION", '"' + generateGitBuild() + '-' + generateDate() + '"' buildConfigField "String", "REMOTE", '"' + generateGitRemote() + '"' diff --git a/app/src/androidTest/java/info/nightscout/androidaps/RealPumpTest.kt b/app/src/androidTest/java/info/nightscout/androidaps/RealPumpTest.kt index 656bdae255..75ae0caa20 100644 --- a/app/src/androidTest/java/info/nightscout/androidaps/RealPumpTest.kt +++ b/app/src/androidTest/java/info/nightscout/androidaps/RealPumpTest.kt @@ -1,35 +1,8 @@ package info.nightscout.androidaps -import android.os.SystemClock import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.LargeTest -import androidx.test.rule.ActivityTestRule -import androidx.test.rule.GrantPermissionRule -import info.nightscout.androidaps.data.Profile -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.aps.openAPSSMB.OpenAPSSMBPlugin -import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin -import info.nightscout.androidaps.interfaces.ProfileFunction -import info.nightscout.androidaps.plugins.constraints.objectives.ObjectivesPlugin -import info.nightscout.androidaps.plugins.general.actions.ActionsPlugin -import info.nightscout.androidaps.plugins.insulin.InsulinOrefUltraRapidActingPlugin -import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin -import info.nightscout.androidaps.danaRv2.DanaRv2Plugin -import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref1Plugin -import info.nightscout.androidaps.plugins.source.RandomBgPlugin -import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin -import info.nightscout.androidaps.utils.DateUtil -import info.nightscout.androidaps.utils.extensions.isRunningTest -import info.nightscout.androidaps.utils.sharedPreferences.SP -import org.json.JSONObject -import org.junit.Assert -import org.junit.Before -import org.junit.Rule -import org.junit.Test import org.junit.runner.RunWith -import javax.inject.Inject @LargeTest @RunWith(AndroidJUnit4::class) @@ -89,7 +62,7 @@ class RealPumpTest { localProfilePlugin.numOfProfiles = 0 val singleProfile = LocalProfilePlugin.SingleProfile().copyFrom(localProfilePlugin.rawProfile, profile, "TestProfile") localProfilePlugin.addProfile(singleProfile) - val profileSwitch = profileFunction.prepareProfileSwitch(localProfilePlugin.createProfileStore(), "TestProfile", 0, 100, 0, DateUtil.now()) + val profileSwitch = profileFunction.prepareProfileSwitch(localProfilePlugin.createProfileStore(), "TestProfile", 0, 100, 0, dateUtil._now()) treatmentsPlugin.addToHistoryProfileSwitch(profileSwitch) // Insulin configBuilderPlugin.performPluginSwitch(insulinOrefUltraRapidActingPlugin, true, PluginType.INSULIN) diff --git a/app/src/main/java/info/nightscout/androidaps/MainActivity.kt b/app/src/main/java/info/nightscout/androidaps/MainActivity.kt index bccdbbcb1a..b5b5974fee 100644 --- a/app/src/main/java/info/nightscout/androidaps/MainActivity.kt +++ b/app/src/main/java/info/nightscout/androidaps/MainActivity.kt @@ -32,15 +32,20 @@ import info.nightscout.androidaps.activities.PreferencesActivity import info.nightscout.androidaps.activities.ProfileHelperActivity import info.nightscout.androidaps.activities.SingleFragmentActivity import info.nightscout.androidaps.activities.StatsActivity +import info.nightscout.androidaps.database.entities.UserEntry.Action +import info.nightscout.androidaps.database.entities.UserEntry.Sources import info.nightscout.androidaps.databinding.ActivityMainBinding import info.nightscout.androidaps.events.EventAppExit import info.nightscout.androidaps.events.EventPreferenceChange import info.nightscout.androidaps.events.EventRebuildTabs import info.nightscout.androidaps.historyBrowser.HistoryBrowseActivity -import info.nightscout.androidaps.interfaces.ActivePluginProvider +import info.nightscout.androidaps.interfaces.ActivePlugin +import info.nightscout.androidaps.interfaces.Config +import info.nightscout.androidaps.interfaces.IconsProvider import info.nightscout.androidaps.interfaces.PluginType import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag +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.ConstraintChecker @@ -56,7 +61,6 @@ import info.nightscout.androidaps.utils.buildHelper.BuildHelper import info.nightscout.androidaps.utils.extensions.isRunningRealPumpTest import info.nightscout.androidaps.utils.locale.LocaleHelper import info.nightscout.androidaps.utils.protection.ProtectionCheck -import info.nightscout.androidaps.utils.resources.IconsProvider import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.utils.sharedPreferences.SP import info.nightscout.androidaps.utils.tabs.TabPageAdapter @@ -80,13 +84,14 @@ class MainActivity : NoSplashAppCompatActivity() { @Inject lateinit var loopPlugin: LoopPlugin @Inject lateinit var nsSettingsStatus: NSSettingsStatus @Inject lateinit var buildHelper: BuildHelper - @Inject lateinit var activePlugin: ActivePluginProvider + @Inject lateinit var activePlugin: ActivePlugin @Inject lateinit var fabricPrivacy: FabricPrivacy @Inject lateinit var protectionCheck: ProtectionCheck @Inject lateinit var iconsProvider: IconsProvider @Inject lateinit var constraintChecker: ConstraintChecker @Inject lateinit var signatureVerifierPlugin: SignatureVerifierPlugin @Inject lateinit var config: Config + @Inject lateinit var uel: UserEntryLogger private lateinit var actionBarDrawerToggle: ActionBarDrawerToggle private var pluginPreferencesMenuItem: MenuItem? = null @@ -316,6 +321,7 @@ class MainActivity : NoSplashAppCompatActivity() { R.id.nav_exit -> { aapsLogger.debug(LTag.CORE, "Exiting") + uel.log(Action.EXIT_AAPS, Sources.Aaps) rxBus.send(EventAppExit()) finish() System.runFinalization() @@ -375,7 +381,7 @@ class MainActivity : NoSplashAppCompatActivity() { if (!config.NSCLIENT && !config.PUMPCONTROL) activePlugin.activeAPS.let { fabricPrivacy.firebaseAnalytics.setUserProperty("Aps", it::class.java.simpleName) } activePlugin.activeBgSource.let { fabricPrivacy.firebaseAnalytics.setUserProperty("BgSource", it::class.java.simpleName) } - fabricPrivacy.firebaseAnalytics.setUserProperty("Profile", activePlugin.activeProfileInterface.javaClass.simpleName) + fabricPrivacy.firebaseAnalytics.setUserProperty("Profile", activePlugin.activeProfileSource.javaClass.simpleName) activePlugin.activeSensitivity.let { fabricPrivacy.firebaseAnalytics.setUserProperty("Sensitivity", it::class.java.simpleName) } activePlugin.activeInsulin.let { fabricPrivacy.firebaseAnalytics.setUserProperty("Insulin", it::class.java.simpleName) } // Add to crash log too diff --git a/app/src/main/java/info/nightscout/androidaps/MainApp.kt b/app/src/main/java/info/nightscout/androidaps/MainApp.kt index 4851452333..9731bf37d6 100644 --- a/app/src/main/java/info/nightscout/androidaps/MainApp.kt +++ b/app/src/main/java/info/nightscout/androidaps/MainApp.kt @@ -5,31 +5,38 @@ import android.content.Intent import android.content.IntentFilter import android.net.ConnectivityManager import android.net.wifi.WifiManager +import android.os.Build import com.j256.ormlite.android.apptools.OpenHelperManager import dagger.android.AndroidInjector import dagger.android.DaggerApplication import info.nightscout.androidaps.database.AppRepository +import info.nightscout.androidaps.database.entities.TherapyEvent +import info.nightscout.androidaps.database.entities.UserEntry +import info.nightscout.androidaps.database.transactions.InsertIfNewByTimestampTherapyEventTransaction import info.nightscout.androidaps.database.transactions.VersionChangeTransaction import info.nightscout.androidaps.db.CompatDBHelper import info.nightscout.androidaps.db.DatabaseHelper import info.nightscout.androidaps.db.StaticInjector import info.nightscout.androidaps.dependencyInjection.DaggerAppComponent +import info.nightscout.androidaps.interfaces.ConfigBuilder +import info.nightscout.androidaps.interfaces.Config import info.nightscout.androidaps.interfaces.PluginBase import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag -import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin +import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.plugins.configBuilder.PluginStore import info.nightscout.androidaps.plugins.constraints.versionChecker.VersionCheckerUtils -import info.nightscout.androidaps.plugins.general.nsclient.NSUpload import info.nightscout.androidaps.receivers.BTReceiver import info.nightscout.androidaps.receivers.ChargingStateReceiver import info.nightscout.androidaps.receivers.KeepAliveReceiver.KeepAliveManager import info.nightscout.androidaps.receivers.NetworkChangeReceiver import info.nightscout.androidaps.receivers.TimeDateOrTZChangeReceiver import info.nightscout.androidaps.utils.ActivityMonitor +import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.locale.LocaleHelper.update import info.nightscout.androidaps.utils.sharedPreferences.SP import io.reactivex.disposables.CompositeDisposable +import io.reactivex.rxkotlin.plusAssign import net.danlew.android.joda.JodaTimeAndroid import javax.inject.Inject @@ -42,14 +49,15 @@ class MainApp : DaggerApplication() { @Inject lateinit var activityMonitor: ActivityMonitor @Inject lateinit var versionCheckersUtils: VersionCheckerUtils @Inject lateinit var sp: SP - @Inject lateinit var nsUpload: NSUpload @Inject lateinit var config: Config - @Inject lateinit var configBuilderPlugin: ConfigBuilderPlugin + @Inject lateinit var configBuilder: ConfigBuilder @Inject lateinit var keepAliveManager: KeepAliveManager @Inject lateinit var plugins: List<@JvmSuppressWildcards PluginBase> @Inject lateinit var compatDBHelper: CompatDBHelper @Inject lateinit var repository: AppRepository + @Inject lateinit var dateUtil: DateUtil @Inject lateinit var staticInjector: StaticInjector// TODO avoid , here fake only to initialize + @Inject lateinit var uel: UserEntryLogger override fun onCreate() { super.onCreate() @@ -63,8 +71,9 @@ class MainApp : DaggerApplication() { gitRemote = null commitHash = null } - disposable.add(repository.runTransaction(VersionChangeTransaction(BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE, gitRemote, commitHash)).subscribe()) - disposable.add(compatDBHelper.dbChangeDisposable()) + disposable += repository.runTransaction(VersionChangeTransaction(BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE, gitRemote, commitHash)).subscribe() + disposable += repository.runTransaction(InsertIfNewByTimestampTherapyEventTransaction(timestamp = dateUtil.now(), type = TherapyEvent.Type.NOTE, note = getString(info.nightscout.androidaps.core.R.string.androidaps_start).toString() + " - " + Build.MANUFACTURER + " " + Build.MODEL, glucoseUnit = TherapyEvent.GlucoseUnit.MGDL)).subscribe() + disposable += compatDBHelper.dbChangeDisposable() registerActivityLifecycleCallbacks(activityMonitor) JodaTimeAndroid.init(this) aapsLogger.debug("Version: " + BuildConfig.VERSION_NAME) @@ -77,10 +86,10 @@ class MainApp : DaggerApplication() { // Register all tabs in app here pluginStore.plugins = plugins - configBuilderPlugin.initialize() - nsUpload.uploadAppStart() - Thread { keepAliveManager.setAlarm(this) }.start() + configBuilder.initialize() + keepAliveManager.setAlarm(this) doMigrations() + uel.log(UserEntry.Action.START_AAPS, UserEntry.Sources.Aaps) } private fun doMigrations() { diff --git a/app/src/main/java/info/nightscout/androidaps/activities/MyPreferenceFragment.kt b/app/src/main/java/info/nightscout/androidaps/activities/MyPreferenceFragment.kt index aed679673b..048b9c0d73 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/MyPreferenceFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/MyPreferenceFragment.kt @@ -8,13 +8,13 @@ import android.os.Bundle import androidx.annotation.XmlRes import androidx.preference.* import dagger.android.support.AndroidSupportInjection -import info.nightscout.androidaps.Config +import info.nightscout.androidaps.interfaces.Config import info.nightscout.androidaps.R import info.nightscout.androidaps.danaRKorean.DanaRKoreanPlugin import info.nightscout.androidaps.danaRv2.DanaRv2Plugin import info.nightscout.androidaps.danar.DanaRPlugin import info.nightscout.androidaps.danars.DanaRSPlugin -import info.nightscout.androidaps.data.Profile +import info.nightscout.androidaps.interfaces.Profile import info.nightscout.androidaps.events.EventPreferenceChange import info.nightscout.androidaps.events.EventRebuildTabs import info.nightscout.androidaps.interfaces.PluginBase diff --git a/app/src/main/java/info/nightscout/androidaps/activities/ProfileHelperActivity.kt b/app/src/main/java/info/nightscout/androidaps/activities/ProfileHelperActivity.kt index 34978739b6..e89e11a735 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/ProfileHelperActivity.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/ProfileHelperActivity.kt @@ -7,14 +7,16 @@ import android.text.TextWatcher import android.view.Menu import android.widget.PopupMenu import info.nightscout.androidaps.R -import info.nightscout.androidaps.data.Profile +import info.nightscout.androidaps.data.ProfileSealed +import info.nightscout.androidaps.data.PureProfile import info.nightscout.androidaps.data.defaultProfile.DefaultProfile import info.nightscout.androidaps.data.defaultProfile.DefaultProfileDPV +import info.nightscout.androidaps.database.AppRepository +import info.nightscout.androidaps.database.entities.EffectiveProfileSwitch import info.nightscout.androidaps.databinding.ActivityProfilehelperBinding -import info.nightscout.androidaps.db.ProfileSwitch import info.nightscout.androidaps.dialogs.ProfileViewerDialog -import info.nightscout.androidaps.interfaces.ActivePluginProvider -import info.nightscout.androidaps.interfaces.DatabaseHelperInterface +import info.nightscout.androidaps.extensions.toVisibility +import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.plugins.bus.RxBusWrapper @@ -24,7 +26,6 @@ import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.ToastUtils import info.nightscout.androidaps.utils.alertDialogs.OKDialog -import info.nightscout.androidaps.utils.extensions.toVisibility import info.nightscout.androidaps.utils.stats.TddCalculator import java.text.DecimalFormat import javax.inject.Inject @@ -39,8 +40,8 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() { @Inject lateinit var localProfilePlugin: LocalProfilePlugin @Inject lateinit var rxBus: RxBusWrapper @Inject lateinit var dateUtil: DateUtil - @Inject lateinit var activePlugin: ActivePluginProvider - @Inject lateinit var databaseHelper: DatabaseHelperInterface + @Inject lateinit var activePlugin: ActivePlugin + @Inject lateinit var repository: AppRepository enum class ProfileType { MOTOL_DEFAULT, @@ -61,7 +62,7 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() { private lateinit var profileList: ArrayList private val profileUsed = arrayOf(0, 0) - private lateinit var profileSwitch: List + private lateinit var profileSwitch: List private val profileSwitchUsed = arrayOf(0, 0) private lateinit var binding: ActivityProfilehelperBinding @@ -85,10 +86,10 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() { setOnMenuItemClickListener { item -> binding.profiletype.setText(item.title) when (item.itemId) { - R.id.menu_default -> switchTab(tabSelected, ProfileType.MOTOL_DEFAULT) - R.id.menu_default_dpv -> switchTab(tabSelected, ProfileType.DPV_DEFAULT) - R.id.menu_current -> switchTab(tabSelected, ProfileType.CURRENT) - R.id.menu_available -> switchTab(tabSelected, ProfileType.AVAILABLE_PROFILE) + R.id.menu_default -> switchTab(tabSelected, ProfileType.MOTOL_DEFAULT) + R.id.menu_default_dpv -> switchTab(tabSelected, ProfileType.DPV_DEFAULT) + R.id.menu_current -> switchTab(tabSelected, ProfileType.CURRENT) + R.id.menu_available -> switchTab(tabSelected, ProfileType.AVAILABLE_PROFILE) R.id.menu_profileswitch -> switchTab(tabSelected, ProfileType.PROFILE_SWITCH) } true @@ -98,7 +99,7 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() { } // Active profile - profileList = activePlugin.activeProfileInterface.profile?.getProfileList() ?: ArrayList() + profileList = activePlugin.activeProfileSource.profile?.getProfileList() ?: ArrayList() binding.availableProfileList.setOnClickListener { PopupMenu(this, binding.availableProfileList).apply { @@ -114,12 +115,12 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() { } // Profile switch - profileSwitch = databaseHelper.getProfileSwitchData(dateUtil._now() - T.months(2).msecs(), true) + profileSwitch = repository.getEffectiveProfileSwitchDataFromTime(dateUtil.now() - T.months(2).msecs(), true).blockingGet() binding.profileswitchList.setOnClickListener { PopupMenu(this, binding.profileswitchList).apply { var order = 0 - for (name in profileSwitch) menu.add(Menu.NONE, order, order++, name.customizedName) + for (name in profileSwitch) menu.add(Menu.NONE, order, order++, name.originalCustomizedName) setOnMenuItemClickListener { item -> binding.profileswitchList.setText(item.title) profileSwitchUsed[tabSelected] = item.itemId @@ -141,7 +142,7 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() { profile?.let { OKDialog.showConfirmation(this, resourceHelper.gs(R.string.careportal_profileswitch), resourceHelper.gs(R.string.copytolocalprofile), Runnable { localProfilePlugin.addProfile(localProfilePlugin.copyFrom(it, "DefaultProfile " + - dateUtil.dateAndTimeAndSecondsString(dateUtil._now()) + dateUtil.dateAndTimeAndSecondsString(dateUtil.now()) .replace(".", "/") )) rxBus.send(EventLocalProfileChanged()) @@ -210,11 +211,10 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() { getProfile(ageUsed[1], tddUsed[1], weightUsed[1], pctUsed[1] / 100.0, 1)?.let { profile1 -> ProfileViewerDialog().also { pvd -> pvd.arguments = Bundle().also { - it.putLong("time", DateUtil.now()) + it.putLong("time", dateUtil.now()) it.putInt("mode", ProfileViewerDialog.Mode.PROFILE_COMPARE.ordinal) - it.putString("customProfile", profile0.data.toString()) - it.putString("customProfile2", profile1.data.toString()) - it.putString("customProfileUnits", profileFunction.getUnits()) + it.putString("customProfile", profile0.jsonObject.toString()) + it.putString("customProfile2", profile1.jsonObject.toString()) it.putString("customProfileName", getProfileName(ageUsed[0], tddUsed[0], weightUsed[0], pctUsed[0] / 100.0, 0) + "\n" + getProfileName(ageUsed[1], tddUsed[1], weightUsed[1], pctUsed[1] / 100.0, 1)) } }.show(supportFragmentManager, "ProfileViewDialog") @@ -227,14 +227,14 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() { switchTab(0, typeSelected[0], false) } - private fun getProfile(age: Double, tdd: Double, weight: Double, basalPct: Double, tab: Int): Profile? = + private fun getProfile(age: Double, tdd: Double, weight: Double, basalPct: Double, tab: Int): PureProfile? = try { // profile must not exist when (typeSelected[tab]) { - ProfileType.MOTOL_DEFAULT -> defaultProfile.profile(age, tdd, weight, profileFunction.getUnits()) - ProfileType.DPV_DEFAULT -> defaultProfileDPV.profile(age, tdd, basalPct, profileFunction.getUnits()) - ProfileType.CURRENT -> profileFunction.getProfile()?.convertToNonCustomizedProfile() - ProfileType.AVAILABLE_PROFILE -> activePlugin.activeProfileInterface.profile?.getSpecificProfile(profileList[profileUsed[tab]].toString()) - ProfileType.PROFILE_SWITCH -> profileSwitch[profileSwitchUsed[tab]].profileObject?.convertToNonCustomizedProfile() + ProfileType.MOTOL_DEFAULT -> defaultProfile.profile(age, tdd, weight, profileFunction.getUnits()) + ProfileType.DPV_DEFAULT -> defaultProfileDPV.profile(age, tdd, basalPct, profileFunction.getUnits()) + ProfileType.CURRENT -> profileFunction.getProfile()?.convertToNonCustomizedProfile(dateUtil) + ProfileType.AVAILABLE_PROFILE -> activePlugin.activeProfileSource.profile?.getSpecificProfile(profileList[profileUsed[tab]].toString()) + ProfileType.PROFILE_SWITCH -> ProfileSealed.EPS(profileSwitch[profileSwitchUsed[tab]]).convertToNonCustomizedProfile(dateUtil) } } catch (e: Exception) { null @@ -242,11 +242,11 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() { private fun getProfileName(age: Double, tdd: Double, weight: Double, basalSumPct: Double, tab: Int): String = when (typeSelected[tab]) { - ProfileType.MOTOL_DEFAULT -> if (tdd > 0) resourceHelper.gs(R.string.formatwithtdd, age, tdd) else resourceHelper.gs(R.string.formatwithweight, age, weight) - ProfileType.DPV_DEFAULT -> resourceHelper.gs(R.string.formatwittddandpct, age, tdd, (basalSumPct * 100).toInt()) - ProfileType.CURRENT -> profileFunction.getProfileName() + ProfileType.MOTOL_DEFAULT -> if (tdd > 0) resourceHelper.gs(R.string.formatwithtdd, age, tdd) else resourceHelper.gs(R.string.formatwithweight, age, weight) + ProfileType.DPV_DEFAULT -> resourceHelper.gs(R.string.formatwittddandpct, age, tdd, (basalSumPct * 100).toInt()) + ProfileType.CURRENT -> profileFunction.getProfileName() ProfileType.AVAILABLE_PROFILE -> profileList[profileUsed[tab]].toString() - ProfileType.PROFILE_SWITCH -> profileSwitch[profileSwitchUsed[tab]].customizedName + ProfileType.PROFILE_SWITCH -> profileSwitch[profileSwitchUsed[tab]].originalCustomizedName } private fun storeValues() { @@ -289,7 +289,7 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() { if (profileList.isNotEmpty()) binding.availableProfileList.setText(profileList[profileUsed[tabSelected]].toString()) if (profileSwitch.isNotEmpty()) - binding.profileswitchList.setText(profileSwitch[profileSwitchUsed[tabSelected]].customizedName) + binding.profileswitchList.setText(profileSwitch[profileSwitchUsed[tabSelected]].originalCustomizedName) } private fun setBackgroundColorOnSelected(tab: Int) { diff --git a/app/src/main/java/info/nightscout/androidaps/activities/StatsActivity.kt b/app/src/main/java/info/nightscout/androidaps/activities/StatsActivity.kt index 896506b93f..8edeccfebd 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/StatsActivity.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/StatsActivity.kt @@ -2,7 +2,8 @@ package info.nightscout.androidaps.activities import android.os.Bundle import info.nightscout.androidaps.R -import info.nightscout.androidaps.database.entities.UserEntry.* +import info.nightscout.androidaps.database.entities.UserEntry.Action +import info.nightscout.androidaps.database.entities.UserEntry.Sources import info.nightscout.androidaps.databinding.ActivityStatsBinding import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.utils.ActivityMonitor @@ -32,7 +33,7 @@ class StatsActivity : NoSplashAppCompatActivity() { binding.ok.setOnClickListener { finish() } binding.reset.setOnClickListener { OKDialog.showConfirmation(this, resourceHelper.gs(R.string.doyouwantresetstats)) { - uel.log(Action.STAT_RESET) + uel.log(Action.STAT_RESET, Sources.Stats) activityMonitor.reset() recreate() } diff --git a/app/src/main/java/info/nightscout/androidaps/activities/SurveyActivity.kt b/app/src/main/java/info/nightscout/androidaps/activities/SurveyActivity.kt index 5286afc33d..b7af9b8154 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/SurveyActivity.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/SurveyActivity.kt @@ -8,7 +8,7 @@ import info.nightscout.androidaps.R import info.nightscout.androidaps.data.defaultProfile.DefaultProfile import info.nightscout.androidaps.databinding.ActivitySurveyBinding import info.nightscout.androidaps.dialogs.ProfileViewerDialog -import info.nightscout.androidaps.interfaces.ActivePluginProvider +import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag @@ -24,12 +24,13 @@ import javax.inject.Inject class SurveyActivity : NoSplashAppCompatActivity() { @Inject lateinit var aapsLogger: AAPSLogger - @Inject lateinit var activePlugin: ActivePluginProvider + @Inject lateinit var activePlugin: ActivePlugin @Inject lateinit var tddCalculator: TddCalculator @Inject lateinit var tirCalculator: TirCalculator @Inject lateinit var profileFunction: ProfileFunction @Inject lateinit var activityMonitor: ActivityMonitor @Inject lateinit var defaultProfile: DefaultProfile + @Inject lateinit var dateUtil: DateUtil private lateinit var binding: ActivitySurveyBinding @@ -40,7 +41,7 @@ class SurveyActivity : NoSplashAppCompatActivity() { binding.id.text = InstanceId.instanceId() - val profileStore = activePlugin.activeProfileInterface.profile + val profileStore = activePlugin.activeProfileSource.profile val profileList = profileStore?.getProfileList() ?: return binding.spinner.adapter = ArrayAdapter(this, R.layout.spinner_centered, profileList) @@ -68,11 +69,10 @@ class SurveyActivity : NoSplashAppCompatActivity() { defaultProfile.profile(age, tdd, weight, profileFunction.getUnits())?.let { profile -> ProfileViewerDialog().also { pvd -> pvd.arguments = Bundle().also { - it.putLong("time", DateUtil.now()) + it.putLong("time", dateUtil.now()) it.putInt("mode", ProfileViewerDialog.Mode.PROFILE_COMPARE.ordinal) - it.putString("customProfile", runningProfile.data.toString()) - it.putString("customProfile2", profile.data.toString()) - it.putString("customProfileUnits", profile.units) + it.putString("customProfile", runningProfile.toPureNsJson(dateUtil).toString()) + it.putString("customProfile2", profile.jsonObject.toString()) it.putString("customProfileName", "Age: $age TDD: $tdd Weight: $weight") } }.show(supportFragmentManager, "ProfileViewDialog") diff --git a/core/src/main/java/info/nightscout/androidaps/data/defaultProfile/DefaultProfile.kt b/app/src/main/java/info/nightscout/androidaps/data/defaultProfile/DefaultProfile.kt similarity index 96% rename from core/src/main/java/info/nightscout/androidaps/data/defaultProfile/DefaultProfile.kt rename to app/src/main/java/info/nightscout/androidaps/data/defaultProfile/DefaultProfile.kt index 2b80598d6d..fa8b2774be 100644 --- a/core/src/main/java/info/nightscout/androidaps/data/defaultProfile/DefaultProfile.kt +++ b/app/src/main/java/info/nightscout/androidaps/data/defaultProfile/DefaultProfile.kt @@ -1,7 +1,10 @@ package info.nightscout.androidaps.data.defaultProfile -import dagger.android.HasAndroidInjector -import info.nightscout.androidaps.data.Profile +import info.nightscout.androidaps.data.PureProfile +import info.nightscout.androidaps.extensions.pureProfileFromJson +import info.nightscout.androidaps.interfaces.GlucoseUnit +import info.nightscout.androidaps.interfaces.Profile +import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.Round import org.json.JSONArray import org.json.JSONObject @@ -10,14 +13,14 @@ import javax.inject.Inject import javax.inject.Singleton @Singleton -class DefaultProfile @Inject constructor(val injector: HasAndroidInjector) { +class DefaultProfile @Inject constructor(val dateUtil: DateUtil) { var oneToFive: TreeMap> = TreeMap() var sixToEleven: TreeMap> = TreeMap() var twelveToSeventeen: TreeMap> = TreeMap() - var eighteenToTwentyfor: TreeMap> = TreeMap() + var eighteenToTwentyFour: TreeMap> = TreeMap() - fun profile(age: Double, tdd: Double, weight: Double, units: String): Profile? { + fun profile(age: Double, tdd: Double, weight: Double, units: GlucoseUnit): PureProfile? { val profile = JSONObject() if (age >= 1 && age < 6) { val _tdd = if (tdd == 0.0) 0.6 * weight else tdd @@ -49,8 +52,8 @@ class DefaultProfile @Inject constructor(val injector: HasAndroidInjector) { profile.put("timezone", TimeZone.getDefault().id) profile.put("target_high", JSONArray().put(JSONObject().put("time", "00:00").put("value", Profile.fromMgdlToUnits(108.0, units)))) profile.put("target_low", JSONArray().put(JSONObject().put("time", "00:00").put("value", Profile.fromMgdlToUnits(108.0, units)))) - profile.put("units", units) - return Profile(injector, profile, units) + profile.put("units", units.asText) + return pureProfileFromJson(profile, dateUtil) } init { @@ -148,7 +151,7 @@ class DefaultProfile @Inject constructor(val injector: HasAndroidInjector) { return array } - private fun singleValueArrayFromMmolToUnits(value: Double, sample: Array, units: String): JSONArray { + private fun singleValueArrayFromMmolToUnits(value: Double, sample: Array, units: GlucoseUnit): JSONArray { val array = JSONArray() array.put(JSONObject().put("time", "00:00").put("value", Profile.fromMmolToUnits(value + sample[0],units)).put("timeAsSeconds", 0 * 3600)) array.put(JSONObject().put("time", "06:00").put("value", Profile.fromMmolToUnits(value + sample[1],units)).put("timeAsSeconds", 6 * 3600)) diff --git a/core/src/main/java/info/nightscout/androidaps/data/defaultProfile/DefaultProfileDPV.kt b/app/src/main/java/info/nightscout/androidaps/data/defaultProfile/DefaultProfileDPV.kt similarity index 86% rename from core/src/main/java/info/nightscout/androidaps/data/defaultProfile/DefaultProfileDPV.kt rename to app/src/main/java/info/nightscout/androidaps/data/defaultProfile/DefaultProfileDPV.kt index c7eb7959ab..78ed97b335 100644 --- a/core/src/main/java/info/nightscout/androidaps/data/defaultProfile/DefaultProfileDPV.kt +++ b/app/src/main/java/info/nightscout/androidaps/data/defaultProfile/DefaultProfileDPV.kt @@ -1,8 +1,11 @@ package info.nightscout.androidaps.data.defaultProfile import dagger.android.HasAndroidInjector -import info.nightscout.androidaps.data.Profile -import info.nightscout.androidaps.utils.Round +import info.nightscout.androidaps.data.PureProfile +import info.nightscout.androidaps.extensions.pureProfileFromJson +import info.nightscout.androidaps.interfaces.GlucoseUnit +import info.nightscout.androidaps.interfaces.Profile +import info.nightscout.androidaps.utils.DateUtil import org.json.JSONArray import org.json.JSONObject import java.util.* @@ -10,13 +13,13 @@ import javax.inject.Inject import javax.inject.Singleton @Singleton -class DefaultProfileDPV @Inject constructor(val injector: HasAndroidInjector) { +class DefaultProfileDPV @Inject constructor(val injector: HasAndroidInjector, val dateUtil: DateUtil) { var oneToFive = arrayOf(3.97, 3.61, 3.46, 3.70, 3.76, 3.87, 4.18, 4.01, 3.76, 3.54, 3.15, 2.80, 2.86, 3.21, 3.61, 3.97, 4.43, 4.96, 5.10, 5.50, 5.81, 6.14, 5.52, 5.10) var sixToEleven = arrayOf(4.20, 4.27, 4.41, 4.62, 4.92, 5.09, 5.01, 4.47, 3.89, 3.33, 3.10, 2.91, 2.97, 3.08, 3.36, 3.93, 4.52, 4.76, 4.69, 4.63, 4.63, 4.47, 4.47, 4.31) var twelveToEighteen = arrayOf(3.47, 3.80, 4.31, 4.95, 5.59, 6.11, 5.89, 5.11, 4.31, 3.78, 3.55, 3.39, 3.35, 3.39, 3.64, 3.97, 4.53, 4.59, 4.50, 4.00, 3.69, 3.39, 3.35, 3.35) - fun profile(age: Double, tdd: Double, basalSumPct: Double, units: String): Profile? { + fun profile(age: Double, tdd: Double, basalSumPct: Double, units: GlucoseUnit): PureProfile? { val basalSum = tdd * basalSumPct val profile = JSONObject() if (age >= 1 && age < 6) { @@ -40,8 +43,8 @@ class DefaultProfileDPV @Inject constructor(val injector: HasAndroidInjector) { profile.put("timezone", TimeZone.getDefault().id) profile.put("target_high", JSONArray().put(JSONObject().put("time", "00:00").put("value", Profile.fromMgdlToUnits(108.0, units)))) profile.put("target_low", JSONArray().put(JSONObject().put("time", "00:00").put("value", Profile.fromMgdlToUnits(108.0, units)))) - profile.put("units", units) - return Profile(injector, profile, units) + profile.put("units", units.asText) + return pureProfileFromJson(profile, dateUtil) } private fun arrayToJson(b: Array, basalSum: Double): JSONArray { @@ -59,7 +62,7 @@ class DefaultProfileDPV @Inject constructor(val injector: HasAndroidInjector) { return array } - private fun singleValueArrayFromMmolToUnits(value: Double, units: String): JSONArray { + private fun singleValueArrayFromMmolToUnits(value: Double, units: GlucoseUnit): JSONArray { val array = JSONArray() array.put(JSONObject().put("time", "00:00").put("value", Profile.fromMmolToUnits(value, units)).put("timeAsSeconds", 0 * 3600)) return array diff --git a/app/src/main/java/info/nightscout/androidaps/db/CompatDBHelper.kt b/app/src/main/java/info/nightscout/androidaps/db/CompatDBHelper.kt index aef3c28a3b..2bb83ef910 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/CompatDBHelper.kt +++ b/app/src/main/java/info/nightscout/androidaps/db/CompatDBHelper.kt @@ -1,14 +1,10 @@ package info.nightscout.androidaps.db import info.nightscout.androidaps.database.AppRepository -import info.nightscout.androidaps.database.entities.Food -import info.nightscout.androidaps.database.entities.GlucoseValue -import info.nightscout.androidaps.database.entities.TemporaryTarget -import info.nightscout.androidaps.database.entities.TherapyEvent -import info.nightscout.androidaps.events.EventFoodDatabaseChanged -import info.nightscout.androidaps.events.EventNewBG -import info.nightscout.androidaps.events.EventTempTargetChange -import info.nightscout.androidaps.events.EventTherapyEventChange +import info.nightscout.androidaps.database.entities.* +import info.nightscout.androidaps.database.entities.ExtendedBolus +import info.nightscout.androidaps.database.entities.TemporaryBasal +import info.nightscout.androidaps.events.* import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.plugins.bus.RxBusWrapper @@ -30,13 +26,41 @@ class CompatDBHelper @Inject constructor( rxBus.send(EventNewBG(null)) } .subscribe { - it.filterIsInstance().firstOrNull()?.let { - aapsLogger.debug(LTag.DATABASE, "Firing EventNewHistoryData") - rxBus.send(EventNewHistoryData(it.timestamp)) - } - it.filterIsInstance().lastOrNull()?.let { + /** + * GlucoseValues can come in batch + * oldest one should be used for invalidation, newest one for for triggering Loop. + * Thus we need to collect both + * + */ + var newestGlucoseValue: GlucoseValue? = null + it.filterIsInstance().lastOrNull()?.let { gv -> aapsLogger.debug(LTag.DATABASE, "Firing EventNewBg") - rxBus.send(EventNewBG(it)) + rxBus.send(EventNewBG(gv)) + newestGlucoseValue = gv + } + it.filterIsInstance().map { gv -> gv.timestamp }.minOrNull()?.let { timestamp -> + aapsLogger.debug(LTag.DATABASE, "Firing EventNewHistoryData") + rxBus.send(EventNewHistoryData(timestamp, true, newestGlucoseValue)) + } + it.filterIsInstance().map { t -> t.timestamp }.minOrNull()?.let { timestamp -> + aapsLogger.debug(LTag.DATABASE, "Firing EventTreatmentChange") + rxBus.send(EventTreatmentChange()) + rxBus.send(EventNewHistoryData(timestamp, false)) + } + it.filterIsInstance().map { t -> t.timestamp }.minOrNull()?.let { timestamp -> + aapsLogger.debug(LTag.DATABASE, "Firing EventTreatmentChange") + rxBus.send(EventTreatmentChange()) + rxBus.send(EventNewHistoryData(timestamp, false)) + } + it.filterIsInstance().map { t -> t.timestamp }.minOrNull()?.let { timestamp -> + aapsLogger.debug(LTag.DATABASE, "Firing EventTempBasalChange") + rxBus.send(EventTempBasalChange()) + rxBus.send(EventNewHistoryData(timestamp, false)) + } + it.filterIsInstance().map { t -> t.timestamp }.minOrNull()?.let { timestamp -> + aapsLogger.debug(LTag.DATABASE, "Firing EventExtendedBolusChange") + rxBus.send(EventExtendedBolusChange()) + rxBus.send(EventNewHistoryData(timestamp, false)) } it.filterIsInstance().firstOrNull()?.let { aapsLogger.debug(LTag.DATABASE, "Firing EventTempTargetChange") @@ -50,5 +74,13 @@ class CompatDBHelper @Inject constructor( aapsLogger.debug(LTag.DATABASE, "Firing EventFoodDatabaseChanged") rxBus.send(EventFoodDatabaseChanged()) } + it.filterIsInstance().firstOrNull()?.let { + aapsLogger.debug(LTag.DATABASE, "Firing EventProfileSwitchChanged") + rxBus.send(EventProfileSwitchChanged()) + } + it.filterIsInstance().firstOrNull()?.let { + aapsLogger.debug(LTag.DATABASE, "Firing EventProfileSwitchChanged") + rxBus.send(EventProfileSwitchChanged()) + } } } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java b/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java index 3e4f21c330..c323a475be 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java +++ b/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java @@ -4,8 +4,6 @@ import android.content.Context; import android.database.DatabaseUtils; import android.database.sqlite.SQLiteDatabase; -import androidx.annotation.Nullable; - import com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper; import com.j256.ormlite.dao.CloseableIterator; import com.j256.ormlite.dao.Dao; @@ -16,43 +14,21 @@ import com.j256.ormlite.stmt.Where; import com.j256.ormlite.support.ConnectionSource; import com.j256.ormlite.table.TableUtils; -import org.json.JSONException; -import org.json.JSONObject; - import java.sql.SQLException; import java.util.ArrayList; -import java.util.Calendar; import java.util.Collections; -import java.util.GregorianCalendar; import java.util.List; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; import javax.inject.Inject; -import info.nightscout.androidaps.dana.comm.RecordTypes; -import info.nightscout.androidaps.data.Profile; -import info.nightscout.androidaps.events.EventExtendedBolusChange; -import info.nightscout.androidaps.events.EventProfileNeedsUpdate; import info.nightscout.androidaps.events.EventRefreshOverview; -import info.nightscout.androidaps.events.EventReloadProfileSwitchData; -import info.nightscout.androidaps.events.EventReloadTempBasalData; -import info.nightscout.androidaps.events.EventReloadTreatmentData; -import info.nightscout.androidaps.events.EventTempBasalChange; -import info.nightscout.androidaps.interfaces.ActivePluginProvider; -import info.nightscout.androidaps.interfaces.DatabaseHelperInterface; -import info.nightscout.androidaps.interfaces.ProfileInterface; -import info.nightscout.androidaps.interfaces.ProfileStore; +import info.nightscout.androidaps.interfaces.ActivePlugin; import info.nightscout.androidaps.logging.AAPSLogger; import info.nightscout.androidaps.logging.LTag; import info.nightscout.androidaps.plugins.bus.RxBusWrapper; -import info.nightscout.androidaps.plugins.general.nsclient.NSUpload; import info.nightscout.androidaps.plugins.general.openhumans.OpenHumansUploader; -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventNewHistoryData; import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin; -import info.nightscout.androidaps.utils.PercentageSplitter; +import info.nightscout.androidaps.utils.DateUtil; /** * This Helper contains all resource to provide a central DB management functionality. Only methods handling @@ -67,28 +43,15 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { @Inject RxBusWrapper rxBus; @Inject VirtualPumpPlugin virtualPumpPlugin; @Inject OpenHumansUploader openHumansUploader; - @Inject ActivePluginProvider activePlugin; - @Inject NSUpload nsUpload; + @Inject ActivePlugin activePlugin; + @Inject DateUtil dateUtil; public static final String DATABASE_NAME = "AndroidAPSDb"; - public static final String DATABASE_EXTENDEDBOLUSES = "ExtendedBoluses"; - public static final String DATABASE_DANARHISTORY = "DanaRHistory"; - public static final String DATABASE_DBREQUESTS = "DBRequests"; - public static final String DATABASE_TDDS = "TDDs"; private static final int DATABASE_VERSION = 13; public static Long earliestDataChange = null; - private static final ScheduledExecutorService tempBasalsWorker = Executors.newSingleThreadScheduledExecutor(); - private static ScheduledFuture scheduledTemBasalsPost = null; - - private static final ScheduledExecutorService extendedBolusWorker = Executors.newSingleThreadScheduledExecutor(); - private static ScheduledFuture scheduledExtendedBolusPost = null; - - private static final ScheduledExecutorService profileSwitchEventWorker = Executors.newSingleThreadScheduledExecutor(); - private static ScheduledFuture scheduledProfileSwitchEventPost = null; - private int oldVersion = 0; private int newVersion = 0; @@ -103,21 +66,8 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { public void onCreate(SQLiteDatabase database, ConnectionSource connectionSource) { try { aapsLogger.info(LTag.DATABASE, "onCreate"); - TableUtils.createTableIfNotExists(connectionSource, DanaRHistoryRecord.class); - TableUtils.createTableIfNotExists(connectionSource, DbRequest.class); - TableUtils.createTableIfNotExists(connectionSource, TemporaryBasal.class); - TableUtils.createTableIfNotExists(connectionSource, ExtendedBolus.class); - TableUtils.createTableIfNotExists(connectionSource, ProfileSwitch.class); - TableUtils.createTableIfNotExists(connectionSource, TDD.class); - TableUtils.createTableIfNotExists(connectionSource, InsightHistoryOffset.class); - TableUtils.createTableIfNotExists(connectionSource, InsightBolusID.class); - TableUtils.createTableIfNotExists(connectionSource, InsightPumpID.class); TableUtils.createTableIfNotExists(connectionSource, OmnipodHistoryRecord.class); TableUtils.createTableIfNotExists(connectionSource, OHQueueItem.class); - database.execSQL("INSERT INTO sqlite_sequence (name, seq) SELECT \"" + DatabaseHelperInterface.Companion.DATABASE_INSIGHT_BOLUS_IDS + "\", " + System.currentTimeMillis() + " " + - "WHERE NOT EXISTS (SELECT 1 FROM sqlite_sequence WHERE name = \"" + DatabaseHelperInterface.Companion.DATABASE_INSIGHT_BOLUS_IDS + "\")"); - database.execSQL("INSERT INTO sqlite_sequence (name, seq) SELECT \"" + DatabaseHelperInterface.Companion.DATABASE_INSIGHT_PUMP_IDS + "\", " + System.currentTimeMillis() + " " + - "WHERE NOT EXISTS (SELECT 1 FROM sqlite_sequence WHERE name = \"" + DatabaseHelperInterface.Companion.DATABASE_INSIGHT_PUMP_IDS + "\")"); } catch (SQLException e) { aapsLogger.error("Can't create database", e); throw new RuntimeException(e); @@ -132,23 +82,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { if (oldVersion < 7) { aapsLogger.info(LTag.DATABASE, "onUpgrade"); - TableUtils.dropTable(connectionSource, DanaRHistoryRecord.class, true); - TableUtils.dropTable(connectionSource, DbRequest.class, true); - TableUtils.dropTable(connectionSource, TemporaryBasal.class, true); - TableUtils.dropTable(connectionSource, ExtendedBolus.class, true); - TableUtils.dropTable(connectionSource, ProfileSwitch.class, true); onCreate(database, connectionSource); - } else if (oldVersion < 10) { - TableUtils.createTableIfNotExists(connectionSource, InsightHistoryOffset.class); - TableUtils.createTableIfNotExists(connectionSource, InsightBolusID.class); - TableUtils.createTableIfNotExists(connectionSource, InsightPumpID.class); - database.execSQL("INSERT INTO sqlite_sequence (name, seq) SELECT \"" + DatabaseHelperInterface.Companion.DATABASE_INSIGHT_BOLUS_IDS + "\", " + System.currentTimeMillis() + " " + - "WHERE NOT EXISTS (SELECT 1 FROM sqlite_sequence WHERE name = \"" + DatabaseHelperInterface.Companion.DATABASE_INSIGHT_BOLUS_IDS + "\")"); - database.execSQL("INSERT INTO sqlite_sequence (name, seq) SELECT \"" + DatabaseHelperInterface.Companion.DATABASE_INSIGHT_PUMP_IDS + "\", " + System.currentTimeMillis() + " " + - "WHERE NOT EXISTS (SELECT 1 FROM sqlite_sequence WHERE name = \"" + DatabaseHelperInterface.Companion.DATABASE_INSIGHT_PUMP_IDS + "\")"); - } else if (oldVersion < 11) { - database.execSQL("UPDATE sqlite_sequence SET seq = " + System.currentTimeMillis() + " WHERE name = \"" + DatabaseHelperInterface.Companion.DATABASE_INSIGHT_BOLUS_IDS + "\""); - database.execSQL("UPDATE sqlite_sequence SET seq = " + System.currentTimeMillis() + " WHERE name = \"" + DatabaseHelperInterface.Companion.DATABASE_INSIGHT_PUMP_IDS + "\""); } TableUtils.createTableIfNotExists(connectionSource, OHQueueItem.class); } catch (SQLException e) { @@ -179,28 +113,13 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { public void resetDatabases() { try { - TableUtils.dropTable(connectionSource, DanaRHistoryRecord.class, true); - TableUtils.dropTable(connectionSource, DbRequest.class, true); - TableUtils.dropTable(connectionSource, TemporaryBasal.class, true); - TableUtils.dropTable(connectionSource, ExtendedBolus.class, true); - TableUtils.dropTable(connectionSource, ProfileSwitch.class, true); - TableUtils.dropTable(connectionSource, TDD.class, true); TableUtils.dropTable(connectionSource, OmnipodHistoryRecord.class, true); - TableUtils.createTableIfNotExists(connectionSource, DanaRHistoryRecord.class); - TableUtils.createTableIfNotExists(connectionSource, DbRequest.class); - TableUtils.createTableIfNotExists(connectionSource, TemporaryBasal.class); - TableUtils.createTableIfNotExists(connectionSource, ExtendedBolus.class); - TableUtils.createTableIfNotExists(connectionSource, ProfileSwitch.class); - TableUtils.createTableIfNotExists(connectionSource, TDD.class); TableUtils.createTableIfNotExists(connectionSource, OmnipodHistoryRecord.class); updateEarliestDataChange(0); } catch (SQLException e) { aapsLogger.error("Unhandled exception", e); } virtualPumpPlugin.setFakingStatus(true); - scheduleTemporaryBasalChange(); - scheduleExtendedBolusChange(); - scheduleProfileSwitchChange(); new java.util.Timer().schedule( new java.util.TimerTask() { @Override @@ -212,86 +131,8 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { ); } - public void resetTemporaryBasals() { - try { - TableUtils.dropTable(connectionSource, TemporaryBasal.class, true); - TableUtils.createTableIfNotExists(connectionSource, TemporaryBasal.class); - updateEarliestDataChange(0); - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - } - virtualPumpPlugin.setFakingStatus(false); - scheduleTemporaryBasalChange(); - } - - public void resetExtededBoluses() { - try { - TableUtils.dropTable(connectionSource, ExtendedBolus.class, true); - TableUtils.createTableIfNotExists(connectionSource, ExtendedBolus.class); - updateEarliestDataChange(0); - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - } - scheduleExtendedBolusChange(); - } - - public void resetProfileSwitch() { - try { - TableUtils.dropTable(connectionSource, ProfileSwitch.class, true); - TableUtils.createTableIfNotExists(connectionSource, ProfileSwitch.class); - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - } - scheduleProfileSwitchChange(); - } - - public void resetTDDs() { - try { - TableUtils.dropTable(connectionSource, TDD.class, true); - TableUtils.createTableIfNotExists(connectionSource, TDD.class); - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - } - } - // ------------------ getDao ------------------------------------------- - private Dao getDaoDanaRHistory() throws SQLException { - return getDao(DanaRHistoryRecord.class); - } - - private Dao getDaoTDD() throws SQLException { - return getDao(TDD.class); - } - - private Dao getDaoDbRequest() throws SQLException { - return getDao(DbRequest.class); - } - - private Dao getDaoTemporaryBasal() throws SQLException { - return getDao(TemporaryBasal.class); - } - - private Dao getDaoExtendedBolus() throws SQLException { - return getDao(ExtendedBolus.class); - } - - private Dao getDaoProfileSwitch() throws SQLException { - return getDao(ProfileSwitch.class); - } - - private Dao getDaoInsightPumpID() throws SQLException { - return getDao(InsightPumpID.class); - } - - private Dao getDaoInsightBolusID() throws SQLException { - return getDao(InsightBolusID.class); - } - - private Dao getDaoInsightHistoryOffset() throws SQLException { - return getDao(InsightHistoryOffset.class); - } - private Dao getDaoPodHistory() throws SQLException { return getDao(OmnipodHistoryRecord.class); } @@ -300,130 +141,6 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { return getDao(OHQueueItem.class); } - public long roundDateToSec(long date) { - long rounded = date - date % 1000; - if (rounded != date) - aapsLogger.debug(LTag.DATABASE, "Rounding " + date + " to " + rounded); - return rounded; - } - - // ------------------- TDD handling ----------------------- - public void createOrUpdateTDD(TDD tdd) { - try { - Dao dao = getDaoTDD(); - dao.createOrUpdate(tdd); - openHumansUploader.enqueueTotalDailyDose(tdd); - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - } - } - - public List getTDDs() { - List tddList; - try { - QueryBuilder queryBuilder = getDaoTDD().queryBuilder(); - queryBuilder.orderBy("date", false); - queryBuilder.limit(10L); - PreparedQuery preparedQuery = queryBuilder.prepare(); - tddList = getDaoTDD().query(preparedQuery); - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - tddList = new ArrayList<>(); - } - return tddList; - } - - public List getAllTDDs() { - try { - return getDaoTDD().queryForAll(); - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - } - return Collections.emptyList(); - } - - public List getTDDsForLastXDays(int days) { - List tddList; - GregorianCalendar gc = new GregorianCalendar(); - gc.add(Calendar.DAY_OF_YEAR, (-1) * days); - - try { - QueryBuilder queryBuilder = getDaoTDD().queryBuilder(); - queryBuilder.orderBy("date", false); - Where where = queryBuilder.where(); - where.ge("date", gc.getTimeInMillis()); - PreparedQuery preparedQuery = queryBuilder.prepare(); - tddList = getDaoTDD().query(preparedQuery); - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - tddList = new ArrayList<>(); - } - return tddList; - } - - // ------------- DbRequests handling ------------------- - - public void create(DbRequest dbr) throws SQLException { - getDaoDbRequest().create(dbr); - } - - public int delete(DbRequest dbr) { - try { - return getDaoDbRequest().delete(dbr); - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - } - return 0; - } - - public int deleteDbRequest(String nsClientId) { - try { - return getDaoDbRequest().deleteById(nsClientId); - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - } - return 0; - } - - public void deleteDbRequestbyMongoId(String action, String id) { - try { - QueryBuilder queryBuilder = getDaoDbRequest().queryBuilder(); - // By nsID - Where where = queryBuilder.where(); - where.eq("_id", id).and().eq("action", action); - queryBuilder.limit(10L); - PreparedQuery preparedQuery = queryBuilder.prepare(); - List dbList = getDaoDbRequest().query(preparedQuery); - for (DbRequest r : dbList) delete(r); - // By nsClientID - where = queryBuilder.where(); - where.eq("nsClientID", id).and().eq("action", action); - queryBuilder.limit(10L); - preparedQuery = queryBuilder.prepare(); - dbList = getDaoDbRequest().query(preparedQuery); - for (DbRequest r : dbList) delete(r); - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - } - } - - public void deleteAllDbRequests() { - try { - TableUtils.clearTable(connectionSource, DbRequest.class); - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - } - } - - public CloseableIterator getDbRequestIterator() { - try { - return getDaoDbRequest().closeableIterator(); - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - return null; - } - } - public static void updateEarliestDataChange(long newDate) { if (earliestDataChange == null) { earliestDataChange = newDate; @@ -434,923 +151,6 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { } } - // ----------------- DanaRHistory handling -------------------- - - public void createOrUpdate(DanaRHistoryRecord record) { - try { - getDaoDanaRHistory().createOrUpdate(record); - - //If it is a TDD, store it for stats also. - if (record.recordCode == RecordTypes.RECORD_TYPE_DAILY) { - createOrUpdateTDD(new TDD(record.recordDate, record.recordDailyBolus, record.recordDailyBasal, 0)); - } - - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - } - } - - public List getDanaRHistoryRecordsByType(byte type) { - List historyList; - try { - QueryBuilder queryBuilder = getDaoDanaRHistory().queryBuilder(); - queryBuilder.orderBy("recordDate", false); - Where where = queryBuilder.where(); - where.eq("recordCode", type); - queryBuilder.limit(200L); - PreparedQuery preparedQuery = queryBuilder.prepare(); - historyList = getDaoDanaRHistory().query(preparedQuery); - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - historyList = new ArrayList<>(); - } - return historyList; - } - - // ------------ TemporaryBasal handling --------------- - - //return true if new record was created - public boolean createOrUpdate(TemporaryBasal tempBasal) { - try { - TemporaryBasal old; - tempBasal.date = roundDateToSec(tempBasal.date); - - if (tempBasal.source == Source.PUMP) { - // check for changed from pump change in NS - QueryBuilder queryBuilder = getDaoTemporaryBasal().queryBuilder(); - Where where = queryBuilder.where(); - where.eq("pumpId", tempBasal.pumpId); - PreparedQuery preparedQuery = queryBuilder.prepare(); - List trList = getDaoTemporaryBasal().query(preparedQuery); - if (trList.size() > 0) { - // do nothing, pump history record cannot be changed - aapsLogger.debug(LTag.DATABASE, "TEMPBASAL: Already exists from: " + Source.getString(tempBasal.source) + " " + tempBasal.toString()); - return false; - } - - // search by date (in case its standard record that has become pump record) - QueryBuilder queryBuilder2 = getDaoTemporaryBasal().queryBuilder(); - Where where2 = queryBuilder2.where(); - where2.eq("date", tempBasal.date); - PreparedQuery preparedQuery2 = queryBuilder2.prepare(); - List trList2 = getDaoTemporaryBasal().query(preparedQuery2); - - if (trList2.size() > 0) { - old = trList2.get(0); - - old.copyFromPump(tempBasal); - old.source = Source.PUMP; - - aapsLogger.debug(LTag.DATABASE, "TEMPBASAL: Updated record with Pump Data : " + Source.getString(tempBasal.source) + " " + tempBasal.toString()); - - getDaoTemporaryBasal().update(old); - openHumansUploader.enqueueTemporaryBasal(old); - - updateEarliestDataChange(tempBasal.date); - scheduleTemporaryBasalChange(); - - return false; - } - - getDaoTemporaryBasal().create(tempBasal); - openHumansUploader.enqueueTemporaryBasal(tempBasal); - aapsLogger.debug(LTag.DATABASE, "TEMPBASAL: New record from: " + Source.getString(tempBasal.source) + " " + tempBasal.toString()); - updateEarliestDataChange(tempBasal.date); - scheduleTemporaryBasalChange(); - return true; - } - if (tempBasal.source == Source.NIGHTSCOUT) { - old = getDaoTemporaryBasal().queryForId(tempBasal.date); - if (old != null) { - if (!old.isAbsolute && tempBasal.isAbsolute) { // converted to absolute by "ns_sync_use_absolute" - // so far ignore, do not convert back because it may not be accurate - return false; - } - if (!old.isEqual(tempBasal)) { - long oldDate = old.date; - getDaoTemporaryBasal().delete(old); // need to delete/create because date may change too - old.copyFrom(tempBasal); - getDaoTemporaryBasal().create(old); - openHumansUploader.enqueueTemporaryBasal(old); - aapsLogger.debug(LTag.DATABASE, "TEMPBASAL: Updating record by date from: " + Source.getString(tempBasal.source) + " " + old.toString()); - updateEarliestDataChange(oldDate); - updateEarliestDataChange(old.date); - scheduleTemporaryBasalChange(); - return true; - } - return false; - } - // find by NS _id - if (tempBasal._id != null) { - QueryBuilder queryBuilder = getDaoTemporaryBasal().queryBuilder(); - Where where = queryBuilder.where(); - where.eq("_id", tempBasal._id); - PreparedQuery preparedQuery = queryBuilder.prepare(); - List trList = getDaoTemporaryBasal().query(preparedQuery); - if (trList.size() > 0) { - old = trList.get(0); - if (!old.isEqual(tempBasal)) { - long oldDate = old.date; - getDaoTemporaryBasal().delete(old); // need to delete/create because date may change too - old.copyFrom(tempBasal); - getDaoTemporaryBasal().create(old); - openHumansUploader.enqueueTemporaryBasal(old); - aapsLogger.debug(LTag.DATABASE, "TEMPBASAL: Updating record by _id from: " + Source.getString(tempBasal.source) + " " + old.toString()); - updateEarliestDataChange(oldDate); - updateEarliestDataChange(old.date); - scheduleTemporaryBasalChange(); - return true; - } - } - } - getDaoTemporaryBasal().create(tempBasal); - openHumansUploader.enqueueTemporaryBasal(tempBasal); - aapsLogger.debug(LTag.DATABASE, "TEMPBASAL: New record from: " + Source.getString(tempBasal.source) + " " + tempBasal.toString()); - updateEarliestDataChange(tempBasal.date); - scheduleTemporaryBasalChange(); - return true; - } - if (tempBasal.source == Source.USER) { - getDaoTemporaryBasal().create(tempBasal); - openHumansUploader.enqueueTemporaryBasal(tempBasal); - aapsLogger.debug(LTag.DATABASE, "TEMPBASAL: New record from: " + Source.getString(tempBasal.source) + " " + tempBasal.toString()); - updateEarliestDataChange(tempBasal.date); - scheduleTemporaryBasalChange(); - return true; - } - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - } - return false; - } - - public void delete(TemporaryBasal tempBasal) { - try { - getDaoTemporaryBasal().delete(tempBasal); - openHumansUploader.enqueueTemporaryBasal(tempBasal, true); - updateEarliestDataChange(tempBasal.date); - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - } - scheduleTemporaryBasalChange(); - } - - public List getAllTemporaryBasals() { - try { - return getDaoTemporaryBasal().queryForAll(); - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - } - return Collections.emptyList(); - } - - public List getTemporaryBasalsDataFromTime(long mills, boolean ascending) { - try { - List tempbasals; - QueryBuilder queryBuilder = getDaoTemporaryBasal().queryBuilder(); - queryBuilder.orderBy("date", ascending); - Where where = queryBuilder.where(); - where.ge("date", mills); - PreparedQuery preparedQuery = queryBuilder.prepare(); - tempbasals = getDaoTemporaryBasal().query(preparedQuery); - return tempbasals; - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - } - return new ArrayList(); - } - - public List getTemporaryBasalsDataFromTime(long from, long to, boolean ascending) { - try { - List tempbasals; - QueryBuilder queryBuilder = getDaoTemporaryBasal().queryBuilder(); - queryBuilder.orderBy("date", ascending); - Where where = queryBuilder.where(); - where.between("date", from, to); - PreparedQuery preparedQuery = queryBuilder.prepare(); - tempbasals = getDaoTemporaryBasal().query(preparedQuery); - return tempbasals; - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - } - return new ArrayList(); - } - - private void scheduleTemporaryBasalChange() { - class PostRunnable implements Runnable { - public void run() { - aapsLogger.debug(LTag.DATABASE, "Firing EventTempBasalChange"); - rxBus.send(new EventReloadTempBasalData()); - rxBus.send(new EventTempBasalChange()); - if (earliestDataChange != null) - rxBus.send(new EventNewHistoryData(earliestDataChange)); - earliestDataChange = null; - scheduledTemBasalsPost = null; - } - } - // prepare task for execution in 1 sec - // cancel waiting task to prevent sending multiple posts - if (scheduledTemBasalsPost != null) - scheduledTemBasalsPost.cancel(false); - Runnable task = new PostRunnable(); - final int sec = 1; - scheduledTemBasalsPost = tempBasalsWorker.schedule(task, sec, TimeUnit.SECONDS); - - } - - /* - { - "_id": "59232e1ddd032d04218dab00", - "eventType": "Temp Basal", - "duration": 60, - "percent": -50, - "created_at": "2017-05-22T18:29:57Z", - "enteredBy": "AndroidAPS", - "notes": "Basal Temp Start 50% 60.0 min", - "NSCLIENT_ID": 1495477797863, - "mills": 1495477797000, - "mgdl": 194.5, - "endmills": 1495481397000 - } - */ - - public void createTempBasalFromJsonIfNotExists(JSONObject trJson) { - try { - if (trJson.has("originalExtendedAmount")) { // extended bolus uploaded as temp basal - ExtendedBolus extendedBolus = new ExtendedBolus(StaticInjector.Companion.getInstance()) - .source(Source.NIGHTSCOUT) - .date(trJson.getLong("mills")) - .pumpId(trJson.has("pumpId") ? trJson.getLong("pumpId") : 0) - .durationInMinutes(trJson.getInt("duration")) - .insulin(trJson.getDouble("originalExtendedAmount")) - ._id(trJson.getString("_id")); - // if faking found in NS, adapt AAPS to use it too - if (!virtualPumpPlugin.getFakingStatus()) { - virtualPumpPlugin.setFakingStatus(true); - updateEarliestDataChange(0); - scheduleTemporaryBasalChange(); - } - createOrUpdate(extendedBolus); - } else if (trJson.has("isFakedTempBasal")) { // extended bolus end uploaded as temp basal end - ExtendedBolus extendedBolus = new ExtendedBolus(StaticInjector.Companion.getInstance()); - extendedBolus.source = Source.NIGHTSCOUT; - extendedBolus.date = trJson.getLong("mills"); - extendedBolus.pumpId = trJson.has("pumpId") ? trJson.getLong("pumpId") : 0; - extendedBolus.durationInMinutes = 0; - extendedBolus.insulin = 0; - extendedBolus._id = trJson.getString("_id"); - // if faking found in NS, adapt AAPS to use it too - if (!virtualPumpPlugin.getFakingStatus()) { - virtualPumpPlugin.setFakingStatus(true); - updateEarliestDataChange(0); - scheduleTemporaryBasalChange(); - } - createOrUpdate(extendedBolus); - } else { - TemporaryBasal tempBasal = new TemporaryBasal(StaticInjector.Companion.getInstance()) - .date(trJson.getLong("mills")) - .source(Source.NIGHTSCOUT) - .pumpId(trJson.has("pumpId") ? trJson.getLong("pumpId") : 0); - if (trJson.has("duration")) { - tempBasal.durationInMinutes = trJson.getInt("duration"); - } - if (trJson.has("percent")) { - tempBasal.percentRate = trJson.getInt("percent") + 100; - tempBasal.isAbsolute = false; - } - if (trJson.has("absolute")) { - tempBasal.absoluteRate = trJson.getDouble("absolute"); - tempBasal.isAbsolute = true; - } - tempBasal._id = trJson.getString("_id"); - createOrUpdate(tempBasal); - } - } catch (JSONException e) { - aapsLogger.error("Unhandled exception: " + trJson.toString(), e); - } - } - - public void deleteTempBasalById(String _id) { - TemporaryBasal stored = findTempBasalById(_id); - if (stored != null) { - aapsLogger.debug(LTag.DATABASE, "TEMPBASAL: Removing TempBasal record from database: " + stored.toString()); - delete(stored); - updateEarliestDataChange(stored.date); - scheduleTemporaryBasalChange(); - } - } - - public TemporaryBasal findTempBasalById(String _id) { - try { - QueryBuilder queryBuilder = null; - queryBuilder = getDaoTemporaryBasal().queryBuilder(); - Where where = queryBuilder.where(); - where.eq("_id", _id); - PreparedQuery preparedQuery = queryBuilder.prepare(); - List list = getDaoTemporaryBasal().query(preparedQuery); - - if (list.size() != 1) { - return null; - } else { - return list.get(0); - } - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - } - return null; - } - - - public TemporaryBasal findTempBasalByPumpId(Long pumpId) { - try { - QueryBuilder queryBuilder = null; - queryBuilder = getDaoTemporaryBasal().queryBuilder(); - queryBuilder.orderBy("date", false); - Where where = queryBuilder.where(); - where.eq("pumpId", pumpId); - PreparedQuery preparedQuery = queryBuilder.prepare(); - List list = getDaoTemporaryBasal().query(preparedQuery); - - if (list.size() > 0) - return list.get(0); - else - return null; - - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - } - return null; - } - - - // ------------ ExtendedBolus handling --------------- - - public boolean createOrUpdate(ExtendedBolus extendedBolus) { - try { - aapsLogger.debug(LTag.DATABASE, "EXTENDEDBOLUS: createOrUpdate: " + Source.getString(extendedBolus.source) + " " + extendedBolus.log()); - - ExtendedBolus old; - extendedBolus.date = roundDateToSec(extendedBolus.date); - - if (extendedBolus.source == Source.PUMP) { - // if pumpId == 0 do not check for existing pumpId - // used with pumps without history - // and insight where record as added first without pumpId - // and then is record updated with pumpId - if (extendedBolus.pumpId == 0) { - getDaoExtendedBolus().createOrUpdate(extendedBolus); - openHumansUploader.enqueueExtendedBolus(extendedBolus); - } else { - QueryBuilder queryBuilder = getDaoExtendedBolus().queryBuilder(); - Where where = queryBuilder.where(); - where.eq("pumpId", extendedBolus.pumpId); - PreparedQuery preparedQuery = queryBuilder.prepare(); - List trList = getDaoExtendedBolus().query(preparedQuery); - if (trList.size() > 1) { - aapsLogger.error("EXTENDEDBOLUS: Multiple records found for pumpId: " + extendedBolus.pumpId); - return false; - } - getDaoExtendedBolus().createOrUpdate(extendedBolus); - openHumansUploader.enqueueExtendedBolus(extendedBolus); - } - aapsLogger.debug(LTag.DATABASE, "EXTENDEDBOLUS: New record from: " + Source.getString(extendedBolus.source) + " " + extendedBolus.log()); - updateEarliestDataChange(extendedBolus.date); - scheduleExtendedBolusChange(); - return true; - } - if (extendedBolus.source == Source.NIGHTSCOUT) { - old = getDaoExtendedBolus().queryForId(extendedBolus.date); - if (old != null) { - if (!old.isEqual(extendedBolus)) { - long oldDate = old.date; - getDaoExtendedBolus().delete(old); // need to delete/create because date may change too - old.copyFrom(extendedBolus); - getDaoExtendedBolus().create(old); - aapsLogger.debug(LTag.DATABASE, "EXTENDEDBOLUS: Updating record by date from: " + Source.getString(extendedBolus.source) + " " + old.log()); - openHumansUploader.enqueueExtendedBolus(old); - updateEarliestDataChange(oldDate); - updateEarliestDataChange(old.date); - scheduleExtendedBolusChange(); - return true; - } - return false; - } - // find by NS _id - if (extendedBolus._id != null) { - QueryBuilder queryBuilder = getDaoExtendedBolus().queryBuilder(); - Where where = queryBuilder.where(); - where.eq("_id", extendedBolus._id); - PreparedQuery preparedQuery = queryBuilder.prepare(); - List trList = getDaoExtendedBolus().query(preparedQuery); - if (trList.size() > 0) { - old = trList.get(0); - if (!old.isEqual(extendedBolus)) { - long oldDate = old.date; - getDaoExtendedBolus().delete(old); // need to delete/create because date may change too - old.copyFrom(extendedBolus); - getDaoExtendedBolus().create(old); - aapsLogger.debug(LTag.DATABASE, "EXTENDEDBOLUS: Updating record by _id from: " + Source.getString(extendedBolus.source) + " " + old.log()); - openHumansUploader.enqueueExtendedBolus(old); - updateEarliestDataChange(oldDate); - updateEarliestDataChange(old.date); - scheduleExtendedBolusChange(); - return true; - } - } - } - getDaoExtendedBolus().create(extendedBolus); - aapsLogger.debug(LTag.DATABASE, "EXTENDEDBOLUS: New record from: " + Source.getString(extendedBolus.source) + " " + extendedBolus.log()); - openHumansUploader.enqueueExtendedBolus(extendedBolus); - updateEarliestDataChange(extendedBolus.date); - scheduleExtendedBolusChange(); - return true; - } - if (extendedBolus.source == Source.USER) { - getDaoExtendedBolus().create(extendedBolus); - aapsLogger.debug(LTag.DATABASE, "EXTENDEDBOLUS: New record from: " + Source.getString(extendedBolus.source) + " " + extendedBolus.log()); - openHumansUploader.enqueueExtendedBolus(extendedBolus); - updateEarliestDataChange(extendedBolus.date); - scheduleExtendedBolusChange(); - return true; - } - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - } - return false; - } - - public List getAllExtendedBoluses() { - try { - return getDaoExtendedBolus().queryForAll(); - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - } - return Collections.emptyList(); - } - - public ExtendedBolus getExtendedBolusByPumpId(long pumpId) { - try { - return getDaoExtendedBolus().queryBuilder() - .where().eq("pumpId", pumpId) - .queryForFirst(); - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - } - return null; - } - - public void delete(ExtendedBolus extendedBolus) { - try { - getDaoExtendedBolus().delete(extendedBolus); - openHumansUploader.enqueueExtendedBolus(extendedBolus, true); - updateEarliestDataChange(extendedBolus.date); - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - } - scheduleExtendedBolusChange(); - } - - public List getExtendedBolusDataFromTime(long mills, boolean ascending) { - try { - List extendedBoluses; - QueryBuilder queryBuilder = getDaoExtendedBolus().queryBuilder(); - queryBuilder.orderBy("date", ascending); - Where where = queryBuilder.where(); - where.ge("date", mills); - PreparedQuery preparedQuery = queryBuilder.prepare(); - extendedBoluses = getDaoExtendedBolus().query(preparedQuery); - return extendedBoluses; - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - } - return new ArrayList(); - } - - public List getExtendedBolusDataFromTime(long from, long to, boolean ascending) { - try { - List extendedBoluses; - QueryBuilder queryBuilder = getDaoExtendedBolus().queryBuilder(); - queryBuilder.orderBy("date", ascending); - Where where = queryBuilder.where(); - where.between("date", from, to); - PreparedQuery preparedQuery = queryBuilder.prepare(); - extendedBoluses = getDaoExtendedBolus().query(preparedQuery); - return extendedBoluses; - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - } - return new ArrayList(); - } - - public void deleteExtendedBolusById(String _id) { - ExtendedBolus stored = findExtendedBolusById(_id); - if (stored != null) { - aapsLogger.debug(LTag.DATABASE, "EXTENDEDBOLUS: Removing ExtendedBolus record from database: " + stored.toString()); - delete(stored); - updateEarliestDataChange(stored.date); - scheduleExtendedBolusChange(); - } - } - - public ExtendedBolus findExtendedBolusById(String _id) { - try { - QueryBuilder queryBuilder = null; - queryBuilder = getDaoExtendedBolus().queryBuilder(); - Where where = queryBuilder.where(); - where.eq("_id", _id); - PreparedQuery preparedQuery = queryBuilder.prepare(); - List list = getDaoExtendedBolus().query(preparedQuery); - - if (list.size() == 1) { - return list.get(0); - } else { - return null; - } - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - } - return null; - } - - /* -{ - "_id": "5924898d577eb0880e355337", - "eventType": "Combo Bolus", - "duration": 120, - "splitNow": 0, - "splitExt": 100, - "enteredinsulin": 1, - "relative": 1, - "created_at": "2017-05-23T19:12:14Z", - "enteredBy": "AndroidAPS", - "NSCLIENT_ID": 1495566734628, - "mills": 1495566734000, - "mgdl": 106 -} - */ - - public void createExtendedBolusFromJsonIfNotExists(JSONObject json) { - ExtendedBolus extendedBolus = ExtendedBolus.createFromJson(StaticInjector.Companion.getInstance(), json); - if (extendedBolus != null) - createOrUpdate(extendedBolus); - } - - private void scheduleExtendedBolusChange() { - class PostRunnable implements Runnable { - public void run() { - aapsLogger.debug(LTag.DATABASE, "Firing EventExtendedBolusChange"); - rxBus.send(new EventReloadTreatmentData(new EventExtendedBolusChange())); - if (earliestDataChange != null) - rxBus.send(new EventNewHistoryData(earliestDataChange)); - earliestDataChange = null; - scheduledExtendedBolusPost = null; - } - } - // prepare task for execution in 1 sec - // cancel waiting task to prevent sending multiple posts - if (scheduledExtendedBolusPost != null) - scheduledExtendedBolusPost.cancel(false); - Runnable task = new PostRunnable(); - final int sec = 1; - scheduledExtendedBolusPost = extendedBolusWorker.schedule(task, sec, TimeUnit.SECONDS); - - } - - // ---------------- ProfileSwitch handling --------------- - - public List getProfileSwitchData(long from, boolean ascending) { - try { - Dao daoProfileSwitch = getDaoProfileSwitch(); - List profileSwitches; - QueryBuilder queryBuilder = daoProfileSwitch.queryBuilder(); - queryBuilder.orderBy("date", ascending); - queryBuilder.limit(100L); - Where where = queryBuilder.where(); - where.ge("date", from); - PreparedQuery preparedQuery = queryBuilder.prepare(); - profileSwitches = daoProfileSwitch.query(preparedQuery); - //add last one without duration - ProfileSwitch last = getLastProfileSwitchWithoutDuration(); - if (last != null) { - if (!isInList(profileSwitches, last)) - profileSwitches.add(last); - } - return profileSwitches; - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - } - return new ArrayList<>(); - } - - boolean isInList(List profileSwitches, ProfileSwitch last) { - for (ProfileSwitch ps : profileSwitches) { - if (ps.isEqual(last)) return true; - } - return false; - } - - public List getAllProfileSwitches() { - try { - return getDaoProfileSwitch().queryForAll(); - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - } - return Collections.emptyList(); - } - - @Nullable - private ProfileSwitch getLastProfileSwitchWithoutDuration() { - try { - Dao daoProfileSwitch = getDaoProfileSwitch(); - List profileSwitches; - QueryBuilder queryBuilder = daoProfileSwitch.queryBuilder(); - queryBuilder.orderBy("date", false); - queryBuilder.limit(1L); - Where where = queryBuilder.where(); - where.eq("durationInMinutes", 0); - PreparedQuery preparedQuery = queryBuilder.prepare(); - profileSwitches = daoProfileSwitch.query(preparedQuery); - if (profileSwitches.size() > 0) - return profileSwitches.get(0); - else - return null; - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - } - return null; - } - - public List getProfileSwitchEventsFromTime(long mills, boolean ascending) { - try { - Dao daoProfileSwitch = getDaoProfileSwitch(); - List profileSwitches; - QueryBuilder queryBuilder = daoProfileSwitch.queryBuilder(); - queryBuilder.orderBy("date", ascending); - queryBuilder.limit(100L); - Where where = queryBuilder.where(); - where.ge("date", mills); - PreparedQuery preparedQuery = queryBuilder.prepare(); - profileSwitches = daoProfileSwitch.query(preparedQuery); - return profileSwitches; - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - } - return new ArrayList<>(); - } - - public List getProfileSwitchEventsFromTime(long from, long to, boolean ascending) { - try { - Dao daoProfileSwitch = getDaoProfileSwitch(); - List profileSwitches; - QueryBuilder queryBuilder = daoProfileSwitch.queryBuilder(); - queryBuilder.orderBy("date", ascending); - queryBuilder.limit(100L); - Where where = queryBuilder.where(); - where.between("date", from, to); - PreparedQuery preparedQuery = queryBuilder.prepare(); - profileSwitches = daoProfileSwitch.query(preparedQuery); - return profileSwitches; - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - } - return new ArrayList<>(); - } - - public boolean createOrUpdate(ProfileSwitch profileSwitch) { - try { - ProfileSwitch old; - profileSwitch.date = roundDateToSec(profileSwitch.date); - - if (profileSwitch.source == Source.NIGHTSCOUT) { - old = getDaoProfileSwitch().queryForId(profileSwitch.date); - if (old != null) { - if (!old.isEqual(profileSwitch)) { - profileSwitch.source = old.source; - profileSwitch.profileName = old.profileName; // preserver profileName to prevent multiple CPP extension - getDaoProfileSwitch().delete(old); // need to delete/create because date may change too - getDaoProfileSwitch().create(profileSwitch); - aapsLogger.debug(LTag.DATABASE, "PROFILESWITCH: Updating record by date from: " + Source.getString(profileSwitch.source) + " " + old.toString()); - openHumansUploader.enqueueProfileSwitch(profileSwitch); - scheduleProfileSwitchChange(); - return true; - } - return false; - } - // find by NS _id - if (profileSwitch._id != null) { - QueryBuilder queryBuilder = getDaoProfileSwitch().queryBuilder(); - Where where = queryBuilder.where(); - where.eq("_id", profileSwitch._id); - PreparedQuery preparedQuery = queryBuilder.prepare(); - List trList = getDaoProfileSwitch().query(preparedQuery); - if (trList.size() > 0) { - old = trList.get(0); - if (!old.isEqual(profileSwitch)) { - getDaoProfileSwitch().delete(old); // need to delete/create because date may change too - old.copyFrom(profileSwitch); - getDaoProfileSwitch().create(old); - aapsLogger.debug(LTag.DATABASE, "PROFILESWITCH: Updating record by _id from: " + Source.getString(profileSwitch.source) + " " + old.toString()); - openHumansUploader.enqueueProfileSwitch(old); - scheduleProfileSwitchChange(); - return true; - } - } - } - // look for already added percentage from NS - profileSwitch.profileName = PercentageSplitter.pureName(profileSwitch.profileName); - getDaoProfileSwitch().create(profileSwitch); - aapsLogger.debug(LTag.DATABASE, "PROFILESWITCH: New record from: " + Source.getString(profileSwitch.source) + " " + profileSwitch.toString()); - openHumansUploader.enqueueProfileSwitch(profileSwitch); - scheduleProfileSwitchChange(); - return true; - } - if (profileSwitch.source == Source.USER) { - getDaoProfileSwitch().create(profileSwitch); - aapsLogger.debug(LTag.DATABASE, "PROFILESWITCH: New record from: " + Source.getString(profileSwitch.source) + " " + profileSwitch.toString()); - openHumansUploader.enqueueProfileSwitch(profileSwitch); - scheduleProfileSwitchChange(); - return true; - } - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - } - return false; - } - - public void delete(ProfileSwitch profileSwitch) { - try { - getDaoProfileSwitch().delete(profileSwitch); - openHumansUploader.enqueueProfileSwitch(profileSwitch, true); - scheduleProfileSwitchChange(); - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - } - } - - private void scheduleProfileSwitchChange() { - class PostRunnable implements Runnable { - public void run() { - aapsLogger.debug(LTag.DATABASE, "Firing EventProfileNeedsUpdate"); - rxBus.send(new EventReloadProfileSwitchData()); - rxBus.send(new EventProfileNeedsUpdate()); - scheduledProfileSwitchEventPost = null; - } - } - // prepare task for execution in 1 sec - // cancel waiting task to prevent sending multiple posts - if (scheduledProfileSwitchEventPost != null) - scheduledProfileSwitchEventPost.cancel(false); - Runnable task = new PostRunnable(); - final int sec = 1; - scheduledProfileSwitchEventPost = profileSwitchEventWorker.schedule(task, sec, TimeUnit.SECONDS); - - } - - /* -{ - "_id":"592fa43ed97496a80da913d2", - "created_at":"2017-06-01T05:20:06Z", - "eventType":"Profile Switch", - "profile":"2016 +30%", - "units":"mmol", - "enteredBy":"sony", - "NSCLIENT_ID":1496294454309, -} - */ - - public void createProfileSwitchFromJsonIfNotExists(JSONObject trJson) { - try { - ProfileSwitch profileSwitch = new ProfileSwitch(StaticInjector.Companion.getInstance()); - profileSwitch.date = trJson.getLong("mills"); - if (trJson.has("duration")) - profileSwitch.durationInMinutes = trJson.getInt("duration"); - profileSwitch._id = trJson.getString("_id"); - profileSwitch.profileName = trJson.getString("profile"); - profileSwitch.isCPP = trJson.has("CircadianPercentageProfile"); - profileSwitch.source = Source.NIGHTSCOUT; - if (trJson.has("timeshift")) - profileSwitch.timeshift = trJson.getInt("timeshift"); - if (trJson.has("percentage")) - profileSwitch.percentage = trJson.getInt("percentage"); - if (trJson.has("profileJson")) - profileSwitch.profileJson = trJson.getString("profileJson"); - else { - ProfileInterface profileInterface = activePlugin.getActiveProfileInterface(); - ProfileStore store = profileInterface.getProfile(); - if (store != null) { - Profile profile = store.getSpecificProfile(profileSwitch.profileName); - if (profile != null) { - profileSwitch.profileJson = profile.getData().toString(); - aapsLogger.debug(LTag.DATABASE, "Profile switch prefilled with JSON from local store"); - // Update data in NS - nsUpload.updateProfileSwitch(profileSwitch); - } else { - aapsLogger.debug(LTag.DATABASE, "JSON for profile switch doesn't exist. Ignoring: " + trJson.toString()); - return; - } - } else { - aapsLogger.debug(LTag.DATABASE, "Store for profile switch doesn't exist. Ignoring: " + trJson.toString()); - return; - } - } - if (trJson.has("profilePlugin")) - profileSwitch.profilePlugin = trJson.getString("profilePlugin"); - createOrUpdate(profileSwitch); - } catch (JSONException e) { - aapsLogger.error("Unhandled exception: " + trJson.toString(), e); - } - } - - public void deleteProfileSwitchById(String _id) { - ProfileSwitch stored = findProfileSwitchById(_id); - if (stored != null) { - aapsLogger.debug(LTag.DATABASE, "PROFILESWITCH: Removing ProfileSwitch record from database: " + stored.toString()); - delete(stored); - scheduleProfileSwitchChange(); - } - } - - public ProfileSwitch findProfileSwitchById(String _id) { - try { - QueryBuilder queryBuilder = getDaoProfileSwitch().queryBuilder(); - Where where = queryBuilder.where(); - where.eq("_id", _id); - PreparedQuery preparedQuery = queryBuilder.prepare(); - List list = getDaoProfileSwitch().query(preparedQuery); - - if (list.size() == 1) { - return list.get(0); - } else { - return null; - } - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - } - return null; - } - - // ---------------- Insight history handling --------------- - - public void createOrUpdate(InsightHistoryOffset offset) { - try { - getDaoInsightHistoryOffset().createOrUpdate(offset); - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - } - } - - public InsightHistoryOffset getInsightHistoryOffset(String pumpSerial) { - try { - return getDaoInsightHistoryOffset().queryForId(pumpSerial); - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - } - return null; - } - - public void createOrUpdate(InsightBolusID bolusID) { - try { - getDaoInsightBolusID().createOrUpdate(bolusID); - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - } - } - - public InsightBolusID getInsightBolusID(String pumpSerial, int bolusID, long timestamp) { - try { - return getDaoInsightBolusID().queryBuilder() - .where().eq("pumpSerial", pumpSerial) - .and().eq("bolusID", bolusID) - .and().between("timestamp", timestamp - 259200000, timestamp + 259200000) - .queryForFirst(); - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - } - return null; - } - - public void createOrUpdate(InsightPumpID pumpID) { - try { - getDaoInsightPumpID().createOrUpdate(pumpID); - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - } - } - - public InsightPumpID getPumpStoppedEvent(String pumpSerial, long before) { - try { - return getDaoInsightPumpID().queryBuilder() - .orderBy("timestamp", false) - .where().eq("pumpSerial", pumpSerial) - .and().in("eventType", "PumpStopped", "PumpPaused") - .and().lt("timestamp", before) - .queryForFirst(); - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - } - return null; - } - // ---------------- Food handling --------------- // ---------------- PodHistory handling --------------- @@ -1493,16 +293,4 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { } return 0L; } - - public long getCountOfAllRows() { - try { - return getDaoExtendedBolus().countOf() - + getDaoProfileSwitch().countOf() - + getDaoTDD().countOf() - + getDaoTemporaryBasal().countOf(); - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - } - return 0L; - } } diff --git a/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelperProvider.java b/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelperProvider.java index 6587fb6371..da0b2479cf 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelperProvider.java +++ b/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelperProvider.java @@ -1,12 +1,10 @@ package info.nightscout.androidaps.db; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.j256.ormlite.dao.CloseableIterator; -import org.jetbrains.annotations.Nullable; -import org.json.JSONObject; - import java.sql.SQLException; import java.util.List; @@ -16,74 +14,31 @@ import javax.inject.Singleton; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.interfaces.DatabaseHelperInterface; +@Deprecated @Singleton public class DatabaseHelperProvider implements DatabaseHelperInterface { @Inject DatabaseHelperProvider() { } - @Override public void createOrUpdate(@NonNull DanaRHistoryRecord record) { - MainApp.Companion.getDbHelper().createOrUpdate(record); - } - @Override public void createOrUpdate(@NonNull OmnipodHistoryRecord record) { MainApp.Companion.getDbHelper().createOrUpdate(record); } - @NonNull @Override public List getDanaRHistoryRecordsByType(byte type) { - return MainApp.Companion.getDbHelper().getDanaRHistoryRecordsByType(type); - } - - @NonNull @Override public List getTDDs() { - return MainApp.Companion.getDbHelper().getTDDs(); - } - - @Override public long size(@NonNull String table) { - return MainApp.Companion.getDbHelper().size(table); - } - - @Override public void create(@NonNull DbRequest record) { - try { - MainApp.Companion.getDbHelper().create(record); - } catch (SQLException e) { - e.printStackTrace(); - } - } - - @Override public void deleteAllDbRequests() { - MainApp.Companion.getDbHelper().deleteAllDbRequests(); - } - - @Override public int deleteDbRequest(@NonNull String id) { - return MainApp.Companion.getDbHelper().deleteDbRequest(id); - } - - @Override public void deleteDbRequestbyMongoId(@NonNull String action, @NonNull String _id) { - MainApp.Companion.getDbHelper().deleteDbRequestbyMongoId(action, _id); - } - - @NonNull @Override public CloseableIterator getDbRequestIterator() { - return MainApp.Companion.getDbHelper().getDbRequestIterator(); - } - - @Override public long roundDateToSec(long date) { - return MainApp.Companion.getDbHelper().roundDateToSec(date); - } - - @Override public void createOrUpdateTDD(@NonNull TDD record) { - MainApp.Companion.getDbHelper().createOrUpdateTDD(record); - } - @Override public boolean createOrUpdate(@NonNull TemporaryBasal tempBasal) { - return MainApp.Companion.getDbHelper().createOrUpdate(tempBasal); +// return MainApp.Companion.getDbHelper().createOrUpdate(tempBasal); + return false; } - @NonNull @Override public TemporaryBasal findTempBasalByPumpId(long id) { - return MainApp.Companion.getDbHelper().findTempBasalByPumpId(id); + @Nullable @Override public TemporaryBasal findTempBasalByPumpId(long id) { +// return MainApp.Companion.getDbHelper().findTempBasalByPumpId(id); + return null; } + @Deprecated @NonNull @Override public List getTemporaryBasalsDataFromTime(long mills, boolean ascending) { - return MainApp.Companion.getDbHelper().getTemporaryBasalsDataFromTime(mills, ascending); +// return MainApp.Companion.getDbHelper().getTemporaryBasalsDataFromTime(mills, ascending); + return null; } @NonNull @Override public List getAllOmnipodHistoryRecordsFromTimestamp(long timestamp, boolean ascending) { @@ -94,84 +49,13 @@ public class DatabaseHelperProvider implements DatabaseHelperInterface { return MainApp.Companion.getDbHelper().findOmnipodHistoryRecordByPumpId(pumpId); } - @NonNull @Override public List getTDDsForLastXDays(int days) { - return MainApp.Companion.getDbHelper().getTDDsForLastXDays(days); - } - - @NonNull @Override public List getProfileSwitchData(long from, boolean ascending) { - return MainApp.Companion.getDbHelper().getProfileSwitchData(from, ascending); - } - - @Override public void createOrUpdate(@NonNull InsightBolusID record) { - MainApp.Companion.getDbHelper().createOrUpdate(record); - } - - @Override public void createOrUpdate(@NonNull InsightPumpID record) { - MainApp.Companion.getDbHelper().createOrUpdate(record); - } - - @Override public void createOrUpdate(@NonNull InsightHistoryOffset record) { - MainApp.Companion.getDbHelper().createOrUpdate(record); - } - @Override public void delete(@NonNull ExtendedBolus extendedBolus) { - MainApp.Companion.getDbHelper().delete(extendedBolus); +// MainApp.Companion.getDbHelper().delete(extendedBolus); } @Nullable @Override public ExtendedBolus getExtendedBolusByPumpId(long pumpId) { - return MainApp.Companion.getDbHelper().getExtendedBolusByPumpId(pumpId); - } - - @Nullable @Override public InsightBolusID getInsightBolusID(@NonNull String pumpSerial, int bolusID, long timestamp) { - return MainApp.Companion.getDbHelper().getInsightBolusID(pumpSerial, bolusID, timestamp); - } - - @Nullable @Override public InsightHistoryOffset getInsightHistoryOffset(@NonNull String pumpSerial) { - return MainApp.Companion.getDbHelper().getInsightHistoryOffset(pumpSerial); - } - - @Nullable @Override public InsightPumpID getPumpStoppedEvent(@NonNull String pumpSerial, long before) { - return MainApp.Companion.getDbHelper().getPumpStoppedEvent(pumpSerial, before); - } - - @Override public boolean createOrUpdate(@NonNull ExtendedBolus extendedBolus) { - return MainApp.Companion.getDbHelper().createOrUpdate(extendedBolus); - } - - @Override public void createOrUpdate(@NonNull ProfileSwitch profileSwitch) { - MainApp.Companion.getDbHelper().createOrUpdate(profileSwitch); - } - - @Override public void delete(@NonNull TemporaryBasal tempBasal) { - MainApp.Companion.getDbHelper().delete(tempBasal); - } - - @NonNull @Override public List getExtendedBolusDataFromTime(long mills, boolean ascending) { - return MainApp.Companion.getDbHelper().getExtendedBolusDataFromTime(mills, ascending); - } - - @Override public void deleteTempBasalById(@NonNull String _id) { - MainApp.Companion.getDbHelper().deleteTempBasalById(_id); - } - - @Override public void deleteExtendedBolusById(@NonNull String _id) { - MainApp.Companion.getDbHelper().deleteExtendedBolusById(_id); - } - - @Override public void deleteProfileSwitchById(@NonNull String _id) { - MainApp.Companion.getDbHelper().deleteProfileSwitchById(_id); - } - - @Override public void createTempBasalFromJsonIfNotExists(@NonNull JSONObject json) { - MainApp.Companion.getDbHelper().createTempBasalFromJsonIfNotExists(json); - } - - @Override public void createExtendedBolusFromJsonIfNotExists(@NonNull JSONObject json) { - MainApp.Companion.getDbHelper().createExtendedBolusFromJsonIfNotExists(json); - } - - @Override public void createProfileSwitchFromJsonIfNotExists(@NonNull JSONObject trJson) { - MainApp.Companion.getDbHelper().createProfileSwitchFromJsonIfNotExists(trJson); +// return MainApp.Companion.getDbHelper().getExtendedBolusByPumpId(pumpId); + return null; } @Override public void resetDatabases() { @@ -182,42 +66,10 @@ public class DatabaseHelperProvider implements DatabaseHelperInterface { MainApp.Companion.getDbHelper().createOrUpdate(record); } - @Override public void delete(@NonNull ProfileSwitch profileSwitch) { - MainApp.Companion.getDbHelper().delete(profileSwitch); - } - - @NonNull @Override public List getProfileSwitchEventsFromTime(long from, long to, boolean ascending) { - return MainApp.Companion.getDbHelper().getProfileSwitchEventsFromTime(from, to, ascending); - } - - @NonNull @Override public List getProfileSwitchEventsFromTime(long mills, boolean ascending) { - return MainApp.Companion.getDbHelper().getProfileSwitchEventsFromTime(mills, ascending); - } - - @NonNull @Override public List getAllExtendedBoluses() { - return MainApp.Companion.getDbHelper().getAllExtendedBoluses(); - } - - @NonNull @Override public List getAllProfileSwitches() { - return MainApp.Companion.getDbHelper().getAllProfileSwitches(); - } - - @NonNull @Override public List getAllTDDs() { - return MainApp.Companion.getDbHelper().getAllTDDs(); - } - - @NonNull @Override public List getAllTemporaryBasals() { - return MainApp.Companion.getDbHelper().getAllTemporaryBasals(); - } - @NonNull @Override public List getAllOHQueueItems(long maxEntries) { return MainApp.Companion.getDbHelper().getAllOHQueueItems(maxEntries); } - @Override public void resetProfileSwitch() { - MainApp.Companion.getDbHelper().resetProfileSwitch(); - } - @Override public long getOHQueueSize() { return MainApp.Companion.getDbHelper().getOHQueueSize(); } @@ -226,10 +78,6 @@ public class DatabaseHelperProvider implements DatabaseHelperInterface { MainApp.Companion.getDbHelper().clearOpenHumansQueue(); } - @Override public long getCountOfAllRows() { - return MainApp.Companion.getDbHelper().getCountOfAllRows(); - } - @Override public void removeAllOHQueueItemsWithIdSmallerThan(long id) { MainApp.Companion.getDbHelper().removeAllOHQueueItemsWithIdSmallerThan(id); } diff --git a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppComponent.kt b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppComponent.kt index d431be7a6c..1421d586ed 100644 --- a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppComponent.kt +++ b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppComponent.kt @@ -7,12 +7,14 @@ import dagger.android.AndroidInjector import info.nightscout.androidaps.MainApp import info.nightscout.androidaps.automation.di.AutomationModule import info.nightscout.androidaps.combo.di.ComboModule -import info.nightscout.androidaps.core.di.CoreModule +import info.nightscout.androidaps.dana.di.DanaHistoryModule +import info.nightscout.androidaps.di.CoreModule import info.nightscout.androidaps.dana.di.DanaModule import info.nightscout.androidaps.danar.di.DanaRModule import info.nightscout.androidaps.danars.di.DanaRSModule -import info.nightscout.androidaps.danars.di.InsightModule import info.nightscout.androidaps.database.DatabaseModule +import info.nightscout.androidaps.insight.di.InsightDatabaseModule +import info.nightscout.androidaps.insight.di.InsightModule import info.nightscout.androidaps.plugins.pump.common.di.RileyLinkModule import info.nightscout.androidaps.plugins.pump.medtronic.di.MedtronicModule import info.nightscout.androidaps.plugins.pump.omnipod.eros.dagger.OmnipodErosModule @@ -45,10 +47,12 @@ import javax.inject.Singleton UIModule::class, CoreModule::class, DanaModule::class, + DanaHistoryModule::class, DanaRModule::class, DanaRSModule::class, ComboModule::class, InsightModule::class, + InsightDatabaseModule::class, WorkersModule::class, OHUploaderModule::class ] diff --git a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppModule.kt b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppModule.kt index c18c1128a3..30037c3948 100644 --- a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppModule.kt +++ b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppModule.kt @@ -6,45 +6,51 @@ import dagger.Lazy import dagger.Module import dagger.Provides import dagger.android.HasAndroidInjector -import info.nightscout.androidaps.Config import info.nightscout.androidaps.MainApp +import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.db.DatabaseHelperProvider import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.logging.AAPSLogger 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.PluginStore -import info.nightscout.androidaps.plugins.general.maintenance.ImportExportPrefs -import info.nightscout.androidaps.plugins.general.nsclient.UploadQueue +import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctionImplementation +import info.nightscout.androidaps.plugins.general.maintenance.ImportExportPrefsImpl +import info.nightscout.androidaps.plugins.general.nsclient.DataSyncSelectorImplementation import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorPlugin import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin +import info.nightscout.androidaps.plugins.pump.PumpSyncImplementation import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin import info.nightscout.androidaps.queue.CommandQueue -import info.nightscout.androidaps.utils.androidNotification.NotificationHolder -import info.nightscout.androidaps.utils.resources.IconsProvider +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.androidNotification.NotificationHolderImpl +import info.nightscout.androidaps.utils.buildHelper.ConfigImpl +import info.nightscout.androidaps.utils.resources.IconsProviderImplementation +import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.utils.rx.DefaultAapsSchedulers import info.nightscout.androidaps.utils.sharedPreferences.SP import info.nightscout.androidaps.utils.storage.FileStorage import info.nightscout.androidaps.utils.storage.Storage import javax.inject.Singleton + +@Suppress("unused") @Module(includes = [ AppModule.AppBindings::class ]) open class AppModule { @Provides - fun providesPlugins(configInterface: ConfigInterface, + fun providesPlugins(config: Config, @PluginsModule.AllConfigs allConfigs: Map<@JvmSuppressWildcards Int, @JvmSuppressWildcards PluginBase>, @PluginsModule.PumpDriver pumpDrivers: Lazy>, @PluginsModule.NotNSClient notNsClient: Lazy>, @PluginsModule.APS aps: Lazy>) : List<@JvmSuppressWildcards PluginBase> { val plugins = allConfigs.toMutableMap() - if (configInterface.PUMPDRIVERS) plugins += pumpDrivers.get() - if (configInterface.APS) plugins += aps.get() - if (!configInterface.NSCLIENT) plugins += notNsClient.get() + if (config.PUMPDRIVERS) plugins += pumpDrivers.get() + if (config.APS) plugins += aps.get() + if (!config.NSCLIENT) plugins += notNsClient.get() return plugins.toList().sortedBy { it.first }.map { it.second } } @@ -60,31 +66,29 @@ open class AppModule { @Provides @Singleton - fun providesUploadQueue( - aapsLogger: AAPSLogger, - databaseHelper: DatabaseHelperInterface, - context: Context, - sp: SP, - rxBus: RxBusWrapper - ): UploadQueueAdminInterface = UploadQueue(aapsLogger, databaseHelper, context, sp, rxBus) + fun provideProfileFunction(aapsLogger: AAPSLogger, sp: SP, resourceHelper: ResourceHelper, activePlugin: ActivePlugin, repository: AppRepository, dateUtil: DateUtil): ProfileFunction { + return ProfileFunctionImplementation(aapsLogger, sp, resourceHelper, activePlugin, repository, dateUtil) + } @Module interface AppBindings { @Binds fun bindContext(mainApp: MainApp): Context @Binds fun bindInjector(mainApp: MainApp): HasAndroidInjector - @Binds fun bindActivePluginProvider(pluginStore: PluginStore): ActivePluginProvider + @Binds fun bindActivePluginProvider(pluginStore: PluginStore): ActivePlugin @Binds fun bindCommandQueueProvider(commandQueue: CommandQueue): CommandQueueProvider - @Binds fun bindConfigInterface(config: Config): ConfigInterface - @Binds fun bindConfigBuilderInterface(configBuilderPlugin: ConfigBuilderPlugin): ConfigBuilderInterface - @Binds fun bindTreatmentInterface(treatmentsPlugin: TreatmentsPlugin): TreatmentsInterface + @Binds fun bindConfigInterface(config: ConfigImpl): Config + @Binds fun bindConfigBuilderInterface(configBuilderPlugin: ConfigBuilderPlugin): ConfigBuilder + @Binds fun bindTreatmentsInterface(treatmentsPlugin: TreatmentsPlugin): TreatmentsInterface @Binds fun bindDatabaseHelperInterface(databaseHelperProvider: DatabaseHelperProvider): DatabaseHelperInterface - @Binds fun bindNotificationHolderInterface(notificationHolder: NotificationHolder): NotificationHolderInterface - @Binds fun bindImportExportPrefsInterface(importExportPrefs: ImportExportPrefs): ImportExportPrefsInterface - @Binds fun bindIconsProviderInterface(iconsProvider: IconsProvider): IconsProviderInterface + @Binds fun bindNotificationHolderInterface(notificationHolder: NotificationHolderImpl): NotificationHolder + @Binds fun bindImportExportPrefsInterface(importExportPrefs: ImportExportPrefsImpl): ImportExportPrefs + @Binds fun bindIconsProviderInterface(iconsProvider: IconsProviderImplementation): IconsProvider @Binds fun bindLoopInterface(loopPlugin: LoopPlugin): LoopInterface - @Binds fun bindIobCobCalculatorInterface(iobCobCalculatorPlugin: IobCobCalculatorPlugin): IobCobCalculatorInterface - @Binds fun bindSmsCommunicatorInterface(smsCommunicatorPlugin: SmsCommunicatorPlugin): SmsCommunicatorInterface - @Binds fun bindUploadQueueAdminInterfaceToUploadQueue(uploadQueueAdminInterface: UploadQueueAdminInterface) : UploadQueueInterface + @Binds fun bindIobCobCalculatorInterface(iobCobCalculatorPlugin: IobCobCalculatorPlugin): IobCobCalculator + @Binds fun bindSmsCommunicatorInterface(smsCommunicatorPlugin: SmsCommunicatorPlugin): SmsCommunicator + @Binds fun bindDataSyncSelector(dataSyncSelectorImplementation: DataSyncSelectorImplementation): DataSyncSelector + @Binds fun bindPumpSync(pumpSyncImplementation: PumpSyncImplementation): PumpSync + } } diff --git a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/DataClassesModule.kt b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/DataClassesModule.kt index de4df287a4..c222f2de55 100644 --- a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/DataClassesModule.kt +++ b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/DataClassesModule.kt @@ -14,7 +14,7 @@ abstract class DataClassesModule { @ContributesAndroidInjector abstract fun glucoseStatusInjector(): GlucoseStatus - @ContributesAndroidInjector abstract fun DatabaseHelperInjector(): DatabaseHelper + @ContributesAndroidInjector abstract fun databaseHelperInjector(): DatabaseHelper @ContributesAndroidInjector abstract fun treatmentServiceInjector(): TreatmentService @ContributesAndroidInjector abstract fun bolusWizardInjector(): BolusWizard diff --git a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/FragmentsModule.kt b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/FragmentsModule.kt index 4acdd1f0db..60eb3ace15 100644 --- a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/FragmentsModule.kt +++ b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/FragmentsModule.kt @@ -29,7 +29,6 @@ import info.nightscout.androidaps.plugins.general.tidepool.TidepoolFragment import info.nightscout.androidaps.plugins.general.wear.WearFragment import info.nightscout.androidaps.plugins.insulin.InsulinFragment import info.nightscout.androidaps.plugins.profile.local.LocalProfileFragment -import info.nightscout.androidaps.plugins.profile.ns.NSProfileFragment import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpFragment import info.nightscout.androidaps.plugins.source.BGSourceFragment import info.nightscout.androidaps.plugins.treatments.TreatmentsFragment @@ -46,8 +45,7 @@ abstract class FragmentsModule { @ContributesAndroidInjector abstract fun contributesAutomationFragment(): AutomationFragment @ContributesAndroidInjector abstract fun contributesBGSourceFragment(): BGSourceFragment - @ContributesAndroidInjector - abstract fun contributesConfigBuilderFragment(): ConfigBuilderFragment + @ContributesAndroidInjector abstract fun contributesConfigBuilderFragment(): ConfigBuilderFragment @ContributesAndroidInjector abstract fun contributesFoodFragment(): FoodFragment @ContributesAndroidInjector abstract fun contributesInsulinFragment(): InsulinFragment @@ -58,15 +56,13 @@ abstract class FragmentsModule { @ContributesAndroidInjector abstract fun contributesOverviewFragment(): OverviewFragment @ContributesAndroidInjector abstract fun contributesLoopFragment(): LoopFragment @ContributesAndroidInjector abstract fun contributesMaintenanceFragment(): MaintenanceFragment - @ContributesAndroidInjector abstract fun contributesNSProfileFragment(): NSProfileFragment @ContributesAndroidInjector abstract fun contributesNSClientFragment(): NSClientFragment - @ContributesAndroidInjector - abstract fun contributesSmsCommunicatorFragment(): SmsCommunicatorFragment + @ContributesAndroidInjector abstract fun contributesSmsCommunicatorFragment(): SmsCommunicatorFragment @ContributesAndroidInjector abstract fun contributesWearFragment(): WearFragment @ContributesAndroidInjector abstract fun contributesTidepoolFragment(): TidepoolFragment @ContributesAndroidInjector abstract fun contributesTreatmentsFragment(): TreatmentsFragment - @ContributesAndroidInjector abstract fun contributesTreatmentsBolusFragment(): TreatmentsBolusFragment + @ContributesAndroidInjector abstract fun contributesTreatmentsBolusFragment(): TreatmentsBolusCarbsFragment @ContributesAndroidInjector abstract fun contributesTreatmentsTemporaryBasalsFragment(): TreatmentsTemporaryBasalsFragment @ContributesAndroidInjector abstract fun contributesTreatmentsTempTargetFragment(): TreatmentsTempTargetFragment @ContributesAndroidInjector abstract fun contributesTreatmentsExtendedBolusesFragment(): TreatmentsExtendedBolusesFragment @@ -85,8 +81,7 @@ abstract class FragmentsModule { @ContributesAndroidInjector abstract fun contributesEditEventDialog(): EditEventDialog @ContributesAndroidInjector abstract fun contributesEditTriggerDialog(): EditTriggerDialog - @ContributesAndroidInjector - abstract fun contributesEditQuickWizardDialog(): EditQuickWizardDialog + @ContributesAndroidInjector abstract fun contributesEditQuickWizardDialog(): EditQuickWizardDialog @ContributesAndroidInjector abstract fun contributesExtendedBolusDialog(): ExtendedBolusDialog @ContributesAndroidInjector abstract fun contributesFillDialog(): FillDialog @@ -102,8 +97,7 @@ abstract class FragmentsModule { @ContributesAndroidInjector abstract fun contributesWizardDialog(): WizardDialog @ContributesAndroidInjector abstract fun contributesWizardInfoDialog(): WizardInfoDialog - @ContributesAndroidInjector - abstract fun contributesExchangeAuthTokenDialot(): OpenHumansLoginActivity.ExchangeAuthTokenDialog + @ContributesAndroidInjector abstract fun contributesExchangeAuthTokenDialog(): OpenHumansLoginActivity.ExchangeAuthTokenDialog @ContributesAndroidInjector abstract fun contributesPasswordCheck(): PasswordCheck } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/OverviewModule.kt b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/OverviewModule.kt index 196e5b1203..42c55758ab 100644 --- a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/OverviewModule.kt +++ b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/OverviewModule.kt @@ -3,6 +3,8 @@ package info.nightscout.androidaps.dependencyInjection import dagger.Module import dagger.android.ContributesAndroidInjector import info.nightscout.androidaps.plugins.general.overview.graphData.GraphData +import info.nightscout.androidaps.plugins.general.overview.graphExtensions.GlucoseValueDataPoint +import info.nightscout.androidaps.plugins.general.overview.graphExtensions.TherapyEventDataPoint import info.nightscout.androidaps.plugins.general.overview.notifications.NotificationWithAction @Module diff --git a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/PluginsModule.kt b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/PluginsModule.kt index 02506911ec..184e0cdbc1 100644 --- a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/PluginsModule.kt +++ b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/PluginsModule.kt @@ -37,7 +37,6 @@ import info.nightscout.androidaps.plugins.insulin.InsulinOrefRapidActingPlugin import info.nightscout.androidaps.plugins.insulin.InsulinOrefUltraRapidActingPlugin import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin -import info.nightscout.androidaps.plugins.profile.ns.NSProfilePlugin import info.nightscout.androidaps.plugins.pump.combo.ComboPlugin import info.nightscout.androidaps.plugins.pump.insight.LocalInsightPlugin import info.nightscout.androidaps.plugins.pump.mdi.MDIPlugin @@ -150,17 +149,17 @@ abstract class PluginsModule { @IntKey(140) abstract fun bindComboPlugin(plugin: ComboPlugin): PluginBase - @Binds - @PumpDriver - @IntoMap - @IntKey(150) - abstract fun bindMedtronicPumpPlugin(plugin: MedtronicPumpPlugin): PluginBase + // @Binds + // @PumpDriver + // @IntoMap + // @IntKey(150) + // abstract fun bindMedtronicPumpPlugin(plugin: MedtronicPumpPlugin): PluginBase - @Binds - @PumpDriver - @IntoMap - @IntKey(155) - abstract fun bindOmnipodPumpPlugin(plugin: OmnipodErosPumpPlugin): PluginBase + // @Binds + // @PumpDriver + // @IntoMap + // @IntKey(155) + // abstract fun bindOmnipodPumpPlugin(plugin: OmnipodErosPumpPlugin): PluginBase @Binds @NotNSClient @@ -195,12 +194,6 @@ abstract class PluginsModule { @Binds @AllConfigs @IntoMap - @IntKey(230) - abstract fun bindNSProfilePlugin(plugin: NSProfilePlugin): PluginBase - - @Binds - @NotNSClient - @IntoMap @IntKey(240) abstract fun bindLocalProfilePlugin(plugin: LocalProfilePlugin): PluginBase @@ -348,11 +341,11 @@ abstract class PluginsModule { @IntKey(470) abstract fun bindRandomBgPlugin(plugin: RandomBgPlugin): PluginBase - @Binds - @NotNSClient - @IntoMap - @IntKey(480) - abstract fun bindOpenHumansPlugin(plugin: OpenHumansUploader): PluginBase + // @Binds + // @NotNSClient + // @IntoMap + // @IntKey(480) + // abstract fun bindOpenHumansPlugin(plugin: OpenHumansUploader): PluginBase @Binds @AllConfigs diff --git a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/ReceiversModule.kt b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/ReceiversModule.kt index a6e7e4104c..2100914ed9 100644 --- a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/ReceiversModule.kt +++ b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/ReceiversModule.kt @@ -17,6 +17,7 @@ abstract class ReceiversModule { @ContributesAndroidInjector abstract fun contributesChargingStateReceiver(): ChargingStateReceiver @ContributesAndroidInjector abstract fun contributesDataReceiver(): DataReceiver @ContributesAndroidInjector abstract fun contributesKeepAliveReceiver(): KeepAliveReceiver + @ContributesAndroidInjector abstract fun contributesKeepAliveWorker(): KeepAliveReceiver.KeepAliveWorker @ContributesAndroidInjector abstract fun contributesRileyLinkBluetoothStateReceiver(): RileyLinkBluetoothStateReceiver @ContributesAndroidInjector abstract fun contributesSmsReceiver(): SmsReceiver @ContributesAndroidInjector abstract fun contributesTimeDateOrTZChangeReceiver(): TimeDateOrTZChangeReceiver diff --git a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/WorkersModule.kt b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/WorkersModule.kt index a46b6b5264..2e7bb25b43 100644 --- a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/WorkersModule.kt +++ b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/WorkersModule.kt @@ -3,11 +3,13 @@ package info.nightscout.androidaps.dependencyInjection import dagger.Module import dagger.android.ContributesAndroidInjector import info.nightscout.androidaps.plugins.general.food.FoodPlugin +import info.nightscout.androidaps.plugins.general.nsclient.NSClientAddAckWorker import info.nightscout.androidaps.plugins.general.nsclient.NSClientAddUpdateWorker import info.nightscout.androidaps.plugins.general.nsclient.NSClientMbgWorker import info.nightscout.androidaps.plugins.general.nsclient.NSClientRemoveWorker +import info.nightscout.androidaps.plugins.general.nsclient.NSClientUpdateRemoveAckWorker import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorPlugin -import info.nightscout.androidaps.plugins.profile.ns.NSProfilePlugin +import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin import info.nightscout.androidaps.plugins.source.* @Module @@ -22,9 +24,11 @@ abstract class WorkersModule { @ContributesAndroidInjector abstract fun contributesTomatoWorker(): TomatoPlugin.TomatoWorker @ContributesAndroidInjector abstract fun contributesEversenseWorker(): EversensePlugin.EversenseWorker @ContributesAndroidInjector abstract fun contributesNSClientSourceWorker(): NSClientSourcePlugin.NSClientSourceWorker - @ContributesAndroidInjector abstract fun contributesNSProfileWorker(): NSProfilePlugin.NSProfileWorker + @ContributesAndroidInjector abstract fun contributesNSProfileWorker(): LocalProfilePlugin.NSProfileWorker @ContributesAndroidInjector abstract fun contributesSmsCommunicatorWorker(): SmsCommunicatorPlugin.SmsCommunicatorWorker @ContributesAndroidInjector abstract fun contributesNSClientWorker(): NSClientAddUpdateWorker + @ContributesAndroidInjector abstract fun contributesNSClientAddAckWorker(): NSClientAddAckWorker + @ContributesAndroidInjector abstract fun contributesNSClientUpdateRemoveAckWorker(): NSClientUpdateRemoveAckWorker @ContributesAndroidInjector abstract fun contributesNSClientRemoveWorker(): NSClientRemoveWorker @ContributesAndroidInjector abstract fun contributesNSClientMbgWorker(): NSClientMbgWorker @ContributesAndroidInjector abstract fun contributesFoodWorker(): FoodPlugin.FoodWorker diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/CalibrationDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/CalibrationDialog.kt index 52dea0d14e..5b4c697814 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/CalibrationDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/CalibrationDialog.kt @@ -6,11 +6,13 @@ import android.view.View import android.view.ViewGroup import com.google.common.base.Joiner import dagger.android.HasAndroidInjector -import info.nightscout.androidaps.Constants import info.nightscout.androidaps.R -import info.nightscout.androidaps.data.Profile -import info.nightscout.androidaps.database.entities.UserEntry.* +import info.nightscout.androidaps.interfaces.Profile +import info.nightscout.androidaps.database.entities.ValueWithUnit +import info.nightscout.androidaps.database.entities.UserEntry.Action +import info.nightscout.androidaps.database.entities.UserEntry.Sources import info.nightscout.androidaps.databinding.DialogCalibrationBinding +import info.nightscout.androidaps.interfaces.GlucoseUnit import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatusProvider @@ -55,13 +57,13 @@ class CalibrationDialog : DialogFragmentWithDate() { val units = profileFunction.getUnits() val bg = Profile.fromMgdlToUnits(glucoseStatusProvider.glucoseStatusData?.glucose ?: 0.0, units) - if (units == Constants.MMOL) + if (units == GlucoseUnit.MMOL) binding.bg.setParams(savedInstanceState?.getDouble("bg") ?: bg, 2.0, 30.0, 0.1, DecimalFormat("0.0"), false, binding.okcancel.ok) else binding.bg.setParams(savedInstanceState?.getDouble("bg") ?: bg, 36.0, 500.0, 1.0, DecimalFormat("0"), false, binding.okcancel.ok) - binding.units.text = if (units == Constants.MMOL) resourceHelper.gs(R.string.mmol) else resourceHelper.gs(R.string.mgdl) + binding.units.text = if (units == GlucoseUnit.MMOL) resourceHelper.gs(R.string.mmol) else resourceHelper.gs(R.string.mgdl) } override fun onDestroyView() { @@ -72,14 +74,14 @@ class CalibrationDialog : DialogFragmentWithDate() { override fun submit(): Boolean { if (_binding == null) return false val units = profileFunction.getUnits() - val unitLabel = if (units == Constants.MMOL) resourceHelper.gs(R.string.mmol) else resourceHelper.gs(R.string.mgdl) + val unitLabel = if (units == GlucoseUnit.MMOL) resourceHelper.gs(R.string.mmol) else resourceHelper.gs(R.string.mgdl) val actions: LinkedList = LinkedList() val bg = binding.bg.value ?: return false actions.add(resourceHelper.gs(R.string.treatments_wizard_bg_label) + ": " + Profile.toCurrentUnitsString(profileFunction, bg) + " " + unitLabel) if (bg > 0) { activity?.let { activity -> OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.overview_calibration), HtmlHelper.fromHtml(Joiner.on("
").join(actions)), { - uel.log(Action.CALIBRATION, ValueWithUnit(bg, units)) + uel.log(Action.CALIBRATION, Sources.CalibrationDialog, ValueWithUnit.fromGlucoseUnit(bg, units.asText)) xdripCalibrations.sendIntent(bg) }) } diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/CarbsDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/CarbsDialog.kt index 56dfbae96c..9d4e34a89b 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/CarbsDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/CarbsDialog.kt @@ -1,5 +1,6 @@ package info.nightscout.androidaps.dialogs +import android.content.Context import android.os.Bundle import android.text.Editable import android.text.TextWatcher @@ -7,28 +8,30 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import com.google.common.base.Joiner -import info.nightscout.androidaps.Constants -import info.nightscout.androidaps.MainApp import info.nightscout.androidaps.R -import info.nightscout.androidaps.data.Profile +import info.nightscout.androidaps.activities.ErrorHelperActivity +import info.nightscout.androidaps.data.DetailedBolusInfo +import info.nightscout.androidaps.interfaces.Profile import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.entities.TemporaryTarget -import info.nightscout.androidaps.database.entities.TherapyEvent -import info.nightscout.androidaps.database.entities.UserEntry.* +import info.nightscout.androidaps.database.entities.UserEntry.Action +import info.nightscout.androidaps.database.entities.UserEntry.Sources +import info.nightscout.androidaps.database.entities.ValueWithUnit import info.nightscout.androidaps.database.transactions.InsertTemporaryTargetAndCancelCurrentTransaction import info.nightscout.androidaps.databinding.DialogCarbsBinding +import info.nightscout.androidaps.extensions.formatColor import info.nightscout.androidaps.interfaces.Constraint +import info.nightscout.androidaps.interfaces.GlucoseUnit +import info.nightscout.androidaps.interfaces.IobCobCalculator import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.logging.LTag 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.queue.Callback +import info.nightscout.androidaps.queue.CommandQueue 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 import io.reactivex.disposables.CompositeDisposable import io.reactivex.rxkotlin.plusAssign @@ -40,17 +43,16 @@ import kotlin.math.max class CarbsDialog : DialogFragmentWithDate() { - @Inject lateinit var mainApp: MainApp + @Inject lateinit var ctx: Context @Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var constraintChecker: ConstraintChecker @Inject lateinit var defaultValueHelper: DefaultValueHelper @Inject lateinit var treatmentsPlugin: TreatmentsPlugin @Inject lateinit var profileFunction: ProfileFunction - @Inject lateinit var iobCobCalculatorPlugin: IobCobCalculatorPlugin - @Inject lateinit var nsUpload: NSUpload - @Inject lateinit var carbsGenerator: CarbsGenerator + @Inject lateinit var iobCobCalculator: IobCobCalculator @Inject lateinit var uel: UserEntryLogger @Inject lateinit var carbTimer: CarbTimer + @Inject lateinit var commandQueue: CommandQueue @Inject lateinit var repository: AppRepository companion object { @@ -76,15 +78,15 @@ class CarbsDialog : DialogFragmentWithDate() { val time = binding.time.value.toInt() if (time > 12 * 60 || time < -12 * 60) { binding.time.value = 0.0 - ToastUtils.showToastInUiThread(mainApp, resourceHelper.gs(R.string.constraintapllied)) + ToastUtils.showToastInUiThread(ctx, resourceHelper.gs(R.string.constraintapllied)) } if (binding.duration.value > 10) { binding.duration.value = 0.0 - ToastUtils.showToastInUiThread(mainApp, resourceHelper.gs(R.string.constraintapllied)) + ToastUtils.showToastInUiThread(ctx, resourceHelper.gs(R.string.constraintapllied)) } if (binding.carbs.value.toInt() > maxCarbs) { binding.carbs.value = 0.0 - ToastUtils.showToastInUiThread(mainApp, resourceHelper.gs(R.string.carbsconstraintapplied)) + ToastUtils.showToastInUiThread(ctx, resourceHelper.gs(R.string.carbsconstraintapplied)) } } @@ -142,7 +144,7 @@ class CarbsDialog : DialogFragmentWithDate() { validateInputs() } - iobCobCalculatorPlugin.actualBg()?.let { bgReading -> + iobCobCalculator.ads.actualBg()?.let { bgReading -> if (bgReading.value < 72) binding.hypoTt.isChecked = true } @@ -182,7 +184,7 @@ class CarbsDialog : DialogFragmentWithDate() { val hypoTTDuration = defaultValueHelper.determineHypoTTDuration() val hypoTT = defaultValueHelper.determineHypoTT() val actions: LinkedList = LinkedList() - val unitLabel = if (units == Constants.MMOL) resourceHelper.gs(R.string.mmol) else resourceHelper.gs(R.string.mgdl) + val unitLabel = if (units == GlucoseUnit.MMOL) resourceHelper.gs(R.string.mmol) else resourceHelper.gs(R.string.mgdl) val useAlarm = binding.alarmCheckBox.isChecked val activitySelected = binding.activityTt.isChecked @@ -222,7 +224,10 @@ class CarbsDialog : DialogFragmentWithDate() { OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.carbs), HtmlHelper.fromHtml(Joiner.on("
").join(actions)), { when { activitySelected -> { - uel.log(Action.TT, ValueWithUnit(TemporaryTarget.Reason.ACTIVITY.text, Units.TherapyEvent), ValueWithUnit(activityTT, units) , ValueWithUnit(activityTTDuration, Units.M)) + uel.log(Action.TT, Sources.CarbDialog, + ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.ACTIVITY), + ValueWithUnit.fromGlucoseUnit(activityTT, units.asText), + ValueWithUnit.Minute(activityTTDuration)) disposable += repository.runTransactionForResult(InsertTemporaryTargetAndCancelCurrentTransaction( timestamp = System.currentTimeMillis(), duration = TimeUnit.MINUTES.toMillis(activityTTDuration.toLong()), @@ -230,15 +235,18 @@ class CarbsDialog : DialogFragmentWithDate() { lowTarget = Profile.toMgdl(activityTT, profileFunction.getUnits()), highTarget = Profile.toMgdl(activityTT, profileFunction.getUnits()) )).subscribe({ result -> - result.inserted.forEach { nsUpload.uploadTempTarget(it) } - result.updated.forEach { nsUpload.updateTempTarget(it) } + result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted temp target $it") } + result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated temp target $it") } }, { - aapsLogger.error(LTag.BGSOURCE, "Error while saving temporary target", it) + aapsLogger.error(LTag.DATABASE, "Error while saving temporary target", it) }) } eatingSoonSelected -> { - uel.log(Action.TT, ValueWithUnit(TemporaryTarget.Reason.EATING_SOON.text, Units.TherapyEvent), ValueWithUnit(eatingSoonTT, units) , ValueWithUnit(eatingSoonTTDuration, Units.M)) + uel.log(Action.TT, Sources.CarbDialog, + ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.EATING_SOON), + ValueWithUnit.fromGlucoseUnit(eatingSoonTT, units.asText), + ValueWithUnit.Minute(eatingSoonTTDuration)) disposable += repository.runTransactionForResult(InsertTemporaryTargetAndCancelCurrentTransaction( timestamp = System.currentTimeMillis(), duration = TimeUnit.MINUTES.toMillis(eatingSoonTTDuration.toLong()), @@ -246,15 +254,18 @@ class CarbsDialog : DialogFragmentWithDate() { lowTarget = Profile.toMgdl(eatingSoonTT, profileFunction.getUnits()), highTarget = Profile.toMgdl(eatingSoonTT, profileFunction.getUnits()) )).subscribe({ result -> - result.inserted.forEach { nsUpload.uploadTempTarget(it) } - result.updated.forEach { nsUpload.updateTempTarget(it) } + result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted temp target $it") } + result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated temp target $it") } }, { - aapsLogger.error(LTag.BGSOURCE, "Error while saving temporary target", it) + aapsLogger.error(LTag.DATABASE, "Error while saving temporary target", it) }) } hypoSelected -> { - uel.log(Action.TT, ValueWithUnit(TemporaryTarget.Reason.HYPOGLYCEMIA.text, Units.TherapyEvent), ValueWithUnit(hypoTT, units) , ValueWithUnit(hypoTTDuration, Units.M)) + uel.log(Action.TT, Sources.CarbDialog, + ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.HYPOGLYCEMIA), + ValueWithUnit.fromGlucoseUnit(hypoTT, units.asText), + ValueWithUnit.Minute(hypoTTDuration)) disposable += repository.runTransactionForResult(InsertTemporaryTargetAndCancelCurrentTransaction( timestamp = System.currentTimeMillis(), duration = TimeUnit.MINUTES.toMillis(hypoTTDuration.toLong()), @@ -262,24 +273,37 @@ class CarbsDialog : DialogFragmentWithDate() { lowTarget = Profile.toMgdl(hypoTT, profileFunction.getUnits()), highTarget = Profile.toMgdl(hypoTT, profileFunction.getUnits()) )).subscribe({ result -> - result.inserted.forEach { nsUpload.uploadTempTarget(it) } - result.updated.forEach { nsUpload.updateTempTarget(it) } + result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted temp target $it") } + result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated temp target $it") } }, { - aapsLogger.error(LTag.BGSOURCE, "Error while saving temporary target", it) + aapsLogger.error(LTag.DATABASE, "Error while saving temporary target", it) }) } } if (carbsAfterConstraints > 0) { - if (duration == 0) { - carbsGenerator.createCarb(carbsAfterConstraints, time, TherapyEvent.Type.CARBS_CORRECTION.text, notes) - } else { - carbsGenerator.generateCarbs(carbsAfterConstraints, time, duration, notes) - nsUpload.uploadEvent(TherapyEvent.Type.NOTE.text, DateUtil.now() - 2000, resourceHelper.gs(R.string.generated_ecarbs_note, carbsAfterConstraints, duration, timeOffset)) - } - uel.log(Action.CARBS, notes, ValueWithUnit(eventTime, Units.Timestamp, eventTimeChanged), ValueWithUnit(carbsAfterConstraints, Units.G), ValueWithUnit(timeOffset, Units.M, timeOffset != 0), ValueWithUnit(duration, Units.H, duration != 0)) + val detailedBolusInfo = DetailedBolusInfo() + detailedBolusInfo.eventType = DetailedBolusInfo.EventType.CORRECTION_BOLUS + detailedBolusInfo.carbs = carbsAfterConstraints.toDouble() + detailedBolusInfo.context = context + detailedBolusInfo.notes = notes + detailedBolusInfo.carbsDuration = T.hours(duration.toLong()).msecs() + detailedBolusInfo.carbsTimestamp = time + uel.log(if (duration == 0) Action.CARBS else Action.EXTENDED_CARBS, Sources.CarbDialog, + notes, + ValueWithUnit.Timestamp(eventTime).takeIf { eventTimeChanged }, + ValueWithUnit.Gram(carbsAfterConstraints), + ValueWithUnit.Minute(timeOffset).takeIf { timeOffset != 0 }, + ValueWithUnit.Hour(duration).takeIf { duration != 0 }) + commandQueue.bolus(detailedBolusInfo, object : Callback() { + override fun run() { + if (!result.success) { + ErrorHelperActivity.runAlarm(ctx, result.comment, resourceHelper.gs(R.string.treatmentdeliveryerror), R.raw.boluserror) + } + } + }) } if (useAlarm && carbs > 0 && timeOffset > 0) { - carbTimer.scheduleReminder(dateUtil._now() + T.mins(timeOffset.toLong()).msecs()) + carbTimer.scheduleReminder(dateUtil.now() + T.mins(timeOffset.toLong()).msecs()) } }, null) } diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/CareDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/CareDialog.kt index 122b38cd81..15883aaf9e 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/CareDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/CareDialog.kt @@ -1,5 +1,6 @@ package info.nightscout.androidaps.dialogs +import android.content.Context import android.os.Bundle import android.text.Editable import android.text.TextWatcher @@ -10,24 +11,25 @@ import androidx.annotation.StringRes import com.google.common.base.Joiner import dagger.android.HasAndroidInjector import info.nightscout.androidaps.Constants -import info.nightscout.androidaps.MainApp import info.nightscout.androidaps.R -import info.nightscout.androidaps.data.Profile +import info.nightscout.androidaps.interfaces.Profile import info.nightscout.androidaps.database.AppRepository +import info.nightscout.androidaps.database.entities.ValueWithUnit import info.nightscout.androidaps.database.entities.TherapyEvent -import info.nightscout.androidaps.database.transactions.InsertTherapyEventIfNewTransaction -import info.nightscout.androidaps.database.entities.UserEntry.* +import info.nightscout.androidaps.database.entities.UserEntry.Action +import info.nightscout.androidaps.database.entities.UserEntry.Sources +import info.nightscout.androidaps.database.transactions.InsertIfNewByTimestampTherapyEventTransaction import info.nightscout.androidaps.databinding.DialogCareBinding import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.logging.UserEntryLogger -import info.nightscout.androidaps.plugins.general.nsclient.NSUpload import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatusProvider import info.nightscout.androidaps.utils.HtmlHelper import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.Translator import info.nightscout.androidaps.utils.alertDialogs.OKDialog -import info.nightscout.androidaps.utils.extensions.fromConstant +import info.nightscout.androidaps.extensions.fromConstant +import info.nightscout.androidaps.interfaces.GlucoseUnit import info.nightscout.androidaps.utils.resources.ResourceHelper import io.reactivex.disposables.CompositeDisposable import io.reactivex.rxkotlin.plusAssign @@ -38,10 +40,9 @@ import javax.inject.Inject class CareDialog : DialogFragmentWithDate() { @Inject lateinit var injector: HasAndroidInjector - @Inject lateinit var mainApp: MainApp + @Inject lateinit var ctx: Context @Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var profileFunction: ProfileFunction - @Inject lateinit var nsUpload: NSUpload @Inject lateinit var translator: Translator @Inject lateinit var uel: UserEntryLogger @Inject lateinit var repository: AppRepository @@ -60,7 +61,8 @@ class CareDialog : DialogFragmentWithDate() { } private var options: EventType = EventType.BGCHECK - private var valuesWithUnit = mutableListOf() + //private var valuesWithUnit = mutableListOf() + private var valuesWithUnit = mutableListOf() @StringRes private var event: Int = R.string.none @@ -150,7 +152,7 @@ class CareDialog : DialogFragmentWithDate() { } } - if (profileFunction.getUnits() == Constants.MMOL) { + if (profileFunction.getUnits() == GlucoseUnit.MMOL) { binding.bgunits.text = resourceHelper.gs(R.string.mmol) binding.bg.setParams(savedInstanceState?.getDouble("bg") ?: bg, 2.0, 30.0, 0.1, DecimalFormat("0.0"), false, binding.okcancel.ok, bgTextWatcher) @@ -172,7 +174,7 @@ class CareDialog : DialogFragmentWithDate() { override fun submit(): Boolean { val enteredBy = sp.getString("careportal_enteredby", "AndroidAPS") - val unitResId = if (profileFunction.getUnits() == Constants.MGDL) R.string.mgdl else R.string.mmol + val unitResId = if (profileFunction.getUnits() == GlucoseUnit.MGDL) R.string.mgdl else R.string.mmol eventTime -= eventTime % 1000 @@ -198,17 +200,17 @@ class CareDialog : DialogFragmentWithDate() { binding.sensor.isChecked -> TherapyEvent.MeterType.SENSOR else -> TherapyEvent.MeterType.MANUAL } - actions.add(resourceHelper.gs(R.string.careportal_newnstreatment_glucosetype) + ": " + translator.translate(meterType.text)) + actions.add(resourceHelper.gs(R.string.careportal_newnstreatment_glucosetype) + ": " + translator.translate(meterType)) actions.add(resourceHelper.gs(R.string.treatments_wizard_bg_label) + ": " + Profile.toCurrentUnitsString(profileFunction, binding.bg.value) + " " + resourceHelper.gs(unitResId)) therapyEvent.glucoseType = meterType therapyEvent.glucose = binding.bg.value - valuesWithUnit.add(ValueWithUnit(binding.bg.value.toDouble(), profileFunction.getUnits())) - valuesWithUnit.add(ValueWithUnit(meterType.text, Units.TherapyEvent)) + valuesWithUnit.add(ValueWithUnit.fromGlucoseUnit(binding.bg.value.toDouble(), profileFunction.getUnits().asText)) + valuesWithUnit.add(ValueWithUnit.TherapyEventMeterType(meterType)) } if (options == EventType.NOTE || options == EventType.EXERCISE) { actions.add(resourceHelper.gs(R.string.careportal_newnstreatment_duration_label) + ": " + resourceHelper.gs(R.string.format_mins, binding.duration.value.toInt())) therapyEvent.duration = T.mins(binding.duration.value.toLong()).msecs() - valuesWithUnit.add(ValueWithUnit(binding.duration.value.toInt(), Units.M, !binding.duration.value.equals(0.0))) + valuesWithUnit.add(ValueWithUnit.Minute(binding.duration.value.toInt()).takeIf { !binding.duration.value.equals(0.0) } ) } val notes = binding.notesLayout.notes.text.toString() if (notes.isNotEmpty()) { @@ -220,16 +222,26 @@ class CareDialog : DialogFragmentWithDate() { therapyEvent.enteredBy = enteredBy + var source = when (options) { + EventType.BGCHECK -> Sources.BgCheck + EventType.SENSOR_INSERT -> Sources.SensorInsert + EventType.BATTERY_CHANGE -> Sources.BatteryChange + EventType.NOTE -> Sources.Note + EventType.EXERCISE -> Sources.Exercise + EventType.QUESTION -> Sources.Question + EventType.ANNOUNCEMENT -> Sources.Announcement + } + activity?.let { activity -> OKDialog.showConfirmation(activity, resourceHelper.gs(event), HtmlHelper.fromHtml(Joiner.on("
").join(actions)), { - disposable += repository.runTransactionForResult(InsertTherapyEventIfNewTransaction(therapyEvent)).subscribe({ result -> - result.inserted.forEach { nsUpload.uploadEvent(it) } - }, { - aapsLogger.error(LTag.BGSOURCE, "Error while saving therapy event", it) - }) - valuesWithUnit.add(0, ValueWithUnit(eventTime, Units.Timestamp, eventTimeChanged)) - valuesWithUnit.add(1, ValueWithUnit(therapyEvent.type.text, Units.TherapyEvent)) - uel.log(Action.CAREPORTAL, notes, valuesWithUnit) + disposable += repository.runTransactionForResult(InsertIfNewByTimestampTherapyEventTransaction(therapyEvent)) + .subscribe( + { result -> result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted therapy event $it") } }, + { aapsLogger.error(LTag.DATABASE, "Error while saving therapy event", it) } + ) + valuesWithUnit.add(0, ValueWithUnit.Timestamp(eventTime).takeIf { eventTimeChanged }) + valuesWithUnit.add(1, ValueWithUnit.TherapyEventType(therapyEvent.type)) + uel.log(Action.CAREPORTAL, source, notes, valuesWithUnit) }, null) } return true diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/ExtendedBolusDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/ExtendedBolusDialog.kt index 4116091eed..e3ed95451c 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/ExtendedBolusDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/ExtendedBolusDialog.kt @@ -8,9 +8,11 @@ import android.view.ViewGroup import com.google.common.base.Joiner import info.nightscout.androidaps.R import info.nightscout.androidaps.activities.ErrorHelperActivity -import info.nightscout.androidaps.database.entities.UserEntry.* +import info.nightscout.androidaps.database.entities.ValueWithUnit +import info.nightscout.androidaps.database.entities.UserEntry.Action +import info.nightscout.androidaps.database.entities.UserEntry.Sources import info.nightscout.androidaps.databinding.DialogExtendedbolusBinding -import info.nightscout.androidaps.interfaces.ActivePluginProvider +import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.CommandQueueProvider import info.nightscout.androidaps.interfaces.Constraint import info.nightscout.androidaps.logging.UserEntryLogger @@ -19,7 +21,7 @@ import info.nightscout.androidaps.queue.Callback import info.nightscout.androidaps.utils.HtmlHelper import info.nightscout.androidaps.utils.SafeParse import info.nightscout.androidaps.utils.alertDialogs.OKDialog -import info.nightscout.androidaps.utils.extensions.formatColor +import info.nightscout.androidaps.extensions.formatColor import info.nightscout.androidaps.utils.resources.ResourceHelper import java.text.DecimalFormat import java.util.* @@ -32,7 +34,7 @@ class ExtendedBolusDialog : DialogFragmentWithDate() { @Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var constraintChecker: ConstraintChecker @Inject lateinit var commandQueue: CommandQueueProvider - @Inject lateinit var activePlugin: ActivePluginProvider + @Inject lateinit var activePlugin: ActivePlugin @Inject lateinit var uel: UserEntryLogger private var _binding: DialogExtendedbolusBinding? = null @@ -88,7 +90,9 @@ class ExtendedBolusDialog : DialogFragmentWithDate() { activity?.let { activity -> OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.extended_bolus), HtmlHelper.fromHtml(Joiner.on("
").join(actions)), { - uel.log(Action.EXTENDED_BOLUS, ValueWithUnit(insulinAfterConstraint, Units.U), ValueWithUnit(durationInMinutes, Units.M)) + uel.log(Action.EXTENDED_BOLUS, Sources.ExtendedBolusDialog, + ValueWithUnit.Insulin(insulinAfterConstraint), + ValueWithUnit.Minute(durationInMinutes)) commandQueue.extendedBolus(insulinAfterConstraint, durationInMinutes, object : Callback() { override fun run() { if (!result.success) { diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/FillDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/FillDialog.kt index 09f9234937..f9cf203e5e 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/FillDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/FillDialog.kt @@ -10,24 +10,24 @@ import info.nightscout.androidaps.R import info.nightscout.androidaps.activities.ErrorHelperActivity import info.nightscout.androidaps.data.DetailedBolusInfo import info.nightscout.androidaps.database.AppRepository +import info.nightscout.androidaps.database.entities.ValueWithUnit import info.nightscout.androidaps.database.entities.TherapyEvent -import info.nightscout.androidaps.database.transactions.InsertTherapyEventIfNewTransaction -import info.nightscout.androidaps.database.entities.UserEntry.* +import info.nightscout.androidaps.database.entities.UserEntry.Action +import info.nightscout.androidaps.database.entities.UserEntry.Sources +import info.nightscout.androidaps.database.transactions.InsertIfNewByTimestampTherapyEventTransaction import info.nightscout.androidaps.databinding.DialogFillBinding -import info.nightscout.androidaps.db.Source -import info.nightscout.androidaps.interfaces.ActivePluginProvider +import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.CommandQueueProvider import info.nightscout.androidaps.interfaces.Constraint import info.nightscout.androidaps.logging.LTag 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 import info.nightscout.androidaps.utils.DecimalFormatter import info.nightscout.androidaps.utils.HtmlHelper import info.nightscout.androidaps.utils.SafeParse import info.nightscout.androidaps.utils.alertDialogs.OKDialog -import info.nightscout.androidaps.utils.extensions.formatColor +import info.nightscout.androidaps.extensions.formatColor import info.nightscout.androidaps.utils.resources.ResourceHelper import io.reactivex.disposables.CompositeDisposable import io.reactivex.rxkotlin.plusAssign @@ -40,9 +40,8 @@ class FillDialog : DialogFragmentWithDate() { @Inject lateinit var constraintChecker: ConstraintChecker @Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var ctx: Context - @Inject lateinit var nsUpload: NSUpload @Inject lateinit var commandQueue: CommandQueueProvider - @Inject lateinit var activePlugin: ActivePluginProvider + @Inject lateinit var activePlugin: ActivePlugin @Inject lateinit var uel: UserEntryLogger @Inject lateinit var repository: AppRepository @@ -124,7 +123,7 @@ class FillDialog : DialogFragmentWithDate() { val insulinChange = binding.fillCartridgeChange.isChecked if (insulinChange) actions.add(resourceHelper.gs(R.string.record_insulin_cartridge_change).formatColor(resourceHelper, R.color.actionsConfirm)) - val notes = binding.notesLayout.notes.text.toString() + val notes: String = binding.notesLayout.notes.text.toString() if (notes.isNotEmpty()) actions.add(resourceHelper.gs(R.string.notes_label) + ": " + notes) eventTime -= eventTime % 1000 @@ -136,35 +135,41 @@ class FillDialog : DialogFragmentWithDate() { activity?.let { activity -> OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.primefill), HtmlHelper.fromHtml(Joiner.on("
").join(actions)), { if (insulinAfterConstraints > 0) { - uel.log(Action.PRIME_BOLUS, notes, ValueWithUnit(insulinAfterConstraints, Units.U, insulinAfterConstraints != 0.0)) + uel.log(Action.PRIME_BOLUS, Sources.FillDialog, + notes, + ValueWithUnit.Insulin(insulinAfterConstraints).takeIf { insulinAfterConstraints != 0.0 }) requestPrimeBolus(insulinAfterConstraints, notes) } if (siteChange) { - uel.log(Action.CAREPORTAL, notes, ValueWithUnit(TherapyEvent.Type.CANNULA_CHANGE.text, Units.TherapyEvent)) - disposable += repository.runTransactionForResult(InsertTherapyEventIfNewTransaction( + uel.log(Action.SITE_CHANGE, Sources.FillDialog, + notes, + ValueWithUnit.Timestamp(eventTime).takeIf { eventTimeChanged }, + ValueWithUnit.TherapyEventType(TherapyEvent.Type.CANNULA_CHANGE)) + disposable += repository.runTransactionForResult(InsertIfNewByTimestampTherapyEventTransaction( timestamp = eventTime, type = TherapyEvent.Type.CANNULA_CHANGE, note = notes, glucoseUnit = TherapyEvent.GlucoseUnit.MGDL - )).subscribe({ result -> - result.inserted.forEach { nsUpload.uploadEvent(it) } - }, { - aapsLogger.error(LTag.BGSOURCE, "Error while saving therapy event", it) - }) + )).subscribe( + { result -> result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted therapy event $it") } }, + { aapsLogger.error(LTag.DATABASE, "Error while saving therapy event", it) } + ) } if (insulinChange) { // add a second for case of both checked - uel.log(Action.CAREPORTAL, notes, ValueWithUnit(TherapyEvent.Type.INSULIN_CHANGE.text, Units.TherapyEvent)) - disposable += repository.runTransactionForResult(InsertTherapyEventIfNewTransaction( + uel.log(Action.RESERVOIR_CHANGE, Sources.FillDialog, + notes, + ValueWithUnit.Timestamp(eventTime).takeIf { eventTimeChanged }, + ValueWithUnit.TherapyEventType(TherapyEvent.Type.INSULIN_CHANGE)) + disposable += repository.runTransactionForResult(InsertIfNewByTimestampTherapyEventTransaction( timestamp = eventTime + 1000, type = TherapyEvent.Type.INSULIN_CHANGE, note = notes, glucoseUnit = TherapyEvent.GlucoseUnit.MGDL - )).subscribe({ result -> - result.inserted.forEach { nsUpload.uploadEvent(it) } - }, { - aapsLogger.error(LTag.BGSOURCE, "Error while saving therapy event", it) - }) + )).subscribe( + { result -> result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted therapy event $it") } }, + { aapsLogger.error(LTag.DATABASE, "Error while saving therapy event", it) } + ) } }, null) } @@ -181,8 +186,7 @@ class FillDialog : DialogFragmentWithDate() { val detailedBolusInfo = DetailedBolusInfo() detailedBolusInfo.insulin = insulin detailedBolusInfo.context = context - detailedBolusInfo.source = Source.USER - detailedBolusInfo.isValid = false // do not count it in IOB (for pump history) + detailedBolusInfo.bolusType = DetailedBolusInfo.BolusType.PRIMING detailedBolusInfo.notes = notes commandQueue.bolus(detailedBolusInfo, object : Callback() { override fun run() { diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/InsulinDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/InsulinDialog.kt index da3347485a..1d624d09d4 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/InsulinDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/InsulinDialog.kt @@ -8,33 +8,26 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import com.google.common.base.Joiner -import info.nightscout.androidaps.Config -import info.nightscout.androidaps.Constants 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.database.entities.ValueWithUnit import info.nightscout.androidaps.database.entities.TemporaryTarget -import info.nightscout.androidaps.database.entities.TherapyEvent -import info.nightscout.androidaps.database.entities.UserEntry.* +import info.nightscout.androidaps.database.entities.UserEntry.Action +import info.nightscout.androidaps.database.entities.UserEntry.Sources import info.nightscout.androidaps.database.transactions.InsertTemporaryTargetAndCancelCurrentTransaction import info.nightscout.androidaps.databinding.DialogInsulinBinding -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.interfaces.ProfileFunction import info.nightscout.androidaps.logging.LTag 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 import info.nightscout.androidaps.utils.* import info.nightscout.androidaps.utils.alertDialogs.OKDialog -import info.nightscout.androidaps.utils.extensions.formatColor +import info.nightscout.androidaps.extensions.formatColor import info.nightscout.androidaps.utils.extensions.toSignedString -import info.nightscout.androidaps.utils.extensions.toVisibility +import info.nightscout.androidaps.extensions.toVisibility +import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.utils.resources.ResourceHelper import io.reactivex.disposables.CompositeDisposable import io.reactivex.rxkotlin.plusAssign @@ -52,12 +45,11 @@ class InsulinDialog : DialogFragmentWithDate() { @Inject lateinit var defaultValueHelper: DefaultValueHelper @Inject lateinit var profileFunction: ProfileFunction @Inject lateinit var commandQueue: CommandQueueProvider - @Inject lateinit var activePlugin: ActivePluginProvider + @Inject lateinit var activePlugin: ActivePlugin @Inject lateinit var ctx: Context @Inject lateinit var repository: AppRepository @Inject lateinit var config: Config @Inject lateinit var uel: UserEntryLogger - @Inject lateinit var nsUpload: NSUpload companion object { @@ -160,7 +152,7 @@ class InsulinDialog : DialogFragmentWithDate() { val insulinAfterConstraints = constraintChecker.applyBolusConstraints(Constraint(insulin)).value() val actions: LinkedList = LinkedList() val units = profileFunction.getUnits() - val unitLabel = if (units == Constants.MMOL) resourceHelper.gs(R.string.mmol) else resourceHelper.gs(R.string.mgdl) + val unitLabel = if (units == GlucoseUnit.MMOL) resourceHelper.gs(R.string.mmol) else resourceHelper.gs(R.string.mgdl) val recordOnlyChecked = binding.recordOnly.isChecked val eatingSoonChecked = binding.startEatingSoonTt.isChecked @@ -177,7 +169,7 @@ class InsulinDialog : DialogFragmentWithDate() { actions.add(resourceHelper.gs(R.string.temptargetshort) + ": " + (DecimalFormatter.to1Decimal(eatingSoonTT) + " " + unitLabel + " (" + resourceHelper.gs(R.string.format_mins, eatingSoonTTDuration) + ")").formatColor(resourceHelper, R.color.tempTargetConfirmation)) val timeOffset = binding.time.value.toInt() - val time = DateUtil.now() + T.mins(timeOffset.toLong()).msecs() + val time = dateUtil.now() + T.mins(timeOffset.toLong()).msecs() if (timeOffset != 0) actions.add(resourceHelper.gs(R.string.time) + ": " + dateUtil.dateAndTimeString(time)) @@ -189,7 +181,11 @@ class InsulinDialog : DialogFragmentWithDate() { activity?.let { activity -> OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.bolus), HtmlHelper.fromHtml(Joiner.on("
").join(actions)), { if (eatingSoonChecked) { - uel.log(Action.TT, notes, ValueWithUnit(TemporaryTarget.Reason.EATING_SOON.text, Units.TherapyEvent), ValueWithUnit(eatingSoonTT, units), ValueWithUnit(eatingSoonTTDuration, Units.M)) + uel.log(Action.TT, Sources.InsulinDialog, + notes, + ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.EATING_SOON), + ValueWithUnit.fromGlucoseUnit(eatingSoonTT, units.asText), + ValueWithUnit.Minute(eatingSoonTTDuration)) disposable += repository.runTransactionForResult(InsertTemporaryTargetAndCancelCurrentTransaction( timestamp = System.currentTimeMillis(), duration = TimeUnit.MINUTES.toMillis(eatingSoonTTDuration.toLong()), @@ -197,26 +193,34 @@ class InsulinDialog : DialogFragmentWithDate() { lowTarget = Profile.toMgdl(eatingSoonTT, profileFunction.getUnits()), highTarget = Profile.toMgdl(eatingSoonTT, profileFunction.getUnits()) )).subscribe({ result -> - result.inserted.forEach { nsUpload.uploadTempTarget(it) } - result.updated.forEach { nsUpload.updateTempTarget(it) } + result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted temp target $it") } + result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated temp target $it") } }, { - aapsLogger.error(LTag.BGSOURCE, "Error while saving temporary target", it) + aapsLogger.error(LTag.DATABASE, "Error while saving temporary target", it) }) } if (insulinAfterConstraints > 0) { val detailedBolusInfo = DetailedBolusInfo() - detailedBolusInfo.eventType = TherapyEvent.Type.CORRECTION_BOLUS.text + detailedBolusInfo.eventType = DetailedBolusInfo.EventType.CORRECTION_BOLUS detailedBolusInfo.insulin = insulinAfterConstraints detailedBolusInfo.context = context - detailedBolusInfo.source = Source.USER detailedBolusInfo.notes = notes + detailedBolusInfo.timestamp = time if (recordOnlyChecked) { - uel.log(Action.BOLUS_RECORD, notes, ValueWithUnit(insulinAfterConstraints, Units.U), ValueWithUnit(timeOffset, Units.M, timeOffset!= 0)) - detailedBolusInfo.date = time - activePlugin.activeTreatments.addToHistoryTreatment(detailedBolusInfo, false) + uel.log(Action.BOLUS, Sources.InsulinDialog, + resourceHelper.gs(R.string.record) + if (notes.isNotEmpty()) ": " + notes else "", + ValueWithUnit.SimpleString(resourceHelper.gsNotLocalised(R.string.record)), + ValueWithUnit.Insulin(insulinAfterConstraints), + ValueWithUnit.Minute(timeOffset).takeIf { timeOffset!= 0 }) + disposable += repository.runTransactionForResult(detailedBolusInfo.insertBolusTransaction()) + .subscribe( + { result -> result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted bolus $it") } }, + { aapsLogger.error(LTag.DATABASE, "Error while saving bolus", it) } + ) } else { - uel.log(Action.BOLUS, notes, ValueWithUnit(insulinAfterConstraints, Units.U)) - detailedBolusInfo.date = DateUtil.now() + uel.log(Action.BOLUS, Sources.InsulinDialog, + notes, + ValueWithUnit.Insulin(insulinAfterConstraints)) commandQueue.bolus(detailedBolusInfo, object : Callback() { override fun run() { if (!result.success) { diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/LoopDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/LoopDialog.kt index 9b10142c8c..efadf17ca7 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/LoopDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/LoopDialog.kt @@ -12,7 +12,9 @@ import androidx.fragment.app.FragmentManager import dagger.android.support.DaggerDialogFragment import info.nightscout.androidaps.R import info.nightscout.androidaps.activities.ErrorHelperActivity -import info.nightscout.androidaps.database.entities.UserEntry.* +import info.nightscout.androidaps.database.entities.ValueWithUnit +import info.nightscout.androidaps.database.entities.UserEntry.Action +import info.nightscout.androidaps.database.entities.UserEntry.Sources import info.nightscout.androidaps.databinding.DialogLoopBinding import info.nightscout.androidaps.events.EventPreferenceChange import info.nightscout.androidaps.events.EventRefreshOverview @@ -21,13 +23,12 @@ 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.queue.Callback import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.ToastUtils import info.nightscout.androidaps.utils.alertDialogs.OKDialog -import info.nightscout.androidaps.utils.extensions.toVisibility +import info.nightscout.androidaps.extensions.toVisibility import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.sharedPreferences.SP import javax.inject.Inject @@ -42,10 +43,10 @@ class LoopDialog : DaggerDialogFragment() { @Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var profileFunction: ProfileFunction @Inject lateinit var loopPlugin: LoopPlugin - @Inject lateinit var activePlugin: ActivePluginProvider + @Inject lateinit var activePlugin: ActivePlugin @Inject lateinit var constraintChecker: ConstraintChecker @Inject lateinit var commandQueue: CommandQueueProvider - @Inject lateinit var configBuilderPlugin: ConfigBuilderPlugin + @Inject lateinit var configBuilder: ConfigBuilder @Inject lateinit var uel: UserEntryLogger private var showOkCancel: Boolean = true @@ -195,9 +196,10 @@ class LoopDialog : DaggerDialogFragment() { binding.overviewDisconnectButtons.visibility = View.GONE binding.overviewReconnect.visibility = View.VISIBLE } + binding.overviewLoop.visibility = (!loopPlugin.isSuspended && !loopPlugin.isDisconnected).toVisibility() } val profile = profileFunction.getProfile() - val profileStore = activePlugin.activeProfileInterface.profile + val profileStore = activePlugin.activeProfileSource.profile if (profile == null || profileStore == null) { ToastUtils.showToastInUiThread(ctx, resourceHelper.gs(R.string.noprofile)) @@ -239,31 +241,31 @@ class LoopDialog : DaggerDialogFragment() { val profile = profileFunction.getProfile() ?: return true when (v.id) { R.id.overview_closeloop -> { - uel.log(Action.CLOSED_LOOP_MODE) + uel.log(Action.CLOSED_LOOP_MODE, Sources.LoopDialog) sp.putString(R.string.key_aps_mode, "closed") rxBus.send(EventPreferenceChange(resourceHelper.gs(R.string.closedloop))) return true } R.id.overview_lgsloop -> { - uel.log(Action.LGS_LOOP_MODE) + uel.log(Action.LGS_LOOP_MODE, Sources.LoopDialog) sp.putString(R.string.key_aps_mode, "lgs") rxBus.send(EventPreferenceChange(resourceHelper.gs(R.string.lowglucosesuspend))) return true } R.id.overview_openloop -> { - uel.log(Action.OPEN_LOOP_MODE) + uel.log(Action.OPEN_LOOP_MODE, Sources.LoopDialog) sp.putString(R.string.key_aps_mode, "open") rxBus.send(EventPreferenceChange(resourceHelper.gs(R.string.lowglucosesuspend))) return true } R.id.overview_disable -> { - uel.log(Action.LOOP_DISABLED) + uel.log(Action.LOOP_DISABLED, Sources.LoopDialog) loopPlugin.setPluginEnabled(PluginType.LOOP, false) loopPlugin.setFragmentVisible(PluginType.LOOP, false) - configBuilderPlugin.storeSettings("DisablingLoop") + configBuilder.storeSettings("DisablingLoop") rxBus.send(EventRefreshOverview("suspendmenu")) commandQueue.cancelTempBasal(true, object : Callback() { override fun run() { @@ -277,17 +279,17 @@ class LoopDialog : DaggerDialogFragment() { } R.id.overview_enable -> { - uel.log(Action.LOOP_ENABLED) + uel.log(Action.LOOP_ENABLED, Sources.LoopDialog) loopPlugin.setPluginEnabled(PluginType.LOOP, true) loopPlugin.setFragmentVisible(PluginType.LOOP, true) - configBuilderPlugin.storeSettings("EnablingLoop") + configBuilder.storeSettings("EnablingLoop") rxBus.send(EventRefreshOverview("suspendmenu")) loopPlugin.createOfflineEvent(0) return true } R.id.overview_resume, R.id.overview_reconnect -> { - uel.log(if (v.id==R.id.overview_resume) Action.RESUME else Action.RECONNECT ) + uel.log(if (v.id==R.id.overview_resume) Action.RESUME else Action.RECONNECT, Sources.LoopDialog) loopPlugin.suspendTo(0L) rxBus.send(EventRefreshOverview("suspendmenu")) commandQueue.cancelTempBasal(true, object : Callback() { @@ -303,49 +305,49 @@ class LoopDialog : DaggerDialogFragment() { } R.id.overview_suspend_1h -> { - uel.log(Action.SUSPEND, ValueWithUnit(1, Units.H)) + uel.log(Action.SUSPEND, Sources.LoopDialog, ValueWithUnit.Hour(1)) loopPlugin.suspendLoop(60) rxBus.send(EventRefreshOverview("suspendmenu")) return true } R.id.overview_suspend_2h -> { - uel.log(Action.SUSPEND, ValueWithUnit(2, Units.H)) + uel.log(Action.SUSPEND, Sources.LoopDialog, ValueWithUnit.Hour(2)) loopPlugin.suspendLoop(120) rxBus.send(EventRefreshOverview("suspendmenu")) return true } R.id.overview_suspend_3h -> { - uel.log(Action.SUSPEND, ValueWithUnit(3, Units.H)) + uel.log(Action.SUSPEND, Sources.LoopDialog, ValueWithUnit.Hour(3)) loopPlugin.suspendLoop(180) rxBus.send(EventRefreshOverview("suspendmenu")) return true } R.id.overview_suspend_10h -> { - uel.log(Action.SUSPEND, ValueWithUnit(10, Units.H)) + uel.log(Action.SUSPEND, Sources.LoopDialog, ValueWithUnit.Hour(10)) loopPlugin.suspendLoop(600) rxBus.send(EventRefreshOverview("suspendmenu")) return true } R.id.overview_disconnect_15m -> { - uel.log(Action.DISCONNECT, ValueWithUnit(15, Units.M)) + uel.log(Action.DISCONNECT, Sources.LoopDialog, ValueWithUnit.Minute(15)) loopPlugin.disconnectPump(15, profile) rxBus.send(EventRefreshOverview("suspendmenu")) return true } R.id.overview_disconnect_30m -> { - uel.log(Action.DISCONNECT, ValueWithUnit(30, Units.M)) + uel.log(Action.DISCONNECT, Sources.LoopDialog, ValueWithUnit.Minute(30)) loopPlugin.disconnectPump(30, profile) rxBus.send(EventRefreshOverview("suspendmenu")) return true } R.id.overview_disconnect_1h -> { - uel.log(Action.DISCONNECT, ValueWithUnit(1, Units.H)) + uel.log(Action.DISCONNECT, Sources.LoopDialog, ValueWithUnit.Hour(1)) loopPlugin.disconnectPump(60, profile) sp.putBoolean(R.string.key_objectiveusedisconnect, true) rxBus.send(EventRefreshOverview("suspendmenu")) @@ -353,14 +355,14 @@ class LoopDialog : DaggerDialogFragment() { } R.id.overview_disconnect_2h -> { - uel.log(Action.DISCONNECT, ValueWithUnit(2, Units.H)) + uel.log(Action.DISCONNECT, Sources.LoopDialog, ValueWithUnit.Hour(2)) loopPlugin.disconnectPump(120, profile) rxBus.send(EventRefreshOverview("suspendmenu")) return true } R.id.overview_disconnect_3h -> { - uel.log(Action.DISCONNECT, ValueWithUnit(3, Units.H)) + uel.log(Action.DISCONNECT, Sources.LoopDialog, ValueWithUnit.Hour(3)) loopPlugin.disconnectPump(180, profile) rxBus.send(EventRefreshOverview("suspendmenu")) return true diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/ProfileSwitchDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/ProfileSwitchDialog.kt index 692f3ee431..d47573ef2f 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/ProfileSwitchDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/ProfileSwitchDialog.kt @@ -8,16 +8,18 @@ import android.widget.ArrayAdapter import com.google.common.base.Joiner import info.nightscout.androidaps.Constants import info.nightscout.androidaps.R -import info.nightscout.androidaps.database.entities.UserEntry.* +import info.nightscout.androidaps.database.AppRepository +import info.nightscout.androidaps.database.entities.UserEntry.Action +import info.nightscout.androidaps.database.entities.UserEntry.Sources +import info.nightscout.androidaps.database.entities.ValueWithUnit import info.nightscout.androidaps.databinding.DialogProfileswitchBinding -import info.nightscout.androidaps.interfaces.ActivePluginProvider +import info.nightscout.androidaps.interfaces.ActivePlugin 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 import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.resources.ResourceHelper +import io.reactivex.disposables.CompositeDisposable import java.text.DecimalFormat import java.util.* import javax.inject.Inject @@ -26,12 +28,14 @@ class ProfileSwitchDialog : DialogFragmentWithDate() { @Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var profileFunction: ProfileFunction - @Inject lateinit var treatmentsPlugin: TreatmentsPlugin - @Inject lateinit var activePlugin: ActivePluginProvider + @Inject lateinit var activePlugin: ActivePlugin + @Inject lateinit var repository: AppRepository @Inject lateinit var uel: UserEntryLogger private var profileIndex: Int? = null + private val disposable = CompositeDisposable() + private var _binding: DialogProfileswitchBinding? = null // This property is only valid between onCreateView and @@ -67,7 +71,7 @@ class ProfileSwitchDialog : DialogFragmentWithDate() { // profile context?.let { context -> - val profileStore = activePlugin.activeProfileInterface.profile + val profileStore = activePlugin.activeProfileSource.profile ?: return val profileList = profileStore.getProfileList() val adapter = ArrayAdapter(context, R.layout.spinner_centered, profileList) @@ -77,17 +81,17 @@ class ProfileSwitchDialog : DialogFragmentWithDate() { binding.profile.setSelection(profileIndex as Int) else for (p in profileList.indices) - if (profileList[p] == profileFunction.getProfileName(false)) + if (profileList[p] == profileFunction.getOriginalProfileName()) binding.profile.setSelection(p) } ?: return - treatmentsPlugin.getProfileSwitchFromHistory(DateUtil.now())?.let { ps -> - if (ps.isCPP) { + profileFunction.getProfile()?.let { profile -> + if (profile.percentage != 100 || profile.timeshift != 0) { binding.reuselayout.visibility = View.VISIBLE - binding.reusebutton.text = resourceHelper.gs(R.string.reuse_profile_pct_hours, ps.percentage, ps.timeshift) + binding.reusebutton.text = resourceHelper.gs(R.string.reuse_profile_pct_hours, profile.percentage, profile.timeshift) binding.reusebutton.setOnClickListener { - binding.percentage.value = ps.percentage.toDouble() - binding.timeshift.value = ps.timeshift.toDouble() + binding.percentage.value = profile.percentage.toDouble() + binding.timeshift.value = profile.timeshift.toDouble() } } else { binding.reuselayout.visibility = View.GONE @@ -97,20 +101,21 @@ class ProfileSwitchDialog : DialogFragmentWithDate() { override fun onDestroyView() { super.onDestroyView() + disposable.clear() _binding = null } override fun submit(): Boolean { if (_binding == null) return false - val profileStore = activePlugin.activeProfileInterface.profile + val profileStore = activePlugin.activeProfileSource.profile ?: return false val actions: LinkedList = LinkedList() val duration = binding.duration.value?.toInt() ?: return false - if (duration > 0) + if (duration > 0L) actions.add(resourceHelper.gs(R.string.duration) + ": " + resourceHelper.gs(R.string.format_mins, duration)) - val profile = binding.profile.selectedItem.toString() - actions.add(resourceHelper.gs(R.string.profile) + ": " + profile) + val profileName = binding.profile.selectedItem.toString() + actions.add(resourceHelper.gs(R.string.profile) + ": " + profileName) val percent = binding.percentage.value.toInt() if (percent != 100) actions.add(resourceHelper.gs(R.string.percent) + ": " + percent + "%") @@ -125,8 +130,20 @@ class ProfileSwitchDialog : DialogFragmentWithDate() { activity?.let { activity -> OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.careportal_profileswitch), HtmlHelper.fromHtml(Joiner.on("
").join(actions)), { - uel.log(Action.PROFILE_SWITCH, notes, ValueWithUnit(eventTime, Units.Timestamp, eventTimeChanged), ValueWithUnit(profile, Units.None), ValueWithUnit(percent, Units.Percent), ValueWithUnit(timeShift, Units.H, timeShift != 0), ValueWithUnit(duration, Units.M, duration != 0)) - treatmentsPlugin.doProfileSwitch(profileStore, profile, duration, percent, timeShift, eventTime) + profileFunction.createProfileSwitch(profileStore, + profileName = profileName, + durationInMinutes = duration, + percentage = percent, + timeShiftInHours = timeShift, + timestamp = eventTime) + uel.log(Action.PROFILE_SWITCH, + Sources.ProfileSwitchDialog, + notes, + ValueWithUnit.Timestamp(eventTime).takeIf { eventTimeChanged }, + ValueWithUnit.SimpleString(profileName), + ValueWithUnit.Percent(percent), + ValueWithUnit.Hour(timeShift).takeIf { timeShift != 0 }, + ValueWithUnit.Minute(duration).takeIf { duration != 0 }) }) } return true diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/TempBasalDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/TempBasalDialog.kt index 01c9aa8d2a..10df72c584 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/TempBasalDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/TempBasalDialog.kt @@ -8,20 +8,18 @@ import android.view.ViewGroup import com.google.common.base.Joiner import info.nightscout.androidaps.R import info.nightscout.androidaps.activities.ErrorHelperActivity -import info.nightscout.androidaps.database.entities.UserEntry.* +import info.nightscout.androidaps.database.entities.ValueWithUnit +import info.nightscout.androidaps.database.entities.UserEntry.Action +import info.nightscout.androidaps.database.entities.UserEntry.Sources import info.nightscout.androidaps.databinding.DialogTempbasalBinding -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.interfaces.PumpDescription +import info.nightscout.androidaps.interfaces.* 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 import info.nightscout.androidaps.utils.SafeParse import info.nightscout.androidaps.utils.alertDialogs.OKDialog -import info.nightscout.androidaps.utils.extensions.formatColor +import info.nightscout.androidaps.extensions.formatColor import info.nightscout.androidaps.utils.resources.ResourceHelper import java.text.DecimalFormat import java.util.* @@ -33,7 +31,7 @@ class TempBasalDialog : DialogFragmentWithDate() { @Inject lateinit var constraintChecker: ConstraintChecker @Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var profileFunction: ProfileFunction - @Inject lateinit var activePlugin: ActivePluginProvider + @Inject lateinit var activePlugin: ActivePlugin @Inject lateinit var commandQueue: CommandQueueProvider @Inject lateinit var ctx: Context @Inject lateinit var uel: UserEntryLogger @@ -73,7 +71,7 @@ class TempBasalDialog : DialogFragmentWithDate() { ?: 100.0, 0.0, maxTempPercent, tempPercentStep, DecimalFormat("0"), true, binding.okcancel.ok) binding.basalabsoluteinput.setParams(savedInstanceState?.getDouble("basalabsoluteinput") - ?: profile.basal, 0.0, pumpDescription.maxTempAbsolute, pumpDescription.tempAbsoluteStep, DecimalFormat("0.00"), true, binding.okcancel.ok) + ?: profile.getBasal(), 0.0, pumpDescription.maxTempAbsolute, pumpDescription.tempAbsoluteStep, DecimalFormat("0.00"), true, binding.okcancel.ok) val tempDurationStep = pumpDescription.tempDurationStep.toDouble() val tempMaxDuration = pumpDescription.tempMaxDuration.toDouble() @@ -126,11 +124,15 @@ class TempBasalDialog : DialogFragmentWithDate() { } } if (isPercentPump) { - uel.log(Action.TEMP_BASAL, ValueWithUnit(percent, Units.Percent), ValueWithUnit(durationInMinutes, Units.M)) - commandQueue.tempBasalPercent(percent, durationInMinutes, true, profile, callback) + uel.log(Action.TEMP_BASAL, Sources.TempBasalDialog, + ValueWithUnit.Percent(percent), + ValueWithUnit.Minute(durationInMinutes)) + commandQueue.tempBasalPercent(percent, durationInMinutes, true, profile, PumpSync.TemporaryBasalType.NORMAL, callback) } else { - uel.log(Action.TEMP_BASAL, ValueWithUnit(absolute, Units.U), ValueWithUnit(durationInMinutes, Units.M)) - commandQueue.tempBasalAbsolute(absolute, durationInMinutes, true, profile, callback) + uel.log(Action.TEMP_BASAL, Sources.TempBasalDialog, + ValueWithUnit.Insulin(absolute), + ValueWithUnit.Minute(durationInMinutes)) + commandQueue.tempBasalAbsolute(absolute, durationInMinutes, true, profile, PumpSync.TemporaryBasalType.NORMAL, callback) } }) } diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/TempTargetDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/TempTargetDialog.kt index 7cebd826f0..699d3c43c1 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/TempTargetDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/TempTargetDialog.kt @@ -9,19 +9,21 @@ import com.google.common.base.Joiner import com.google.common.collect.Lists import info.nightscout.androidaps.Constants import info.nightscout.androidaps.R -import info.nightscout.androidaps.data.Profile +import info.nightscout.androidaps.interfaces.Profile import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.ValueWrapper +import info.nightscout.androidaps.database.entities.ValueWithUnit import info.nightscout.androidaps.database.entities.TemporaryTarget -import info.nightscout.androidaps.database.entities.UserEntry.* +import info.nightscout.androidaps.database.entities.UserEntry.Action +import info.nightscout.androidaps.database.entities.UserEntry.Sources import info.nightscout.androidaps.database.transactions.CancelCurrentTemporaryTargetIfAnyTransaction import info.nightscout.androidaps.database.transactions.InsertTemporaryTargetAndCancelCurrentTransaction import info.nightscout.androidaps.databinding.DialogTemptargetBinding +import info.nightscout.androidaps.interfaces.GlucoseUnit import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.logging.LTag 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.utils.DefaultValueHelper import info.nightscout.androidaps.utils.HtmlHelper import info.nightscout.androidaps.utils.alertDialogs.OKDialog @@ -41,7 +43,6 @@ class TempTargetDialog : DialogFragmentWithDate() { @Inject lateinit var defaultValueHelper: DefaultValueHelper @Inject lateinit var uel: UserEntryLogger @Inject lateinit var repository: AppRepository - @Inject lateinit var nsUpload: NSUpload private lateinit var reasonList: List @@ -72,7 +73,7 @@ class TempTargetDialog : DialogFragmentWithDate() { binding.duration.setParams(savedInstanceState?.getDouble("duration") ?: 0.0, 0.0, Constants.MAX_PROFILE_SWITCH_DURATION, 10.0, DecimalFormat("0"), false, binding.okcancel.ok) - if (profileFunction.getUnits() == Constants.MMOL) + if (profileFunction.getUnits() == GlucoseUnit.MMOL) binding.temptarget.setParams( savedInstanceState?.getDouble("tempTarget") ?: 8.0, @@ -84,11 +85,11 @@ class TempTargetDialog : DialogFragmentWithDate() { Constants.MIN_TT_MGDL, Constants.MAX_TT_MGDL, 1.0, DecimalFormat("0"), false, binding.okcancel.ok) val units = profileFunction.getUnits() - binding.units.text = if (units == Constants.MMOL) resourceHelper.gs(R.string.mmol) else resourceHelper.gs(R.string.mgdl) + binding.units.text = if (units == GlucoseUnit.MMOL) resourceHelper.gs(R.string.mmol) else resourceHelper.gs(R.string.mgdl) // temp target context?.let { context -> - if (repository.getTemporaryTargetActiveAt(dateUtil._now()).blockingGet() is ValueWrapper.Existing) + if (repository.getTemporaryTargetActiveAt(dateUtil.now()).blockingGet() is ValueWrapper.Existing) binding.targetCancel.visibility = View.VISIBLE else binding.targetCancel.visibility = View.GONE @@ -135,19 +136,19 @@ class TempTargetDialog : DialogFragmentWithDate() { binding.reason.setSelection(reasonList.indexOf(resourceHelper.gs(R.string.eatingsoon))) } - R.id.activity -> { + R.id.activity -> { binding.temptarget.value = defaultValueHelper.determineActivityTT() binding.duration.value = defaultValueHelper.determineActivityTTDuration().toDouble() binding.reason.setSelection(reasonList.indexOf(resourceHelper.gs(R.string.activity))) } - R.id.hypo -> { + R.id.hypo -> { binding.temptarget.value = defaultValueHelper.determineHypoTT() binding.duration.value = defaultValueHelper.determineHypoTTDuration().toDouble() binding.reason.setSelection(reasonList.indexOf(resourceHelper.gs(R.string.hypo))) } - R.id.cancel -> { + R.id.cancel -> { binding.duration.value = 0.0 } } @@ -163,7 +164,7 @@ class TempTargetDialog : DialogFragmentWithDate() { if (_binding == null) return false val actions: LinkedList = LinkedList() var reason = binding.reason.selectedItem?.toString() ?: return false - val unitResId = if (profileFunction.getUnits() == Constants.MGDL) R.string.mgdl else R.string.mmol + val unitResId = if (profileFunction.getUnits() == GlucoseUnit.MGDL) R.string.mgdl else R.string.mmol val target = binding.temptarget.value val duration = binding.duration.value.toInt() if (target != 0.0 && duration != 0) { @@ -181,18 +182,18 @@ class TempTargetDialog : DialogFragmentWithDate() { OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.careportal_temporarytarget), HtmlHelper.fromHtml(Joiner.on("
").join(actions)), { val units = profileFunction.getUnits() when(reason) { - resourceHelper.gs(R.string.eatingsoon) -> uel.log(Action.TT, ValueWithUnit(eventTime, Units.Timestamp, eventTimeChanged), ValueWithUnit(TemporaryTarget.Reason.EATING_SOON.text, Units.TherapyEvent), ValueWithUnit(target, units), ValueWithUnit(duration, Units.M)) - resourceHelper.gs(R.string.activity) -> uel.log(Action.TT, ValueWithUnit(eventTime, Units.Timestamp, eventTimeChanged), ValueWithUnit(TemporaryTarget.Reason.ACTIVITY.text, Units.TherapyEvent), ValueWithUnit(target, units), ValueWithUnit(duration, Units.M)) - resourceHelper.gs(R.string.hypo) -> uel.log(Action.TT, ValueWithUnit(eventTime, Units.Timestamp, eventTimeChanged), ValueWithUnit(TemporaryTarget.Reason.HYPOGLYCEMIA.text, Units.TherapyEvent), ValueWithUnit(target, units), ValueWithUnit(duration, Units.M)) - resourceHelper.gs(R.string.manual) -> uel.log(Action.TT, ValueWithUnit(eventTime, Units.Timestamp, eventTimeChanged), ValueWithUnit(TemporaryTarget.Reason.CUSTOM.text, Units.TherapyEvent), ValueWithUnit(target, units), ValueWithUnit(duration, Units.M)) - resourceHelper.gs(R.string.stoptemptarget) -> uel.log(Action.CANCEL_TT, ValueWithUnit(eventTime, Units.Timestamp, eventTimeChanged)) + resourceHelper.gs(R.string.eatingsoon) -> uel.log(Action.TT, Sources.TTDialog, ValueWithUnit.Timestamp(eventTime).takeIf { eventTimeChanged }, ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.EATING_SOON), ValueWithUnit.fromGlucoseUnit(target, units.asText), ValueWithUnit.Minute(duration)) + resourceHelper.gs(R.string.activity) -> uel.log(Action.TT, Sources.TTDialog, ValueWithUnit.Timestamp(eventTime).takeIf { eventTimeChanged }, ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.ACTIVITY), ValueWithUnit.fromGlucoseUnit(target, units.asText), ValueWithUnit.Minute(duration)) + resourceHelper.gs(R.string.hypo) -> uel.log(Action.TT, Sources.TTDialog, ValueWithUnit.Timestamp(eventTime).takeIf { eventTimeChanged }, ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.HYPOGLYCEMIA), ValueWithUnit.fromGlucoseUnit(target, units.asText), ValueWithUnit.Minute(duration)) + resourceHelper.gs(R.string.manual) -> uel.log(Action.TT, Sources.TTDialog, ValueWithUnit.Timestamp(eventTime).takeIf { eventTimeChanged }, ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.CUSTOM), ValueWithUnit.fromGlucoseUnit(target, units.asText), ValueWithUnit.Minute(duration)) + resourceHelper.gs(R.string.stoptemptarget) -> uel.log(Action.CANCEL_TT, Sources.TTDialog, ValueWithUnit.Timestamp(eventTime).takeIf { eventTimeChanged }) } if (target == 0.0 || duration == 0) { disposable += repository.runTransactionForResult(CancelCurrentTemporaryTargetIfAnyTransaction(eventTime)) .subscribe({ result -> - result.updated.forEach { nsUpload.updateTempTarget(it) } + result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated temp target $it") } }, { - aapsLogger.error(LTag.BGSOURCE, "Error while saving temporary target", it) + aapsLogger.error(LTag.DATABASE, "Error while saving temporary target", it) }) } else { disposable += repository.runTransactionForResult(InsertTemporaryTargetAndCancelCurrentTransaction( @@ -200,17 +201,17 @@ class TempTargetDialog : DialogFragmentWithDate() { duration = TimeUnit.MINUTES.toMillis(duration.toLong()), reason = when (reason) { resourceHelper.gs(R.string.eatingsoon) -> TemporaryTarget.Reason.EATING_SOON - resourceHelper.gs(R.string.activity) -> TemporaryTarget.Reason.ACTIVITY - resourceHelper.gs(R.string.hypo) -> TemporaryTarget.Reason.HYPOGLYCEMIA - else -> TemporaryTarget.Reason.CUSTOM + resourceHelper.gs(R.string.activity) -> TemporaryTarget.Reason.ACTIVITY + resourceHelper.gs(R.string.hypo) -> TemporaryTarget.Reason.HYPOGLYCEMIA + else -> TemporaryTarget.Reason.CUSTOM }, lowTarget = Profile.toMgdl(target, profileFunction.getUnits()), highTarget = Profile.toMgdl(target, profileFunction.getUnits()) )).subscribe({ result -> - result.inserted.forEach { nsUpload.uploadTempTarget(it) } - result.updated.forEach { nsUpload.updateTempTarget(it) } + result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted temp target $it") } + result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated temp target $it") } }, { - aapsLogger.error(LTag.BGSOURCE, "Error while saving temporary target", it) + aapsLogger.error(LTag.DATABASE, "Error while saving temporary target", it) }) } diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/TreatmentDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/TreatmentDialog.kt index 5f421134a3..a76863bce2 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/TreatmentDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/TreatmentDialog.kt @@ -8,17 +8,19 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import com.google.common.base.Joiner -import info.nightscout.androidaps.Config +import info.nightscout.androidaps.interfaces.Config import info.nightscout.androidaps.R import info.nightscout.androidaps.activities.ErrorHelperActivity import info.nightscout.androidaps.data.DetailedBolusInfo -import info.nightscout.androidaps.database.entities.TherapyEvent -import info.nightscout.androidaps.database.entities.UserEntry.* +import info.nightscout.androidaps.database.AppRepository +import info.nightscout.androidaps.database.entities.ValueWithUnit +import info.nightscout.androidaps.database.entities.UserEntry.Action +import info.nightscout.androidaps.database.entities.UserEntry.Sources import info.nightscout.androidaps.databinding.DialogTreatmentBinding -import info.nightscout.androidaps.db.Source -import info.nightscout.androidaps.interfaces.ActivePluginProvider +import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.CommandQueueProvider import info.nightscout.androidaps.interfaces.Constraint +import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker import info.nightscout.androidaps.queue.Callback @@ -27,8 +29,10 @@ import info.nightscout.androidaps.utils.HtmlHelper import info.nightscout.androidaps.utils.SafeParse import info.nightscout.androidaps.utils.ToastUtils import info.nightscout.androidaps.utils.alertDialogs.OKDialog -import info.nightscout.androidaps.utils.extensions.formatColor +import info.nightscout.androidaps.extensions.formatColor import info.nightscout.androidaps.utils.resources.ResourceHelper +import io.reactivex.disposables.CompositeDisposable +import io.reactivex.rxkotlin.plusAssign import java.text.DecimalFormat import java.util.* import javax.inject.Inject @@ -38,11 +42,14 @@ class TreatmentDialog : DialogFragmentWithDate() { @Inject lateinit var constraintChecker: ConstraintChecker @Inject lateinit var resourceHelper: ResourceHelper - @Inject lateinit var activePlugin: ActivePluginProvider + @Inject lateinit var activePlugin: ActivePlugin @Inject lateinit var commandQueue: CommandQueueProvider @Inject lateinit var ctx: Context @Inject lateinit var config: Config @Inject lateinit var uel: UserEntryLogger + @Inject lateinit var repository: AppRepository + + private val disposable = CompositeDisposable() private val textWatcher: TextWatcher = object : TextWatcher { override fun afterTextChanged(s: Editable) {} @@ -130,24 +137,51 @@ 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("
").join(actions)), { - uel.log(Action.TREATMENT, ValueWithUnit(insulin, Units.U, insulin != 0.0), ValueWithUnit(carbs, Units.G, carbs != 0)) + val action = when { + insulinAfterConstraints.equals(0.0) -> Action.CARBS + carbsAfterConstraints.equals(0) -> Action.BOLUS + else -> Action.TREATMENT + } val detailedBolusInfo = DetailedBolusInfo() - if (insulinAfterConstraints == 0.0) detailedBolusInfo.eventType = TherapyEvent.Type.CARBS_CORRECTION.text - if (carbsAfterConstraints == 0) detailedBolusInfo.eventType = TherapyEvent.Type.CORRECTION_BOLUS.text + if (insulinAfterConstraints == 0.0) detailedBolusInfo.eventType = DetailedBolusInfo.EventType.CARBS_CORRECTION + if (carbsAfterConstraints == 0) detailedBolusInfo.eventType = DetailedBolusInfo.EventType.CORRECTION_BOLUS detailedBolusInfo.insulin = insulinAfterConstraints detailedBolusInfo.carbs = carbsAfterConstraints.toDouble() detailedBolusInfo.context = context - detailedBolusInfo.source = Source.USER - if (!(recordOnlyChecked && (detailedBolusInfo.insulin > 0 || pumpDescription.storesCarbInfo))) { - commandQueue.bolus(detailedBolusInfo, object : Callback() { - override fun run() { - if (!result.success) { - ErrorHelperActivity.runAlarm(ctx, result.comment, resourceHelper.gs(R.string.treatmentdeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror) + if (recordOnlyChecked) { + uel.log(action, Sources.TreatmentDialog, if (insulinAfterConstraints != 0.0) resourceHelper.gs(R.string.record) else "", + ValueWithUnit.Timestamp(detailedBolusInfo.timestamp).takeIf { eventTimeChanged }, + ValueWithUnit.SimpleString(resourceHelper.gsNotLocalised(R.string.record)).takeIf { insulinAfterConstraints != 0.0 }, + ValueWithUnit.Insulin(insulinAfterConstraints).takeIf { insulinAfterConstraints != 0.0 }, + ValueWithUnit.Gram(carbsAfterConstraints).takeIf { carbsAfterConstraints != 0 }) + if (detailedBolusInfo.insulin > 0) + disposable += repository.runTransactionForResult(detailedBolusInfo.insertBolusTransaction()) + .subscribe( + { result -> result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted bolus $it") } }, + { aapsLogger.error(LTag.DATABASE, "Error while saving bolus", it) } + ) + if (detailedBolusInfo.carbs > 0) + disposable += repository.runTransactionForResult(detailedBolusInfo.insertCarbsTransaction()) + .subscribe( + { result -> result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted carbs $it") } }, + { aapsLogger.error(LTag.DATABASE, "Error while saving carbs", it) } + ) + } else { + if (detailedBolusInfo.insulin > 0) { + uel.log(action, Sources.TreatmentDialog, + ValueWithUnit.Insulin(insulinAfterConstraints), + ValueWithUnit.Gram(carbsAfterConstraints).takeIf { carbsAfterConstraints != 0 }) + commandQueue.bolus(detailedBolusInfo, object : Callback() { + override fun run() { + if (!result.success) { + ErrorHelperActivity.runAlarm(ctx, result.comment, resourceHelper.gs(R.string.treatmentdeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror) + } } - } - }) - } else - activePlugin.activeTreatments.addToHistoryTreatment(detailedBolusInfo, false) + }) + } else + uel.log(action, Sources.TreatmentDialog, + ValueWithUnit.Gram(carbsAfterConstraints).takeIf { carbs != 0 }) + } }) } } else diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/WizardDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/WizardDialog.kt index 4b4744e6ea..9d65cc01e1 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/WizardDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/WizardDialog.kt @@ -1,5 +1,6 @@ package info.nightscout.androidaps.dialogs +import android.content.Context import android.os.Bundle import android.text.Editable import android.text.TextWatcher @@ -16,25 +17,24 @@ import androidx.fragment.app.FragmentManager import dagger.android.HasAndroidInjector import dagger.android.support.DaggerDialogFragment import info.nightscout.androidaps.Constants -import info.nightscout.androidaps.MainApp import info.nightscout.androidaps.R -import info.nightscout.androidaps.data.Profile +import info.nightscout.androidaps.data.ProfileSealed import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.ValueWrapper import info.nightscout.androidaps.databinding.DialogWizardBinding import info.nightscout.androidaps.events.EventAutosensCalculationFinished -import info.nightscout.androidaps.interfaces.ActivePluginProvider -import info.nightscout.androidaps.interfaces.Constraint -import info.nightscout.androidaps.interfaces.ProfileFunction -import info.nightscout.androidaps.interfaces.TreatmentsInterface import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin -import info.nightscout.androidaps.utils.* -import info.nightscout.androidaps.utils.extensions.toVisibility -import info.nightscout.androidaps.utils.extensions.valueToUnits +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.DecimalFormatter +import info.nightscout.androidaps.utils.FabricPrivacy +import info.nightscout.androidaps.utils.SafeParse +import info.nightscout.androidaps.utils.ToastUtils +import info.nightscout.androidaps.extensions.toVisibility +import info.nightscout.androidaps.extensions.valueToUnits +import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.utils.sharedPreferences.SP @@ -51,16 +51,15 @@ class WizardDialog : DaggerDialogFragment() { @Inject lateinit var aapsLogger: AAPSLogger @Inject lateinit var aapsSchedulers: AapsSchedulers @Inject lateinit var constraintChecker: ConstraintChecker - @Inject lateinit var mainApp: MainApp + @Inject lateinit var ctx: Context @Inject lateinit var sp: SP @Inject lateinit var rxBus: RxBusWrapper @Inject lateinit var fabricPrivacy: FabricPrivacy @Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var profileFunction: ProfileFunction - @Inject lateinit var activePlugin: ActivePluginProvider - @Inject lateinit var iobCobCalculatorPlugin: IobCobCalculatorPlugin + @Inject lateinit var activePlugin: ActivePlugin + @Inject lateinit var iobCobCalculator: IobCobCalculator @Inject lateinit var repository: AppRepository - @Inject lateinit var treatmentsPlugin: TreatmentsInterface @Inject lateinit var dateUtil: DateUtil private var wizard: BolusWizard? = null @@ -126,7 +125,7 @@ class WizardDialog : DaggerDialogFragment() { val maxCarbs = constraintChecker.getMaxCarbsAllowed().value() val maxCorrection = constraintChecker.getMaxBolusAllowed().value() - if (profileFunction.getUnits() == Constants.MGDL) + if (profileFunction.getUnits() == GlucoseUnit.MGDL) binding.bgInput.setParams(savedInstanceState?.getDouble("bg_input") ?: 0.0, 0.0, 500.0, 1.0, DecimalFormat("0"), false, binding.ok, timeTextWatcher) else @@ -180,7 +179,7 @@ class WizardDialog : DaggerDialogFragment() { // profile spinner binding.profile.onItemSelectedListener = object : OnItemSelectedListener { override fun onNothingSelected(parent: AdapterView<*>?) { - ToastUtils.showToastInUiThread(mainApp, resourceHelper.gs(R.string.noprofileselected)) + ToastUtils.showToastInUiThread(ctx, resourceHelper.gs(R.string.noprofileselected)) binding.ok.visibility = View.GONE } @@ -208,7 +207,7 @@ class WizardDialog : DaggerDialogFragment() { private fun onCheckedChanged(buttonView: CompoundButton, @Suppress("UNUSED_PARAMETER") state: Boolean) { saveCheckedStates() - binding.ttcheckbox.isEnabled = binding.bgcheckbox.isChecked && repository.getTemporaryTargetActiveAt(dateUtil._now()).blockingGet() is ValueWrapper.Existing + binding.ttcheckbox.isEnabled = binding.bgcheckbox.isChecked && repository.getTemporaryTargetActiveAt(dateUtil.now()).blockingGet() is ValueWrapper.Existing if (buttonView.id == binding.cobcheckbox.id) processCobCheckBox() calculateInsulin() @@ -242,10 +241,10 @@ class WizardDialog : DaggerDialogFragment() { private fun initDialog() { val profile = profileFunction.getProfile() - val profileStore = activePlugin.activeProfileInterface.profile + val profileStore = activePlugin.activeProfileSource.profile if (profile == null || profileStore == null) { - ToastUtils.showToastInUiThread(mainApp, resourceHelper.gs(R.string.noprofile)) + ToastUtils.showToastInUiThread(ctx, resourceHelper.gs(R.string.noprofile)) dismiss() return } @@ -258,27 +257,19 @@ class WizardDialog : DaggerDialogFragment() { } ?: return val units = profileFunction.getUnits() - binding.bgunits.text = units - if (units == Constants.MGDL) + binding.bgunits.text = units.asText + if (units == GlucoseUnit.MGDL) binding.bgInput.setStep(1.0) else binding.bgInput.setStep(0.1) // Set BG if not old - val lastBg = iobCobCalculatorPlugin.actualBg() - - if (lastBg != null) { - binding.bgInput.value = lastBg.valueToUnits(units) - } else { - binding.bgInput.value = 0.0 - } - binding.ttcheckbox.isEnabled = repository.getTemporaryTargetActiveAt(dateUtil._now()).blockingGet() is ValueWrapper.Existing + binding.bgInput.value = iobCobCalculator.ads.actualBg()?.valueToUnits(units) ?: 0.0 + binding.ttcheckbox.isEnabled = repository.getTemporaryTargetActiveAt(dateUtil.now()).blockingGet() is ValueWrapper.Existing // IOB calculation - treatmentsPlugin.updateTotalIOBTreatments() - val bolusIob = treatmentsPlugin.lastCalculationTreatments.round() - treatmentsPlugin.updateTotalIOBTempBasals() - val basalIob = treatmentsPlugin.lastCalculationTempBasals.round() + val bolusIob = iobCobCalculator.calculateIobFromBolus().round() + val basalIob = iobCobCalculator.calculateIobFromTempBasalsIncludingConvertedExtended().round() binding.bolusiobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, -bolusIob.iob) binding.basaliobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, -basalIob.basaliob) @@ -289,7 +280,7 @@ class WizardDialog : DaggerDialogFragment() { } private fun calculateInsulin() { - val profileStore = activePlugin.activeProfileInterface.profile + val profileStore = activePlugin.activeProfileSource.profile if (binding.profile.selectedItem == null || profileStore == null) return // not initialized yet var profileName = binding.profile.selectedItem.toString() @@ -298,7 +289,7 @@ class WizardDialog : DaggerDialogFragment() { specificProfile = profileFunction.getProfile() profileName = profileFunction.getProfileName() } else - specificProfile = profileStore.getSpecificProfile(profileName) + specificProfile = profileStore.getSpecificProfile(profileName)?.let { ProfileSealed.Pure(it) } if (specificProfile == null) return @@ -309,25 +300,25 @@ class WizardDialog : DaggerDialogFragment() { val carbsAfterConstraint = constraintChecker.applyCarbsConstraints(Constraint(carbs)).value() if (abs(carbs - carbsAfterConstraint) > 0.01) { binding.carbsInput.value = 0.0 - ToastUtils.showToastInUiThread(mainApp, resourceHelper.gs(R.string.carbsconstraintapplied)) + ToastUtils.showToastInUiThread(ctx, resourceHelper.gs(R.string.carbsconstraintapplied)) return } bg = if (binding.bgcheckbox.isChecked) bg else 0.0 - val dbRecord = repository.getTemporaryTargetActiveAt(dateUtil._now()).blockingGet() + val dbRecord = repository.getTemporaryTargetActiveAt(dateUtil.now()).blockingGet() val tempTarget = if (binding.ttcheckbox.isChecked && dbRecord is ValueWrapper.Existing) dbRecord.value else null // COB var cob = 0.0 if (binding.cobcheckbox.isChecked) { - val cobInfo = iobCobCalculatorPlugin.getCobInfo(false, "Wizard COB") + val cobInfo = iobCobCalculator.getCobInfo(false, "Wizard COB") cobInfo.displayCob?.let { cob = it } } val carbTime = SafeParse.stringToInt(binding.carbTimeInput.text) - wizard = BolusWizard(mainApp).doCalc(specificProfile, profileName, tempTarget, carbsAfterConstraint, cob, bg, correction, - sp.getInt(R.string.key_boluswizard_percentage, 100).toDouble(), + wizard = BolusWizard(injector).doCalc(specificProfile, profileName, tempTarget, carbsAfterConstraint, cob, bg, correction, + sp.getInt(R.string.key_boluswizard_percentage, 100), binding.bgcheckbox.isChecked, binding.cobcheckbox.isChecked, binding.bolusiobcheckbox.isChecked, @@ -339,7 +330,7 @@ class WizardDialog : DaggerDialogFragment() { binding.notes.text.toString(), carbTime) wizard?.let { wizard -> - binding.bg.text = String.format(resourceHelper.gs(R.string.format_bg_isf), valueToUnitsToString(Profile.toMgdl(bg, profileFunction.getUnits()), profileFunction.getUnits()), wizard.sens) + binding.bg.text = String.format(resourceHelper.gs(R.string.format_bg_isf), valueToUnitsToString(Profile.toMgdl(bg, profileFunction.getUnits()), profileFunction.getUnits().asText), wizard.sens) binding.bginsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromBG) binding.carbs.text = String.format(resourceHelper.gs(R.string.format_carbs_ic), carbs.toDouble(), wizard.ic) diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/WizardInfoDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/WizardInfoDialog.kt index aa76bde8c3..1ae378454e 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/WizardInfoDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/WizardInfoDialog.kt @@ -9,12 +9,12 @@ import android.view.WindowManager import dagger.android.support.DaggerDialogFragment import info.nightscout.androidaps.Constants import info.nightscout.androidaps.R +import info.nightscout.androidaps.interfaces.Profile +import info.nightscout.androidaps.database.entities.BolusCalculatorResult import info.nightscout.androidaps.databinding.DialogWizardinfoBinding import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.utils.DecimalFormatter -import info.nightscout.androidaps.utils.JsonHelper import info.nightscout.androidaps.utils.resources.ResourceHelper -import org.json.JSONObject import javax.inject.Inject class WizardInfoDialog : DaggerDialogFragment() { @@ -22,10 +22,10 @@ class WizardInfoDialog : DaggerDialogFragment() { @Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var profileFunction: ProfileFunction - private var json: JSONObject? = null + private lateinit var data: BolusCalculatorResult - fun setData(json: JSONObject) { - this.json = json + fun setData(bolusCalculatorResult: BolusCalculatorResult) { + this.data = bolusCalculatorResult } private var _binding: DialogWizardinfoBinding? = null @@ -49,44 +49,42 @@ class WizardInfoDialog : DaggerDialogFragment() { binding.close.setOnClickListener { dismiss() } val units = profileFunction.getUnits() - val bgString = - if (units == Constants.MGDL) DecimalFormatter.to0Decimal(JsonHelper.safeGetDouble(json, "bg")) - else DecimalFormatter.to1Decimal(JsonHelper.safeGetDouble(json, "bg")) + val bgString = Profile.toUnitsString(data.glucoseValue, data.glucoseValue * Constants.MGDL_TO_MMOLL, units) // BG - binding.bg.text = resourceHelper.gs(R.string.format_bg_isf, bgString, JsonHelper.safeGetDouble(json, "isf")) - binding.bginsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "insulinbg")) - binding.bgcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "insulinbgused") - binding.ttcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "ttused") + binding.bg.text = resourceHelper.gs(R.string.format_bg_isf, bgString, data.isf) + binding.bginsulin.text = resourceHelper.gs(R.string.formatinsulinunits, data.glucoseInsulin) + binding.bgcheckbox.isChecked = data.wasGlucoseUsed + binding.ttcheckbox.isChecked = data.wasTempTargetUsed // Trend - binding.bgtrend.text = JsonHelper.safeGetString(json, "trend") - binding.bgtrendinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "insulintrend")) - binding.bgtrendcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "trendused") + binding.bgtrend.text = DecimalFormatter.to1Decimal(data.glucoseTrend) + binding.bgtrendinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, data.trendInsulin) + binding.bgtrendcheckbox.isChecked = data.wasTrendUsed // COB - binding.cob.text = resourceHelper.gs(R.string.format_cob_ic, JsonHelper.safeGetDouble(json, "cob"), JsonHelper.safeGetDouble(json, "ic")) - binding.cobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "insulincob")) - binding.cobcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "cobused") + binding.cob.text = resourceHelper.gs(R.string.format_cob_ic, data.cob, data.ic) + binding.cobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, data.cobInsulin) + binding.cobcheckbox.isChecked = data.wasCOBUsed // Bolus IOB - binding.bolusiobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "bolusiob")) - binding.bolusiobcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "bolusiobused") + binding.bolusiobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, data.bolusIOB) + binding.bolusiobcheckbox.isChecked = data.wasBolusIOBUsed // Basal IOB - binding.basaliobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "basaliob")) - binding.basaliobcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "basaliobused") + binding.basaliobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, data.basalIOB) + binding.basaliobcheckbox.isChecked = data.wasBasalIOBUsed // Superbolus - binding.sbinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "insulinsuperbolus")) - binding.sbcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "superbolusused") + binding.sbinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, data.superbolusInsulin) + binding.sbcheckbox.isChecked = data.wasSuperbolusUsed // Carbs - binding.carbs.text = resourceHelper.gs(R.string.format_carbs_ic, JsonHelper.safeGetDouble(json, "carbs"), JsonHelper.safeGetDouble(json, "ic")) - binding.carbsinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "insulincarbs")) + binding.carbs.text = resourceHelper.gs(R.string.format_carbs_ic, data.carbs, data.ic) + binding.carbsinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, data.carbsInsulin) // Correction - binding.correctioninsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "othercorrection")) + binding.correctioninsulin.text = resourceHelper.gs(R.string.formatinsulinunits, data.otherCorrection) // Profile - binding.profile.text = JsonHelper.safeGetString(json, "profile") + binding.profile.text = data.profileName // Notes - binding.notes.text = JsonHelper.safeGetString(json, "notes") + binding.notes.text = data.note // Percentage - binding.percentUsed.text = resourceHelper.gs(R.string.format_percent, (JsonHelper.safeGetInt(json, "percentageCorrection", 100))) + binding.percentUsed.text = resourceHelper.gs(R.string.format_percent, data.percentageCorrection) // Total - binding.totalinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "insulin")) + binding.totalinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, data.totalInsulin) } override fun onStart() { diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventNsTreatment.kt b/app/src/main/java/info/nightscout/androidaps/events/EventNsTreatment.kt deleted file mode 100644 index 149894c221..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/events/EventNsTreatment.kt +++ /dev/null @@ -1,21 +0,0 @@ -package info.nightscout.androidaps.events - -import org.json.JSONObject - - -/** - * Event which is published with data fetched from NightScout specific for the - * Treatment-class. - * - * - * Payload is the from NS retrieved JSON-String which should be handled by all - * subscriber. - */ - -class EventNsTreatment(val mode: Int, val payload: JSONObject) : Event() { - companion object { - val ADD = 0 - val UPDATE = 1 - val REMOVE = 2 - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventReloadTempBasalData.kt b/app/src/main/java/info/nightscout/androidaps/events/EventReloadTempBasalData.kt deleted file mode 100644 index fa8f720896..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/events/EventReloadTempBasalData.kt +++ /dev/null @@ -1,3 +0,0 @@ -package info.nightscout.androidaps.events - -class EventReloadTempBasalData : Event() diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventReloadTreatmentData.kt b/app/src/main/java/info/nightscout/androidaps/events/EventReloadTreatmentData.kt deleted file mode 100644 index 1f8b2938b9..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/events/EventReloadTreatmentData.kt +++ /dev/null @@ -1,3 +0,0 @@ -package info.nightscout.androidaps.events - -class EventReloadTreatmentData(var next: Event) : Event() diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventTreatmentChange.kt b/app/src/main/java/info/nightscout/androidaps/events/EventTreatmentChange.kt index 679b62ee30..2f25231b00 100644 --- a/app/src/main/java/info/nightscout/androidaps/events/EventTreatmentChange.kt +++ b/app/src/main/java/info/nightscout/androidaps/events/EventTreatmentChange.kt @@ -1,5 +1,3 @@ package info.nightscout.androidaps.events -import info.nightscout.androidaps.db.Treatment - -class EventTreatmentChange(val treatment: Treatment?) : EventLoop() +class EventTreatmentChange : EventLoop() diff --git a/app/src/main/java/info/nightscout/androidaps/historyBrowser/HistoryBrowseActivity.kt b/app/src/main/java/info/nightscout/androidaps/historyBrowser/HistoryBrowseActivity.kt index 8ee346247e..1abda3295f 100644 --- a/app/src/main/java/info/nightscout/androidaps/historyBrowser/HistoryBrowseActivity.kt +++ b/app/src/main/java/info/nightscout/androidaps/historyBrowser/HistoryBrowseActivity.kt @@ -14,24 +14,23 @@ import dagger.android.HasAndroidInjector import info.nightscout.androidaps.R import info.nightscout.androidaps.activities.NoSplashAppCompatActivity import info.nightscout.androidaps.databinding.ActivityHistorybrowseBinding +import info.nightscout.androidaps.events.EventAutosensCalculationFinished import info.nightscout.androidaps.events.EventCustomCalculationFinished import info.nightscout.androidaps.events.EventRefreshOverview -import info.nightscout.androidaps.interfaces.ActivePluginProvider +import info.nightscout.androidaps.extensions.toVisibility +import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.general.overview.OverviewMenus import info.nightscout.androidaps.plugins.general.overview.graphData.GraphData -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventAutosensBgLoaded -import info.nightscout.androidaps.events.EventAutosensCalculationFinished import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventIobCalculationProgress import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DefaultValueHelper import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.buildHelper.BuildHelper -import info.nightscout.androidaps.utils.extensions.toVisibility import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.utils.sharedPreferences.SP import io.reactivex.disposables.CompositeDisposable @@ -51,8 +50,7 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() { @Inject lateinit var profileFunction: ProfileFunction @Inject lateinit var defaultValueHelper: DefaultValueHelper @Inject lateinit var iobCobCalculatorPluginHistory: IobCobCalculatorPluginHistory - @Inject lateinit var treatmentsPluginHistory: TreatmentsPluginHistory - @Inject lateinit var activePlugin: ActivePluginProvider + @Inject lateinit var activePlugin: ActivePlugin @Inject lateinit var buildHelper: BuildHelper @Inject lateinit var fabricPrivacy: FabricPrivacy @Inject lateinit var overviewMenus: OverviewMenus @@ -181,16 +179,6 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() { } }, fabricPrivacy::logException) ) - disposable.add(rxBus - .toObservable(EventAutosensBgLoaded::class.java) - .observeOn(aapsSchedulers.io) - .subscribe({ - // catch only events from iobCobCalculatorPluginHistory - if (it.cause is EventCustomCalculationFinished) { - updateGUI("EventAutosensCalculationFinished", bgOnly = true) - } - }, fabricPrivacy::logException) - ) disposable.add(rxBus .toObservable(EventIobCalculationProgress::class.java) .observeOn(aapsSchedulers.main) @@ -266,7 +254,6 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() { private fun runCalculation(from: String) { lifecycleScope.launch(Dispatchers.Default) { - treatmentsPluginHistory.initializeData(start - T.hours(8).msecs()) val end = start + T.hours(rangeToDisplay.toLong()).msecs() iobCobCalculatorPluginHistory.stopCalculation(from) iobCobCalculatorPluginHistory.clearCache() @@ -292,7 +279,7 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() { if (destroyed) return@launch binding.date.text = dateUtil.dateAndTimeString(start) binding.zoom.text = rangeToDisplay.toString() - val graphData = GraphData(injector, binding.bggraph, iobCobCalculatorPluginHistory, treatmentsPluginHistory) + val graphData = GraphData(injector, binding.bggraph) val secondaryGraphsData: ArrayList = ArrayList() // do preparation in different thread @@ -306,28 +293,29 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() { graphData.addInRangeArea(fromTime, toTime, lowLine, highLine) // **** BG **** - graphData.addBgReadings(fromTime, toTime, lowLine, highLine, null) +// graphData.addBgReadings(fromTime, toTime, highLine, null) +// if (buildHelper.isDev()) graphData.addBucketedData(fromTime, toTime) // add target line - graphData.addTargetLine(fromTime, toTime, profile, null) +// graphData.addTargetLine(fromTime, toTime, profile, null) // **** NOW line **** graphData.addNowLine(pointer) if (!bgOnly) { // Treatments - graphData.addTreatments(fromTime, toTime) +// graphData.addTreatments(fromTime, toTime) if (menuChartSettings[0][OverviewMenus.CharType.ACT.ordinal]) - graphData.addActivity(fromTime, toTime, false, 0.8) +// graphData.addActivity(fromTime, toTime, false, 0.8) // add basal data if (pump.pumpDescription.isTempBasalCapable && menuChartSettings[0][OverviewMenus.CharType.BAS.ordinal]) { - graphData.addBasals(fromTime, toTime, lowLine / graphData.maxY / 1.2) +// graphData.addBasals(fromTime, toTime, lowLine / graphData.maxY / 1.2) } // ------------------ 2nd graph synchronized(graphLock) { for (g in 0 until secondaryGraphs.size) { - val secondGraphData = GraphData(injector, secondaryGraphs[g], iobCobCalculatorPluginHistory, treatmentsPluginHistory) + val secondGraphData = GraphData(injector, secondaryGraphs[g]) var useIobForScale = false var useCobForScale = false var useDevForScale = false @@ -348,13 +336,13 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() { 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) +// 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 secondGraphData.formatAxis(fromTime, toTime) diff --git a/app/src/main/java/info/nightscout/androidaps/historyBrowser/IobCobCalculatorPluginHistory.kt b/app/src/main/java/info/nightscout/androidaps/historyBrowser/IobCobCalculatorPluginHistory.kt index 8dc1ebfb32..1dfc35f70a 100644 --- a/app/src/main/java/info/nightscout/androidaps/historyBrowser/IobCobCalculatorPluginHistory.kt +++ b/app/src/main/java/info/nightscout/androidaps/historyBrowser/IobCobCalculatorPluginHistory.kt @@ -2,7 +2,7 @@ package info.nightscout.androidaps.historyBrowser import dagger.android.HasAndroidInjector import info.nightscout.androidaps.database.AppRepository -import info.nightscout.androidaps.interfaces.ActivePluginProvider +import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.plugins.bus.RxBusWrapper @@ -27,8 +27,7 @@ class IobCobCalculatorPluginHistory @Inject constructor( sp: SP, resourceHelper: ResourceHelper, profileFunction: ProfileFunction, - activePlugin: ActivePluginProvider, - treatmentsPluginHistory: TreatmentsPluginHistory, + activePlugin: ActivePlugin, sensitivityOref1Plugin: SensitivityOref1Plugin, sensitivityAAPSPlugin: SensitivityAAPSPlugin, sensitivityWeightedAveragePlugin: SensitivityWeightedAveragePlugin, @@ -36,7 +35,7 @@ class IobCobCalculatorPluginHistory @Inject constructor( dateUtil: DateUtil, repository: AppRepository ) : IobCobCalculatorPlugin(injector, aapsLogger, aapsSchedulers, rxBus, sp, resourceHelper, profileFunction, - activePlugin, treatmentsPluginHistory, sensitivityOref1Plugin, sensitivityAAPSPlugin, sensitivityWeightedAveragePlugin, fabricPrivacy, dateUtil, repository) { + activePlugin, sensitivityOref1Plugin, sensitivityAAPSPlugin, sensitivityWeightedAveragePlugin, fabricPrivacy, dateUtil, repository) { override fun onStart() { // do not attach to rxbus } diff --git a/app/src/main/java/info/nightscout/androidaps/historyBrowser/TreatmentsPluginHistory.kt b/app/src/main/java/info/nightscout/androidaps/historyBrowser/TreatmentsPluginHistory.kt deleted file mode 100644 index 4b003538b3..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/historyBrowser/TreatmentsPluginHistory.kt +++ /dev/null @@ -1,50 +0,0 @@ -package info.nightscout.androidaps.historyBrowser - -import android.content.Context -import dagger.android.HasAndroidInjector -import info.nightscout.androidaps.database.AppRepository -import info.nightscout.androidaps.interfaces.ActivePluginProvider -import info.nightscout.androidaps.interfaces.DatabaseHelperInterface -import info.nightscout.androidaps.interfaces.ProfileFunction -import info.nightscout.androidaps.interfaces.UploadQueueInterface -import info.nightscout.androidaps.logging.AAPSLogger -import info.nightscout.androidaps.plugins.bus.RxBusWrapper -import info.nightscout.androidaps.plugins.general.nsclient.NSUpload -import info.nightscout.androidaps.plugins.treatments.TreatmentService -import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin -import info.nightscout.androidaps.utils.DateUtil -import info.nightscout.androidaps.utils.FabricPrivacy -import info.nightscout.androidaps.utils.resources.ResourceHelper -import info.nightscout.androidaps.utils.rx.AapsSchedulers -import info.nightscout.androidaps.utils.sharedPreferences.SP -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class TreatmentsPluginHistory @Inject constructor( - injector: HasAndroidInjector, - aapsLogger: AAPSLogger, - aapsSchedulers: AapsSchedulers, - rxBus: RxBusWrapper, - resourceHelper: ResourceHelper, - context: Context, - sp: SP, - profileFunction: ProfileFunction, - activePlugin: ActivePluginProvider, - nsUpload: NSUpload, - fabricPrivacy: FabricPrivacy, - dateUtil: DateUtil, - uploadQueue: UploadQueueInterface, - databaseHelper: DatabaseHelperInterface, - repository: AppRepository -) : TreatmentsPlugin(injector, aapsLogger, rxBus, aapsSchedulers, resourceHelper, context, sp, profileFunction, activePlugin, nsUpload, fabricPrivacy, dateUtil, uploadQueue, databaseHelper, repository) { - - init { - onStart() - } - - override fun onStart() { - service = TreatmentService(injector) - initializeData(range()) - } -} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/events/EventLoopInvoked.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/events/EventLoopInvoked.kt new file mode 100644 index 0000000000..e90e8cfb88 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/events/EventLoopInvoked.kt @@ -0,0 +1,5 @@ +package info.nightscout.androidaps.plugins.aps.events + +import info.nightscout.androidaps.events.Event + +class EventLoopInvoked : Event() \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopFragment.kt index 74f581b298..0c0b592a30 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopFragment.kt @@ -98,7 +98,6 @@ class LoopFragment : DaggerFragment() { binding.constraintsprocessed.text = it.constraintsProcessed?.toSpanned() ?: "" binding.source.text = it.source ?: "" binding.lastrun.text = dateUtil.dateAndTimeString(it.lastAPSRun) - ?: "" binding.smbrequestTime.text = dateUtil.dateAndTimeAndSecondsString(it.lastSMBRequest) binding.smbexecutionTime.text = dateUtil.dateAndTimeAndSecondsString(it.lastSMBEnact) binding.tbrrequestTime.text = dateUtil.dateAndTimeAndSecondsString(it.lastTBRRequest) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.kt index 6abeea43b0..072a9fb1d5 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.kt @@ -13,12 +13,15 @@ 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.interfaces.Profile import info.nightscout.androidaps.data.PumpEnactResult import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.entities.TherapyEvent -import info.nightscout.androidaps.database.transactions.InsertTherapyEventIfNewTransaction -import info.nightscout.androidaps.db.Source +import info.nightscout.androidaps.database.entities.UserEntry.Action +import info.nightscout.androidaps.database.entities.UserEntry.Sources +import info.nightscout.androidaps.database.entities.ValueWithUnit +import info.nightscout.androidaps.database.transactions.InsertIfNewByTimestampTherapyEventTransaction +import info.nightscout.androidaps.database.transactions.InsertTherapyEventAnnouncementTransaction import info.nightscout.androidaps.events.EventAcceptOpenLoopChange import info.nightscout.androidaps.events.EventAutosensCalculationFinished import info.nightscout.androidaps.events.EventNewBG @@ -27,20 +30,19 @@ 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.logging.UserEntryLogger 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.configBuilder.RunningConfiguration 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.EventWearConfirmAction import info.nightscout.androidaps.plugins.general.wear.events.EventWearInitiateAction -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.queue.Callback import info.nightscout.androidaps.queue.commands.Command import info.nightscout.androidaps.receivers.ReceiverStatusStore @@ -48,6 +50,11 @@ 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.extensions.buildDeviceStatus +import info.nightscout.androidaps.extensions.convertedToAbsolute +import info.nightscout.androidaps.extensions.convertedToPercent +import info.nightscout.androidaps.extensions.plannedRemainingMinutes +import info.nightscout.androidaps.plugins.aps.events.EventLoopInvoked import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.utils.sharedPreferences.SP @@ -70,15 +77,15 @@ open class LoopPlugin @Inject constructor( private val profileFunction: ProfileFunction, private val context: Context, private val commandQueue: CommandQueueProvider, - private val activePlugin: ActivePluginProvider, - private val treatmentsPlugin: TreatmentsPlugin, + private val activePlugin: ActivePlugin, private val virtualPumpPlugin: VirtualPumpPlugin, - private val iobCobCalculatorPlugin: IobCobCalculatorPlugin, + private val iobCobCalculator: IobCobCalculator, private val receiverStatusStore: ReceiverStatusStore, private val fabricPrivacy: FabricPrivacy, - private val nsUpload: NSUpload, private val dateUtil: DateUtil, - private val repository: AppRepository + private val uel: UserEntryLogger, + private val repository: AppRepository, + private val runningConfiguration: RunningConfiguration ) : PluginBase(PluginDescription() .mainType(PluginType.LOOP) .fragmentClass(LoopFragment::class.java.name) @@ -96,6 +103,7 @@ open class LoopPlugin @Inject constructor( private var carbsSuggestionsSuspendedUntil: Long = 0 private var prevCarbsreq = 0 override var lastRun: LastRun? = null + override fun onStart() { createNotificationChannel() super.onStart() @@ -117,7 +125,7 @@ open class LoopPlugin @Inject constructor( .subscribe({ event: EventAutosensCalculationFinished -> // Autosens calculation not triggered by a new BG if (event.cause !is EventNewBG) return@subscribe - val glucoseValue = iobCobCalculatorPlugin.actualBg() ?: return@subscribe + val glucoseValue = iobCobCalculator.ads.actualBg() ?: return@subscribe // BG outdated // already looped with that value if (glucoseValue.timestamp <= lastBgTriggeredRun) return@subscribe @@ -233,7 +241,9 @@ open class LoopPlugin @Inject constructor( 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 + val lastBolusTime = repository.getLastBolusRecord()?.timestamp ?: 0L + val lastCarbsTime = repository.getLastCarbsRecord()?.timestamp ?: 0L + if (lastBolusTime > threshold || lastCarbsTime > threshold) bool = true return bool } @@ -277,13 +287,13 @@ open class LoopPlugin @Inject constructor( if (apsResult == null) { rxBus.send(EventLoopSetLastRunGui(resourceHelper.gs(R.string.noapsselected))) return - } + } else rxBus.send(EventLoopInvoked()) // Prepare for pumps using % basals if (pump.pumpDescription.tempBasalStyle == PumpDescription.PERCENT && allowPercentage()) { apsResult.usePercent = true } - apsResult.percent = (apsResult.rate / profile.basal * 100).toInt() + apsResult.percent = (apsResult.rate / profile.getBasal() * 100).toInt() // check rate for constraints val resultAfterConstraints = apsResult.newAndClone(injector) @@ -295,155 +305,162 @@ open class LoopPlugin @Inject constructor( resultAfterConstraints.smb = constraintChecker.applyBolusConstraints(resultAfterConstraints.smbConstraint!!).value() // safety check for multiple SMBs - val lastBolusTime = treatmentsPlugin.lastBolusTime + val lastBolusTime = repository.getLastBolusRecord()?.timestamp ?: 0L 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 + prevCarbsreq = lastRun?.constraintsProcessed?.carbsReq ?: prevCarbsreq + lastRun = (lastRun ?: LastRun()).also { 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 + buildDeviceStatus(dateUtil, this, iobCobCalculator, profileFunction, + activePlugin.activePump, receiverStatusStore, runningConfiguration, + BuildConfig.VERSION_NAME + "-" + BuildConfig.BUILDVERSION)?.also { + repository.insert(it) + } - // mId allows you to update the notification later on. - mNotificationManager.notify(Constants.notificationID, builder.build()) - rxBus.send(EventNewOpenLoopNotification()) + 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)) { + disposable += repository.runTransaction(InsertTherapyEventAnnouncementTransaction(resultAfterConstraints.carbsRequiredText)).subscribe() + } + 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 - //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(EventWearInitiateAction("changeRequest")) + // mId allows you to update the notification later on. + mNotificationManager.notify(Constants.notificationID, builder.build()) + uel.log(Action.CAREPORTAL, Sources.Loop, resourceHelper.gs(R.string.carbsreq, resultAfterConstraints.carbsReq, resultAfterConstraints.carbsReqWithin), + ValueWithUnit.Gram(resultAfterConstraints.carbsReq), + ValueWithUnit.Minute(resultAfterConstraints.carbsReqWithin)) + 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(EventWearInitiateAction("changeRequest")) + } + } + } else { + //If carbs were required previously, but are no longer needed, dismiss notifications + if (prevCarbsreq > 0) { + dismissSuggestion() + rxBus.send(EventDismissNotification(Notification.CARBS_REQUIRED)) } } - } 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() + 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()) } - rxBus.send(EventLoopUpdateGui()) - } - }) - } else { - lastRun!!.tbrSetByPump = result - lastRun!!.lastTBRRequest = lastRun!!.lastAPSRun + }) + } else { + lastRun.tbrSetByPump = result + lastRun.lastTBRRequest = lastRun.lastAPSRun + } + rxBus.send(EventLoopUpdateGui()) } - 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) + }) + } 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() } - presentSuggestion(builder) - } else if (allowNotification) { - dismissSuggestion() } + rxBus.send(EventLoopUpdateGui()) } - rxBus.send(EventLoopUpdateGui()) } finally { aapsLogger.debug(LTag.APS, "invoke end") } @@ -486,21 +503,28 @@ open class LoopPlugin @Inject constructor( } 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()) + val profile = profileFunction.getProfile() ?: return + lastRun?.let { lastRun -> + lastRun.constraintsProcessed?.let { constraintsProcessed -> + applyTBRRequest(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() + buildDeviceStatus(dateUtil, this@LoopPlugin, iobCobCalculator, profileFunction, + activePlugin.activePump, receiverStatusStore, runningConfiguration, + BuildConfig.VERSION_NAME + "-" + BuildConfig.BUILDVERSION)?.also { + repository.insert(it) + } + sp.incInt(R.string.key_ObjectivesmanualEnacts) + } + rxBus.send(EventAcceptOpenLoopChange()) + } + }) } - }) + } fabricPrivacy.logCustom("AcceptTemp") } @@ -508,8 +532,8 @@ open class LoopPlugin @Inject constructor( * 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) { + private fun applyTBRRequest(request: APSResult, profile: Profile, callback: Callback?) { + if (!request.tempBasalRequested) { callback?.result(PumpEnactResult(injector).enacted(false).success(true).comment(R.string.nochangerequested))?.run() return } @@ -526,54 +550,62 @@ open class LoopPlugin @Inject constructor( } aapsLogger.debug(LTag.APS, "applyAPSRequest: $request") val now = System.currentTimeMillis() - val activeTemp = treatmentsPlugin.getTempBasalFromHistory(now) + val activeTemp = iobCobCalculator.getTempBasalIncludingConvertedExtended(now) if (request.usePercent && allowPercentage()) { if (request.percent == 100 && request.duration == 0) { if (activeTemp != null) { aapsLogger.debug(LTag.APS, "applyAPSRequest: cancelTempBasal()") + uel.log(Action.CANCEL_TEMP_BASAL, Sources.Loop) 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(R.string.basal_set_correctly))?.run() } - } else if (activeTemp != null && activeTemp.plannedRemainingMinutes > 5 && request.duration - activeTemp.plannedRemainingMinutes < 30 && request.percent == activeTemp.percentRate) { + } else if (activeTemp != null && activeTemp.plannedRemainingMinutes > 5 && request.duration - activeTemp.plannedRemainingMinutes < 30 && request.percent == activeTemp.convertedToPercent(now, profile)) { aapsLogger.debug(LTag.APS, "applyAPSRequest: Temp basal set correctly") callback?.result(PumpEnactResult(injector).percent(request.percent) .enacted(false).success(true).duration(activeTemp.plannedRemainingMinutes) .comment(R.string.let_temp_basal_run))?.run() } else { aapsLogger.debug(LTag.APS, "applyAPSRequest: tempBasalPercent()") - commandQueue.tempBasalPercent(request.percent, request.duration, false, profile!!, callback) + uel.log(Action.TEMP_BASAL, Sources.Loop, + ValueWithUnit.Percent(request.percent), + ValueWithUnit.Minute(request.duration)) + commandQueue.tempBasalPercent(request.percent, request.duration, false, profile, PumpSync.TemporaryBasalType.NORMAL, 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()") + uel.log(Action.CANCEL_TEMP_BASAL, Sources.Loop) 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(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) { + } else if (activeTemp != null && activeTemp.plannedRemainingMinutes > 5 && request.duration - activeTemp.plannedRemainingMinutes < 30 && abs(request.rate - activeTemp.convertedToAbsolute(now, profile)) < pump.pumpDescription.basalStep) { aapsLogger.debug(LTag.APS, "applyAPSRequest: Temp basal set correctly") - callback?.result(PumpEnactResult(injector).absolute(activeTemp.tempBasalConvertedToAbsolute(now, profile)) + callback?.result(PumpEnactResult(injector).absolute(activeTemp.convertedToAbsolute(now, profile)) .enacted(false).success(true).duration(activeTemp.plannedRemainingMinutes) .comment(R.string.let_temp_basal_run))?.run() } else { aapsLogger.debug(LTag.APS, "applyAPSRequest: setTempBasalAbsolute()") - commandQueue.tempBasalAbsolute(request.rate, request.duration, false, profile!!, callback) + uel.log(Action.TEMP_BASAL, Sources.Loop, + ValueWithUnit.UnitPerHour(request.rate), + ValueWithUnit.Minute(request.duration)) + commandQueue.tempBasalAbsolute(request.rate, request.duration, false, profile, PumpSync.TemporaryBasalType.NORMAL, callback) } } } private fun applySMBRequest(request: APSResult, callback: Callback?) { - if (!request.bolusRequested) { + if (!request.bolusRequested()) { return } val pump = activePlugin.activePump - val lastBolusTime = treatmentsPlugin.lastBolusTime + val lastBolusTime = repository.getLastBolusRecord()?.timestamp ?: 0L 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) @@ -595,13 +627,14 @@ open class LoopPlugin @Inject constructor( // deliver SMB val detailedBolusInfo = DetailedBolusInfo() - detailedBolusInfo.lastKnownBolusTime = treatmentsPlugin.lastBolusTime - detailedBolusInfo.eventType = TherapyEvent.Type.CORRECTION_BOLUS.text + detailedBolusInfo.lastKnownBolusTime = repository.getLastBolusRecord()?.timestamp ?: 0L + detailedBolusInfo.eventType = DetailedBolusInfo.EventType.CORRECTION_BOLUS detailedBolusInfo.insulin = request.smb - detailedBolusInfo.isSMB = true - detailedBolusInfo.source = Source.USER - detailedBolusInfo.deliverAt = request.deliverAt + detailedBolusInfo.bolusType = DetailedBolusInfo.BolusType.SMB + detailedBolusInfo.deliverAtTheLatest = request.deliverAt aapsLogger.debug(LTag.APS, "applyAPSRequest: bolus()") + if (request.smb > 0.0) + uel.log(Action.SMB, Sources.Loop, ValueWithUnit.Insulin(detailedBolusInfo.insulin)) commandQueue.bolus(detailedBolusInfo, callback) } @@ -613,7 +646,7 @@ open class LoopPlugin @Inject constructor( 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() { + commandQueue.tempBasalAbsolute(0.0, durationInMinutes, true, profile!!, PumpSync.TemporaryBasalType.EMULATED_PUMP_SUSPEND, 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) @@ -621,7 +654,7 @@ open class LoopPlugin @Inject constructor( } }) } else { - commandQueue.tempBasalPercent(0, durationInMinutes, true, profile!!, object : Callback() { + commandQueue.tempBasalPercent(0, durationInMinutes, true, profile!!, PumpSync.TemporaryBasalType.EMULATED_PUMP_SUSPEND, 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) @@ -629,7 +662,7 @@ open class LoopPlugin @Inject constructor( } }) } - if (pump.pumpDescription.isExtendedBolusCapable && treatmentsPlugin.isInHistoryExtendedBolusInProgress) { + if (pump.pumpDescription.isExtendedBolusCapable && iobCobCalculator.getExtendedBolus(dateUtil.now()) != null) { commandQueue.cancelExtended(object : Callback() { override fun run() { if (!result.success) { @@ -654,17 +687,16 @@ open class LoopPlugin @Inject constructor( } override fun createOfflineEvent(durationInMinutes: Int) { - disposable += repository.runTransactionForResult(InsertTherapyEventIfNewTransaction( - timestamp = dateUtil._now(), + disposable += repository.runTransactionForResult(InsertIfNewByTimestampTherapyEventTransaction( + timestamp = dateUtil.now(), type = TherapyEvent.Type.APS_OFFLINE, duration = T.mins(durationInMinutes.toLong()).msecs(), enteredBy = "openaps://" + "AndroidAPS", glucoseUnit = TherapyEvent.GlucoseUnit.MGDL - )).subscribe({ result -> - result.inserted.forEach { nsUpload.uploadEvent(it) } - }, { - aapsLogger.error(LTag.BGSOURCE, "Error while saving therapy event", it) - }) + )).subscribe( + { result -> result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted therapy event $it") } }, + { aapsLogger.error(LTag.DATABASE, "Error while saving therapy event", it) } + ) } companion object { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/DetermineBasalAdapterAMAJS.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/DetermineBasalAdapterAMAJS.kt index a2334030ea..adb15e8b29 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/DetermineBasalAdapterAMAJS.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/DetermineBasalAdapterAMAJS.kt @@ -1,11 +1,15 @@ 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.extensions.convertedToAbsolute +import info.nightscout.androidaps.extensions.getPassedDurationToTimeInMinutes +import info.nightscout.androidaps.extensions.plannedRemainingMinutes +import info.nightscout.androidaps.interfaces.GlucoseUnit +import info.nightscout.androidaps.interfaces.IobCobCalculator +import info.nightscout.androidaps.interfaces.Profile import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag @@ -15,8 +19,6 @@ 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 @@ -37,7 +39,7 @@ class DetermineBasalAdapterAMAJS internal constructor(scriptReader: ScriptReader @Inject lateinit var constraintChecker: ConstraintChecker @Inject lateinit var sp: SP @Inject lateinit var profileFunction: ProfileFunction - @Inject lateinit var treatmentsPlugin: TreatmentsPlugin + @Inject lateinit var iobCobCalculator: IobCobCalculator @Inject lateinit var openHumansUploader: OpenHumansUploader private val mScriptReader: ScriptReader @@ -160,13 +162,13 @@ class DetermineBasalAdapterAMAJS internal constructor(scriptReader: ScriptReader 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_daily_basal", profile.getMaxDailyBasal()) 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("carb_ratio", profile.getIc()) + this.profile.put("sens", profile.getIsfMgdl()) 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) @@ -179,22 +181,19 @@ class DetermineBasalAdapterAMAJS internal constructor(scriptReader: ScriptReader } 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) { + if (profileFunction.getUnits() == GlucoseUnit.MMOL) { this.profile.put("out_units", "mmol/L") } val now = System.currentTimeMillis() - val tb = treatmentsPlugin.getTempBasalFromHistory(now) + val tb = iobCobCalculator.getTempBasalIncludingConvertedExtended(now) currentTemp = JSONObject() currentTemp.put("temp", "absolute") currentTemp.put("duration", tb?.plannedRemainingMinutes ?: 0) - currentTemp.put("rate", tb?.tempBasalConvertedToAbsolute(now, profile) ?: 0.0) - + currentTemp.put("rate", tb?.convertedToAbsolute(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) + if (tb != null) currentTemp.put("minutesrunning", tb.getPassedDurationToTimeInMinutes(now)) + + iobData = iobCobCalculator.convertToJSONArray(iobArray) this.glucoseStatus = JSONObject() this.glucoseStatus.put("glucose", glucoseStatus.glucose) if (sp.getBoolean(R.string.key_always_use_shortavg, false)) { @@ -206,7 +205,6 @@ class DetermineBasalAdapterAMAJS internal constructor(scriptReader: ScriptReader 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) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/DetermineBasalResultAMA.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/DetermineBasalResultAMA.kt index 8f462b2f19..5411bd2fa3 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/DetermineBasalResultAMA.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/DetermineBasalResultAMA.kt @@ -3,7 +3,6 @@ package info.nightscout.androidaps.plugins.aps.openAPSAMA import dagger.android.HasAndroidInjector import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.plugins.aps.loop.APSResult -import info.nightscout.androidaps.utils.DateUtil import org.json.JSONException import org.json.JSONObject import org.mozilla.javascript.NativeObject @@ -14,7 +13,7 @@ class DetermineBasalResultAMA private constructor(injector: HasAndroidInjector) private var snoozeBG = 0.0 internal constructor(injector: HasAndroidInjector, result: NativeObject, j: JSONObject) : this(injector) { - date = DateUtil.now() + date = dateUtil.now() json = j if (result.containsKey("error")) { reason = result["error"].toString() @@ -41,7 +40,6 @@ class DetermineBasalResultAMA private constructor(injector: HasAndroidInjector) tempBasalRequested = false } } - bolusRequested = false } override fun newAndClone(injector: HasAndroidInjector): DetermineBasalResultAMA { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAFragment.kt index 84bc71daf7..acbbf1191b 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAFragment.kt @@ -35,6 +35,7 @@ class OpenAPSAMAFragment : DaggerFragment() { @Inject lateinit var fabricPrivacy: FabricPrivacy @Inject lateinit var openAPSAMAPlugin: OpenAPSAMAPlugin @Inject lateinit var dateUtil: DateUtil + @Inject lateinit var jsonFormatter: JSONFormatter private var _binding: OpenapsamaFragmentBinding? = null @@ -92,30 +93,30 @@ class OpenAPSAMAFragment : DaggerFragment() { private fun updateGUI() { if (_binding == null) return openAPSAMAPlugin.lastAPSResult?.let { lastAPSResult -> - binding.result.text = JSONFormatter.format(lastAPSResult.json) + binding.result.text = jsonFormatter.format(lastAPSResult.json) binding.request.text = lastAPSResult.toSpanned() } openAPSAMAPlugin.lastDetermineBasalAdapterAMAJS?.let { determineBasalAdapterAMAJS -> - binding.glucosestatus.text = JSONFormatter.format(determineBasalAdapterAMAJS.glucoseStatusParam) - binding.currenttemp.text = JSONFormatter.format(determineBasalAdapterAMAJS.currentTempParam) + binding.glucosestatus.text = jsonFormatter.format(determineBasalAdapterAMAJS.glucoseStatusParam) + binding.currenttemp.text = jsonFormatter.format(determineBasalAdapterAMAJS.currentTempParam) try { val iobArray = JSONArray(determineBasalAdapterAMAJS.iobDataParam) - binding.iobdata.text = TextUtils.concat(resourceHelper.gs(R.string.array_of_elements, iobArray.length()) + "\n", JSONFormatter.format(iobArray.getString(0))) + binding.iobdata.text = TextUtils.concat(resourceHelper.gs(R.string.array_of_elements, iobArray.length()) + "\n", jsonFormatter.format(iobArray.getString(0))) } catch (e: JSONException) { aapsLogger.error(LTag.APS, "Unhandled exception", e) @Suppress("SetTextI18n") binding.iobdata.text = "JSONException see log for details" } - binding.profile.text = JSONFormatter.format(determineBasalAdapterAMAJS.profileParam) - binding.mealdata.text = JSONFormatter.format(determineBasalAdapterAMAJS.mealDataParam) + binding.profile.text = jsonFormatter.format(determineBasalAdapterAMAJS.profileParam) + binding.mealdata.text = jsonFormatter.format(determineBasalAdapterAMAJS.mealDataParam) binding.scriptdebugdata.text = determineBasalAdapterAMAJS.scriptDebug } if (openAPSAMAPlugin.lastAPSRun != 0L) { binding.lastrun.text = dateUtil.dateAndTimeString(openAPSAMAPlugin.lastAPSRun) } openAPSAMAPlugin.lastAutosensResult.let { - binding.autosensdata.text = JSONFormatter.format(it.json()) + binding.autosensdata.text = jsonFormatter.format(it.json()) } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAPlugin.kt index 0847f100ac..2c34f41a06 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAPlugin.kt @@ -3,7 +3,7 @@ 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.Profile import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.ValueWrapper import info.nightscout.androidaps.interfaces.* @@ -15,15 +15,13 @@ 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.GlucoseStatusProvider -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.extensions.target +import info.nightscout.androidaps.extensions.target import info.nightscout.androidaps.utils.resources.ResourceHelper import org.json.JSONException import javax.inject.Inject @@ -38,9 +36,8 @@ open class OpenAPSAMAPlugin @Inject constructor( resourceHelper: ResourceHelper, private val profileFunction: ProfileFunction, private val context: Context, - private val activePlugin: ActivePluginProvider, - private val treatmentsPlugin: TreatmentsInterface, - private val iobCobCalculatorPlugin: IobCobCalculatorPlugin, + private val activePlugin: ActivePlugin, + private val iobCobCalculator: IobCobCalculator, private val hardLimits: HardLimits, private val profiler: Profiler, private val fabricPrivacy: FabricPrivacy, @@ -56,7 +53,7 @@ open class OpenAPSAMAPlugin @Inject constructor( .preferencesId(R.xml.pref_openapsama) .description(R.string.description_ama), aapsLogger, resourceHelper, injector -), APSInterface { +), APS { // last values override var lastAPSRun: Long = 0 @@ -107,19 +104,19 @@ open class OpenAPSAMAPlugin @Inject constructor( }.value() var start = System.currentTimeMillis() var startPart = System.currentTimeMillis() - val iobArray = iobCobCalculatorPlugin.calculateIobArrayInDia(profile) + val iobArray = iobCobCalculator.calculateIobArrayInDia(profile) profiler.log(LTag.APS, "calculateIobArrayInDia()", startPart) startPart = System.currentTimeMillis() - val mealData = iobCobCalculatorPlugin.mealData + val mealData = iobCobCalculator.getMealDataWithWaitingForCalculationFinish() 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), R.string.profile_low_target, 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), R.string.profile_high_target, HardLimits.VERY_HARD_LIMIT_MAX_BG[0].toDouble(), HardLimits.VERY_HARD_LIMIT_MAX_BG[1].toDouble()) - var targetBg = hardLimits.verifyHardLimits(profile.targetMgdl, R.string.temp_target_value, HardLimits.VERY_HARD_LIMIT_TARGET_BG[0].toDouble(), HardLimits.VERY_HARD_LIMIT_TARGET_BG[1].toDouble()) + var minBg = hardLimits.verifyHardLimits(Round.roundTo(profile.getTargetLowMgdl(), 0.1), R.string.profile_low_target, HardLimits.VERY_HARD_LIMIT_MIN_BG[0].toDouble(), HardLimits.VERY_HARD_LIMIT_MIN_BG[1].toDouble()) + var maxBg = hardLimits.verifyHardLimits(Round.roundTo(profile.getTargetHighMgdl(), 0.1), R.string.profile_high_target, HardLimits.VERY_HARD_LIMIT_MAX_BG[0].toDouble(), HardLimits.VERY_HARD_LIMIT_MAX_BG[1].toDouble()) + var targetBg = hardLimits.verifyHardLimits(profile.getTargetMgdl(), R.string.temp_target_value, HardLimits.VERY_HARD_LIMIT_TARGET_BG[0].toDouble(), HardLimits.VERY_HARD_LIMIT_TARGET_BG[1].toDouble()) var isTempTarget = false - val tempTarget = repository.getTemporaryTargetActiveAt(dateUtil._now()).blockingGet() + val tempTarget = repository.getTemporaryTargetActiveAt(dateUtil.now()).blockingGet() if (tempTarget is ValueWrapper.Existing) { isTempTarget = true minBg = hardLimits.verifyHardLimits(tempTarget.value.lowTarget, R.string.temp_target_low_target, HardLimits.VERY_HARD_LIMIT_TEMP_MIN_BG[0].toDouble(), HardLimits.VERY_HARD_LIMIT_TEMP_MIN_BG[1].toDouble()) @@ -128,12 +125,12 @@ open class OpenAPSAMAPlugin @Inject constructor( } if (!hardLimits.checkOnlyHardLimits(profile.dia, R.string.profile_dia, hardLimits.minDia(), hardLimits.maxDia())) return if (!hardLimits.checkOnlyHardLimits(profile.getIcTimeFromMidnight(Profile.secondsFromMidnight()), R.string.profile_carbs_ratio_value, hardLimits.minIC(), hardLimits.maxIC())) return - if (!hardLimits.checkOnlyHardLimits(profile.isfMgdl, R.string.profile_sensitivity_value, HardLimits.MIN_ISF, HardLimits.MAX_ISF)) return - if (!hardLimits.checkOnlyHardLimits(profile.maxDailyBasal, R.string.profile_max_daily_basal_value, 0.02, hardLimits.maxBasal())) return + if (!hardLimits.checkOnlyHardLimits(profile.getIsfMgdl(), R.string.profile_sensitivity_value, HardLimits.MIN_ISF, HardLimits.MAX_ISF)) return + if (!hardLimits.checkOnlyHardLimits(profile.getMaxDailyBasal(), R.string.profile_max_daily_basal_value, 0.02, hardLimits.maxBasal())) return if (!hardLimits.checkOnlyHardLimits(pump.baseBasalRate, R.string.current_basal_value, 0.01, hardLimits.maxBasal())) return startPart = System.currentTimeMillis() if (constraintChecker.isAutosensModeEnabled().value()) { - val autosensData = iobCobCalculatorPlugin.getLastAutosensDataSynchronized("OpenAPSPlugin") + val autosensData = iobCobCalculator.getLastAutosensDataWithWaitForCalculationFinish("OpenAPSPlugin") if (autosensData == null) { rxBus.send(EventOpenAPSUpdateResultGui(resourceHelper.gs(R.string.openaps_noasdata))) return @@ -163,10 +160,10 @@ open class OpenAPSAMAPlugin @Inject constructor( lastAPSResult = null lastAPSRun = 0 } else { - if (determineBasalResultAMA.rate == 0.0 && determineBasalResultAMA.duration == 0 && !treatmentsPlugin.isTempBasalInProgress) determineBasalResultAMA.tempBasalRequested = false + if (determineBasalResultAMA.rate == 0.0 && determineBasalResultAMA.duration == 0 && iobCobCalculator.getTempBasalIncludingConvertedExtended(dateUtil.now()) == null) determineBasalResultAMA.tempBasalRequested = false determineBasalResultAMA.iob = iobArray[0] val now = System.currentTimeMillis() - determineBasalResultAMA.json?.put("timestamp", DateUtil.toISOString(now)) + determineBasalResultAMA.json?.put("timestamp", dateUtil.toISOString(now)) determineBasalResultAMA.inputConstraints = inputConstraints lastDetermineBasalAdapterAMAJS = determineBasalAdapterAMAJS lastAPSResult = determineBasalResultAMA diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.kt index a980bb1bd4..b32682c102 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.kt @@ -1,12 +1,16 @@ 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.extensions.convertedToAbsolute +import info.nightscout.androidaps.extensions.getPassedDurationToTimeInMinutes +import info.nightscout.androidaps.extensions.plannedRemainingMinutes +import info.nightscout.androidaps.interfaces.ActivePlugin +import info.nightscout.androidaps.interfaces.GlucoseUnit +import info.nightscout.androidaps.interfaces.IobCobCalculator +import info.nightscout.androidaps.interfaces.Profile import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag @@ -15,8 +19,6 @@ 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 @@ -37,8 +39,8 @@ class DetermineBasalAdapterSMBJS internal constructor(private val scriptReader: @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 iobCobCalculator: IobCobCalculator + @Inject lateinit var activePlugin: ActivePlugin @Inject lateinit var openHumansUploader: OpenHumansUploader private var profile = JSONObject() @@ -173,18 +175,18 @@ class DetermineBasalAdapterSMBJS internal constructor(private val scriptReader: advancedFiltering: Boolean, isSaveCgmSource: Boolean ) { - val pump = activePluginProvider.activePump + val pump = activePlugin.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_daily_basal", profile.getMaxDailyBasal()) 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("carb_ratio", profile.getIc()) + this.profile.put("sens", profile.getIsfMgdl()) 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)) @@ -223,21 +225,18 @@ class DetermineBasalAdapterSMBJS internal constructor(private val scriptReader: 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) { + if (profileFunction.getUnits() == GlucoseUnit.MMOL) { this.profile.put("out_units", "mmol/L") } val now = System.currentTimeMillis() - val tb = treatmentsPlugin.getTempBasalFromHistory(now) + val tb = iobCobCalculator.getTempBasalIncludingConvertedExtended(now) currentTemp.put("temp", "absolute") currentTemp.put("duration", tb?.plannedRemainingMinutes ?: 0) - currentTemp.put("rate", tb?.tempBasalConvertedToAbsolute(now, profile) ?: 0.0) - + currentTemp.put("rate", tb?.convertedToAbsolute(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) + if (tb != null) currentTemp.put("minutesrunning", tb.getPassedDurationToTimeInMinutes(now)) + + iobData = iobCobCalculator.convertToJSONArray(iobArray) mGlucoseStatus.put("glucose", glucoseStatus.glucose) mGlucoseStatus.put("noise", glucoseStatus.noise) if (sp.getBoolean(R.string.key_always_use_shortavg, false)) { @@ -249,7 +248,6 @@ class DetermineBasalAdapterSMBJS internal constructor(private val scriptReader: 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) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalResultSMB.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalResultSMB.kt index 46b68bbd0a..aa5dff4b38 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalResultSMB.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalResultSMB.kt @@ -3,7 +3,6 @@ package info.nightscout.androidaps.plugins.aps.openAPSSMB import dagger.android.HasAndroidInjector import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.plugins.aps.loop.APSResult -import info.nightscout.androidaps.utils.DateUtil import org.json.JSONException import org.json.JSONObject @@ -13,7 +12,7 @@ class DetermineBasalResultSMB private constructor(injector: HasAndroidInjector) private var snoozeBG = 0.0 internal constructor(injector: HasAndroidInjector, result: JSONObject) : this(injector) { - date = DateUtil.now() + date = dateUtil.now() json = result try { if (result.has("error")) { @@ -36,7 +35,6 @@ class DetermineBasalResultSMB private constructor(injector: HasAndroidInjector) duration = -1 } if (result.has("units")) { - bolusRequested = true smb = result.getDouble("units") } else { smb = 0.0 @@ -47,7 +45,7 @@ class DetermineBasalResultSMB private constructor(injector: HasAndroidInjector) if (result.has("deliverAt")) { val date = result.getString("deliverAt") try { - deliverAt = DateUtil.fromISODateString(date).time + deliverAt = dateUtil.fromISODateString(date) } catch (e: Exception) { aapsLogger.error(LTag.APS, "Error parsing 'deliverAt' date: $date", e) } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBFragment.kt index be4e1015fa..61e52b0821 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBFragment.kt @@ -36,6 +36,7 @@ class OpenAPSSMBFragment : DaggerFragment() { @Inject lateinit var fabricPrivacy: FabricPrivacy @Inject lateinit var openAPSSMBPlugin: OpenAPSSMBPlugin @Inject lateinit var dateUtil: DateUtil + @Inject lateinit var jsonFormatter: JSONFormatter private var _binding: OpenapsamaFragmentBinding? = null @@ -92,23 +93,23 @@ class OpenAPSSMBFragment : DaggerFragment() { fun updateGUI() { if (_binding == null) return openAPSSMBPlugin.lastAPSResult?.let { lastAPSResult -> - binding.result.text = JSONFormatter.format(lastAPSResult.json) + binding.result.text = jsonFormatter.format(lastAPSResult.json) binding.request.text = lastAPSResult.toSpanned() } openAPSSMBPlugin.lastDetermineBasalAdapterSMBJS?.let { determineBasalAdapterSMBJS -> - binding.glucosestatus.text = JSONFormatter.format(determineBasalAdapterSMBJS.glucoseStatusParam) - binding.currenttemp.text = JSONFormatter.format(determineBasalAdapterSMBJS.currentTempParam) + binding.glucosestatus.text = jsonFormatter.format(determineBasalAdapterSMBJS.glucoseStatusParam) + binding.currenttemp.text = jsonFormatter.format(determineBasalAdapterSMBJS.currentTempParam) try { val iobArray = JSONArray(determineBasalAdapterSMBJS.iobDataParam) - binding.iobdata.text = TextUtils.concat(resourceHelper.gs(R.string.array_of_elements, iobArray.length()) + "\n", JSONFormatter.format(iobArray.getString(0))) + binding.iobdata.text = TextUtils.concat(resourceHelper.gs(R.string.array_of_elements, iobArray.length()) + "\n", jsonFormatter.format(iobArray.getString(0))) } catch (e: JSONException) { aapsLogger.error(LTag.APS, "Unhandled exception", e) @SuppressLint("SetTextI18n") binding.iobdata.text = "JSONException see log for details" } - binding.profile.text = JSONFormatter.format(determineBasalAdapterSMBJS.profileParam) - binding.mealdata.text = JSONFormatter.format(determineBasalAdapterSMBJS.mealDataParam) + binding.profile.text = jsonFormatter.format(determineBasalAdapterSMBJS.profileParam) + binding.mealdata.text = jsonFormatter.format(determineBasalAdapterSMBJS.mealDataParam) binding.scriptdebugdata.text = determineBasalAdapterSMBJS.scriptDebug openAPSSMBPlugin.lastAPSResult?.inputConstraints?.let { binding.constraints.text = it.getReasons(aapsLogger) @@ -118,7 +119,7 @@ class OpenAPSSMBFragment : DaggerFragment() { binding.lastrun.text = dateUtil.dateAndTimeString(openAPSSMBPlugin.lastAPSRun) } openAPSSMBPlugin.lastAutosensResult.let { - binding.autosensdata.text = JSONFormatter.format(it.json()) + binding.autosensdata.text = jsonFormatter.format(it.json()) } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBPlugin.kt index 29d071867d..fb5d265335 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBPlugin.kt @@ -5,9 +5,9 @@ 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.database.AppRepository import info.nightscout.androidaps.database.ValueWrapper +import info.nightscout.androidaps.extensions.target import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag @@ -17,14 +17,11 @@ 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.GlucoseStatusProvider -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.extensions.target import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.sharedPreferences.SP import javax.inject.Inject @@ -39,9 +36,8 @@ open class OpenAPSSMBPlugin @Inject constructor( resourceHelper: ResourceHelper, private val profileFunction: ProfileFunction, private val context: Context, - private val activePlugin: ActivePluginProvider, - private val treatmentsPlugin: TreatmentsInterface, - private val iobCobCalculatorPlugin: IobCobCalculatorPlugin, + private val activePlugin: ActivePlugin, + private val iobCobCalculator: IobCobCalculator, private val hardLimits: HardLimits, private val profiler: Profiler, private val sp: SP, @@ -58,7 +54,7 @@ open class OpenAPSSMBPlugin @Inject constructor( .description(R.string.description_smb) .setDefault(), aapsLogger, resourceHelper, injector -), APSInterface, ConstraintsInterface { +), APS, Constraints { // last values override var lastAPSRun: Long = 0 @@ -121,11 +117,11 @@ open class OpenAPSSMBPlugin @Inject constructor( inputConstraints.copyReasons(maxIOBAllowedConstraint) }.value() - var minBg = hardLimits.verifyHardLimits(Round.roundTo(profile.targetLowMgdl, 0.1), R.string.profile_low_target, 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), R.string.profile_high_target, HardLimits.VERY_HARD_LIMIT_MAX_BG[0].toDouble(), HardLimits.VERY_HARD_LIMIT_MAX_BG[1].toDouble()) - var targetBg = hardLimits.verifyHardLimits(profile.targetMgdl, R.string.temp_target_value, HardLimits.VERY_HARD_LIMIT_TARGET_BG[0].toDouble(), HardLimits.VERY_HARD_LIMIT_TARGET_BG[1].toDouble()) + var minBg = hardLimits.verifyHardLimits(Round.roundTo(profile.getTargetLowMgdl(), 0.1), R.string.profile_low_target, HardLimits.VERY_HARD_LIMIT_MIN_BG[0].toDouble(), HardLimits.VERY_HARD_LIMIT_MIN_BG[1].toDouble()) + var maxBg = hardLimits.verifyHardLimits(Round.roundTo(profile.getTargetHighMgdl(), 0.1), R.string.profile_high_target, HardLimits.VERY_HARD_LIMIT_MAX_BG[0].toDouble(), HardLimits.VERY_HARD_LIMIT_MAX_BG[1].toDouble()) + var targetBg = hardLimits.verifyHardLimits(profile.getTargetMgdl(), R.string.temp_target_value, HardLimits.VERY_HARD_LIMIT_TARGET_BG[0].toDouble(), HardLimits.VERY_HARD_LIMIT_TARGET_BG[1].toDouble()) var isTempTarget = false - val tempTarget = repository.getTemporaryTargetActiveAt(dateUtil._now()).blockingGet() + val tempTarget = repository.getTemporaryTargetActiveAt(dateUtil.now()).blockingGet() if (tempTarget is ValueWrapper.Existing) { isTempTarget = true minBg = hardLimits.verifyHardLimits(tempTarget.value.lowTarget, R.string.temp_target_low_target, HardLimits.VERY_HARD_LIMIT_TEMP_MIN_BG[0].toDouble(), HardLimits.VERY_HARD_LIMIT_TEMP_MIN_BG[1].toDouble()) @@ -134,12 +130,12 @@ open class OpenAPSSMBPlugin @Inject constructor( } if (!hardLimits.checkOnlyHardLimits(profile.dia, R.string.profile_dia, hardLimits.minDia(), hardLimits.maxDia())) return if (!hardLimits.checkOnlyHardLimits(profile.getIcTimeFromMidnight(Profile.secondsFromMidnight()), R.string.profile_carbs_ratio_value, hardLimits.minIC(), hardLimits.maxIC())) return - if (!hardLimits.checkOnlyHardLimits(profile.isfMgdl, R.string.profile_sensitivity_value, HardLimits.MIN_ISF, HardLimits.MAX_ISF)) return - if (!hardLimits.checkOnlyHardLimits(profile.maxDailyBasal, R.string.profile_max_daily_basal_value, 0.02, hardLimits.maxBasal())) return + if (!hardLimits.checkOnlyHardLimits(profile.getIsfMgdl(), R.string.profile_sensitivity_value, HardLimits.MIN_ISF, HardLimits.MAX_ISF)) return + if (!hardLimits.checkOnlyHardLimits(profile.getMaxDailyBasal(), R.string.profile_max_daily_basal_value, 0.02, hardLimits.maxBasal())) return if (!hardLimits.checkOnlyHardLimits(pump.baseBasalRate, R.string.current_basal_value, 0.01, hardLimits.maxBasal())) return startPart = System.currentTimeMillis() if (constraintChecker.isAutosensModeEnabled().value()) { - val autosensData = iobCobCalculatorPlugin.getLastAutosensDataSynchronized("OpenAPSPlugin") + val autosensData = iobCobCalculator.getLastAutosensDataWithWaitForCalculationFinish("OpenAPSPlugin") if (autosensData == null) { rxBus.send(EventOpenAPSUpdateResultGui(resourceHelper.gs(R.string.openaps_noasdata))) return @@ -148,7 +144,7 @@ open class OpenAPSSMBPlugin @Inject constructor( } else { lastAutosensResult.sensResult = "autosens disabled" } - val iobArray = iobCobCalculatorPlugin.calculateIobArrayForSMB(lastAutosensResult, SMBDefaults.exercise_mode, SMBDefaults.half_basal_exercise_target, isTempTarget) + val iobArray = iobCobCalculator.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 { @@ -172,7 +168,7 @@ open class OpenAPSSMBPlugin @Inject constructor( activePlugin.activePump.baseBasalRate, iobArray, glucoseStatus, - iobCobCalculatorPlugin.mealData, + iobCobCalculator.getMealDataWithWaitingForCalculationFinish(), lastAutosensResult.ratio, isTempTarget, smbAllowed.value(), @@ -190,9 +186,9 @@ open class OpenAPSSMBPlugin @Inject constructor( } else { // TODO still needed with oref1? // Fix bug determine basal - if (determineBasalResultSMB.rate == 0.0 && determineBasalResultSMB.duration == 0 && !treatmentsPlugin.isTempBasalInProgress) determineBasalResultSMB.tempBasalRequested = false + if (determineBasalResultSMB.rate == 0.0 && determineBasalResultSMB.duration == 0 && iobCobCalculator.getTempBasalIncludingConvertedExtended(dateUtil.now()) == null) determineBasalResultSMB.tempBasalRequested = false determineBasalResultSMB.iob = iobArray[0] - determineBasalResultSMB.json?.put("timestamp", DateUtil.toISOString(now)) + determineBasalResultSMB.json?.put("timestamp", dateUtil.toISOString(now)) determineBasalResultSMB.inputConstraints = inputConstraints lastDetermineBasalAdapterSMBJS = determineBasalAdapterSMBJS lastAPSResult = determineBasalResultSMB diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ConfigBuilderFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ConfigBuilderFragment.kt index 8a19808833..36c00a74e6 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ConfigBuilderFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ConfigBuilderFragment.kt @@ -9,7 +9,7 @@ import android.widget.* import androidx.annotation.StringRes import androidx.core.content.ContextCompat import dagger.android.support.DaggerFragment -import info.nightscout.androidaps.Config +import info.nightscout.androidaps.interfaces.Config import info.nightscout.androidaps.R import info.nightscout.androidaps.activities.PreferencesActivity import info.nightscout.androidaps.databinding.ConfigbuilderFragmentBinding @@ -19,7 +19,7 @@ import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.configBuilder.events.EventConfigBuilderUpdateGui import info.nightscout.androidaps.utils.FabricPrivacy import io.reactivex.rxkotlin.plusAssign -import info.nightscout.androidaps.utils.extensions.toVisibility +import info.nightscout.androidaps.extensions.toVisibility import info.nightscout.androidaps.utils.protection.ProtectionCheck import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers @@ -34,7 +34,7 @@ class ConfigBuilderFragment : DaggerFragment() { @Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var configBuilderPlugin: ConfigBuilderPlugin @Inject lateinit var fabricPrivacy: FabricPrivacy - @Inject lateinit var activePlugin: ActivePluginProvider + @Inject lateinit var activePlugin: ActivePlugin @Inject lateinit var protectionCheck: ProtectionCheck @Inject lateinit var config: Config @@ -101,18 +101,18 @@ class ConfigBuilderFragment : DaggerFragment() { private fun updateGUI() { binding.categories.removeAllViews() if (!config.NSCLIENT) { - createViewsForPlugins(R.string.configbuilder_profile, R.string.configbuilder_profile_description, PluginType.PROFILE, activePlugin.getSpecificPluginsVisibleInListByInterface(ProfileInterface::class.java, PluginType.PROFILE)) + createViewsForPlugins(R.string.configbuilder_profile, R.string.configbuilder_profile_description, PluginType.PROFILE, activePlugin.getSpecificPluginsVisibleInList(PluginType.PROFILE)) } - createViewsForPlugins(R.string.configbuilder_insulin, R.string.configbuilder_insulin_description, PluginType.INSULIN, activePlugin.getSpecificPluginsVisibleInListByInterface(InsulinInterface::class.java, PluginType.INSULIN)) + createViewsForPlugins(R.string.configbuilder_insulin, R.string.configbuilder_insulin_description, PluginType.INSULIN, activePlugin.getSpecificPluginsVisibleInList(PluginType.INSULIN)) if (!config.NSCLIENT) { - createViewsForPlugins(R.string.configbuilder_bgsource, R.string.configbuilder_bgsource_description, PluginType.BGSOURCE, activePlugin.getSpecificPluginsVisibleInListByInterface(BgSourceInterface::class.java, PluginType.BGSOURCE)) + createViewsForPlugins(R.string.configbuilder_bgsource, R.string.configbuilder_bgsource_description, PluginType.BGSOURCE, activePlugin.getSpecificPluginsVisibleInList(PluginType.BGSOURCE)) createViewsForPlugins(R.string.configbuilder_pump, R.string.configbuilder_pump_description, PluginType.PUMP, activePlugin.getSpecificPluginsVisibleInList(PluginType.PUMP)) } - createViewsForPlugins(R.string.configbuilder_sensitivity, R.string.configbuilder_sensitivity_description, PluginType.SENSITIVITY, activePlugin.getSpecificPluginsVisibleInListByInterface(SensitivityInterface::class.java, PluginType.SENSITIVITY)) + createViewsForPlugins(R.string.configbuilder_sensitivity, R.string.configbuilder_sensitivity_description, PluginType.SENSITIVITY, activePlugin.getSpecificPluginsVisibleInList(PluginType.SENSITIVITY)) if (config.APS) { createViewsForPlugins(R.string.configbuilder_aps, R.string.configbuilder_aps_description, PluginType.APS, activePlugin.getSpecificPluginsVisibleInList(PluginType.APS)) createViewsForPlugins(R.string.configbuilder_loop, R.string.configbuilder_loop_description, PluginType.LOOP, activePlugin.getSpecificPluginsVisibleInList(PluginType.LOOP)) - createViewsForPlugins(R.string.constraints, R.string.configbuilder_constraints_description, PluginType.CONSTRAINTS, activePlugin.getSpecificPluginsVisibleInListByInterface(ConstraintsInterface::class.java, PluginType.CONSTRAINTS)) + createViewsForPlugins(R.string.constraints, R.string.configbuilder_constraints_description, PluginType.CONSTRAINTS, activePlugin.getSpecificPluginsVisibleInList(PluginType.CONSTRAINTS)) } createViewsForPlugins(R.string.configbuilder_treatments, R.string.configbuilder_treatments_description, PluginType.TREATMENT, activePlugin.getSpecificPluginsVisibleInList(PluginType.TREATMENT)) createViewsForPlugins(R.string.configbuilder_general, R.string.configbuilder_general_description, PluginType.GENERAL, activePlugin.getSpecificPluginsVisibleInList(PluginType.GENERAL)) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ConfigBuilderPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ConfigBuilderPlugin.kt index d2065c238c..3b0ab58075 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ConfigBuilderPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ConfigBuilderPlugin.kt @@ -3,7 +3,9 @@ package info.nightscout.androidaps.plugins.configBuilder import androidx.fragment.app.FragmentActivity import dagger.android.HasAndroidInjector import info.nightscout.androidaps.R -import info.nightscout.androidaps.database.entities.UserEntry.* +import info.nightscout.androidaps.database.entities.UserEntry.Action +import info.nightscout.androidaps.database.entities.UserEntry.Sources +import info.nightscout.androidaps.database.entities.ValueWithUnit import info.nightscout.androidaps.events.EventAppInitialized import info.nightscout.androidaps.events.EventConfigBuilderChange import info.nightscout.androidaps.events.EventRebuildTabs @@ -27,8 +29,9 @@ class ConfigBuilderPlugin @Inject constructor( resourceHelper: ResourceHelper, private val sp: SP, private val rxBus: RxBusWrapper, - private val activePlugin: ActivePluginProvider, - private val uel: UserEntryLogger + private val activePlugin: ActivePlugin, + private val uel: UserEntryLogger, + private val pumpSync: PumpSync ) : PluginBase(PluginDescription() .mainType(PluginType.GENERAL) .fragmentClass(ConfigBuilderFragment::class.java.name) @@ -40,9 +43,9 @@ class ConfigBuilderPlugin @Inject constructor( .shortName(R.string.configbuilder_shortname) .description(R.string.description_config_builder), aapsLogger, resourceHelper, injector -), ConfigBuilderInterface { +), ConfigBuilder { - fun initialize() { + override fun initialize() { (activePlugin as PluginStore).loadDefaults() loadSettings() setAlwaysEnabledPluginsEnabled() @@ -66,7 +69,7 @@ class ConfigBuilderPlugin @Inject constructor( if (p.pluginDescription.alwaysEnabled && p.pluginDescription.neverVisible) continue savePref(p, type, true) if (type == PluginType.PUMP) { - if (p is ProfileInterface) { // Store state of optional Profile interface + if (p is ProfileSource) { // Store state of optional Profile interface savePref(p, PluginType.PROFILE, false) } } @@ -90,7 +93,7 @@ class ConfigBuilderPlugin @Inject constructor( val type = p.getType() loadPref(p, type, true) if (p.getType() == PluginType.PUMP) { - if (p is ProfileInterface) { + if (p is ProfileSource) { loadPref(p, PluginType.PROFILE, false) } } @@ -139,13 +142,16 @@ class ConfigBuilderPlugin @Inject constructor( val allowHardwarePump = sp.getBoolean("allow_hardware_pump", false) if (allowHardwarePump || activity == null) { performPluginSwitch(changedPlugin, newState, type) + pumpSync.connectNewPump() } else { - OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.allow_hardware_pump_text), Runnable { + OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.allow_hardware_pump_text), { performPluginSwitch(changedPlugin, newState, type) + pumpSync.connectNewPump() sp.putBoolean("allow_hardware_pump", true) - uel.log(Action.HW_PUMP_ALLOWED) + uel.log(Action.HW_PUMP_ALLOWED, Sources.ConfigBuilder, resourceHelper.gs(changedPlugin.pluginDescription.pluginName), + ValueWithUnit.SimpleString(resourceHelper.gsNotLocalised(changedPlugin.pluginDescription.pluginName))) aapsLogger.debug(LTag.PUMP, "First time HW pump allowed!") - }, Runnable { + }, { rxBus.send(EventConfigBuilderUpdateGui()) aapsLogger.debug(LTag.PUMP, "User does not allow switching to HW pump!") }) @@ -153,6 +159,14 @@ class ConfigBuilderPlugin @Inject constructor( } override fun performPluginSwitch(changedPlugin: PluginBase, enabled: Boolean, type: PluginType) { + if(enabled && !changedPlugin.isEnabled()) { + uel.log(Action.PLUGIN_ENABLED, Sources.ConfigBuilder, resourceHelper.gs(changedPlugin.pluginDescription.pluginName), + ValueWithUnit.SimpleString(resourceHelper.gsNotLocalised(changedPlugin.pluginDescription.pluginName))) + } + else if(!enabled) { + uel.log(Action.PLUGIN_DISABLED, Sources.ConfigBuilder, resourceHelper.gs(changedPlugin.pluginDescription.pluginName), + ValueWithUnit.SimpleString(resourceHelper.gsNotLocalised(changedPlugin.pluginDescription.pluginName))) + } changedPlugin.setPluginEnabled(type, enabled) changedPlugin.setFragmentVisible(type, enabled) processOnEnabledCategoryChanged(changedPlugin, type) @@ -166,13 +180,13 @@ class ConfigBuilderPlugin @Inject constructor( fun processOnEnabledCategoryChanged(changedPlugin: PluginBase, type: PluginType?) { var pluginsInCategory: ArrayList? = null when (type) { - PluginType.INSULIN -> pluginsInCategory = activePlugin.getSpecificPluginsListByInterface(InsulinInterface::class.java) - PluginType.SENSITIVITY -> pluginsInCategory = activePlugin.getSpecificPluginsListByInterface(SensitivityInterface::class.java) - PluginType.APS -> pluginsInCategory = activePlugin.getSpecificPluginsListByInterface(APSInterface::class.java) - PluginType.PROFILE -> pluginsInCategory = activePlugin.getSpecificPluginsListByInterface(ProfileInterface::class.java) - PluginType.BGSOURCE -> pluginsInCategory = activePlugin.getSpecificPluginsListByInterface(BgSourceInterface::class.java) + PluginType.INSULIN -> pluginsInCategory = activePlugin.getSpecificPluginsListByInterface(Insulin::class.java) + PluginType.SENSITIVITY -> pluginsInCategory = activePlugin.getSpecificPluginsListByInterface(Sensitivity::class.java) + PluginType.APS -> pluginsInCategory = activePlugin.getSpecificPluginsListByInterface(APS::class.java) + PluginType.PROFILE -> pluginsInCategory = activePlugin.getSpecificPluginsListByInterface(ProfileSource::class.java) + PluginType.BGSOURCE -> pluginsInCategory = activePlugin.getSpecificPluginsListByInterface(BgSource::class.java) PluginType.TREATMENT -> pluginsInCategory = activePlugin.getSpecificPluginsListByInterface(TreatmentsInterface::class.java) - PluginType.PUMP -> pluginsInCategory = activePlugin.getSpecificPluginsListByInterface(PumpInterface::class.java) + PluginType.PUMP -> pluginsInCategory = activePlugin.getSpecificPluginsListByInterface(Pump::class.java) else -> { } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/PluginStore.kt b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/PluginStore.kt index f0945429c5..75946070ed 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/PluginStore.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/PluginStore.kt @@ -1,6 +1,6 @@ package info.nightscout.androidaps.plugins.configBuilder -import info.nightscout.androidaps.Config +import info.nightscout.androidaps.interfaces.Config import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag @@ -11,23 +11,23 @@ import javax.inject.Singleton class PluginStore @Inject constructor( private val aapsLogger: AAPSLogger, private val config: Config -) : ActivePluginProvider { +) : ActivePlugin { lateinit var plugins: List<@JvmSuppressWildcards PluginBase> - private var activeBgSourceStore: BgSourceInterface? = null - private var activePumpStore: PumpInterface? = null - private var activeProfile: ProfileInterface? = null - private var activeAPSStore: APSInterface? = null - private var activeInsulinStore: InsulinInterface? = null - private var activeSensitivityStore: SensitivityInterface? = null + private var activeBgSourceStore: BgSource? = null + private var activePumpStore: Pump? = null + private var activeProfile: ProfileSource? = null + private var activeAPSStore: APS? = null + private var activeInsulinStore: Insulin? = null + private var activeSensitivityStore: Sensitivity? = null private var activeTreatmentsStore: TreatmentsInterface? = null fun loadDefaults() { verifySelectionInCategories() } - fun getDefaultPlugin(type: PluginType): PluginBase { + private fun getDefaultPlugin(type: PluginType): PluginBase { for (p in plugins) if (p.getType() == type && p.isDefault()) return p throw IllegalStateException("Default plugin not found") @@ -41,14 +41,6 @@ class PluginStore @Inject constructor( return newList } - override fun getSpecificPluginsVisibleInList(type: PluginType): ArrayList { - val newList = ArrayList() - for (p in plugins) { - if (p.getType() == type) if (p.showInList(type)) newList.add(p) - } - return newList - } - override fun getSpecificPluginsListByInterface(interfaceClass: Class<*>): ArrayList { val newList = ArrayList() for (p in plugins) { @@ -57,10 +49,10 @@ class PluginStore @Inject constructor( return newList } - override fun getSpecificPluginsVisibleInListByInterface(interfaceClass: Class<*>, type: PluginType): ArrayList { + override fun getSpecificPluginsVisibleInList(type: PluginType): ArrayList { val newList = ArrayList() for (p in plugins) { - if (p.javaClass != ConfigBuilderPlugin::class.java && interfaceClass.isAssignableFrom(p.javaClass)) if (p.showInList(type)) newList.add(p) + if (p.getType() == type) if (p.showInList(type)) newList.add(p) } return newList } @@ -71,64 +63,64 @@ class PluginStore @Inject constructor( // PluginType.APS if (!config.NSCLIENT && !config.PUMPCONTROL) { pluginsInCategory = getSpecificPluginsList(PluginType.APS) - activeAPSStore = getTheOneEnabledInArray(pluginsInCategory, PluginType.APS) as APSInterface? + activeAPSStore = getTheOneEnabledInArray(pluginsInCategory, PluginType.APS) as APS? if (activeAPSStore == null) { - activeAPSStore = getDefaultPlugin(PluginType.APS) as APSInterface + activeAPSStore = getDefaultPlugin(PluginType.APS) as APS (activeAPSStore as PluginBase).setPluginEnabled(PluginType.APS, true) aapsLogger.debug(LTag.CONFIGBUILDER, "Defaulting APSInterface") } - setFragmentVisiblities((activeAPSStore as PluginBase).name, pluginsInCategory, PluginType.APS) + setFragmentVisibilities((activeAPSStore as PluginBase).name, pluginsInCategory, PluginType.APS) } // PluginType.INSULIN pluginsInCategory = getSpecificPluginsList(PluginType.INSULIN) - activeInsulinStore = getTheOneEnabledInArray(pluginsInCategory, PluginType.INSULIN) as InsulinInterface? + activeInsulinStore = getTheOneEnabledInArray(pluginsInCategory, PluginType.INSULIN) as Insulin? if (activeInsulinStore == null) { - activeInsulinStore = getDefaultPlugin(PluginType.INSULIN) as InsulinInterface + activeInsulinStore = getDefaultPlugin(PluginType.INSULIN) as Insulin (activeInsulinStore as PluginBase).setPluginEnabled(PluginType.INSULIN, true) aapsLogger.debug(LTag.CONFIGBUILDER, "Defaulting InsulinInterface") } - setFragmentVisiblities((activeInsulinStore as PluginBase).name, pluginsInCategory, PluginType.INSULIN) + setFragmentVisibilities((activeInsulinStore as PluginBase).name, pluginsInCategory, PluginType.INSULIN) // PluginType.SENSITIVITY pluginsInCategory = getSpecificPluginsList(PluginType.SENSITIVITY) - activeSensitivityStore = getTheOneEnabledInArray(pluginsInCategory, PluginType.SENSITIVITY) as SensitivityInterface? + activeSensitivityStore = getTheOneEnabledInArray(pluginsInCategory, PluginType.SENSITIVITY) as Sensitivity? if (activeSensitivityStore == null) { - activeSensitivityStore = getDefaultPlugin(PluginType.SENSITIVITY) as SensitivityInterface + activeSensitivityStore = getDefaultPlugin(PluginType.SENSITIVITY) as Sensitivity (activeSensitivityStore as PluginBase).setPluginEnabled(PluginType.SENSITIVITY, true) aapsLogger.debug(LTag.CONFIGBUILDER, "Defaulting SensitivityInterface") } - setFragmentVisiblities((activeSensitivityStore as PluginBase).name, pluginsInCategory, PluginType.SENSITIVITY) + setFragmentVisibilities((activeSensitivityStore as PluginBase).name, pluginsInCategory, PluginType.SENSITIVITY) // PluginType.PROFILE pluginsInCategory = getSpecificPluginsList(PluginType.PROFILE) - activeProfile = getTheOneEnabledInArray(pluginsInCategory, PluginType.PROFILE) as ProfileInterface? + activeProfile = getTheOneEnabledInArray(pluginsInCategory, PluginType.PROFILE) as ProfileSource? if (activeProfile == null) { - activeProfile = getDefaultPlugin(PluginType.PROFILE) as ProfileInterface + activeProfile = getDefaultPlugin(PluginType.PROFILE) as ProfileSource (activeProfile as PluginBase).setPluginEnabled(PluginType.PROFILE, true) aapsLogger.debug(LTag.CONFIGBUILDER, "Defaulting ProfileInterface") } - setFragmentVisiblities((activeProfile as PluginBase).name, pluginsInCategory, PluginType.PROFILE) + setFragmentVisibilities((activeProfile as PluginBase).name, pluginsInCategory, PluginType.PROFILE) // PluginType.BGSOURCE pluginsInCategory = getSpecificPluginsList(PluginType.BGSOURCE) - activeBgSourceStore = getTheOneEnabledInArray(pluginsInCategory, PluginType.BGSOURCE) as BgSourceInterface? + activeBgSourceStore = getTheOneEnabledInArray(pluginsInCategory, PluginType.BGSOURCE) as BgSource? if (activeBgSourceStore == null) { - activeBgSourceStore = getDefaultPlugin(PluginType.BGSOURCE) as BgSourceInterface + activeBgSourceStore = getDefaultPlugin(PluginType.BGSOURCE) as BgSource (activeBgSourceStore as PluginBase).setPluginEnabled(PluginType.BGSOURCE, true) aapsLogger.debug(LTag.CONFIGBUILDER, "Defaulting BgInterface") } - setFragmentVisiblities((activeBgSourceStore as PluginBase).name, pluginsInCategory, PluginType.BGSOURCE) + setFragmentVisibilities((activeBgSourceStore as PluginBase).name, pluginsInCategory, PluginType.BGSOURCE) // PluginType.PUMP pluginsInCategory = getSpecificPluginsList(PluginType.PUMP) - activePumpStore = getTheOneEnabledInArray(pluginsInCategory, PluginType.PUMP) as PumpInterface? + activePumpStore = getTheOneEnabledInArray(pluginsInCategory, PluginType.PUMP) as Pump? if (activePumpStore == null) { - activePumpStore = getDefaultPlugin(PluginType.PUMP) as PumpInterface + activePumpStore = getDefaultPlugin(PluginType.PUMP) as Pump (activePumpStore as PluginBase).setPluginEnabled(PluginType.PUMP, true) aapsLogger.debug(LTag.CONFIGBUILDER, "Defaulting PumpInterface") } - setFragmentVisiblities((activePumpStore as PluginBase).name, pluginsInCategory, PluginType.PUMP) + setFragmentVisibilities((activePumpStore as PluginBase).name, pluginsInCategory, PluginType.PUMP) // PluginType.TREATMENT pluginsInCategory = getSpecificPluginsList(PluginType.TREATMENT) @@ -138,50 +130,11 @@ class PluginStore @Inject constructor( (activeTreatmentsStore as PluginBase).setPluginEnabled(PluginType.TREATMENT, true) aapsLogger.debug(LTag.CONFIGBUILDER, "Defaulting PumpInterface") } - setFragmentVisiblities((activeTreatmentsStore as PluginBase).name, pluginsInCategory, PluginType.TREATMENT) + setFragmentVisibilities((activeTreatmentsStore as PluginBase).name, pluginsInCategory, PluginType.TREATMENT) } - /** - * disables the visibility for all fragments of Plugins with the given PluginType - * which are not equally named to the Plugin implementing the given Plugin Interface. - * - * @param pluginInterface - * @param pluginType - * @param - * @return - */ - private fun determineActivePlugin(pluginInterface: Class, pluginType: PluginType): T? { - val pluginsInCategory: ArrayList = getSpecificPluginsListByInterface(pluginInterface) - return determineActivePlugin(pluginsInCategory, pluginType) - } - - /** - * disables the visibility for all fragments of Plugins in the given pluginsInCategory - * with the given PluginType which are not equally named to the Plugin implementing the - * given Plugin Interface. - * - * - * TODO we are casting an interface to PluginBase, which seems to be rather odd, since - * TODO the interface is not implementing PluginBase (this is just avoiding errors through - * TODO conventions. - * - * @param pluginsInCategory - * @param pluginType - * @param - * @return - */ - private fun determineActivePlugin(pluginsInCategory: ArrayList, - pluginType: PluginType): T? { - @Suppress("UNCHECKED_CAST") - val activePlugin = getTheOneEnabledInArray(pluginsInCategory, pluginType) as T? - if (activePlugin != null) { - setFragmentVisiblities((activePlugin as PluginBase).name, pluginsInCategory, pluginType) - } - return activePlugin - } - - private fun setFragmentVisiblities(activePluginName: String, pluginsInCategory: ArrayList, - pluginType: PluginType) { + private fun setFragmentVisibilities(activePluginName: String, pluginsInCategory: ArrayList, + pluginType: PluginType) { aapsLogger.debug(LTag.CONFIGBUILDER, "Selected interface: $activePluginName") for (p in pluginsInCategory) if (p.name != activePluginName) @@ -203,22 +156,22 @@ class PluginStore @Inject constructor( // ***** Interface ***** - override val activeBgSource: BgSourceInterface + override val activeBgSource: BgSource get() = activeBgSourceStore ?: checkNotNull(activeBgSourceStore) { "No bg source selected" } - override val activeProfileInterface: ProfileInterface + override val activeProfileSource: ProfileSource get() = activeProfile ?: checkNotNull(activeProfile) { "No profile selected" } - override val activeInsulin: InsulinInterface + override val activeInsulin: Insulin get() = activeInsulinStore ?: checkNotNull(activeInsulinStore) { "No insulin selected" } - override val activeAPS: APSInterface + override val activeAPS: APS get() = activeAPSStore ?: checkNotNull(activeAPSStore) { "No APS selected" } - override val activePump: PumpInterface + override val activePump: Pump get() = activePumpStore ?: checkNotNull(activePumpStore) { "No pump selected" } - override val activeSensitivity: SensitivityInterface + override val activeSensitivity: Sensitivity get() = activeSensitivityStore ?: checkNotNull(activeSensitivityStore) { "No sensitivity selected" } @@ -226,8 +179,8 @@ class PluginStore @Inject constructor( get() = activeTreatmentsStore ?: checkNotNull(activeTreatmentsStore) { "No treatments selected" } - override val activeOverview: OverviewInterface - get() = getSpecificPluginsListByInterface(OverviewInterface::class.java).first() as OverviewInterface + override val activeOverview: Overview + get() = getSpecificPluginsListByInterface(Overview::class.java).first() as Overview override fun getPluginsList(): ArrayList = ArrayList(plugins) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ProfileFunctionImplementation.kt b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ProfileFunctionImplementation.kt new file mode 100644 index 0000000000..c5049d171d --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ProfileFunctionImplementation.kt @@ -0,0 +1,123 @@ +package info.nightscout.androidaps.plugins.configBuilder + +import androidx.collection.LongSparseArray +import info.nightscout.androidaps.Constants +import info.nightscout.androidaps.core.R +import info.nightscout.androidaps.data.ProfileSealed +import info.nightscout.androidaps.database.AppRepository +import info.nightscout.androidaps.database.ValueWrapper +import info.nightscout.androidaps.database.entities.ProfileSwitch +import info.nightscout.androidaps.database.transactions.InsertOrUpdateProfileSwitch +import info.nightscout.androidaps.extensions.fromConstant +import info.nightscout.androidaps.interfaces.ActivePlugin +import info.nightscout.androidaps.interfaces.GlucoseUnit +import info.nightscout.androidaps.interfaces.Profile +import info.nightscout.androidaps.interfaces.ProfileFunction +import info.nightscout.androidaps.interfaces.ProfileStore +import info.nightscout.androidaps.logging.AAPSLogger +import info.nightscout.androidaps.logging.LTag +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 io.reactivex.disposables.CompositeDisposable +import io.reactivex.rxkotlin.plusAssign +import java.security.spec.InvalidParameterSpecException +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class ProfileFunctionImplementation @Inject constructor( + private val aapsLogger: AAPSLogger, + private val sp: SP, + private val resourceHelper: ResourceHelper, + private val activePlugin: ActivePlugin, + private val repository: AppRepository, + private val dateUtil: DateUtil +) : ProfileFunction { + + val cache = LongSparseArray() + + private val disposable = CompositeDisposable() + + override fun getProfileName(): String = + getProfileName(System.currentTimeMillis(), customized = true, showRemainingTime = false) + + override fun getOriginalProfileName(): String = + getProfileName(System.currentTimeMillis(), customized = false, showRemainingTime = false) + + override fun getProfileNameWithRemainingTime(): String = + getProfileName(System.currentTimeMillis(), customized = true, showRemainingTime = true) + + fun getProfileName(time: Long, customized: Boolean, showRemainingTime: Boolean): String { + var profileName = resourceHelper.gs(R.string.noprofileselected) + + val profileSwitch = repository.getEffectiveProfileSwitchActiveAt(time).blockingGet() + if (profileSwitch is ValueWrapper.Existing) { + profileName = if (customized) profileSwitch.value.originalCustomizedName else profileSwitch.value.originalProfileName + if (showRemainingTime && profileSwitch.value.originalDuration != 0L) { + profileName += dateUtil.untilString(profileSwitch.value.originalEnd, resourceHelper) + } + } + return profileName + } + + override fun isProfileValid(from: String): Boolean = getProfile() != null + + override fun getProfile(): Profile? = + getProfile(dateUtil.now()) + + override fun getProfile(time: Long): Profile? { + val rounded = time - time % 1000 + val cached = cache[rounded] + if (cached != null) { +// aapsLogger.debug("XXXXXXXXXXXXXXX HIT getProfile for $time $rounded") + return cached + } +// aapsLogger.debug("XXXXXXXXXXXXXXX getProfile called for $time") + val ps = repository.getEffectiveProfileSwitchActiveAt(time).blockingGet() + if (ps is ValueWrapper.Existing) { + val sealed = ProfileSealed.EPS(ps.value) + cache.put(rounded, sealed) + return sealed + } + return null + } + + override fun getRequestedProfile(): ProfileSwitch? = repository.getActiveProfileSwitch(dateUtil.now()) + + override fun getUnits(): GlucoseUnit = + if (sp.getString(R.string.key_units, Constants.MGDL) == Constants.MGDL) GlucoseUnit.MGDL + else GlucoseUnit.MMOL + + override fun createProfileSwitch(profileStore: ProfileStore, profileName: String, durationInMinutes: Int, percentage: Int, timeShiftInHours: Int, timestamp: Long) { + val pureProfile = profileStore.getSpecificProfile(profileName) + ?: throw InvalidParameterSpecException(profileName) + val ps = ProfileSwitch( + timestamp = timestamp, + basalBlocks = pureProfile.basalBlocks, + isfBlocks = pureProfile.isfBlocks, + icBlocks = pureProfile.icBlocks, + targetBlocks = pureProfile.targetBlocks, + glucoseUnit = ProfileSwitch.GlucoseUnit.fromConstant(pureProfile.glucoseUnit), + profileName = profileName, + timeshift = T.hours(timeShiftInHours.toLong()).msecs(), + percentage = percentage, + duration = T.mins(durationInMinutes.toLong()).msecs(), + insulinConfiguration = activePlugin.activeInsulin.insulinConfiguration) + disposable += repository.runTransactionForResult(InsertOrUpdateProfileSwitch(ps)) + .subscribe({ result -> + result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted ProfileSwitch $it") } + result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated ProfileSwitch $it") } + }, { + aapsLogger.error(LTag.DATABASE, "Error while saving ProfileSwitch", it) + }) + } + + override fun createProfileSwitch(durationInMinutes: Int, percentage: Int, timeShiftInHours: Int) { + val profileStore = activePlugin.activeProfileSource.profile ?: return + val profileName = activePlugin.activeProfileSource.profile?.getDefaultProfileName() + ?: return + createProfileSwitch(profileStore, profileName, durationInMinutes, percentage, timeShiftInHours, dateUtil.now()) + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/dstHelper/DstHelperPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/dstHelper/DstHelperPlugin.kt index 8292346a11..42ff9ea765 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/dstHelper/DstHelperPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/dstHelper/DstHelperPlugin.kt @@ -24,7 +24,7 @@ class DstHelperPlugin @Inject constructor( private var rxBus: RxBusWrapper, resourceHelper: ResourceHelper, private var sp: SP, - private var activePlugin: ActivePluginProvider, + private var activePlugin: ActivePlugin, private var loopPlugin: LoopPlugin ) : PluginBase(PluginDescription() .mainType(PluginType.CONSTRAINTS) @@ -33,7 +33,7 @@ class DstHelperPlugin @Inject constructor( .showInList(false) .pluginName(R.string.dst_plugin_name), aapsLogger, resourceHelper, injector -), ConstraintsInterface { +), Constraints { companion object { private const val DISABLE_TIME_FRAME_HOURS = -3 diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesFragment.kt index 22fcb7052f..8f161e6ae2 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesFragment.kt @@ -17,7 +17,9 @@ import androidx.recyclerview.widget.LinearSmoothScroller import androidx.recyclerview.widget.RecyclerView import dagger.android.support.DaggerFragment import info.nightscout.androidaps.R -import info.nightscout.androidaps.database.entities.UserEntry.* +import info.nightscout.androidaps.database.entities.UserEntry.Action +import info.nightscout.androidaps.database.entities.UserEntry.Sources +import info.nightscout.androidaps.database.entities.ValueWithUnit import info.nightscout.androidaps.databinding.ObjectivesFragmentBinding import info.nightscout.androidaps.databinding.ObjectivesItemBinding import info.nightscout.androidaps.dialogs.NtpProgressDialog @@ -235,7 +237,7 @@ class ObjectivesFragment : DaggerFragment() { holder.binding.verify.setOnClickListener { receiverStatusStore.updateNetworkStatus() if (binding.fake.isChecked) { - objective.accomplishedOn = DateUtil.now() + objective.accomplishedOn = dateUtil.now() scrollToCurrentObjective() startUpdateTimer() rxBus.send(EventObjectivesUpdateGui()) @@ -247,7 +249,7 @@ class ObjectivesFragment : DaggerFragment() { rxBus.send(EventNtpStatus(resourceHelper.gs(R.string.timedetection), 0)) sntpClient.ntpTime(object : SntpClient.Callback() { override fun run() { - aapsLogger.debug("NTP time: $time System time: ${DateUtil.now()}") + aapsLogger.debug("NTP time: $time System time: ${dateUtil.now()}") SystemClock.sleep(300) if (!networkConnected) { rxBus.send(EventNtpStatus(resourceHelper.gs(R.string.notconnected), 99)) @@ -274,7 +276,7 @@ class ObjectivesFragment : DaggerFragment() { holder.binding.start.setOnClickListener { receiverStatusStore.updateNetworkStatus() if (binding.fake.isChecked) { - objective.startedOn = DateUtil.now() + objective.startedOn = dateUtil.now() scrollToCurrentObjective() startUpdateTimer() rxBus.send(EventObjectivesUpdateGui()) @@ -286,7 +288,7 @@ class ObjectivesFragment : DaggerFragment() { rxBus.send(EventNtpStatus(resourceHelper.gs(R.string.timedetection), 0)) sntpClient.ntpTime(object : SntpClient.Callback() { override fun run() { - aapsLogger.debug("NTP time: $time System time: ${DateUtil.now()}") + aapsLogger.debug("NTP time: $time System time: ${dateUtil.now()}") SystemClock.sleep(300) if (!networkConnected) { rxBus.send(EventNtpStatus(resourceHelper.gs(R.string.notconnected), 99)) @@ -308,7 +310,8 @@ 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(Action.OBJECTIVE_UNSTARTED, ValueWithUnit(position + 1, Units.None)) + uel.log(Action.OBJECTIVE_UNSTARTED, Sources.Objectives, + ValueWithUnit.SimpleInt(position + 1)) objective.startedOn = 0 scrollToCurrentObjective() rxBus.send(EventObjectivesUpdateGui()) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesPlugin.kt index 93090c7533..0a7462b099 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesPlugin.kt @@ -5,9 +5,9 @@ import com.google.common.base.Charsets import com.google.common.hash.Hashing import dagger.android.HasAndroidInjector import info.nightscout.androidaps.BuildConfig -import info.nightscout.androidaps.Config import info.nightscout.androidaps.R -import info.nightscout.androidaps.database.entities.UserEntry.* +import info.nightscout.androidaps.database.entities.UserEntry.Action +import info.nightscout.androidaps.database.entities.UserEntry.Sources import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.UserEntryLogger @@ -25,9 +25,10 @@ class ObjectivesPlugin @Inject constructor( injector: HasAndroidInjector, aapsLogger: AAPSLogger, resourceHelper: ResourceHelper, - private val activePlugin: ActivePluginProvider, + private val activePlugin: ActivePlugin, private val sp: SP, config: Config, + private val dateUtil: DateUtil, private val uel: UserEntryLogger ) : PluginBase(PluginDescription() .mainType(PluginType.CONSTRAINTS) @@ -39,7 +40,7 @@ class ObjectivesPlugin @Inject constructor( .shortName(R.string.objectives_shortname) .description(R.string.description_objectives), aapsLogger, resourceHelper, injector -), ConstraintsInterface { +), Constraints { var objectives: MutableList = ArrayList() @@ -125,25 +126,25 @@ class ObjectivesPlugin @Inject constructor( if (!url.endsWith("/")) url = "$url/" @Suppress("DEPRECATION") val hashNS = Hashing.sha1().hashString(url + BuildConfig.APPLICATION_ID + "/" + requestCode, Charsets.UTF_8).toString() if (request.equals(hashNS.substring(0, 10), ignoreCase = true)) { - sp.putLong("Objectives_" + "openloop" + "_started", DateUtil.now()) - sp.putLong("Objectives_" + "openloop" + "_accomplished", DateUtil.now()) - sp.putLong("Objectives_" + "maxbasal" + "_started", DateUtil.now()) - sp.putLong("Objectives_" + "maxbasal" + "_accomplished", DateUtil.now()) - sp.putLong("Objectives_" + "maxiobzero" + "_started", DateUtil.now()) - sp.putLong("Objectives_" + "maxiobzero" + "_accomplished", DateUtil.now()) - sp.putLong("Objectives_" + "maxiob" + "_started", DateUtil.now()) - sp.putLong("Objectives_" + "maxiob" + "_accomplished", DateUtil.now()) - sp.putLong("Objectives_" + "autosens" + "_started", DateUtil.now()) - sp.putLong("Objectives_" + "autosens" + "_accomplished", DateUtil.now()) - sp.putLong("Objectives_" + "ama" + "_started", DateUtil.now()) - sp.putLong("Objectives_" + "ama" + "_accomplished", DateUtil.now()) - sp.putLong("Objectives_" + "smb" + "_started", DateUtil.now()) - sp.putLong("Objectives_" + "smb" + "_accomplished", DateUtil.now()) - sp.putLong("Objectives_" + "auto" + "_started", DateUtil.now()) - sp.putLong("Objectives_" + "auto" + "_accomplished", DateUtil.now()) + sp.putLong("Objectives_" + "openloop" + "_started", dateUtil.now()) + sp.putLong("Objectives_" + "openloop" + "_accomplished", dateUtil.now()) + sp.putLong("Objectives_" + "maxbasal" + "_started", dateUtil.now()) + sp.putLong("Objectives_" + "maxbasal" + "_accomplished", dateUtil.now()) + sp.putLong("Objectives_" + "maxiobzero" + "_started", dateUtil.now()) + sp.putLong("Objectives_" + "maxiobzero" + "_accomplished", dateUtil.now()) + sp.putLong("Objectives_" + "maxiob" + "_started", dateUtil.now()) + sp.putLong("Objectives_" + "maxiob" + "_accomplished", dateUtil.now()) + sp.putLong("Objectives_" + "autosens" + "_started", dateUtil.now()) + sp.putLong("Objectives_" + "autosens" + "_accomplished", dateUtil.now()) + sp.putLong("Objectives_" + "ama" + "_started", dateUtil.now()) + sp.putLong("Objectives_" + "ama" + "_accomplished", dateUtil.now()) + sp.putLong("Objectives_" + "smb" + "_started", dateUtil.now()) + sp.putLong("Objectives_" + "smb" + "_accomplished", dateUtil.now()) + sp.putLong("Objectives_" + "auto" + "_started", dateUtil.now()) + sp.putLong("Objectives_" + "auto" + "_accomplished", dateUtil.now()) setupObjectives() OKDialog.show(activity, resourceHelper.gs(R.string.objectives), resourceHelper.gs(R.string.codeaccepted)) - uel.log(Action.OBJECTIVES_SKIPPED) + uel.log(Action.OBJECTIVES_SKIPPED, Sources.Objectives) } else { OKDialog.show(activity, resourceHelper.gs(R.string.objectives), resourceHelper.gs(R.string.codeinvalid)) } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/activities/ObjectivesExamDialog.kt b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/activities/ObjectivesExamDialog.kt index c45d61bfd4..50c91dd6b9 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/activities/ObjectivesExamDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/activities/ObjectivesExamDialog.kt @@ -108,7 +108,7 @@ class ObjectivesExamDialog : DaggerDialogFragment() { } task.answered = result if (!result) { - task.disabledTo = DateUtil.now() + T.hours(1).msecs() + task.disabledTo = dateUtil.now() + T.hours(1).msecs() ToastUtils.showToastInUiThread(context, R.string.wronganswer) } else task.disabledTo = 0 updateGui() diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/objectives/Objective.kt b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/objectives/Objective.kt index 876fb881db..71f258781d 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/objectives/Objective.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/objectives/Objective.kt @@ -21,6 +21,7 @@ abstract class Objective(injector: HasAndroidInjector, spName: String, @StringRe @Inject lateinit var sp: SP @Inject lateinit var resourceHelper: ResourceHelper + @Inject lateinit var dateUtil: DateUtil private val spName: String @StringRes val objective: Int @@ -55,7 +56,7 @@ abstract class Objective(injector: HasAndroidInjector, spName: String, @StringRe 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 + 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 } @@ -69,7 +70,7 @@ abstract class Objective(injector: HasAndroidInjector, spName: String, @StringRe } val isAccomplished: Boolean - get() = accomplishedOn != 0L && accomplishedOn < DateUtil.now() + get() = accomplishedOn != 0L && accomplishedOn < dateUtil.now() val isStarted: Boolean get() = startedOn != 0L @@ -144,7 +145,7 @@ abstract class Objective(injector: HasAndroidInjector, spName: String, @StringRe override fun isCompleted(): Boolean = answered - fun isEnabledAnswer(): Boolean = disabledTo < DateUtil.now() + fun isEnabledAnswer(): Boolean = disabledTo < dateUtil.now() fun option(option: Option): ExamTask { options.add(option) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/objectives/Objective0.kt b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/objectives/Objective0.kt index 63eaa326fc..c58e0cb355 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/objectives/Objective0.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/objectives/Objective0.kt @@ -2,25 +2,25 @@ 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.database.AppRepository +import info.nightscout.androidaps.database.ValueWrapper +import info.nightscout.androidaps.interfaces.ActivePlugin +import info.nightscout.androidaps.interfaces.IobCobCalculator 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 activePlugin: ActivePlugin @Inject lateinit var virtualPumpPlugin: VirtualPumpPlugin - @Inject lateinit var treatmentsPlugin: TreatmentsPlugin + @Inject lateinit var repository: AppRepository @Inject lateinit var loopPlugin: LoopPlugin @Inject lateinit var nsClientPlugin: NSClientPlugin - @Inject lateinit var iobCobCalculatorPlugin: IobCobCalculatorPlugin + @Inject lateinit var iobCobCalculator: IobCobCalculator init { tasks.add(object : Task(this, R.string.objectives_bgavailableinns) { @@ -49,7 +49,7 @@ class Objective0(injector: HasAndroidInjector) : Objective(injector, "config", R }) tasks.add(object : Task(this, R.string.hasbgdata) { override fun isCompleted(): Boolean { - return iobCobCalculatorPlugin.lastBg() != null + return iobCobCalculator.ads.lastBg() != null } }) tasks.add(object : Task(this, R.string.loopenabled) { @@ -65,7 +65,7 @@ class Objective0(injector: HasAndroidInjector) : Objective(injector, "config", R }) tasks.add(object : Task(this, R.string.activate_profile) { override fun isCompleted(): Boolean { - return treatmentsPlugin.getProfileSwitchFromHistory(DateUtil.now()) != null + return repository.getEffectiveProfileSwitchActiveAt(dateUtil.now()).blockingGet() is ValueWrapper.Existing } }) } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/objectives/Objective3.kt b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/objectives/Objective3.kt index da4fb503a1..a40f4fb752 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/objectives/Objective3.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/objectives/Objective3.kt @@ -28,7 +28,7 @@ class Objective3 @Inject constructor(injector: HasAndroidInjector) : Objective(i } override fun specialActionEnabled(): Boolean = - NSClientService.isConnected && NSClientService.hasWriteAuth + nsClientPlugin.nsClientService?.isConnected == true && nsClientPlugin.nsClientService?.hasWriteAuth == true override fun specialAction(activity: FragmentActivity, input: String) { objectivesPlugin.completeObjectives(activity, input) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/phoneChecker/PhoneCheckerPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/phoneChecker/PhoneCheckerPlugin.kt index 44466da5af..19dcb7d9fd 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/phoneChecker/PhoneCheckerPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/phoneChecker/PhoneCheckerPlugin.kt @@ -5,7 +5,7 @@ import android.os.Build import com.scottyab.rootbeer.RootBeer import dagger.android.HasAndroidInjector import info.nightscout.androidaps.R -import info.nightscout.androidaps.interfaces.ConstraintsInterface +import info.nightscout.androidaps.interfaces.Constraints import info.nightscout.androidaps.interfaces.PluginBase import info.nightscout.androidaps.interfaces.PluginDescription import info.nightscout.androidaps.interfaces.PluginType @@ -27,7 +27,7 @@ class PhoneCheckerPlugin @Inject constructor( .showInList(false) .pluginName(R.string.phonechecker), aapsLogger, resourceHelper, injector -), ConstraintsInterface { +), Constraints { var phoneRooted: Boolean = false var devMode: Boolean = false diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/safety/SafetyPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/safety/SafetyPlugin.kt index 0a5e1927e9..cfabe73346 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/safety/SafetyPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/safety/SafetyPlugin.kt @@ -1,9 +1,9 @@ package info.nightscout.androidaps.plugins.constraints.safety import dagger.android.HasAndroidInjector -import info.nightscout.androidaps.Config +import info.nightscout.androidaps.interfaces.Config import info.nightscout.androidaps.R -import info.nightscout.androidaps.data.Profile +import info.nightscout.androidaps.interfaces.Profile import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.plugins.aps.openAPSAMA.OpenAPSAMAPlugin @@ -13,6 +13,7 @@ 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.DateUtil import info.nightscout.androidaps.utils.DecimalFormatter import info.nightscout.androidaps.utils.HardLimits import info.nightscout.androidaps.utils.Round @@ -34,11 +35,12 @@ class SafetyPlugin @Inject constructor( private val openAPSAMAPlugin: OpenAPSAMAPlugin, private val openAPSSMBPlugin: OpenAPSSMBPlugin, private val sensitivityOref1Plugin: SensitivityOref1Plugin, - private val activePlugin: ActivePluginProvider, + private val activePlugin: ActivePlugin, private val hardLimits: HardLimits, private val buildHelper: BuildHelper, - private val treatmentsPlugin: TreatmentsInterface, - private val config: Config + private val iobCobCalculator: IobCobCalculator, + private val config: Config, + private val dateUtil: DateUtil ) : PluginBase(PluginDescription() .mainType(PluginType.CONSTRAINTS) .neverVisible(true) @@ -47,7 +49,7 @@ class SafetyPlugin @Inject constructor( .pluginName(R.string.safety) .preferencesId(R.xml.pref_safety), aapsLogger, resourceHelper, injector -), ConstraintsInterface { +), Constraints { /** * Constraints interface @@ -68,7 +70,7 @@ class SafetyPlugin @Inject constructor( value[aapsLogger, false, resourceHelper.gs(R.string.closed_loop_disabled_on_dev_branch)] = this } val pump = activePlugin.activePump - if (!pump.isFakingTempsByExtendedBoluses && treatmentsPlugin.isInHistoryExtendedBolusInProgress) { + if (!pump.isFakingTempsByExtendedBoluses && iobCobCalculator.getExtendedBolus(dateUtil.now()) != null) { value[aapsLogger, false, resourceHelper.gs(R.string.closed_loop_disabled_with_eb)] = this } return value @@ -106,25 +108,25 @@ class SafetyPlugin @Inject constructor( 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 + if (maxBasal < profile.getMaxDailyBasal()) { + maxBasal = profile.getMaxDailyBasal() 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 + val maxFromBasalMultiplier = floor(maxBasalMultiplier * profile.getBasal() * 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 + val maxFromDaily = floor(profile.getMaxDailyBasal() * 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 + val pumpLimit = pump.pumpDescription.pumpType.tbrSettings?.maxDose ?: 0.0 absoluteRate.setIfSmaller(aapsLogger, pumpLimit, String.format(resourceHelper.gs(R.string.limitingbasalratio), pumpLimit, resourceHelper.gs(R.string.pumplimit)), this) } @@ -136,7 +138,7 @@ class SafetyPlugin @Inject constructor( } override fun applyBasalPercentConstraints(percentRate: Constraint, profile: Profile): Constraint { - val currentBasal = profile.basal + val currentBasal = profile.getBasal() 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) @@ -147,7 +149,7 @@ class SafetyPlugin @Inject constructor( 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 + val pumpLimit = pump.pumpDescription.pumpType.tbrSettings?.maxDose ?: 0.0 percentRate.setIfSmaller(aapsLogger, pumpLimit.toInt(), String.format(resourceHelper.gs(R.string.limitingbasalratio), pumpLimit, resourceHelper.gs(R.string.pumplimit)), this) } return percentRate diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/signatureVerifier/SignatureVerifierPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/signatureVerifier/SignatureVerifierPlugin.kt index b67def6ec7..dd7245601c 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/signatureVerifier/SignatureVerifierPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/signatureVerifier/SignatureVerifierPlugin.kt @@ -5,7 +5,7 @@ import android.content.pm.PackageManager import dagger.android.HasAndroidInjector import info.nightscout.androidaps.R import info.nightscout.androidaps.interfaces.Constraint -import info.nightscout.androidaps.interfaces.ConstraintsInterface +import info.nightscout.androidaps.interfaces.Constraints import info.nightscout.androidaps.interfaces.PluginBase import info.nightscout.androidaps.interfaces.PluginDescription import info.nightscout.androidaps.interfaces.PluginType @@ -46,7 +46,7 @@ class SignatureVerifierPlugin @Inject constructor( .showInList(false) .pluginName(R.string.signature_verifier), aapsLogger, resourceHelper, injector -), ConstraintsInterface { +), Constraints { private val REVOKED_CERTS_URL = "https://raw.githubusercontent.com/nightscout/AndroidAPS/master/app/src/main/assets/revoked_certs.txt" private val UPDATE_INTERVAL = TimeUnit.DAYS.toMillis(1) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/storage/StorageConstraintPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/storage/StorageConstraintPlugin.kt index 2824fbc101..cebf039967 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/storage/StorageConstraintPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/storage/StorageConstraintPlugin.kt @@ -6,7 +6,7 @@ import dagger.android.HasAndroidInjector import info.nightscout.androidaps.Constants import info.nightscout.androidaps.R import info.nightscout.androidaps.interfaces.Constraint -import info.nightscout.androidaps.interfaces.ConstraintsInterface +import info.nightscout.androidaps.interfaces.Constraints import info.nightscout.androidaps.interfaces.PluginBase import info.nightscout.androidaps.interfaces.PluginDescription import info.nightscout.androidaps.interfaces.PluginType @@ -33,7 +33,7 @@ open class StorageConstraintPlugin @Inject constructor( .showInList(false) .pluginName(R.string.storage), aapsLogger, resourceHelper, injector -), ConstraintsInterface { +), Constraints { override fun isClosedLoopAllowed(value: Constraint): Constraint { val diskFree = availableInternalMemorySize() diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/versionChecker/VersionCheckerPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/versionChecker/VersionCheckerPlugin.kt index 2eb4b34612..19e92e5015 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/versionChecker/VersionCheckerPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/versionChecker/VersionCheckerPlugin.kt @@ -4,7 +4,7 @@ import dagger.android.HasAndroidInjector import info.nightscout.androidaps.BuildConfig import info.nightscout.androidaps.R import info.nightscout.androidaps.interfaces.Constraint -import info.nightscout.androidaps.interfaces.ConstraintsInterface +import info.nightscout.androidaps.interfaces.Constraints import info.nightscout.androidaps.interfaces.PluginBase import info.nightscout.androidaps.interfaces.PluginDescription import info.nightscout.androidaps.interfaces.PluginType @@ -35,7 +35,7 @@ class VersionCheckerPlugin @Inject constructor( .showInList(false) .pluginName(R.string.versionChecker), aapsLogger, resourceHelper, injector -), ConstraintsInterface { +), Constraints { enum class GracePeriod(val warning: Long, val old: Long, val veryOld: Long) { RELEASE(30, 60, 90), diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/actions/ActionsFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/actions/ActionsFragment.kt index 6f735ba04b..1e18a5f54f 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/actions/ActionsFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/actions/ActionsFragment.kt @@ -11,17 +11,28 @@ import android.widget.LinearLayout import android.widget.TextView import androidx.core.content.ContextCompat import dagger.android.support.DaggerFragment -import info.nightscout.androidaps.Config import info.nightscout.androidaps.Constants import info.nightscout.androidaps.R import info.nightscout.androidaps.activities.ErrorHelperActivity import info.nightscout.androidaps.activities.TDDStatsActivity -import info.nightscout.androidaps.database.entities.UserEntry.* +import info.nightscout.androidaps.database.AppRepository +import info.nightscout.androidaps.database.ValueWrapper +import info.nightscout.androidaps.database.entities.UserEntry.Action +import info.nightscout.androidaps.database.entities.UserEntry.Sources import info.nightscout.androidaps.dialogs.* -import info.nightscout.androidaps.events.* +import info.nightscout.androidaps.events.EventCustomActionsChanged +import info.nightscout.androidaps.events.EventExtendedBolusChange +import info.nightscout.androidaps.events.EventInitializationChanged +import info.nightscout.androidaps.events.EventTempBasalChange +import info.nightscout.androidaps.events.EventTherapyEventChange +import info.nightscout.androidaps.extensions.toStringMedium +import info.nightscout.androidaps.extensions.toStringShort +import info.nightscout.androidaps.extensions.toVisibility import info.nightscout.androidaps.historyBrowser.HistoryBrowseActivity -import info.nightscout.androidaps.interfaces.ActivePluginProvider +import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.CommandQueueProvider +import info.nightscout.androidaps.interfaces.Config +import info.nightscout.androidaps.interfaces.IobCobCalculator import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.UserEntryLogger @@ -31,10 +42,10 @@ import info.nightscout.androidaps.plugins.general.overview.StatusLightHandler import info.nightscout.androidaps.plugins.pump.omnipod.eros.OmnipodErosPumpPlugin import info.nightscout.androidaps.queue.Callback import info.nightscout.androidaps.skins.SkinProvider +import info.nightscout.androidaps.utils.DateUtil 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.toVisibility import info.nightscout.androidaps.utils.protection.ProtectionCheck import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers @@ -52,18 +63,21 @@ class ActionsFragment : DaggerFragment() { @Inject lateinit var aapsSchedulers: AapsSchedulers @Inject lateinit var rxBus: RxBusWrapper @Inject lateinit var sp: SP + @Inject lateinit var dateUtil: DateUtil @Inject lateinit var profileFunction: ProfileFunction @Inject lateinit var ctx: Context @Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var statusLightHandler: StatusLightHandler @Inject lateinit var fabricPrivacy: FabricPrivacy - @Inject lateinit var activePlugin: ActivePluginProvider + @Inject lateinit var activePlugin: ActivePlugin + @Inject lateinit var iobCobCalculator: IobCobCalculator @Inject lateinit var commandQueue: CommandQueueProvider @Inject lateinit var buildHelper: BuildHelper @Inject lateinit var protectionCheck: ProtectionCheck @Inject lateinit var skinProvider: SkinProvider @Inject lateinit var config: Config @Inject lateinit var uel: UserEntryLogger + @Inject lateinit var repository: AppRepository private var disposable: CompositeDisposable = CompositeDisposable() @@ -154,8 +168,8 @@ class ActionsFragment : DaggerFragment() { } } extendedBolusCancel?.setOnClickListener { - if (activePlugin.activeTreatments.isInHistoryExtendedBolusInProgress) { - uel.log(Action.CANCEL_EXTENDED_BOLUS) + if (iobCobCalculator.getExtendedBolus(dateUtil.now()) != null) { + uel.log(Action.CANCEL_EXTENDED_BOLUS, Sources.Actions) commandQueue.cancelExtended(object : Callback() { override fun run() { if (!result.success) { @@ -169,8 +183,8 @@ class ActionsFragment : DaggerFragment() { TempBasalDialog().show(childFragmentManager, "Actions") } cancelTempBasal?.setOnClickListener { - if (activePlugin.activeTreatments.isTempBasalInProgress) { - uel.log(Action.CANCEL_TEMP_BASAL) + if (iobCobCalculator.getTempBasalIncludingConvertedExtended(dateUtil.now()) != null) { + uel.log(Action.CANCEL_TEMP_BASAL, Sources.Actions) commandQueue.cancelTempBasal(true, object : Callback() { override fun run() { if (!result.success) { @@ -219,10 +233,6 @@ class ActionsFragment : DaggerFragment() { .toObservable(EventInitializationChanged::class.java) .observeOn(aapsSchedulers.main) .subscribe({ updateGui() }, fabricPrivacy::logException) - disposable += rxBus - .toObservable(EventRefreshOverview::class.java) - .observeOn(aapsSchedulers.main) - .subscribe({ updateGui() }, fabricPrivacy::logException) disposable += rxBus .toObservable(EventExtendedBolusChange::class.java) .observeOn(aapsSchedulers.main) @@ -255,7 +265,7 @@ class ActionsFragment : DaggerFragment() { val pump = activePlugin.activePump profileSwitch?.visibility = ( - activePlugin.activeProfileInterface.profile != null && + activePlugin.activeProfileSource.profile != null && pump.pumpDescription.isSetBasalProfileCapable && pump.isInitialized() && !pump.isSuspended()).toVisibility() @@ -264,12 +274,12 @@ class ActionsFragment : DaggerFragment() { extendedBolus?.visibility = View.GONE extendedBolusCancel?.visibility = View.GONE } else { - val activeExtendedBolus = activePlugin.activeTreatments.getExtendedBolusFromHistory(System.currentTimeMillis()) - if (activeExtendedBolus != null) { + val activeExtendedBolus = repository.getExtendedBolusActiveAt(dateUtil.now()).blockingGet() + if (activeExtendedBolus is ValueWrapper.Existing) { extendedBolus?.visibility = View.GONE extendedBolusCancel?.visibility = View.VISIBLE @Suppress("SetTextI18n") - extendedBolusCancel?.text = resourceHelper.gs(R.string.cancel) + " " + activeExtendedBolus.toStringMedium() + extendedBolusCancel?.text = resourceHelper.gs(R.string.cancel) + " " + activeExtendedBolus.value.toStringMedium(dateUtil) } else { extendedBolus?.visibility = View.VISIBLE extendedBolusCancel?.visibility = View.GONE @@ -280,7 +290,7 @@ class ActionsFragment : DaggerFragment() { setTempBasal?.visibility = View.GONE cancelTempBasal?.visibility = View.GONE } else { - val activeTemp = activePlugin.activeTreatments.getTempBasalFromHistory(System.currentTimeMillis()) + val activeTemp = iobCobCalculator.getTempBasalIncludingConvertedExtended(System.currentTimeMillis()) if (activeTemp != null) { setTempBasal?.visibility = View.GONE cancelTempBasal?.visibility = View.VISIBLE diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/actions/ActionsPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/actions/ActionsPlugin.kt index 9b4e4478b7..3fadf5be53 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/actions/ActionsPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/actions/ActionsPlugin.kt @@ -1,7 +1,7 @@ package info.nightscout.androidaps.plugins.general.actions import dagger.android.HasAndroidInjector -import info.nightscout.androidaps.Config +import info.nightscout.androidaps.interfaces.Config import info.nightscout.androidaps.R import info.nightscout.androidaps.interfaces.PluginBase import info.nightscout.androidaps.interfaces.PluginDescription diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/dataBroadcaster/DataBroadcastPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/dataBroadcaster/DataBroadcastPlugin.kt index 4ff7ce4d14..3b5b11c37d 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/dataBroadcaster/DataBroadcastPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/dataBroadcaster/DataBroadcastPlugin.kt @@ -5,15 +5,10 @@ import android.content.Intent import android.content.pm.ResolveInfo import android.os.Bundle import dagger.android.HasAndroidInjector -import info.nightscout.androidaps.Config +import info.nightscout.androidaps.interfaces.Config import info.nightscout.androidaps.R -import info.nightscout.androidaps.data.IobTotal import info.nightscout.androidaps.events.* -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.* import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.plugins.aps.events.EventOpenAPSUpdateGui @@ -22,11 +17,13 @@ import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.general.nsclient.data.NSDeviceStatus import info.nightscout.androidaps.plugins.general.overview.events.EventOverviewBolusProgress import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatusProvider -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin import info.nightscout.androidaps.receivers.ReceiverStatusStore import info.nightscout.androidaps.services.Intents +import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DefaultValueHelper import info.nightscout.androidaps.utils.FabricPrivacy +import info.nightscout.androidaps.extensions.durationInMinutes +import info.nightscout.androidaps.extensions.toStringFull import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers import io.reactivex.disposables.CompositeDisposable @@ -40,14 +37,15 @@ class DataBroadcastPlugin @Inject constructor( resourceHelper: ResourceHelper, private val aapsSchedulers: AapsSchedulers, private val context: Context, + private val dateUtil: DateUtil, private val fabricPrivacy: FabricPrivacy, private val rxBus: RxBusWrapper, - private val iobCobCalculatorPlugin: IobCobCalculatorPlugin, + private val iobCobCalculator: IobCobCalculator, private val profileFunction: ProfileFunction, private val defaultValueHelper: DefaultValueHelper, private val nsDeviceStatus: NSDeviceStatus, private val loopPlugin: LoopPlugin, - private val activePlugin: ActivePluginProvider, + private val activePlugin: ActivePlugin, private var receiverStatusStore: ReceiverStatusStore, private val config: Config, private val glucoseStatusProvider: GlucoseStatusProvider @@ -121,12 +119,12 @@ class DataBroadcastPlugin @Inject constructor( } private fun bgStatus(bundle: Bundle) { - val lastBG = iobCobCalculatorPlugin.lastBg() ?: return + val lastBG = iobCobCalculator.ads.lastBg() ?: return val glucoseStatus = glucoseStatusProvider.glucoseStatusData ?: return bundle.putDouble("glucoseMgdl", lastBG.value) // last BG in mgdl bundle.putLong("glucoseTimeStamp", lastBG.timestamp) // timestamp - bundle.putString("units", profileFunction.getUnits()) // units used in AAPS "mg/dl" or "mmol" + bundle.putString("units", profileFunction.getUnits().asText) // 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.shortAvgDelta) // average bg delta @@ -136,15 +134,13 @@ class DataBroadcastPlugin @Inject constructor( private fun iobCob(bundle: Bundle) { profileFunction.getProfile() ?: return - activePlugin.activeTreatments.updateTotalIOBTreatments() - val bolusIob: IobTotal = activePlugin.activeTreatments.lastCalculationTreatments.round() - activePlugin.activeTreatments.updateTotalIOBTempBasals() - val basalIob: IobTotal = activePlugin.activeTreatments.lastCalculationTempBasals.round() + val bolusIob = iobCobCalculator.calculateIobFromBolus().round() + val basalIob = iobCobCalculator.calculateIobFromTempBasalsIncludingConvertedExtended().round() bundle.putDouble("bolusIob", bolusIob.iob) bundle.putDouble("basalIob", basalIob.basaliob) bundle.putDouble("iob", bolusIob.iob + basalIob.basaliob) // total IOB - val cob = iobCobCalculatorPlugin.getCobInfo(false, "broadcast") + val cob = iobCobCalculator.getCobInfo(false, "broadcast") bundle.putDouble("cob", cob.displayCob ?: -1.0) // COB [g] or -1 if N/A bundle.putDouble("futureCarbs", cob.futureCarbs) // future scheduled carbs } @@ -179,14 +175,14 @@ class DataBroadcastPlugin @Inject constructor( val now = System.currentTimeMillis() val profile = profileFunction.getProfile() ?: return bundle.putLong("basalTimeStamp", now) - bundle.putDouble("baseBasal", profile.basal) + bundle.putDouble("baseBasal", profile.getBasal()) bundle.putString("profile", profileFunction.getProfileName()) - activePlugin.activeTreatments.getTempBasalFromHistory(now)?.let { - bundle.putLong("tempBasalStart", it.date) - bundle.putInt("tempBasalDurationInMinutes", it.durationInMinutes) - if (it.isAbsolute) bundle.putDouble("tempBasalAbsolute", it.absoluteRate) // U/h for absolute TBR - else bundle.putInt("tempBasalPercent", it.percentRate) // % for percent type TBR - bundle.putString("tempBasalString", it.toStringFull()) // user friendly string + iobCobCalculator.getTempBasalIncludingConvertedExtended(now)?.let { + bundle.putLong("tempBasalStart", it.timestamp) + bundle.putLong("tempBasalDurationInMinutes", it.durationInMinutes) + if (it.isAbsolute) bundle.putDouble("tempBasalAbsolute", it.rate) // U/h for absolute TBR + else bundle.putInt("tempBasalPercent", it.rate.toInt()) // % for percent type TBR + bundle.putString("tempBasalString", it.toStringFull(profile, dateUtil)) // user friendly string } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/food/FoodFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/food/FoodFragment.kt index ceb0855819..4d8105a655 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/food/FoodFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/food/FoodFragment.kt @@ -17,6 +17,8 @@ import info.nightscout.androidaps.R import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.entities.Food import info.nightscout.androidaps.database.entities.UserEntry.Action +import info.nightscout.androidaps.database.entities.UserEntry.Sources +import info.nightscout.androidaps.database.entities.ValueWithUnit import info.nightscout.androidaps.database.transactions.InvalidateFoodTransaction import info.nightscout.androidaps.databinding.FoodFragmentBinding import info.nightscout.androidaps.databinding.FoodItemBinding @@ -25,11 +27,10 @@ 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.nsclient.NSUpload import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.alertDialogs.OKDialog -import info.nightscout.androidaps.utils.extensions.toVisibility +import info.nightscout.androidaps.extensions.toVisibility import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers import io.reactivex.Completable @@ -48,13 +49,12 @@ class FoodFragment : DaggerFragment() { @Inject lateinit var aapsLogger: AAPSLogger @Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var fabricPrivacy: FabricPrivacy - @Inject lateinit var nsUpload: NSUpload @Inject lateinit var repository: AppRepository @Inject lateinit var uel: UserEntryLogger private val disposable = CompositeDisposable() - private lateinit var unfiltered: List - private lateinit var filtered: MutableList + private var unfiltered: List = arrayListOf() + private var filtered: MutableList = arrayListOf() private var _binding: FoodFragmentBinding? = null @@ -76,7 +76,8 @@ class FoodFragment : DaggerFragment() { binding.refreshFromNightscout.setOnClickListener { context?.let { context -> OKDialog.showConfirmation(context, resourceHelper.gs(R.string.refresheventsfromnightscout) + " ?", { - uel.log(Action.FOOD_FROM_NS) + uel.log(Action.FOOD, Sources.Food, resourceHelper.gs(R.string.refresheventsfromnightscout), + ValueWithUnit.SimpleString(resourceHelper.gsNotLocalised(R.string.refresheventsfromnightscout))) disposable += Completable.fromAction { repository.deleteAllFoods() } .subscribeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.main) @@ -200,8 +201,10 @@ class FoodFragment : DaggerFragment() { private fun filterData() { val textFilter = binding.filter.text.toString() - val categoryFilter = binding.category.selectedItem?.toString() ?: resourceHelper.gs(R.string.none) - val subcategoryFilter = binding.subcategory.selectedItem?.toString() ?: resourceHelper.gs(R.string.none) + val categoryFilter = binding.category.selectedItem?.toString() + ?: resourceHelper.gs(R.string.none) + val subcategoryFilter = binding.subcategory.selectedItem?.toString() + ?: resourceHelper.gs(R.string.none) val newFiltered = ArrayList() for (f in unfiltered) { if (f.category == null || f.subCategory == null) continue @@ -250,16 +253,12 @@ class FoodFragment : DaggerFragment() { val food = v.tag as Food activity?.let { activity -> OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.removerecord) + "\n" + food.name, { - uel.log(Action.FOOD_REMOVED, food.name) + uel.log(Action.FOOD_REMOVED, Sources.Food, food.name) disposable += repository.runTransactionForResult(InvalidateFoodTransaction(food.id)) - .subscribe({ - val id = food.interfaceIDs.nightscoutId - if (NSUpload.isIdValid(id)) nsUpload.removeFoodFromNS(id) - // no create at the moment - // else uploadQueue.removeID("dbAdd", food.timestamp.toString()) - }, { - aapsLogger.error(LTag.BGSOURCE, "Error while invalidating food", it) - }) + .subscribe( + { aapsLogger.error(LTag.DATABASE, "Invalidated food $it") }, + { aapsLogger.error(LTag.DATABASE, "Error while invalidating food", it) } + ) }, null) } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/food/FoodPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/food/FoodPlugin.kt index f0f78a8577..7b9de28680 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/food/FoodPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/food/FoodPlugin.kt @@ -3,11 +3,13 @@ package info.nightscout.androidaps.plugins.general.food import android.content.Context import androidx.work.Worker import androidx.work.WorkerParameters +import androidx.work.workDataOf import dagger.android.HasAndroidInjector import info.nightscout.androidaps.R import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.entities.Food -import info.nightscout.androidaps.database.transactions.SyncFoodTransaction +import info.nightscout.androidaps.database.transactions.SyncNsFoodTransaction +import info.nightscout.androidaps.extensions.foodFromJson import info.nightscout.androidaps.interfaces.PluginBase import info.nightscout.androidaps.interfaces.PluginDescription import info.nightscout.androidaps.interfaces.PluginType @@ -15,10 +17,8 @@ import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.receivers.DataWorker import info.nightscout.androidaps.utils.JsonHelper -import info.nightscout.androidaps.utils.extensions.foodFromJson import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.sharedPreferences.SP -import org.json.JSONArray import org.json.JSONObject import javax.inject.Inject import javax.inject.Singleton @@ -56,8 +56,8 @@ class FoodPlugin @Inject constructor( override fun doWork(): Result { val foods = dataWorker.pickupJSONArray(inputData.getLong(DataWorker.STORE_KEY, -1)) - ?: return Result.failure() - aapsLogger.debug(LTag.DATAFOOD, "Received Food Data: $foods") + ?: return Result.failure(workDataOf("Error" to "missing input data")) + aapsLogger.debug(LTag.DATABASE, "Received Food Data: $foods") var ret = Result.success() @@ -75,34 +75,34 @@ class FoodPlugin @Inject constructor( isValid = false ).also { it.interfaceIDs.nightscoutId = JsonHelper.safeGetString(jsonFood, "_id") } - repository.runTransactionForResult(SyncFoodTransaction(delFood)) + repository.runTransactionForResult(SyncNsFoodTransaction(delFood, true)) .doOnError { - aapsLogger.error(LTag.DATAFOOD, "Error while removing food", it) - ret = Result.failure() + aapsLogger.error(LTag.DATABASE, "Error while removing food", it) + ret = Result.failure(workDataOf("Error" to it.toString())) } .blockingGet() .also { - it.invalidated.forEach { f -> aapsLogger.debug(LTag.DATAFOOD, "Invalidated food ${f.interfaceIDs.nightscoutId}") } + it.invalidated.forEach { f -> aapsLogger.debug(LTag.DATABASE, "Invalidated food ${f.interfaceIDs.nightscoutId}") } } } else -> { val food = foodFromJson(jsonFood) if (food != null) { - repository.runTransactionForResult(SyncFoodTransaction(food)) + repository.runTransactionForResult(SyncNsFoodTransaction(food, false)) .doOnError { - aapsLogger.error(LTag.DATAFOOD, "Error while adding/updating food", it) - ret = Result.failure() + aapsLogger.error(LTag.DATABASE, "Error while adding/updating food", it) + ret = Result.failure(workDataOf("Error" to it.toString())) } .blockingGet() .also { result -> - result.inserted.forEach { aapsLogger.debug(LTag.DATAFOOD, "Inserted food $it") } - result.updated.forEach { aapsLogger.debug(LTag.DATAFOOD, "Updated food $it") } - result.invalidated.forEach { aapsLogger.debug(LTag.DATAFOOD, "Invalidated food $it") } + result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted food $it") } + result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated food $it") } + result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated food $it") } } } else { - aapsLogger.error(LTag.DATAFOOD, "Error parsing food", jsonFood.toString()) - ret = Result.failure() + aapsLogger.error(LTag.DATABASE, "Error parsing food", jsonFood.toString()) + ret = Result.failure(workDataOf("Error" to "Error parsing food")) } } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/ImportExportPrefs.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/ImportExportPrefsImpl.kt similarity index 96% rename from app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/ImportExportPrefs.kt rename to app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/ImportExportPrefsImpl.kt index 9acdf53e8a..bf83f2c5bc 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/ImportExportPrefs.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/ImportExportPrefsImpl.kt @@ -16,10 +16,11 @@ import info.nightscout.androidaps.R import info.nightscout.androidaps.activities.DaggerAppCompatActivityWithResult import info.nightscout.androidaps.activities.PreferencesActivity import info.nightscout.androidaps.database.entities.UserEntry -import info.nightscout.androidaps.database.entities.UserEntry.* +import info.nightscout.androidaps.database.entities.UserEntry.Action +import info.nightscout.androidaps.database.entities.UserEntry.Sources import info.nightscout.androidaps.events.EventAppExit -import info.nightscout.androidaps.interfaces.ConfigInterface -import info.nightscout.androidaps.interfaces.ImportExportPrefsInterface +import info.nightscout.androidaps.interfaces.Config +import info.nightscout.androidaps.interfaces.ImportExportPrefs import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.logging.UserEntryLogger @@ -40,7 +41,6 @@ import io.reactivex.Single import java.io.File import java.io.FileNotFoundException import java.io.IOException -import java.util.* import javax.inject.Inject import javax.inject.Singleton import kotlin.system.exitProcess @@ -50,20 +50,21 @@ import kotlin.system.exitProcess */ @Singleton -class ImportExportPrefs @Inject constructor( +class ImportExportPrefsImpl @Inject constructor( private var log: AAPSLogger, private val resourceHelper: ResourceHelper, private val sp: SP, private val buildHelper: BuildHelper, private val rxBus: RxBusWrapper, private val passwordCheck: PasswordCheck, - private val config: ConfigInterface, + private val config: Config, private val androidPermission: AndroidPermission, private val classicPrefsFormat: ClassicPrefsFormat, private val encryptedPrefsFormat: EncryptedPrefsFormat, private val prefFileList: PrefFileListProvider, - private val uel: UserEntryLogger -) : ImportExportPrefsInterface { + private val uel: UserEntryLogger, + private val dateUtil: DateUtil +) : ImportExportPrefs { override fun prefsFileExists(): Boolean { return prefFileList.listPreferenceFiles().size > 0 @@ -94,7 +95,7 @@ class ImportExportPrefs @Inject constructor( val metadata: MutableMap = mutableMapOf() metadata[PrefsMetadataKey.DEVICE_NAME] = PrefMetadata(detectUserName(context), PrefsStatus.OK) - metadata[PrefsMetadataKey.CREATED_AT] = PrefMetadata(DateUtil.toISOString(Date()), PrefsStatus.OK) + metadata[PrefsMetadataKey.CREATED_AT] = PrefMetadata(dateUtil.toISOString(dateUtil.now()), PrefsStatus.OK) metadata[PrefsMetadataKey.AAPS_VERSION] = PrefMetadata(BuildConfig.VERSION_NAME, PrefsStatus.OK) metadata[PrefsMetadataKey.AAPS_FLAVOUR] = PrefMetadata(BuildConfig.FLAVOR, PrefsStatus.OK) metadata[PrefsMetadataKey.DEVICE_MODEL] = PrefMetadata(config.currentDeviceModelString, PrefsStatus.OK) @@ -348,7 +349,7 @@ class ImportExportPrefs @Inject constructor( private fun restartAppAfterImport(context: Context) { sp.putBoolean(R.string.key_setupwizard_processed, true) OKDialog.show(context, resourceHelper.gs(R.string.setting_imported), resourceHelper.gs(R.string.restartingapp)) { - uel.log(Action.IMPORT_SETTINGS) + uel.log(Action.IMPORT_SETTINGS, Sources.Maintenance) log.debug(LTag.CORE, "Exiting") rxBus.send(EventAppExit()) if (context is AppCompatActivity) { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/MaintenanceFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/MaintenanceFragment.kt index 32b3001911..36df705147 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/MaintenanceFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/MaintenanceFragment.kt @@ -7,17 +7,21 @@ import android.view.View import android.view.ViewGroup import dagger.android.support.DaggerFragment import info.nightscout.androidaps.R +import info.nightscout.androidaps.dana.database.DanaHistoryDatabase import info.nightscout.androidaps.database.AppRepository -import info.nightscout.androidaps.database.entities.UserEntry.* +import info.nightscout.androidaps.database.entities.UserEntry.Action +import info.nightscout.androidaps.database.entities.UserEntry.Sources import info.nightscout.androidaps.databinding.MaintenanceFragmentBinding import info.nightscout.androidaps.events.EventNewBG -import info.nightscout.androidaps.interfaces.DatabaseHelperInterface -import info.nightscout.androidaps.interfaces.ImportExportPrefsInterface +import info.nightscout.androidaps.insight.database.InsightDatabase +import info.nightscout.androidaps.interfaces.DataSyncSelector +import info.nightscout.androidaps.interfaces.ImportExportPrefs +import info.nightscout.androidaps.interfaces.PumpSync 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.maintenance.activities.LogSettingActivity -import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventNewHistoryData import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers @@ -32,12 +36,14 @@ class MaintenanceFragment : DaggerFragment() { @Inject lateinit var maintenancePlugin: MaintenancePlugin @Inject lateinit var rxBus: RxBusWrapper @Inject lateinit var resourceHelper: ResourceHelper - @Inject lateinit var treatmentsPlugin: TreatmentsPlugin - @Inject lateinit var importExportPrefs: ImportExportPrefsInterface + @Inject lateinit var importExportPrefs: ImportExportPrefs @Inject lateinit var aapsSchedulers: AapsSchedulers @Inject lateinit var repository: AppRepository - @Inject lateinit var databaseHelper: DatabaseHelperInterface + @Inject lateinit var danaHistoryDatabase: DanaHistoryDatabase + @Inject lateinit var insightDatabase: InsightDatabase @Inject lateinit var uel: UserEntryLogger + @Inject lateinit var dataSyncSelector: DataSyncSelector + @Inject lateinit var pumpSync: PumpSync private val compositeDisposable = CompositeDisposable() @@ -56,40 +62,43 @@ class MaintenanceFragment : DaggerFragment() { super.onViewCreated(view, savedInstanceState) binding.logSend.setOnClickListener { maintenancePlugin.sendLogs() } binding.logDelete.setOnClickListener { - uel.log(Action.DELETE_LOGS) + uel.log(Action.DELETE_LOGS, Sources.Maintenance) maintenancePlugin.deleteLogs() } binding.navResetdb.setOnClickListener { activity?.let { activity -> OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.maintenance), resourceHelper.gs(R.string.reset_db_confirm), Runnable { - uel.log(Action.RESET_DATABASES) compositeDisposable.add( fromAction { - databaseHelper.resetDatabases() - // should be handled by Plugin-Interface and - // additional service interface and plugin registry - treatmentsPlugin.service.resetTreatments() repository.clearDatabases() + danaHistoryDatabase.clearAllTables() + insightDatabase.clearAllTables() + dataSyncSelector.resetToNextFullSync() + pumpSync.connectNewPump() } .subscribeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.main) .subscribeBy( onError = { aapsLogger.error("Error clearing databases", it) }, - onComplete = { rxBus.send(EventNewBG(null)) } + onComplete = { + rxBus.send(EventNewBG(null)) + rxBus.send(EventNewHistoryData(0, true)) + } ) ) + uel.log(Action.RESET_DATABASES, Sources.Maintenance) }) } } binding.navExport.setOnClickListener { - uel.log(Action.EXPORT_SETTINGS) + uel.log(Action.EXPORT_SETTINGS, Sources.Maintenance) // start activity for checking permissions... importExportPrefs.verifyStoragePermissions(this) { importExportPrefs.exportSharedPreferences(this) } } binding.navImport.setOnClickListener { - uel.log(Action.IMPORT_SETTINGS) + uel.log(Action.IMPORT_SETTINGS, Sources.Maintenance) // start activity for checking permissions... importExportPrefs.verifyStoragePermissions(this) { importExportPrefs.importSharedPreferences(this) @@ -99,7 +108,7 @@ class MaintenanceFragment : DaggerFragment() { binding.exportCsv.setOnClickListener { activity?.let { activity -> OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.ue_export_to_csv) + "?") { - uel.log(Action.EXPORT_CSV) + uel.log(Action.EXPORT_CSV, Sources.Maintenance) importExportPrefs.exportUserEntriesCsv(activity, repository.getAllUserEntries()) } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/MaintenancePlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/MaintenancePlugin.kt index a193b73030..20bdfa269a 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/MaintenancePlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/MaintenancePlugin.kt @@ -8,7 +8,7 @@ import androidx.preference.PreferenceFragmentCompat import androidx.preference.SwitchPreference import dagger.android.HasAndroidInjector import info.nightscout.androidaps.BuildConfig -import info.nightscout.androidaps.Config +import info.nightscout.androidaps.interfaces.Config import info.nightscout.androidaps.R import info.nightscout.androidaps.interfaces.PluginBase import info.nightscout.androidaps.interfaces.PluginDescription diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/DataSyncSelectorImplementation.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/DataSyncSelectorImplementation.kt new file mode 100644 index 0000000000..debcb920a7 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/DataSyncSelectorImplementation.kt @@ -0,0 +1,569 @@ +package info.nightscout.androidaps.plugins.general.nsclient + +import info.nightscout.androidaps.R +import info.nightscout.androidaps.database.AppRepository +import info.nightscout.androidaps.database.ValueWrapper +import info.nightscout.androidaps.database.entities.* +import info.nightscout.androidaps.extensions.toJson +import info.nightscout.androidaps.interfaces.ActivePlugin +import info.nightscout.androidaps.interfaces.DataSyncSelector +import info.nightscout.androidaps.interfaces.ProfileFunction +import info.nightscout.androidaps.logging.AAPSLogger +import info.nightscout.androidaps.logging.LTag +import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.sharedPreferences.SP +import javax.inject.Inject + +class DataSyncSelectorImplementation @Inject constructor( + private val sp: SP, + private val aapsLogger: AAPSLogger, + private val dateUtil: DateUtil, + private val profileFunction: ProfileFunction, + private val nsClientPlugin: NSClientPlugin, + private val activePlugin: ActivePlugin, + private val appRepository: AppRepository, + private val localProfilePlugin: LocalProfilePlugin +) : DataSyncSelector { + + override fun doUpload() { + if (sp.getBoolean(R.string.key_ns_upload, true)) { + processChangedBolusesCompat() + processChangedCarbsCompat() + processChangedBolusCalculatorResultsCompat() + processChangedTemporaryBasalsCompat() + processChangedExtendedBolusesCompat() + processChangedProfileSwitchesCompat() + processChangedGlucoseValuesCompat() + processChangedTempTargetsCompat() + processChangedFoodsCompat() + processChangedTherapyEventsCompat() + processChangedDeviceStatusesCompat() + processChangedProfileStore() + } + } + + override fun resetToNextFullSync() { + sp.remove(R.string.key_ns_temporary_target_last_synced_id) + sp.remove(R.string.key_ns_glucose_value_last_synced_id) + sp.remove(R.string.key_ns_food_last_synced_id) + sp.remove(R.string.key_ns_bolus_last_synced_id) + sp.remove(R.string.key_ns_carbs_last_synced_id) + sp.remove(R.string.key_ns_bolus_calculator_result_last_synced_id) + sp.remove(R.string.key_ns_device_status_last_synced_id) + sp.remove(R.string.key_ns_temporary_basal_last_synced_id) + sp.remove(R.string.key_ns_extended_bolus_last_synced_id) + sp.remove(R.string.key_ns_therapy_event_last_synced_id) + sp.remove(R.string.key_ns_profile_switch_last_synced_id) + sp.remove(R.string.key_ns_profile_store_last_synced_timestamp) + } + + override fun confirmLastBolusIdIfGreater(lastSynced: Long) { + if (lastSynced > sp.getLong(R.string.key_ns_bolus_last_synced_id, 0)) { + aapsLogger.debug(LTag.NSCLIENT, "Setting Bolus data sync from $lastSynced") + sp.putLong(R.string.key_ns_bolus_last_synced_id, lastSynced) + } + } + + // Prepared for v3 (returns all modified after) + override fun changedBoluses(): List { + val startId = sp.getLong(R.string.key_ns_bolus_last_synced_id, 0) + return appRepository.getModifiedBolusesDataFromId(startId) + .blockingGet() + .filter { it.type != Bolus.Type.PRIMING } + .also { + aapsLogger.debug(LTag.NSCLIENT, "Loading Bolus data for sync from $startId. Records ${it.size}") + } + } + + private var lastBolusId = -1L + private var lastBolusTime = -1L + override fun processChangedBolusesCompat(): Boolean { + val lastDbIdWrapped = appRepository.getLastBolusIdWrapped().blockingGet() + val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L + var startId = sp.getLong(R.string.key_ns_bolus_last_synced_id, 0) + if (startId > lastDbId) { + sp.putLong(R.string.key_ns_bolus_last_synced_id, 0) + startId = 0 + } + if (startId == lastBolusId && dateUtil.now() - lastBolusTime < 5000) return false + lastBolusId = startId + lastBolusTime = dateUtil.now() + appRepository.getNextSyncElementBolus(startId).blockingGet()?.let { bolus -> + aapsLogger.info(LTag.DATABASE, "Loading Bolus data Start: $startId ID: ${bolus.first.id} HistoryID: ${bolus.second} ") + when { + // without nsId = create new + bolus.first.interfaceIDs.nightscoutId == null -> + nsClientPlugin.nsClientService?.dbAdd("treatments", bolus.first.toJson(dateUtil), DataSyncSelector.PairBolus(bolus.first, bolus.second), "$startId/$lastDbId") + // with nsId = update + bolus.first.interfaceIDs.nightscoutId != null -> + nsClientPlugin.nsClientService?.dbUpdate("treatments", bolus.first.interfaceIDs.nightscoutId, bolus.first.toJson(dateUtil), DataSyncSelector.PairBolus(bolus.first, bolus.second), "$startId/$lastDbId") + } + return true + } + return false + } + + override fun confirmLastCarbsIdIfGreater(lastSynced: Long) { + if (lastSynced > sp.getLong(R.string.key_ns_carbs_last_synced_id, 0)) { + aapsLogger.debug(LTag.NSCLIENT, "Setting Carbs data sync from $lastSynced") + sp.putLong(R.string.key_ns_carbs_last_synced_id, lastSynced) + } + } + + // Prepared for v3 (returns all modified after) + override fun changedCarbs(): List { + val startId = sp.getLong(R.string.key_ns_carbs_last_synced_id, 0) + return appRepository.getModifiedCarbsDataFromId(startId).blockingGet().also { + aapsLogger.debug(LTag.NSCLIENT, "Loading Carbs data for sync from $startId. Records ${it.size}") + } + } + + private var lastCarbsId = -1L + private var lastCarbsTime = -1L + override fun processChangedCarbsCompat(): Boolean { + val lastDbIdWrapped = appRepository.getLastCarbsIdWrapped().blockingGet() + val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L + var startId = sp.getLong(R.string.key_ns_carbs_last_synced_id, 0) + if (startId > lastDbId) { + sp.putLong(R.string.key_ns_carbs_last_synced_id, 0) + startId = 0 + } + if (startId == lastCarbsId && dateUtil.now() - lastCarbsTime < 5000) return false + lastCarbsId = startId + lastCarbsTime = dateUtil.now() + appRepository.getNextSyncElementCarbs(startId).blockingGet()?.let { carb -> + aapsLogger.info(LTag.DATABASE, "Loading Carbs data Start: $startId ID: ${carb.first.id} HistoryID: ${carb.second} ") + when { + // without nsId = create new + carb.first.interfaceIDs.nightscoutId == null -> + nsClientPlugin.nsClientService?.dbAdd("treatments", carb.first.toJson(dateUtil), DataSyncSelector.PairCarbs(carb.first, carb.second), "$startId/$lastDbId") + // with nsId = update + carb.first.interfaceIDs.nightscoutId != null -> + nsClientPlugin.nsClientService?.dbUpdate("treatments", carb.first.interfaceIDs.nightscoutId, carb.first.toJson(dateUtil), DataSyncSelector.PairCarbs(carb.first, carb.second), "$startId/$lastDbId") + } + return true + } + return false + } + + override fun confirmLastBolusCalculatorResultsIdIfGreater(lastSynced: Long) { + if (lastSynced > sp.getLong(R.string.key_ns_bolus_calculator_result_last_synced_id, 0)) { + aapsLogger.debug(LTag.NSCLIENT, "Setting BolusCalculatorResult data sync from $lastSynced") + sp.putLong(R.string.key_ns_bolus_calculator_result_last_synced_id, lastSynced) + } + } + + // Prepared for v3 (returns all modified after) + override fun changedBolusCalculatorResults(): List { + val startId = sp.getLong(R.string.key_ns_bolus_calculator_result_last_synced_id, 0) + return appRepository.getModifiedBolusCalculatorResultsDataFromId(startId).blockingGet().also { + aapsLogger.debug(LTag.NSCLIENT, "Loading BolusCalculatorResult data for sync from $startId. Records ${it.size}") + } + } + + private var lastBcrId = -1L + private var lastBcrTime = -1L + override fun processChangedBolusCalculatorResultsCompat(): Boolean { + val lastDbIdWrapped = appRepository.getLastBolusCalculatorResultIdWrapped().blockingGet() + val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L + var startId = sp.getLong(R.string.key_ns_bolus_calculator_result_last_synced_id, 0) + if (startId > lastDbId) { + sp.putLong(R.string.key_ns_bolus_calculator_result_last_synced_id, 0) + startId = 0 + } + if (startId == lastBcrId && dateUtil.now() - lastBcrTime < 5000) return false + lastBcrId = startId + lastBcrTime = dateUtil.now() + appRepository.getNextSyncElementBolusCalculatorResult(startId).blockingGet()?.let { bolusCalculatorResult -> + aapsLogger.info(LTag.DATABASE, "Loading BolusCalculatorResult data Start: $startId ID: ${bolusCalculatorResult.first.id} HistoryID: ${bolusCalculatorResult.second} ") + when { + // without nsId = create new + bolusCalculatorResult.first.interfaceIDs.nightscoutId == null -> + nsClientPlugin.nsClientService?.dbAdd("treatments", bolusCalculatorResult.first.toJson(dateUtil), DataSyncSelector.PairBolusCalculatorResult(bolusCalculatorResult.first, bolusCalculatorResult.second), "$startId/$lastDbId") + // with nsId = update + bolusCalculatorResult.first.interfaceIDs.nightscoutId != null -> + nsClientPlugin.nsClientService?.dbUpdate("treatments", bolusCalculatorResult.first.interfaceIDs.nightscoutId, bolusCalculatorResult.first.toJson(dateUtil), DataSyncSelector.PairBolusCalculatorResult(bolusCalculatorResult.first, bolusCalculatorResult.second), "$startId/$lastDbId") + } + return true + } + return false + } + + override fun confirmLastTempTargetsIdIfGreater(lastSynced: Long) { + if (lastSynced > sp.getLong(R.string.key_ns_temporary_target_last_synced_id, 0)) { + aapsLogger.debug(LTag.NSCLIENT, "Setting TemporaryTarget data sync from $lastSynced") + sp.putLong(R.string.key_ns_temporary_target_last_synced_id, lastSynced) + } + } + + // Prepared for v3 (returns all modified after) + override fun changedTempTargets(): List { + val startId = sp.getLong(R.string.key_ns_temporary_target_last_synced_id, 0) + return appRepository.getModifiedTemporaryTargetsDataFromId(startId).blockingGet().also { + aapsLogger.debug(LTag.NSCLIENT, "Loading TemporaryTarget data for sync from $startId. Records ${it.size}") + } + } + + private var lastTtId = -1L + private var lastTtTime = -1L + override fun processChangedTempTargetsCompat(): Boolean { + val lastDbIdWrapped = appRepository.getLastTempTargetIdWrapped().blockingGet() + val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L + var startId = sp.getLong(R.string.key_ns_temporary_target_last_synced_id, 0) + if (startId > lastDbId) { + sp.putLong(R.string.key_ns_temporary_target_last_synced_id, 0) + startId = 0 + } + if (startId == lastTtId && dateUtil.now() - lastTtTime < 5000) return false + lastTtId = startId + lastTtTime = dateUtil.now() + appRepository.getNextSyncElementTemporaryTarget(startId).blockingGet()?.let { tt -> + aapsLogger.info(LTag.DATABASE, "Loading TemporaryTarget data Start: $startId ID: ${tt.first.id} HistoryID: ${tt.second} ") + when { + // without nsId = create new + tt.first.interfaceIDs.nightscoutId == null -> + nsClientPlugin.nsClientService?.dbAdd("treatments", tt.first.toJson(profileFunction.getUnits(), dateUtil), DataSyncSelector.PairTemporaryTarget(tt.first, tt.second), "$startId/$lastDbId") + // existing with nsId = update + tt.first.interfaceIDs.nightscoutId != null -> + nsClientPlugin.nsClientService?.dbUpdate("treatments", tt.first.interfaceIDs.nightscoutId, tt.first.toJson(profileFunction.getUnits(), dateUtil), DataSyncSelector.PairTemporaryTarget(tt.first, tt.second), "$startId/$lastDbId") + } + return true + } + return false + } + + override fun confirmLastFoodIdIfGreater(lastSynced: Long) { + if (lastSynced > sp.getLong(R.string.key_ns_food_last_synced_id, 0)) { + aapsLogger.debug(LTag.NSCLIENT, "Setting Food data sync from $lastSynced") + sp.putLong(R.string.key_ns_food_last_synced_id, lastSynced) + } + } + + // Prepared for v3 (returns all modified after) + override fun changedFoods(): List { + val startId = sp.getLong(R.string.key_ns_food_last_synced_id, 0) + return appRepository.getModifiedFoodDataFromId(startId).blockingGet().also { + aapsLogger.debug(LTag.NSCLIENT, "Loading Food data for sync from $startId. Records ${it.size}") + } + } + + private var lastFoodId = -1L + private var lastFoodTime = -1L + override fun processChangedFoodsCompat(): Boolean { + val lastDbIdWrapped = appRepository.getLastFoodIdWrapped().blockingGet() + val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L + var startId = sp.getLong(R.string.key_ns_food_last_synced_id, 0) + if (startId > lastDbId) { + sp.putLong(R.string.key_ns_food_last_synced_id, 0) + startId = 0 + } + if (startId == lastFoodId && dateUtil.now() - lastFoodTime < 5000) return false + lastFoodId = startId + lastFoodTime = dateUtil.now() + appRepository.getNextSyncElementFood(startId).blockingGet()?.let { food -> + aapsLogger.info(LTag.DATABASE, "Loading Food data Start: $startId ID: ${food.first.id} HistoryID: ${food.second} ") + when { + // without nsId = create new + food.first.interfaceIDs.nightscoutId == null -> + nsClientPlugin.nsClientService?.dbAdd("food", food.first.toJson(), DataSyncSelector.PairFood(food.first, food.second), "$startId/$lastDbId") + // with nsId = update + food.first.interfaceIDs.nightscoutId != null -> + nsClientPlugin.nsClientService?.dbUpdate("food", food.first.interfaceIDs.nightscoutId, food.first.toJson(), DataSyncSelector.PairFood(food.first, food.second), "$startId/$lastDbId") + } + return true + } + return false + } + + override fun confirmLastGlucoseValueIdIfGreater(lastSynced: Long) { + if (lastSynced > sp.getLong(R.string.key_ns_glucose_value_last_synced_id, 0)) { + aapsLogger.debug(LTag.NSCLIENT, "Setting GlucoseValue data sync from $lastSynced") + sp.putLong(R.string.key_ns_glucose_value_last_synced_id, lastSynced) + } + } + + // Prepared for v3 (returns all modified after) + override fun changedGlucoseValues(): List { + val startId = sp.getLong(R.string.key_ns_glucose_value_last_synced_id, 0) + return appRepository.getModifiedBgReadingsDataFromId(startId).blockingGet().also { + aapsLogger.debug(LTag.NSCLIENT, "Loading GlucoseValue data for sync from $startId . Records ${it.size}") + } + } + + private var lastGvId = -1L + private var lastGvTime = -1L + override fun processChangedGlucoseValuesCompat(): Boolean { + val lastDbIdWrapped = appRepository.getLastGlucoseValueIdWrapped().blockingGet() + val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L + var startId = sp.getLong(R.string.key_ns_glucose_value_last_synced_id, 0) + if (startId > lastDbId) { + sp.putLong(R.string.key_ns_glucose_value_last_synced_id, 0) + startId = 0 + } + if (startId == lastGvId && dateUtil.now() - lastGvTime < 5000) return false + lastGvId = startId + lastGvTime = dateUtil.now() + appRepository.getNextSyncElementGlucoseValue(startId).blockingGet()?.let { gv -> + aapsLogger.info(LTag.DATABASE, "Loading GlucoseValue data Start: $startId ID: ${gv.first.id} HistoryID: ${gv.second} ") + if (activePlugin.activeBgSource.shouldUploadToNs(gv.first)) { + when { + // without nsId = create new + gv.first.interfaceIDs.nightscoutId == null -> + nsClientPlugin.nsClientService?.dbAdd("entries", gv.first.toJson(dateUtil), DataSyncSelector.PairGlucoseValue(gv.first, gv.second), "$startId/$lastDbId") + // with nsId = update + gv.first.interfaceIDs.nightscoutId != null -> + nsClientPlugin.nsClientService?.dbUpdate("entries", gv.first.interfaceIDs.nightscoutId, gv.first.toJson(dateUtil), DataSyncSelector.PairGlucoseValue(gv.first, gv.second), "$startId/$lastDbId") + } + return true + } else { + confirmLastGlucoseValueIdIfGreater(gv.second) + lastGvId = -1 + processChangedGlucoseValuesCompat() + } + } + return false + } + + override fun confirmLastTherapyEventIdIfGreater(lastSynced: Long) { + if (lastSynced > sp.getLong(R.string.key_ns_therapy_event_last_synced_id, 0)) { + aapsLogger.debug(LTag.NSCLIENT, "Setting TherapyEvents data sync from $lastSynced") + sp.putLong(R.string.key_ns_therapy_event_last_synced_id, lastSynced) + } + } + + // Prepared for v3 (returns all modified after) + override fun changedTherapyEvents(): List { + val startId = sp.getLong(R.string.key_ns_therapy_event_last_synced_id, 0) + return appRepository.getModifiedTherapyEventDataFromId(startId).blockingGet().also { + aapsLogger.debug(LTag.NSCLIENT, "Loading TherapyEvents data for sync from $startId. Records ${it.size}") + } + } + + private var lastTeId = -1L + private var lastTeTime = -1L + override fun processChangedTherapyEventsCompat(): Boolean { + val lastDbIdWrapped = appRepository.getLastTherapyEventIdWrapped().blockingGet() + val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L + var startId = sp.getLong(R.string.key_ns_therapy_event_last_synced_id, 0) + if (startId > lastDbId) { + sp.putLong(R.string.key_ns_therapy_event_last_synced_id, 0) + startId = 0 + } + if (startId == lastTeId && dateUtil.now() - lastTeTime < 5000) return false + lastTeId = startId + lastTeTime = dateUtil.now() + appRepository.getNextSyncElementTherapyEvent(startId).blockingGet()?.let { te -> + aapsLogger.info(LTag.DATABASE, "Loading TherapyEvents data Start: $startId ID: ${te.first.id} HistoryID: ${te.second} ") + when { + // without nsId = create new + te.first.interfaceIDs.nightscoutId == null -> + nsClientPlugin.nsClientService?.dbAdd("treatments", te.first.toJson(), DataSyncSelector.PairTherapyEvent(te.first, te.second), "$startId/$lastDbId") + // nsId = update + te.first.interfaceIDs.nightscoutId != null -> + nsClientPlugin.nsClientService?.dbUpdate("treatments", te.first.interfaceIDs.nightscoutId, te.first.toJson(), DataSyncSelector.PairTherapyEvent(te.first, te.second), "$startId/$lastDbId") + } + return true + } + return false + } + + override fun confirmLastDeviceStatusIdIfGreater(lastSynced: Long) { + if (lastSynced > sp.getLong(R.string.key_ns_device_status_last_synced_id, 0)) { + aapsLogger.debug(LTag.NSCLIENT, "Setting DeviceStatus data sync from $lastSynced") + sp.putLong(R.string.key_ns_device_status_last_synced_id, lastSynced) + } + } + + override fun changedDeviceStatuses(): List { + val startId = sp.getLong(R.string.key_ns_device_status_last_synced_id, 0) + return appRepository.getModifiedDeviceStatusDataFromId(startId).blockingGet().also { + aapsLogger.debug(LTag.NSCLIENT, "Loading DeviceStatus data for sync from $startId. Records ${it.size}") + } + } + + private var lastDsId = -1L + private var lastDsTime = -1L + override fun processChangedDeviceStatusesCompat(): Boolean { + val lastDbIdWrapped = appRepository.getLastDeviceStatusIdWrapped().blockingGet() + val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L + var startId = sp.getLong(R.string.key_ns_device_status_last_synced_id, 0) + if (startId > lastDbId) { + sp.putLong(R.string.key_ns_device_status_last_synced_id, 0) + startId = 0 + } + if (startId == lastDsId && dateUtil.now() - lastDsTime < 5000) return false + lastDsId = startId + lastDsTime = dateUtil.now() + appRepository.getNextSyncElementDeviceStatus(startId).blockingGet()?.let { deviceStatus -> + aapsLogger.info(LTag.DATABASE, "Loading DeviceStatus data Start: $startId ID: ${deviceStatus.id}") + when { + // without nsId = create new + deviceStatus.interfaceIDs.nightscoutId == null -> + nsClientPlugin.nsClientService?.dbAdd("devicestatus", deviceStatus.toJson(dateUtil), deviceStatus, "$startId/$lastDbId") + // with nsId = ignore + deviceStatus.interfaceIDs.nightscoutId != null -> Any() + } + return true + } + return false + } + + override fun confirmLastTemporaryBasalIdIfGreater(lastSynced: Long) { + if (lastSynced > sp.getLong(R.string.key_ns_temporary_basal_last_synced_id, 0)) { + aapsLogger.debug(LTag.NSCLIENT, "Setting TemporaryBasal data sync from $lastSynced") + sp.putLong(R.string.key_ns_temporary_basal_last_synced_id, lastSynced) + } + } + + // Prepared for v3 (returns all modified after) + override fun changedTemporaryBasals(): List { + val startId = sp.getLong(R.string.key_ns_temporary_basal_last_synced_id, 0) + return appRepository.getModifiedTemporaryBasalDataFromId(startId).blockingGet().also { + aapsLogger.debug(LTag.NSCLIENT, "Loading TemporaryBasal data for sync from $startId. Records ${it.size}") + } + } + + private var lastTbrId = -1L + private var lastTbrTime = -1L + override fun processChangedTemporaryBasalsCompat(): Boolean { + val useAbsolute = sp.getBoolean(R.string.key_ns_sync_use_absolute, false) + val lastDbIdWrapped = appRepository.getLastTemporaryBasalIdWrapped().blockingGet() + val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L + var startId = sp.getLong(R.string.key_ns_temporary_basal_last_synced_id, 0) + if (startId > lastDbId) { + sp.putLong(R.string.key_ns_temporary_basal_last_synced_id, 0) + startId = 0 + } + if (startId == lastTbrId && dateUtil.now() - lastTbrTime < 5000) return false + lastTbrId = startId + lastTbrTime = dateUtil.now() + appRepository.getNextSyncElementTemporaryBasal(startId).blockingGet()?.let { tb -> + aapsLogger.info(LTag.DATABASE, "Loading TemporaryBasal data Start: $startId ID: ${tb.first.id} HistoryID: ${tb.second} ") + val profile = profileFunction.getProfile(tb.first.timestamp) + if (profile != null) { + when { + // without nsId = create new + tb.first.interfaceIDs.nightscoutId == null -> + nsClientPlugin.nsClientService?.dbAdd("treatments", tb.first.toJson(profile, dateUtil, useAbsolute), DataSyncSelector.PairTemporaryBasal(tb.first, tb.second), "$startId/$lastDbId") + // with nsId = update + tb.first.interfaceIDs.nightscoutId != null -> + nsClientPlugin.nsClientService?.dbUpdate("treatments", tb.first.interfaceIDs.nightscoutId, tb.first.toJson(profile, dateUtil, useAbsolute), DataSyncSelector.PairTemporaryBasal(tb.first, tb.second), "$startId/$lastDbId") + } + return true + } else { + confirmLastTemporaryBasalIdIfGreater(tb.second) + lastTbrId = -1 + processChangedTemporaryBasalsCompat() + } + } + return false + } + + override fun confirmLastExtendedBolusIdIfGreater(lastSynced: Long) { + if (lastSynced > sp.getLong(R.string.key_ns_extended_bolus_last_synced_id, 0)) { + aapsLogger.debug(LTag.NSCLIENT, "Setting ExtendedBolus data sync from $lastSynced") + sp.putLong(R.string.key_ns_extended_bolus_last_synced_id, lastSynced) + } + } + + // Prepared for v3 (returns all modified after) + override fun changedExtendedBoluses(): List { + val startId = sp.getLong(R.string.key_ns_extended_bolus_last_synced_id, 0) + return appRepository.getModifiedExtendedBolusDataFromId(startId).blockingGet().also { + aapsLogger.debug(LTag.NSCLIENT, "Loading ExtendedBolus data for sync from $startId. Records ${it.size}") + } + } + + private var lastEbId = -1L + private var lastEbTime = -1L + override fun processChangedExtendedBolusesCompat(): Boolean { + val useAbsolute = sp.getBoolean(R.string.key_ns_sync_use_absolute, false) + val lastDbIdWrapped = appRepository.getLastExtendedBolusIdWrapped().blockingGet() + val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L + var startId = sp.getLong(R.string.key_ns_extended_bolus_last_synced_id, 0) + if (startId > lastDbId) { + sp.putLong(R.string.key_ns_extended_bolus_last_synced_id, 0) + startId = 0 + } + if (startId == lastEbId && dateUtil.now() - lastEbTime < 5000) return false + lastEbId = startId + lastEbTime = dateUtil.now() + appRepository.getNextSyncElementExtendedBolus(startId).blockingGet()?.let { eb -> + aapsLogger.info(LTag.DATABASE, "Loading ExtendedBolus data Start: $startId ID: ${eb.first.id} HistoryID: ${eb.second} ") + val profile = profileFunction.getProfile(eb.first.timestamp) + if (profile != null) { + when { + // without nsId = create new + eb.first.interfaceIDs.nightscoutId == null -> + nsClientPlugin.nsClientService?.dbAdd("treatments", eb.first.toJson(profile, dateUtil, useAbsolute), DataSyncSelector.PairExtendedBolus(eb.first, eb.second), "$startId/$lastDbId") + // with nsId = update + eb.first.interfaceIDs.nightscoutId != null -> + nsClientPlugin.nsClientService?.dbUpdate("treatments", eb.first.interfaceIDs.nightscoutId, eb.first.toJson(profile, dateUtil, useAbsolute), DataSyncSelector.PairExtendedBolus(eb.first, eb.second), "$startId/$lastDbId") + } + return true + } else { + confirmLastExtendedBolusIdIfGreater(eb.second) + lastEbId = -1 + processChangedExtendedBolusesCompat() + } + } + return false + } + + override fun confirmLastProfileSwitchIdIfGreater(lastSynced: Long) { + if (lastSynced > sp.getLong(R.string.key_ns_profile_switch_last_synced_id, 0)) { + aapsLogger.debug(LTag.NSCLIENT, "Setting ProfileSwitch data sync from $lastSynced") + sp.putLong(R.string.key_ns_profile_switch_last_synced_id, lastSynced) + } + } + + override fun changedProfileSwitch(): List { + val startId = sp.getLong(R.string.key_ns_profile_switch_last_synced_id, 0) + return appRepository.getModifiedProfileSwitchDataFromId(startId).blockingGet().also { + aapsLogger.debug(LTag.NSCLIENT, "Loading ProfileSwitch data for sync from $startId. Records ${it.size}") + } + } + + private var lastPsId = -1L + private var lastPsTime = -1L + override fun processChangedProfileSwitchesCompat(): Boolean { + val lastDbIdWrapped = appRepository.getLastProfileSwitchIdWrapped().blockingGet() + val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L + var startId = sp.getLong(R.string.key_ns_profile_switch_last_synced_id, 0) + if (startId > lastDbId) { + sp.putLong(R.string.key_ns_profile_switch_last_synced_id, 0) + startId = 0 + } + if (startId == lastPsId && dateUtil.now() - lastPsTime < 5000) return false + lastPsId = startId + lastPsTime = dateUtil.now() + appRepository.getNextSyncElementProfileSwitch(startId).blockingGet()?.let { ps -> + aapsLogger.info(LTag.DATABASE, "Loading ProfileSwitch data Start: $startId ID: ${ps.first.id} HistoryID: ${ps.second} ") + when { + // without nsId = create new + ps.first.interfaceIDs.nightscoutId == null -> + nsClientPlugin.nsClientService?.dbAdd("treatments", ps.first.toJson(dateUtil), DataSyncSelector.PairProfileSwitch(ps.first, ps.second), "$startId/$lastDbId") + // with nsId = update + ps.first.interfaceIDs.nightscoutId != null -> + nsClientPlugin.nsClientService?.dbUpdate("treatments", ps.first.interfaceIDs.nightscoutId, ps.first.toJson(dateUtil), DataSyncSelector.PairProfileSwitch(ps.first, ps.second), "$startId/$lastDbId") + } + return true + } + return false + } + + override fun confirmLastProfileStore(lastSynced: Long) { + sp.putLong(R.string.key_ns_profile_store_last_synced_timestamp, lastSynced) + } + + override fun processChangedProfileStore() { + val lastSync = sp.getLong(R.string.key_ns_profile_store_last_synced_timestamp, 0) + val lastChange = sp.getLong(R.string.key_local_profile_last_change, 0) + if (lastChange == 0L) return + localProfilePlugin.createProfileStore() + val profileJson = localProfilePlugin.profile?.data ?: return + if (lastChange > lastSync) + nsClientPlugin.nsClientService?.dbAdd("profile", profileJson, DataSyncSelector.PairProfileStore(profileJson, dateUtil.now()), "") + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientAddAckWorker.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientAddAckWorker.kt new file mode 100644 index 0000000000..cc3c6b6aea --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientAddAckWorker.kt @@ -0,0 +1,261 @@ +package info.nightscout.androidaps.plugins.general.nsclient + +import android.content.Context +import androidx.work.Worker +import androidx.work.WorkerParameters +import androidx.work.workDataOf +import dagger.android.HasAndroidInjector +import info.nightscout.androidaps.database.AppRepository +import info.nightscout.androidaps.database.entities.DeviceStatus +import info.nightscout.androidaps.database.transactions.* +import info.nightscout.androidaps.interfaces.DataSyncSelector +import info.nightscout.androidaps.interfaces.DataSyncSelector.* +import info.nightscout.androidaps.logging.AAPSLogger +import info.nightscout.androidaps.logging.LTag +import info.nightscout.androidaps.plugins.bus.RxBusWrapper +import info.nightscout.androidaps.plugins.general.nsclient.acks.NSAddAck +import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientNewLog +import info.nightscout.androidaps.receivers.DataWorker +import info.nightscout.androidaps.utils.rx.AapsSchedulers +import javax.inject.Inject + +class NSClientAddAckWorker( + context: Context, + params: WorkerParameters +) : Worker(context, params) { + + @Inject lateinit var dataWorker: DataWorker + @Inject lateinit var aapsLogger: AAPSLogger + @Inject lateinit var repository: AppRepository + @Inject lateinit var rxBus: RxBusWrapper + @Inject lateinit var dataSyncSelector: DataSyncSelector + @Inject lateinit var aapsSchedulers: AapsSchedulers + + override fun doWork(): Result { + var ret = Result.success() + + val ack = dataWorker.pickupObject(inputData.getLong(DataWorker.STORE_KEY, -1)) as NSAddAck? + ?: return Result.failure(workDataOf("Error" to "missing input data")) + + when (ack.originalObject) { + is PairTemporaryTarget -> { + val pair = ack.originalObject + pair.value.interfaceIDs.nightscoutId = ack.id + repository.runTransactionForResult(UpdateNsIdTemporaryTargetTransaction(pair.value)) + .doOnError { error -> + aapsLogger.error(LTag.DATABASE, "Updated ns id of TemporaryTarget failed", error) + ret = Result.failure((workDataOf("Error" to error.toString()))) + } + .doOnSuccess { + ret = Result.success(workDataOf("ProcessedData" to pair.toString())) + aapsLogger.debug(LTag.DATABASE, "Updated ns id of TemporaryTarget " + pair.value) + dataSyncSelector.confirmLastTempTargetsIdIfGreater(pair.updateRecordId) + } + .blockingGet() + rxBus.send(EventNSClientNewLog("DBADD", "Acked TemporaryTarget " + pair.value.interfaceIDs.nightscoutId)) + // Send new if waiting + dataSyncSelector.processChangedTempTargetsCompat() + } + + is PairGlucoseValue -> { + val pair = ack.originalObject + pair.value.interfaceIDs.nightscoutId = ack.id + repository.runTransactionForResult(UpdateNsIdGlucoseValueTransaction(pair.value)) + .doOnError { error -> + aapsLogger.error(LTag.DATABASE, "Updated ns id of GlucoseValue failed", error) + ret = Result.failure((workDataOf("Error" to error.toString()))) + } + .doOnSuccess { + ret = Result.success(workDataOf("ProcessedData" to pair.toString())) + aapsLogger.debug(LTag.DATABASE, "Updated ns id of GlucoseValue " + pair.value) + dataSyncSelector.confirmLastGlucoseValueIdIfGreater(pair.updateRecordId) + } + .blockingGet() + rxBus.send(EventNSClientNewLog("DBADD", "Acked GlucoseValue " + pair.value.interfaceIDs.nightscoutId)) + // Send new if waiting + dataSyncSelector.processChangedGlucoseValuesCompat() + } + + is PairFood -> { + val pair = ack.originalObject + pair.value.interfaceIDs.nightscoutId = ack.id + repository.runTransactionForResult(UpdateNsIdFoodTransaction(pair.value)) + .doOnError { error -> + aapsLogger.error(LTag.DATABASE, "Updated ns id of Food failed", error) + ret = Result.failure((workDataOf("Error" to error.toString()))) + } + .doOnSuccess { + ret = Result.success(workDataOf("ProcessedData" to pair.toString())) + aapsLogger.debug(LTag.DATABASE, "Updated ns id of Food " + pair.value) + dataSyncSelector.confirmLastFoodIdIfGreater(pair.updateRecordId) + } + .blockingGet() + rxBus.send(EventNSClientNewLog("DBADD", "Acked Food " + pair.value.interfaceIDs.nightscoutId)) + // Send new if waiting + dataSyncSelector.processChangedFoodsCompat() + } + + is PairTherapyEvent -> { + val pair = ack.originalObject + pair.value.interfaceIDs.nightscoutId = ack.id + repository.runTransactionForResult(UpdateNsIdTherapyEventTransaction(pair.value)) + .doOnError { error -> + aapsLogger.error(LTag.DATABASE, "Updated ns id of TherapyEvent failed", error) + ret = Result.failure((workDataOf("Error" to error.toString()))) + } + .doOnSuccess { + ret = Result.success(workDataOf("ProcessedData" to pair.toString())) + aapsLogger.debug(LTag.DATABASE, "Updated ns id of TherapyEvent " + pair.value) + dataSyncSelector.confirmLastTherapyEventIdIfGreater(pair.updateRecordId) + } + .blockingGet() + rxBus.send(EventNSClientNewLog("DBADD", "Acked TherapyEvent " + pair.value.interfaceIDs.nightscoutId)) + // Send new if waiting + dataSyncSelector.processChangedTherapyEventsCompat() + } + + is PairBolus -> { + val pair = ack.originalObject + pair.value.interfaceIDs.nightscoutId = ack.id + repository.runTransactionForResult(UpdateNsIdBolusTransaction(pair.value)) + .doOnError { error -> + aapsLogger.error(LTag.DATABASE, "Updated ns id of Bolus failed", error) + ret = Result.failure((workDataOf("Error" to error.toString()))) + } + .doOnSuccess { + ret = Result.success(workDataOf("ProcessedData" to pair.toString())) + aapsLogger.debug(LTag.DATABASE, "Updated ns id of Bolus " + pair.value) + dataSyncSelector.confirmLastBolusIdIfGreater(pair.updateRecordId) + } + .blockingGet() + rxBus.send(EventNSClientNewLog("DBADD", "Acked Bolus " + pair.value.interfaceIDs.nightscoutId)) + // Send new if waiting + dataSyncSelector.processChangedBolusesCompat() + } + + is PairCarbs -> { + val pair = ack.originalObject + pair.value.interfaceIDs.nightscoutId = ack.id + repository.runTransactionForResult(UpdateNsIdCarbsTransaction(pair.value)) + .doOnError { error -> + aapsLogger.error(LTag.DATABASE, "Updated ns id of Carbs failed", error) + ret = Result.failure((workDataOf("Error" to error.toString()))) + } + .doOnSuccess { + ret = Result.success(workDataOf("ProcessedData" to pair.toString())) + aapsLogger.debug(LTag.DATABASE, "Updated ns id of Carbs " + pair.value) + dataSyncSelector.confirmLastCarbsIdIfGreater(pair.updateRecordId) + } + .blockingGet() + rxBus.send(EventNSClientNewLog("DBADD", "Acked Carbs " + pair.value.interfaceIDs.nightscoutId)) + // Send new if waiting + dataSyncSelector.processChangedCarbsCompat() + } + + is PairBolusCalculatorResult -> { + val pair = ack.originalObject + pair.value.interfaceIDs.nightscoutId = ack.id + repository.runTransactionForResult(UpdateNsIdBolusCalculatorResultTransaction(pair.value)) + .doOnError { error -> + aapsLogger.error(LTag.DATABASE, "Updated ns id of BolusCalculatorResult failed", error) + ret = Result.failure((workDataOf("Error" to error.toString()))) + } + .doOnSuccess { + ret = Result.success(workDataOf("ProcessedData" to pair.toString())) + aapsLogger.debug(LTag.DATABASE, "Updated ns id of BolusCalculatorResult " + pair.value) + dataSyncSelector.confirmLastBolusCalculatorResultsIdIfGreater(pair.updateRecordId) + } + .blockingGet() + rxBus.send(EventNSClientNewLog("DBADD", "Acked BolusCalculatorResult " + pair.value.interfaceIDs.nightscoutId)) + // Send new if waiting + dataSyncSelector.processChangedBolusCalculatorResultsCompat() + } + + is PairTemporaryBasal -> { + val pair = ack.originalObject + pair.value.interfaceIDs.nightscoutId = ack.id + repository.runTransactionForResult(UpdateNsIdTemporaryBasalTransaction(pair.value)) + .doOnError { error -> + aapsLogger.error(LTag.DATABASE, "Updated ns id of TemporaryBasal failed", error) + ret = Result.failure((workDataOf("Error" to error.toString()))) + } + .doOnSuccess { + ret = Result.success(workDataOf("ProcessedData" to pair.toString())) + aapsLogger.debug(LTag.DATABASE, "Updated ns id of TemporaryBasal " + pair.value) + dataSyncSelector.confirmLastTemporaryBasalIdIfGreater(pair.updateRecordId) + } + .blockingGet() + rxBus.send(EventNSClientNewLog("DBADD", "Acked TemporaryBasal " + pair.value.interfaceIDs.nightscoutId)) + // Send new if waiting + dataSyncSelector.processChangedTemporaryBasalsCompat() + } + + is PairExtendedBolus -> { + val pair = ack.originalObject + pair.value.interfaceIDs.nightscoutId = ack.id + repository.runTransactionForResult(UpdateNsIdExtendedBolusTransaction(pair.value)) + .doOnError { error -> + aapsLogger.error(LTag.DATABASE, "Updated ns id of ExtendedBolus failed", error) + ret = Result.failure((workDataOf("Error" to error.toString()))) + } + .doOnSuccess { + ret = Result.success(workDataOf("ProcessedData" to pair.toString())) + aapsLogger.debug(LTag.DATABASE, "Updated ns id of ExtendedBolus " + pair.value) + dataSyncSelector.confirmLastExtendedBolusIdIfGreater(pair.updateRecordId) + } + .blockingGet() + rxBus.send(EventNSClientNewLog("DBADD", "Acked ExtendedBolus " + pair.value.interfaceIDs.nightscoutId)) + // Send new if waiting + dataSyncSelector.processChangedExtendedBolusesCompat() + } + + is PairProfileSwitch -> { + val pair = ack.originalObject + pair.value.interfaceIDs.nightscoutId = ack.id + repository.runTransactionForResult(UpdateNsIdProfileSwitchTransaction(pair.value)) + .doOnError { error -> + aapsLogger.error(LTag.DATABASE, "Updated ns id of ProfileSwitch failed", error) + ret = Result.failure((workDataOf("Error" to error.toString()))) + } + .doOnSuccess { + ret = Result.success(workDataOf("ProcessedData" to pair.toString())) + aapsLogger.debug(LTag.DATABASE, "Updated ns id of ProfileSwitch " + pair.value) + dataSyncSelector.confirmLastProfileSwitchIdIfGreater(pair.updateRecordId) + } + .blockingGet() + rxBus.send(EventNSClientNewLog("DBADD", "Acked ProfileSwitch " + pair.value.interfaceIDs.nightscoutId)) + // Send new if waiting + dataSyncSelector.processChangedProfileSwitchesCompat() + } + + is DeviceStatus -> { + val deviceStatus = ack.originalObject + deviceStatus.interfaceIDs.nightscoutId = ack.id + repository.runTransactionForResult(UpdateNsIdDeviceStatusTransaction(deviceStatus)) + .doOnError { error -> + aapsLogger.error(LTag.DATABASE, "Updated ns id of DeviceStatus failed", error) + ret = Result.failure((workDataOf("Error" to error.toString()))) + } + .doOnSuccess { + ret = Result.success(workDataOf("ProcessedData" to deviceStatus.toString())) + aapsLogger.debug(LTag.DATABASE, "Updated ns id of DeviceStatus $deviceStatus") + dataSyncSelector.confirmLastDeviceStatusIdIfGreater(deviceStatus.id) + } + .blockingGet() + rxBus.send(EventNSClientNewLog("DBADD", "Acked DeviceStatus " + deviceStatus.interfaceIDs.nightscoutId)) + // Send new if waiting + dataSyncSelector.processChangedDeviceStatusesCompat() + } + + is PairProfileStore -> { + dataSyncSelector.confirmLastProfileStore(ack.originalObject.timestampSync) + rxBus.send(EventNSClientNewLog("DBADD", "Acked ProfileStore " + ack.id)) + } + } + return ret + } + + init { + (context.applicationContext as HasAndroidInjector).androidInjector().inject(this) + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientAddUpdateWorker.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientAddUpdateWorker.kt index 868993ddc8..7520cc1326 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientAddUpdateWorker.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientAddUpdateWorker.kt @@ -3,17 +3,19 @@ package info.nightscout.androidaps.plugins.general.nsclient import android.content.Context import androidx.work.Worker import androidx.work.WorkerParameters +import androidx.work.workDataOf import dagger.android.HasAndroidInjector +import info.nightscout.androidaps.Constants import info.nightscout.androidaps.R import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.entities.TherapyEvent -import info.nightscout.androidaps.database.entities.UserEntry -import info.nightscout.androidaps.database.entities.UserEntry.ValueWithUnit -import info.nightscout.androidaps.database.transactions.SyncTemporaryTargetTransaction -import info.nightscout.androidaps.database.transactions.SyncTherapyEventTransaction -import info.nightscout.androidaps.events.EventNsTreatment -import info.nightscout.androidaps.interfaces.ConfigInterface -import info.nightscout.androidaps.interfaces.DatabaseHelperInterface +import info.nightscout.androidaps.database.entities.UserEntry.Action +import info.nightscout.androidaps.database.entities.UserEntry.Sources +import info.nightscout.androidaps.database.entities.ValueWithUnit +import info.nightscout.androidaps.database.transactions.* +import info.nightscout.androidaps.extensions.* +import info.nightscout.androidaps.interfaces.ActivePlugin +import info.nightscout.androidaps.interfaces.Config import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.logging.UserEntryLogger @@ -25,9 +27,8 @@ import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.JsonHelper import info.nightscout.androidaps.utils.JsonHelper.safeGetLong import info.nightscout.androidaps.utils.buildHelper.BuildHelper -import info.nightscout.androidaps.utils.extensions.temporaryTargetFromJson -import info.nightscout.androidaps.utils.extensions.therapyEventFromJson import info.nightscout.androidaps.utils.sharedPreferences.SP +import java.util.concurrent.TimeUnit import javax.inject.Inject class NSClientAddUpdateWorker( @@ -40,133 +41,321 @@ class NSClientAddUpdateWorker( @Inject lateinit var aapsLogger: AAPSLogger @Inject lateinit var buildHelper: BuildHelper @Inject lateinit var sp: SP - @Inject lateinit var dateutil: DateUtil - @Inject lateinit var config: ConfigInterface + @Inject lateinit var dateUtil: DateUtil + @Inject lateinit var config: Config @Inject lateinit var repository: AppRepository - @Inject lateinit var databaseHelper: DatabaseHelperInterface + @Inject lateinit var activePlugin: ActivePlugin @Inject lateinit var rxBus: RxBusWrapper @Inject lateinit var uel: UserEntryLogger override fun doWork(): Result { - val acceptNSData = !sp.getBoolean(R.string.key_ns_upload_only, true) && buildHelper.isEngineeringMode() || config.NSCLIENT - if (!acceptNSData) return Result.failure() - val treatments = dataWorker.pickupJSONArray(inputData.getLong(DataWorker.STORE_KEY, -1)) - ?: return Result.failure() + ?: return Result.failure(workDataOf("Error" to "missing input data")) var ret = Result.success() var latestDateInReceivedData = 0L for (i in 0 until treatments.length()) { - val json = treatments.getJSONObject(i) + var json = treatments.getJSONObject(i) // new DB model val insulin = JsonHelper.safeGetDouble(json, "insulin") val carbs = JsonHelper.safeGetDouble(json, "carbs") val eventType = JsonHelper.safeGetString(json, "eventType") if (eventType == null) { - aapsLogger.debug(LTag.DATASERVICE, "Wrong treatment. Ignoring : $json") + aapsLogger.debug(LTag.NSCLIENT, "Wrong treatment. Ignoring : $json") continue } //Find latest date in treatment val mills = safeGetLong(json, "mills") - if (mills != 0L && mills < dateutil._now()) + if (mills != 0L && mills < dateUtil.now()) if (mills > latestDateInReceivedData) latestDateInReceivedData = mills - when { - insulin > 0 || carbs > 0 -> - rxBus.send(EventNsTreatment(EventNsTreatment.ADD, json)) - eventType == TherapyEvent.Type.TEMPORARY_TARGET.text -> - temporaryTargetFromJson(json)?.let { temporaryTarget -> - repository.runTransactionForResult(SyncTemporaryTargetTransaction(temporaryTarget)) + if (insulin > 0) { + if (sp.getBoolean(R.string.key_ns_receive_insulin, false) && buildHelper.isEngineeringMode() || config.NSCLIENT) { + bolusFromJson(json)?.let { bolus -> + repository.runTransactionForResult(SyncNsBolusTransaction(bolus, invalidateByNsOnly = false)) .doOnError { - aapsLogger.error(LTag.DATABASE, "Error while saving temporary target", it) - ret = Result.failure() + aapsLogger.error(LTag.DATABASE, "Error while saving bolus", it) + ret = Result.failure(workDataOf("Error" to it.toString())) } .blockingGet() .also { result -> result.inserted.forEach { - uel.log(UserEntry.Action.TT_FROM_NS, - ValueWithUnit(it.reason.text, UserEntry.Units.TherapyEvent), - ValueWithUnit(it.lowTarget, UserEntry.Units.Mg_Dl, true), - ValueWithUnit(it.highTarget, UserEntry.Units.Mg_Dl, it.lowTarget != it.highTarget), - ValueWithUnit(it.duration.toInt() / 60000, UserEntry.Units.M, true) + uel.log(Action.BOLUS, Sources.NSClient, + ValueWithUnit.Timestamp(it.timestamp), + ValueWithUnit.Insulin(it.amount) ) + aapsLogger.debug(LTag.DATABASE, "Inserted bolus $it") } result.invalidated.forEach { - uel.log(UserEntry.Action.TT_DELETED_FROM_NS, - ValueWithUnit(it.reason.text, UserEntry.Units.TherapyEvent), - ValueWithUnit(it.lowTarget, UserEntry.Units.Mg_Dl, true), - ValueWithUnit(it.highTarget, UserEntry.Units.Mg_Dl, it.lowTarget != it.highTarget), - ValueWithUnit(it.duration.toInt() / 60000, UserEntry.Units.M, true) + uel.log(Action.BOLUS_REMOVED, Sources.NSClient, + ValueWithUnit.Timestamp(it.timestamp), + ValueWithUnit.Insulin(it.amount) ) + aapsLogger.debug(LTag.DATABASE, "Invalidated bolus $it") } - result.ended.forEach { - uel.log(UserEntry.Action.TT_CANCELED_FROM_NS, - ValueWithUnit(it.reason.text, UserEntry.Units.TherapyEvent), - ValueWithUnit(it.lowTarget, UserEntry.Units.Mg_Dl, true), - ValueWithUnit(it.highTarget, UserEntry.Units.Mg_Dl, it.lowTarget != it.highTarget), - ValueWithUnit(it.duration.toInt() / 60000, UserEntry.Units.M, true) - ) + result.updatedNsId.forEach { + aapsLogger.debug(LTag.DATABASE, "Updated nsId bolus $it") } } - } ?: aapsLogger.error("Error parsing TT json $json") + } ?: aapsLogger.error("Error parsing bolus json $json") + } + } + if (carbs > 0) { + if (sp.getBoolean(R.string.key_ns_receive_carbs, false) && buildHelper.isEngineeringMode() || config.NSCLIENT) { + carbsFromJson(json)?.let { carb -> + repository.runTransactionForResult(SyncNsCarbsTransaction(carb, invalidateByNsOnly = false)) + .doOnError { + aapsLogger.error(LTag.DATABASE, "Error while saving carbs", it) + ret = Result.failure(workDataOf("Error" to it.toString())) + } + .blockingGet() + .also { result -> + result.inserted.forEach { + uel.log(Action.CARBS, Sources.NSClient, + ValueWithUnit.Timestamp(it.timestamp), + ValueWithUnit.Gram(it.amount.toInt()) + ) + aapsLogger.debug(LTag.DATABASE, "Inserted carbs $it") + } + result.invalidated.forEach { + uel.log(Action.CARBS_REMOVED, Sources.NSClient, + ValueWithUnit.Timestamp(it.timestamp), + ValueWithUnit.Gram(it.amount.toInt()) + ) + aapsLogger.debug(LTag.DATABASE, "Invalidated carbs $it") + } + result.updatedNsId.forEach { + aapsLogger.debug(LTag.DATABASE, "Updated nsId carbs $it") + } + } + } ?: aapsLogger.error("Error parsing bolus json $json") + } + } + // Convert back emulated TBR -> EB + if (eventType == TherapyEvent.Type.TEMPORARY_BASAL.text && json.has("extendedEmulated")) { + val ebJson = json.getJSONObject("extendedEmulated") + ebJson.put("_id", json.getString("_id")) + ebJson.put("isValid", json.getBoolean("isValid")) + json = ebJson + } + when { + insulin > 0 || carbs > 0 -> Any() + eventType == TherapyEvent.Type.TEMPORARY_TARGET.text -> + if (sp.getBoolean(R.string.key_ns_receive_temp_target, false) && buildHelper.isEngineeringMode() || config.NSCLIENT) { + temporaryTargetFromJson(json)?.let { temporaryTarget -> + repository.runTransactionForResult(SyncNsTemporaryTargetTransaction(temporaryTarget, invalidateByNsOnly = false)) + .doOnError { + aapsLogger.error(LTag.DATABASE, "Error while saving temporary target", it) + ret = Result.failure(workDataOf("Error" to it.toString())) + } + .blockingGet() + .also { result -> + result.inserted.forEach { tt -> + uel.log(Action.TT, Sources.NSClient, + ValueWithUnit.TherapyEventTTReason(tt.reason), + ValueWithUnit.fromGlucoseUnit(tt.lowTarget, Constants.MGDL), + ValueWithUnit.fromGlucoseUnit(tt.highTarget, Constants.MGDL).takeIf { tt.lowTarget != tt.highTarget }, + ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(tt.duration).toInt()) + ) + aapsLogger.debug(LTag.DATABASE, "Inserted TemporaryTarget $tt") + } + result.invalidated.forEach { tt -> + uel.log(Action.TT_REMOVED, Sources.NSClient, + ValueWithUnit.TherapyEventTTReason(tt.reason), + ValueWithUnit.Mgdl(tt.lowTarget), + ValueWithUnit.Mgdl(tt.highTarget).takeIf { tt.lowTarget != tt.highTarget }, + ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(tt.duration).toInt()) + ) + aapsLogger.debug(LTag.DATABASE, "Invalidated TemporaryTarget $tt") + } + result.ended.forEach { tt -> + uel.log(Action.CANCEL_TT, Sources.NSClient, + ValueWithUnit.TherapyEventTTReason(tt.reason), + ValueWithUnit.Mgdl(tt.lowTarget), + ValueWithUnit.Mgdl(tt.highTarget).takeIf { tt.lowTarget != tt.highTarget }, + ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(tt.duration).toInt()) + ) + aapsLogger.debug(LTag.DATABASE, "Updated TemporaryTarget $tt") + } + result.updatedNsId.forEach { + aapsLogger.debug(LTag.DATABASE, "Updated nsId TemporaryTarget $it") + } + } + } ?: aapsLogger.error("Error parsing TT json $json") + } eventType == TherapyEvent.Type.CANNULA_CHANGE.text || eventType == TherapyEvent.Type.INSULIN_CHANGE.text || eventType == TherapyEvent.Type.SENSOR_CHANGE.text || eventType == TherapyEvent.Type.FINGER_STICK_BG_VALUE.text || - eventType == TherapyEvent.Type.NOTE.text || eventType == TherapyEvent.Type.NONE.text || eventType == TherapyEvent.Type.ANNOUNCEMENT.text || eventType == TherapyEvent.Type.QUESTION.text || eventType == TherapyEvent.Type.EXERCISE.text || eventType == TherapyEvent.Type.APS_OFFLINE.text || eventType == TherapyEvent.Type.PUMP_BATTERY_CHANGE.text -> - therapyEventFromJson(json)?.let { therapyEvent -> - repository.runTransactionForResult(SyncTherapyEventTransaction(therapyEvent)) - .doOnError { - aapsLogger.error(LTag.DATABASE, "Error while saving therapy event", it) - ret = Result.failure() - } - .blockingGet() - .also { result -> - result.inserted.forEach { - uel.log(UserEntry.Action.CAREPORTAL_FROM_NS, - it.note ?: "", - ValueWithUnit(it.timestamp, UserEntry.Units.Timestamp, true), - ValueWithUnit(it.type.text, UserEntry.Units.TherapyEvent) - ) + if (sp.getBoolean(R.string.key_ns_receive_therapy_events, false) || config.NSCLIENT) { + therapyEventFromJson(json)?.let { therapyEvent -> + repository.runTransactionForResult(SyncNsTherapyEventTransaction(therapyEvent, invalidateByNsOnly = false)) + .doOnError { + aapsLogger.error(LTag.DATABASE, "Error while saving therapy event", it) + ret = Result.failure(workDataOf("Error" to it.toString())) } - result.invalidated.forEach { - uel.log(UserEntry.Action.CAREPORTAL_DELETED_FROM_NS, - it.note ?: "", - ValueWithUnit(it.timestamp, UserEntry.Units.Timestamp, true), - ValueWithUnit(it.type.text, UserEntry.Units.TherapyEvent) - ) + .blockingGet() + .also { result -> + val action = when (eventType) { + TherapyEvent.Type.CANNULA_CHANGE.text -> Action.SITE_CHANGE + TherapyEvent.Type.INSULIN_CHANGE.text -> Action.RESERVOIR_CHANGE + else -> Action.CAREPORTAL + } + result.inserted.forEach { + uel.log(action, Sources.NSClient, + it.note ?: "", + ValueWithUnit.Timestamp(it.timestamp), + ValueWithUnit.TherapyEventType(it.type) + ) + aapsLogger.debug(LTag.DATABASE, "Inserted TherapyEvent $it") + } + result.invalidated.forEach { + uel.log(Action.CAREPORTAL_REMOVED, Sources.NSClient, + it.note ?: "", + ValueWithUnit.Timestamp(it.timestamp), + ValueWithUnit.TherapyEventType(it.type) + ) + aapsLogger.debug(LTag.DATABASE, "Invalidated TherapyEvent $it") + } + result.updatedNsId.forEach { + aapsLogger.debug(LTag.DATABASE, "Updated nsId TherapyEvent $it") + } } - } - } ?: aapsLogger.error("Error parsing TherapyEvent json $json") - eventType == TherapyEvent.Type.TEMPORARY_BASAL.text -> - databaseHelper.createTempBasalFromJsonIfNotExists(json) + } ?: aapsLogger.error("Error parsing TherapyEvent json $json") + } eventType == TherapyEvent.Type.COMBO_BOLUS.text -> - databaseHelper.createExtendedBolusFromJsonIfNotExists(json) + if (config.NSCLIENT) { + extendedBolusFromJson(json)?.let { extendedBolus -> + repository.runTransactionForResult(SyncNsExtendedBolusTransaction(extendedBolus, invalidateByNsOnly = false)) + .doOnError { + aapsLogger.error(LTag.DATABASE, "Error while saving extended bolus", it) + ret = Result.failure(workDataOf("Error" to it.toString())) + } + .blockingGet() + .also { result -> + result.inserted.forEach { + uel.log(Action.EXTENDED_BOLUS, Sources.NSClient, + ValueWithUnit.Timestamp(it.timestamp), + ValueWithUnit.Insulin(it.amount), + ValueWithUnit.UnitPerHour(it.rate), + ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(it.duration).toInt()) + ) + aapsLogger.debug(LTag.DATABASE, "Inserted ExtendedBolus $it") + } + result.invalidated.forEach { + uel.log(Action.EXTENDED_BOLUS_REMOVED, Sources.NSClient, + ValueWithUnit.Timestamp(it.timestamp), + ValueWithUnit.Insulin(it.amount), + ValueWithUnit.UnitPerHour(it.rate), + ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(it.duration).toInt()) + ) + aapsLogger.debug(LTag.DATABASE, "Invalidated ExtendedBolus $it") + } + result.ended.forEach { + uel.log(Action.CANCEL_EXTENDED_BOLUS, Sources.NSClient, + ValueWithUnit.Timestamp(it.timestamp), + ValueWithUnit.Insulin(it.amount), + ValueWithUnit.UnitPerHour(it.rate), + ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(it.duration).toInt()) + ) + aapsLogger.debug(LTag.DATABASE, "Updated ExtendedBolus $it") + } + result.updatedNsId.forEach { + aapsLogger.debug(LTag.DATABASE, "Updated nsId ExtendedBolus $it") + } + } + } ?: aapsLogger.error("Error parsing ExtendedBolus json $json") + } + eventType == TherapyEvent.Type.TEMPORARY_BASAL.text -> + if (config.NSCLIENT) { + temporaryBasalFromJson(json)?.let { temporaryBasal -> + repository.runTransactionForResult(SyncNsTemporaryBasalTransaction(temporaryBasal, invalidateByNsOnly = false)) + .doOnError { + aapsLogger.error(LTag.DATABASE, "Error while saving temporary basal", it) + ret = Result.failure(workDataOf("Error" to it.toString())) + } + .blockingGet() + .also { result -> + result.inserted.forEach { + uel.log(Action.TEMP_BASAL, Sources.NSClient, + ValueWithUnit.Timestamp(it.timestamp), + ValueWithUnit.UnitPerHour(it.rate), + ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(it.duration).toInt()) + ) + aapsLogger.debug(LTag.DATABASE, "Inserted TemporaryBasal $it") + } + result.invalidated.forEach { + uel.log(Action.TEMP_BASAL_REMOVED, Sources.NSClient, + ValueWithUnit.Timestamp(it.timestamp), + ValueWithUnit.UnitPerHour(it.rate), + ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(it.duration).toInt()) + ) + aapsLogger.debug(LTag.DATABASE, "Invalidated TemporaryBasal $it") + } + result.ended.forEach { + uel.log(Action.CANCEL_TEMP_BASAL, Sources.NSClient, + ValueWithUnit.Timestamp(it.timestamp), + ValueWithUnit.UnitPerHour(it.rate), + ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(it.duration).toInt()) + ) + aapsLogger.debug(LTag.DATABASE, "Ended TemporaryBasal $it") + } + result.updatedNsId.forEach { + aapsLogger.debug(LTag.DATABASE, "Updated nsId TemporaryBasal $it") + } + } + } ?: aapsLogger.error("Error parsing TemporaryBasal json $json") + } eventType == TherapyEvent.Type.PROFILE_SWITCH.text -> - databaseHelper.createProfileSwitchFromJsonIfNotExists(json) + if (sp.getBoolean(R.string.key_ns_receive_profile_switch, false) && buildHelper.isEngineeringMode() || config.NSCLIENT) { + profileSwitchFromJson(json, dateUtil, activePlugin)?.let { profileSwitch -> + repository.runTransactionForResult(SyncNsProfileSwitchTransaction(profileSwitch, invalidateByNsOnly = false)) + .doOnError { + aapsLogger.error(LTag.DATABASE, "Error while saving ProfileSwitch", it) + ret = Result.failure(workDataOf("Error" to it.toString())) + } + .blockingGet() + .also { result -> + result.inserted.forEach { + uel.log(Action.PROFILE_SWITCH, Sources.NSClient, + ValueWithUnit.Timestamp(it.timestamp)) + aapsLogger.debug(LTag.DATABASE, "Inserted ProfileSwitch $it") + } + result.invalidated.forEach { + uel.log(Action.PROFILE_SWITCH_REMOVED, Sources.NSClient, + ValueWithUnit.Timestamp(it.timestamp)) + aapsLogger.debug(LTag.DATABASE, "Invalidated ProfileSwitch $it") + } + result.updatedNsId.forEach { + aapsLogger.debug(LTag.DATABASE, "Updated nsId ProfileSwitch $it") + } + } + } ?: aapsLogger.error("Error parsing ProfileSwitch json $json") + } } - if (eventType == TherapyEvent.Type.ANNOUNCEMENT.text) { - val date = safeGetLong(json, "mills") - val now = System.currentTimeMillis() - val enteredBy = JsonHelper.safeGetString(json, "enteredBy", "") - val notes = JsonHelper.safeGetString(json, "notes", "") - if (date > now - 15 * 60 * 1000L && notes.isNotEmpty() - && enteredBy != sp.getString("careportal_enteredby", "AndroidAPS")) { - val defaultVal = config.NSCLIENT - if (sp.getBoolean(R.string.key_ns_announcements, defaultVal)) { - val announcement = Notification(Notification.NS_ANNOUNCEMENT, notes, Notification.ANNOUNCEMENT, 60) - rxBus.send(EventNewNotification(announcement)) + if (sp.getBoolean(R.string.key_ns_receive_therapy_events, false) || config.NSCLIENT) + if (eventType == TherapyEvent.Type.ANNOUNCEMENT.text) { + val date = safeGetLong(json, "mills") + val now = System.currentTimeMillis() + val enteredBy = JsonHelper.safeGetString(json, "enteredBy", "") + val notes = JsonHelper.safeGetString(json, "notes", "") + if (date > now - 15 * 60 * 1000L && notes.isNotEmpty() + && enteredBy != sp.getString("careportal_enteredby", "AndroidAPS")) { + val defaultVal = config.NSCLIENT + if (sp.getBoolean(R.string.key_ns_announcements, defaultVal)) { + val announcement = Notification(Notification.NS_ANNOUNCEMENT, notes, Notification.ANNOUNCEMENT, 60) + rxBus.send(EventNewNotification(announcement)) + } } } - } } nsClientPlugin.updateLatestDateReceivedIfNewer(latestDateInReceivedData) return ret diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientFragment.java deleted file mode 100644 index 333c510309..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientFragment.java +++ /dev/null @@ -1,171 +0,0 @@ -package info.nightscout.androidaps.plugins.general.nsclient; - - -import android.graphics.Paint; -import android.os.Bundle; -import android.text.Spanned; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.CheckBox; -import android.widget.CompoundButton; -import android.widget.ScrollView; -import android.widget.TextView; - -import javax.inject.Inject; - -import dagger.android.support.DaggerFragment; -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.interfaces.UploadQueueAdminInterface; -import info.nightscout.androidaps.database.entities.UserEntry; -import info.nightscout.androidaps.database.entities.UserEntry.*; -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; -import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientUpdateGUI; -import info.nightscout.androidaps.utils.FabricPrivacy; -import info.nightscout.androidaps.utils.HtmlHelper; -import info.nightscout.androidaps.utils.alertDialogs.OKDialog; -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; - -public class NSClientFragment extends DaggerFragment implements View.OnClickListener, CompoundButton.OnCheckedChangeListener { - @Inject NSClientPlugin nsClientPlugin; - @Inject SP sp; - @Inject ResourceHelper resourceHelper; - @Inject RxBusWrapper rxBus; - @Inject UploadQueueAdminInterface uploadQueue; - @Inject FabricPrivacy fabricPrivacy; - @Inject AapsSchedulers aapsSchedulers; - @Inject UserEntryLogger uel; - - private final CompositeDisposable disposable = new CompositeDisposable(); - - private TextView logTextView; - private TextView queueTextView; - private TextView urlTextView; - private TextView statusTextView; - private TextView clearlog; - private TextView restart; - private TextView delivernow; - private TextView clearqueue; - private TextView showqueue; - private ScrollView logScrollview; - private CheckBox autoscrollCheckbox; - private CheckBox pausedCheckbox; - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.nsclientinternal_fragment, container, false); - - logScrollview = view.findViewById(R.id.nsclientinternal_logscrollview); - autoscrollCheckbox = view.findViewById(R.id.nsclientinternal_autoscroll); - autoscrollCheckbox.setChecked(nsClientPlugin.autoscroll); - autoscrollCheckbox.setOnCheckedChangeListener(this); - pausedCheckbox = view.findViewById(R.id.nsclientinternal_paused); - pausedCheckbox.setChecked(nsClientPlugin.paused); - pausedCheckbox.setOnCheckedChangeListener(this); - logTextView = view.findViewById(R.id.nsclientinternal_log); - queueTextView = view.findViewById(R.id.nsclientinternal_queue); - urlTextView = view.findViewById(R.id.nsclientinternal_url); - statusTextView = view.findViewById(R.id.nsclientinternal_status); - - clearlog = view.findViewById(R.id.nsclientinternal_clearlog); - clearlog.setOnClickListener(this); - clearlog.setPaintFlags(clearlog.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG); - restart = view.findViewById(R.id.nsclientinternal_restart); - restart.setOnClickListener(this); - restart.setPaintFlags(restart.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG); - delivernow = view.findViewById(R.id.nsclientinternal_delivernow); - delivernow.setOnClickListener(this); - delivernow.setPaintFlags(delivernow.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG); - clearqueue = view.findViewById(R.id.nsclientinternal_clearqueue); - clearqueue.setOnClickListener(this); - clearqueue.setPaintFlags(clearqueue.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG); - showqueue = view.findViewById(R.id.nsclientinternal_showqueue); - showqueue.setOnClickListener(this); - showqueue.setPaintFlags(showqueue.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG); - - return view; - } - - @Override - public synchronized void onResume() { - super.onResume(); - disposable.add(rxBus - .toObservable(EventNSClientUpdateGUI.class) - .observeOn(aapsSchedulers.getMain()) - .subscribe(event -> updateGui(), fabricPrivacy::logException) - ); - updateGui(); - } - - @Override - public synchronized void onPause() { - super.onPause(); - disposable.clear(); - } - - @Override - public void onClick(View view) { - switch (view.getId()) { - case R.id.nsclientinternal_restart: - rxBus.send(new EventNSClientRestart()); - fabricPrivacy.logCustom("NSClientRestart"); - break; - case R.id.nsclientinternal_delivernow: - nsClientPlugin.resend("GUI"); - fabricPrivacy.logCustom("NSClientDeliverNow"); - break; - case R.id.nsclientinternal_clearlog: - nsClientPlugin.clearLog(); - break; - case R.id.nsclientinternal_clearqueue: - OKDialog.showConfirmation(getContext(), resourceHelper.gs(R.string.nsclientinternal), resourceHelper.gs(R.string.clearqueueconfirm), () -> { - uel.log(Action.NS_QUEUE_CLEARED); - uploadQueue.clearQueue(); - updateGui(); - fabricPrivacy.logCustom("NSClientClearQueue"); - }); - break; - case R.id.nsclientinternal_showqueue: - rxBus.send(new EventNSClientNewLog("QUEUE", uploadQueue.textList())); - break; - } - } - - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - switch (buttonView.getId()) { - case R.id.nsclientinternal_paused: - uel.log(isChecked ? Action.NS_PAUSED : Action.NS_RESUME); - nsClientPlugin.pause(isChecked); - updateGui(); - fabricPrivacy.logCustom("NSClientPause"); - break; - case R.id.nsclientinternal_autoscroll: - sp.putBoolean(R.string.key_nsclientinternal_autoscroll, isChecked); - nsClientPlugin.autoscroll = isChecked; - updateGui(); - break; - } - } - - protected void updateGui() { - nsClientPlugin.updateLog(); - pausedCheckbox.setChecked(sp.getBoolean(R.string.key_nsclientinternal_paused, false)); - logTextView.setText(nsClientPlugin.textLog); - if (nsClientPlugin.autoscroll) { - logScrollview.fullScroll(ScrollView.FOCUS_DOWN); - } - urlTextView.setText(nsClientPlugin.url()); - Spanned queuetext = HtmlHelper.INSTANCE.fromHtml(resourceHelper.gs(R.string.queue) + " " + uploadQueue.size() + ""); - queueTextView.setText(queuetext); - statusTextView.setText(nsClientPlugin.status); - } - -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientFragment.kt new file mode 100644 index 0000000000..80b508766d --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientFragment.kt @@ -0,0 +1,105 @@ +package info.nightscout.androidaps.plugins.general.nsclient + +import android.graphics.Paint +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ScrollView +import dagger.android.support.DaggerFragment +import info.nightscout.androidaps.R +import info.nightscout.androidaps.database.entities.UserEntry.Action +import info.nightscout.androidaps.database.entities.UserEntry.Sources +import info.nightscout.androidaps.databinding.NsClientFragmentBinding +import info.nightscout.androidaps.interfaces.DataSyncSelector +import info.nightscout.androidaps.logging.UserEntryLogger +import info.nightscout.androidaps.plugins.bus.RxBusWrapper +import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart +import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientUpdateGUI +import info.nightscout.androidaps.utils.FabricPrivacy +import info.nightscout.androidaps.utils.alertDialogs.OKDialog +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 javax.inject.Inject + +class NSClientFragment : DaggerFragment() { + + @Inject lateinit var nsClientPlugin: NSClientPlugin + @Inject lateinit var sp: SP + @Inject lateinit var resourceHelper: ResourceHelper + @Inject lateinit var rxBus: RxBusWrapper + @Inject lateinit var fabricPrivacy: FabricPrivacy + @Inject lateinit var aapsSchedulers: AapsSchedulers + @Inject lateinit var dataSyncSelector: DataSyncSelector + @Inject lateinit var uel: UserEntryLogger + + private val disposable = CompositeDisposable() + + private var _binding: NsClientFragmentBinding? = null + + // This property is only valid between onCreateView and + // onDestroyView. + private val binding get() = _binding!! + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = + NsClientFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + binding.autoscroll.isChecked = nsClientPlugin.autoscroll + binding.autoscroll.setOnCheckedChangeListener { _, isChecked -> + sp.putBoolean(R.string.key_nsclientinternal_autoscroll, isChecked) + nsClientPlugin.autoscroll = isChecked + updateGui() + } + + binding.paused.isChecked = nsClientPlugin.paused + binding.paused.setOnCheckedChangeListener { _, isChecked -> + uel.log(if (isChecked) Action.NS_PAUSED else Action.NS_RESUME, Sources.NSClient) + nsClientPlugin.pause(isChecked) + updateGui() + } + binding.clearLog.setOnClickListener { nsClientPlugin.clearLog() } + binding.clearLog.paintFlags = binding.clearLog.paintFlags or Paint.UNDERLINE_TEXT_FLAG + binding.restart.setOnClickListener { rxBus.send(EventNSClientRestart()) } + binding.restart.paintFlags = binding.restart.paintFlags or Paint.UNDERLINE_TEXT_FLAG + binding.deliverNow.setOnClickListener { nsClientPlugin.resend("GUI") } + binding.deliverNow.paintFlags = binding.deliverNow.paintFlags or Paint.UNDERLINE_TEXT_FLAG + binding.fullSync.setOnClickListener { + context?.let { context -> + OKDialog.showConfirmation(context, resourceHelper.gs(R.string.nsclientinternal), resourceHelper.gs(R.string.full_sync), Runnable { + dataSyncSelector.resetToNextFullSync() + }) + } + } + binding.fullSync.paintFlags = binding.fullSync.paintFlags or Paint.UNDERLINE_TEXT_FLAG + } + + @Synchronized override fun onResume() { + super.onResume() + disposable.add(rxBus + .toObservable(EventNSClientUpdateGUI::class.java) + .observeOn(aapsSchedulers.main) + .subscribe({ updateGui() }, fabricPrivacy::logException) + ) + updateGui() + } + + @Synchronized override fun onPause() { + super.onPause() + disposable.clear() + } + + private fun updateGui() { + if (_binding == null) return + nsClientPlugin.updateLog() + binding.paused.isChecked = sp.getBoolean(R.string.key_nsclientinternal_paused, false) + binding.log.text = nsClientPlugin.textLog + if (nsClientPlugin.autoscroll) binding.logScrollview.fullScroll(ScrollView.FOCUS_DOWN) + binding.url.text = nsClientPlugin.url() + binding.status.text = nsClientPlugin.status + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientMbgWorker.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientMbgWorker.kt index 7c05af0fc8..8c73fd908f 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientMbgWorker.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientMbgWorker.kt @@ -3,17 +3,18 @@ package info.nightscout.androidaps.plugins.general.nsclient import android.content.Context import androidx.work.Worker import androidx.work.WorkerParameters +import androidx.work.workDataOf import dagger.android.HasAndroidInjector import info.nightscout.androidaps.R import info.nightscout.androidaps.database.AppRepository -import info.nightscout.androidaps.database.transactions.SyncTherapyEventTransaction -import info.nightscout.androidaps.interfaces.ConfigInterface +import info.nightscout.androidaps.database.transactions.SyncNsTherapyEventTransaction +import info.nightscout.androidaps.extensions.therapyEventFromNsMbg +import info.nightscout.androidaps.interfaces.Config import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.plugins.general.nsclient.data.NSMbg import info.nightscout.androidaps.receivers.DataWorker import info.nightscout.androidaps.utils.buildHelper.BuildHelper -import info.nightscout.androidaps.utils.extensions.therapyEventFromNsMbg import info.nightscout.androidaps.utils.sharedPreferences.SP import javax.inject.Inject @@ -27,23 +28,23 @@ class NSClientMbgWorker( @Inject lateinit var aapsLogger: AAPSLogger @Inject lateinit var sp: SP @Inject lateinit var buildHelper: BuildHelper - @Inject lateinit var config: ConfigInterface + @Inject lateinit var config: Config override fun doWork(): Result { var ret = Result.success() - val acceptNSData = !sp.getBoolean(R.string.key_ns_upload_only, true) && buildHelper.isEngineeringMode() || config.NSCLIENT + val acceptNSData = sp.getBoolean(R.string.key_ns_receive_therapy_events, false) || config.NSCLIENT if (!acceptNSData) return ret val mbgArray = dataWorker.pickupJSONArray(inputData.getLong(DataWorker.STORE_KEY, -1)) - ?: return Result.failure() + ?: return Result.failure(workDataOf("Error" to "missing input data")) for (i in 0 until mbgArray.length()) { val nsMbg = NSMbg(mbgArray.getJSONObject(i)) if (!nsMbg.isValid()) continue - repository.runTransactionForResult(SyncTherapyEventTransaction(therapyEventFromNsMbg(nsMbg))) + repository.runTransactionForResult(SyncNsTherapyEventTransaction(therapyEventFromNsMbg(nsMbg), false)) .doOnError { aapsLogger.error("Error while saving therapy event", it) - ret = Result.failure() + ret = Result.failure(workDataOf("Error" to it.toString())) } .blockingGet() .also { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientPlugin.java deleted file mode 100644 index 48257ee342..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientPlugin.java +++ /dev/null @@ -1,360 +0,0 @@ -package info.nightscout.androidaps.plugins.general.nsclient; - -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; -import android.os.Bundle; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.IBinder; -import android.text.Spanned; - -import androidx.annotation.NonNull; -import androidx.preference.PreferenceFragmentCompat; -import androidx.preference.SwitchPreference; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import java.util.ArrayList; -import java.util.List; - -import javax.inject.Inject; -import javax.inject.Singleton; - -import dagger.android.HasAndroidInjector; -import info.nightscout.androidaps.Config; -import info.nightscout.androidaps.Constants; -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.database.AppRepository; -import info.nightscout.androidaps.database.entities.TemporaryTarget; -import info.nightscout.androidaps.database.entities.TherapyEvent; -import info.nightscout.androidaps.database.entities.UserEntry.Action; -import info.nightscout.androidaps.database.entities.UserEntry.Units; -import info.nightscout.androidaps.database.entities.UserEntry.ValueWithUnit; -import info.nightscout.androidaps.database.transactions.SyncTemporaryTargetTransaction; -import info.nightscout.androidaps.database.transactions.SyncTherapyEventTransaction; -import info.nightscout.androidaps.events.EventAppExit; -import info.nightscout.androidaps.events.EventChargingState; -import info.nightscout.androidaps.events.EventNetworkChange; -import info.nightscout.androidaps.events.EventNsTreatment; -import info.nightscout.androidaps.events.EventPreferenceChange; -import info.nightscout.androidaps.interfaces.ActivePluginProvider; -import info.nightscout.androidaps.interfaces.DatabaseHelperInterface; -import info.nightscout.androidaps.interfaces.PluginBase; -import info.nightscout.androidaps.interfaces.PluginDescription; -import info.nightscout.androidaps.interfaces.PluginType; -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.nsclient.data.AlarmAck; -import info.nightscout.androidaps.plugins.general.nsclient.data.NSAlarm; -import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientNewLog; -import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientResend; -import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientStatus; -import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientUpdateGUI; -import info.nightscout.androidaps.plugins.general.nsclient.services.NSClientService; -import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification; -import info.nightscout.androidaps.plugins.general.overview.notifications.Notification; -import info.nightscout.androidaps.services.Intents; -import info.nightscout.androidaps.utils.FabricPrivacy; -import info.nightscout.androidaps.utils.HtmlHelper; -import info.nightscout.androidaps.utils.JsonHelper; -import info.nightscout.androidaps.utils.ToastUtils; -import info.nightscout.androidaps.utils.buildHelper.BuildHelper; -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 static info.nightscout.androidaps.utils.extensions.TemporaryTargetExtensionKt.temporaryTargetFromJson; -import static info.nightscout.androidaps.utils.extensions.TemporaryTargetExtensionKt.temporaryTargetFromNsIdForInvalidating; -import static info.nightscout.androidaps.utils.extensions.TherapyEventExtensionKt.therapyEventFromJson; -import static info.nightscout.androidaps.utils.extensions.TherapyEventExtensionKt.therapyEventFromNsIdForInvalidating; - -@Singleton -public class NSClientPlugin extends PluginBase { - private final CompositeDisposable disposable = new CompositeDisposable(); - - private final AAPSLogger aapsLogger; - private final RxBusWrapper rxBus; - private final ResourceHelper resourceHelper; - private final Context context; - private final AapsSchedulers aapsSchedulers; - private final FabricPrivacy fabricPrivacy; - private final SP sp; - private final Config config; - private final BuildHelper buildHelper; - private final ActivePluginProvider activePlugin; - private final NSUpload nsUpload; - private final AppRepository repository; - private final DatabaseHelperInterface databaseHelper; - private final UserEntryLogger uel; - - public Handler handler; - - private final List listLog = new ArrayList<>(); - Spanned textLog = HtmlHelper.INSTANCE.fromHtml(""); - - public boolean paused; - boolean autoscroll; - - public String status = ""; - - public NSClientService nsClientService = null; - - private final NsClientReceiverDelegate nsClientReceiverDelegate; - - @Inject - public NSClientPlugin( - HasAndroidInjector injector, - AAPSLogger aapsLogger, - AapsSchedulers aapsSchedulers, - RxBusWrapper rxBus, - ResourceHelper resourceHelper, - Context context, - FabricPrivacy fabricPrivacy, - SP sp, - NsClientReceiverDelegate nsClientReceiverDelegate, - Config config, - BuildHelper buildHelper, - ActivePluginProvider activePlugin, - NSUpload nsUpload, - DatabaseHelperInterface databaseHelper, - AppRepository repository, - UserEntryLogger uel - ) { - super(new PluginDescription() - .mainType(PluginType.GENERAL) - .fragmentClass(NSClientFragment.class.getName()) - .pluginIcon(R.drawable.ic_nightscout_syncs) - .pluginName(R.string.nsclientinternal) - .shortName(R.string.nsclientinternal_shortname) - .preferencesId(R.xml.pref_nsclientinternal) - .description(R.string.description_ns_client), - aapsLogger, resourceHelper, injector - ); - - this.aapsLogger = aapsLogger; - this.aapsSchedulers = aapsSchedulers; - this.rxBus = rxBus; - this.resourceHelper = resourceHelper; - this.context = context; - this.fabricPrivacy = fabricPrivacy; - this.sp = sp; - this.nsClientReceiverDelegate = nsClientReceiverDelegate; - this.config = config; - this.buildHelper = buildHelper; - this.activePlugin = activePlugin; - this.nsUpload = nsUpload; - this.databaseHelper = databaseHelper; - this.repository = repository; - this.uel = uel; - - if (config.getNSCLIENT()) { - getPluginDescription().alwaysEnabled(true).visibleByDefault(true); - } - if (handler == null) { - HandlerThread handlerThread = new HandlerThread(NSClientPlugin.class.getSimpleName() + "Handler"); - handlerThread.start(); - handler = new Handler(handlerThread.getLooper()); - } - - } - - public boolean isAllowed() { - return nsClientReceiverDelegate.allowed; - } - - - @Override - protected void onStart() { - paused = sp.getBoolean(R.string.key_nsclientinternal_paused, false); - autoscroll = sp.getBoolean(R.string.key_nsclientinternal_autoscroll, true); - - Intent intent = new Intent(context, NSClientService.class); - context.bindService(intent, mConnection, Context.BIND_AUTO_CREATE); - super.onStart(); - - nsClientReceiverDelegate.grabReceiversState(); - disposable.add(rxBus - .toObservable(EventNSClientStatus.class) - .observeOn(aapsSchedulers.getIo()) - .subscribe(event -> { - status = event.getStatus(resourceHelper); - rxBus.send(new EventNSClientUpdateGUI()); - }, fabricPrivacy::logException) - ); - disposable.add(rxBus - .toObservable(EventNetworkChange.class) - .observeOn(aapsSchedulers.getIo()) - .subscribe(nsClientReceiverDelegate::onStatusEvent, fabricPrivacy::logException) - ); - disposable.add(rxBus - .toObservable(EventPreferenceChange.class) - .observeOn(aapsSchedulers.getIo()) - .subscribe(nsClientReceiverDelegate::onStatusEvent, fabricPrivacy::logException) - ); - disposable.add(rxBus - .toObservable(EventAppExit.class) - .observeOn(aapsSchedulers.getIo()) - .subscribe(event -> { - if (nsClientService != null) { - context.unbindService(mConnection); - } - }, fabricPrivacy::logException) - ); - disposable.add(rxBus - .toObservable(EventNSClientNewLog.class) - .observeOn(aapsSchedulers.getIo()) - .subscribe(event -> { - addToLog(event); - aapsLogger.debug(LTag.NSCLIENT, event.getAction() + " " + event.getLogText()); - }, fabricPrivacy::logException) - ); - disposable.add(rxBus - .toObservable(EventChargingState.class) - .observeOn(aapsSchedulers.getIo()) - .subscribe(nsClientReceiverDelegate::onStatusEvent, fabricPrivacy::logException) - ); - disposable.add(rxBus - .toObservable(EventNSClientResend.class) - .observeOn(aapsSchedulers.getIo()) - .subscribe(event -> resend(event.getReason()), fabricPrivacy::logException) - ); - } - - @Override - protected void onStop() { - context.getApplicationContext().unbindService(mConnection); - disposable.clear(); - super.onStop(); - } - - @Override - public void preprocessPreferences(@NonNull PreferenceFragmentCompat preferenceFragment) { - super.preprocessPreferences(preferenceFragment); - - if (config.getNSCLIENT()) { - SwitchPreference key_ns_uploadlocalprofile = preferenceFragment.findPreference(resourceHelper.gs(R.string.key_ns_uploadlocalprofile)); - if (key_ns_uploadlocalprofile != null) key_ns_uploadlocalprofile.setVisible(false); - SwitchPreference key_ns_autobackfill = preferenceFragment.findPreference(resourceHelper.gs(R.string.key_ns_autobackfill)); - if (key_ns_autobackfill != null) key_ns_autobackfill.setVisible(false); - SwitchPreference key_ns_create_announcements_from_errors = preferenceFragment.findPreference(resourceHelper.gs(R.string.key_ns_create_announcements_from_errors)); - if (key_ns_create_announcements_from_errors != null) - key_ns_create_announcements_from_errors.setVisible(false); - SwitchPreference key_ns_create_announcements_from_carbs_req = preferenceFragment.findPreference(resourceHelper.gs(R.string.key_ns_create_announcements_from_carbs_req)); - if (key_ns_create_announcements_from_carbs_req != null) - key_ns_create_announcements_from_carbs_req.setVisible(false); - SwitchPreference key_ns_upload_only = preferenceFragment.findPreference(resourceHelper.gs(R.string.key_ns_upload_only)); - if (key_ns_upload_only != null) { - key_ns_upload_only.setVisible(false); - key_ns_upload_only.setEnabled(false); - } - SwitchPreference key_ns_sync_use_absolute = preferenceFragment.findPreference(resourceHelper.gs(R.string.key_ns_sync_use_absolute)); - if (key_ns_sync_use_absolute != null) key_ns_sync_use_absolute.setVisible(false); - } else { - // APS or pumpControl mode - SwitchPreference key_ns_upload_only = preferenceFragment.findPreference(resourceHelper.gs(R.string.key_ns_upload_only)); - if (key_ns_upload_only != null) - key_ns_upload_only.setVisible(buildHelper.isEngineeringMode()); - } - } - - private final ServiceConnection mConnection = new ServiceConnection() { - - public void onServiceDisconnected(ComponentName name) { - aapsLogger.debug(LTag.NSCLIENT, "Service is disconnected"); - nsClientService = null; - } - - public void onServiceConnected(ComponentName name, IBinder service) { - aapsLogger.debug(LTag.NSCLIENT, "Service is connected"); - NSClientService.LocalBinder mLocalBinder = (NSClientService.LocalBinder) service; - if (mLocalBinder != null) // is null when running in roboelectric - nsClientService = mLocalBinder.getServiceInstance(); - } - }; - - synchronized void clearLog() { - handler.post(() -> { - synchronized (listLog) { - listLog.clear(); - } - rxBus.send(new EventNSClientUpdateGUI()); - }); - } - - private synchronized void addToLog(final EventNSClientNewLog ev) { - handler.post(() -> { - synchronized (listLog) { - listLog.add(ev); - // remove the first line if log is too large - if (listLog.size() >= Constants.MAX_LOG_LINES) { - listLog.remove(0); - } - } - rxBus.send(new EventNSClientUpdateGUI()); - }); - } - - synchronized void updateLog() { - try { - StringBuilder newTextLog = new StringBuilder(); - synchronized (listLog) { - for (EventNSClientNewLog log : listLog) { - newTextLog.append(log.toPreparedHtml()); - } - } - textLog = HtmlHelper.INSTANCE.fromHtml(newTextLog.toString()); - } catch (OutOfMemoryError e) { - ToastUtils.showToastInUiThread(context, rxBus, "Out of memory!\nStop using this phone !!!", R.raw.error); - } - } - - void resend(String reason) { - if (nsClientService != null) - nsClientService.resend(reason); - } - - public void pause(boolean newState) { - sp.putBoolean(R.string.key_nsclientinternal_paused, newState); - paused = newState; - rxBus.send(new EventPreferenceChange(resourceHelper, R.string.key_nsclientinternal_paused)); - } - - public String url() { - return NSClientService.nsURL; - } - - public boolean hasWritePermission() { - return NSClientService.hasWriteAuth; - } - - public void handleClearAlarm(NSAlarm originalAlarm, long silenceTimeInMsec) { - - if (!isEnabled(PluginType.GENERAL)) { - return; - } - if (sp.getBoolean(R.string.key_ns_noupload, false)) { - aapsLogger.debug(LTag.NSCLIENT, "Upload disabled. Message dropped"); - return; - } - - AlarmAck ack = new AlarmAck(); - ack.level = originalAlarm.level(); - ack.group = originalAlarm.group(); - ack.silenceTime = silenceTimeInMsec; - - if (nsClientService != null) - nsClientService.sendAlarmAck(ack); - } - - public void updateLatestDateReceivedIfNewer(long latestReceived) { - if (latestReceived > nsClientService.latestDateInReceivedData) - nsClientService.latestDateInReceivedData = latestReceived; - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientPlugin.kt new file mode 100644 index 0000000000..54bc13f0bd --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientPlugin.kt @@ -0,0 +1,243 @@ +package info.nightscout.androidaps.plugins.general.nsclient + +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import android.content.ServiceConnection +import android.os.Handler +import android.os.HandlerThread +import android.os.IBinder +import androidx.preference.PreferenceFragmentCompat +import androidx.preference.PreferenceScreen +import androidx.preference.SwitchPreference +import dagger.android.HasAndroidInjector +import info.nightscout.androidaps.Constants +import info.nightscout.androidaps.R +import info.nightscout.androidaps.events.EventAppExit +import info.nightscout.androidaps.events.EventChargingState +import info.nightscout.androidaps.events.EventNetworkChange +import info.nightscout.androidaps.events.EventPreferenceChange +import info.nightscout.androidaps.interfaces.Config +import info.nightscout.androidaps.interfaces.PluginBase +import info.nightscout.androidaps.interfaces.PluginDescription +import info.nightscout.androidaps.interfaces.PluginType +import info.nightscout.androidaps.logging.AAPSLogger +import info.nightscout.androidaps.logging.LTag +import info.nightscout.androidaps.plugins.bus.RxBusWrapper +import info.nightscout.androidaps.plugins.general.nsclient.data.AlarmAck +import info.nightscout.androidaps.plugins.general.nsclient.data.NSAlarm +import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientNewLog +import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientResend +import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientStatus +import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientUpdateGUI +import info.nightscout.androidaps.plugins.general.nsclient.services.NSClientService +import info.nightscout.androidaps.utils.FabricPrivacy +import info.nightscout.androidaps.utils.HtmlHelper.fromHtml +import info.nightscout.androidaps.utils.ToastUtils +import info.nightscout.androidaps.utils.buildHelper.BuildHelper +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 java.util.* +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class NSClientPlugin @Inject constructor( + injector: HasAndroidInjector, + aapsLogger: AAPSLogger, + private val aapsSchedulers: AapsSchedulers, + private val rxBus: RxBusWrapper, + resourceHelper: ResourceHelper, + private val context: Context, + private val fabricPrivacy: FabricPrivacy, + private val sp: SP, + private val nsClientReceiverDelegate: NsClientReceiverDelegate, + private val config: Config, + private val buildHelper: BuildHelper +) : PluginBase(PluginDescription() + .mainType(PluginType.GENERAL) + .fragmentClass(NSClientFragment::class.java.name) + .pluginIcon(R.drawable.ic_nightscout_syncs) + .pluginName(R.string.nsclientinternal) + .shortName(R.string.nsclientinternal_shortname) + .preferencesId(R.xml.pref_nsclientinternal) + .description(R.string.description_ns_client), + aapsLogger, resourceHelper, injector +) { + + private val disposable = CompositeDisposable() + var handler: Handler? = null + private val listLog: MutableList = ArrayList() + var textLog = fromHtml("") + var paused = false + var autoscroll = false + var status = "" + var nsClientService: NSClientService? = null + val isAllowed: Boolean + get() = nsClientReceiverDelegate.allowed + + init { + if (config.NSCLIENT) { + pluginDescription.alwaysEnabled(true).visibleByDefault(true) + } + if (handler == null) { + val handlerThread = HandlerThread(NSClientPlugin::class.java.simpleName + "Handler") + handlerThread.start() + handler = Handler(handlerThread.looper) + } + } + + override fun onStart() { + paused = sp.getBoolean(R.string.key_nsclientinternal_paused, false) + autoscroll = sp.getBoolean(R.string.key_nsclientinternal_autoscroll, true) + val intent = Intent(context, NSClientService::class.java) + context.bindService(intent, mConnection, Context.BIND_AUTO_CREATE) + super.onStart() + nsClientReceiverDelegate.grabReceiversState() + disposable.add(rxBus + .toObservable(EventNSClientStatus::class.java) + .observeOn(aapsSchedulers.io) + .subscribe({ event: EventNSClientStatus -> + status = event.getStatus(resourceHelper) + rxBus.send(EventNSClientUpdateGUI()) + }, fabricPrivacy::logException) + ) + disposable.add(rxBus + .toObservable(EventNetworkChange::class.java) + .observeOn(aapsSchedulers.io) + .subscribe({ ev -> nsClientReceiverDelegate.onStatusEvent(ev) }, fabricPrivacy::logException) + ) + disposable.add(rxBus + .toObservable(EventPreferenceChange::class.java) + .observeOn(aapsSchedulers.io) + .subscribe({ ev -> nsClientReceiverDelegate.onStatusEvent(ev) }, fabricPrivacy::logException) + ) + disposable.add(rxBus + .toObservable(EventAppExit::class.java) + .observeOn(aapsSchedulers.io) + .subscribe({ if (nsClientService != null) context.unbindService(mConnection) }, fabricPrivacy::logException) + ) + disposable.add(rxBus + .toObservable(EventNSClientNewLog::class.java) + .observeOn(aapsSchedulers.io) + .subscribe({ event: EventNSClientNewLog -> + addToLog(event) + aapsLogger.debug(LTag.NSCLIENT, event.action + " " + event.logText) + }, fabricPrivacy::logException) + ) + disposable.add(rxBus + .toObservable(EventChargingState::class.java) + .observeOn(aapsSchedulers.io) + .subscribe({ ev -> nsClientReceiverDelegate.onStatusEvent(ev) }, fabricPrivacy::logException) + ) + disposable.add(rxBus + .toObservable(EventNSClientResend::class.java) + .observeOn(aapsSchedulers.io) + .subscribe({ event -> resend(event.reason) }, fabricPrivacy::logException) + ) + } + + override fun onStop() { + context.applicationContext.unbindService(mConnection) + disposable.clear() + super.onStop() + } + + override fun preprocessPreferences(preferenceFragment: PreferenceFragmentCompat) { + super.preprocessPreferences(preferenceFragment) + if (config.NSCLIENT) { + preferenceFragment.findPreference(resourceHelper.gs(R.string.ns_sync_options))?.isVisible = false + + preferenceFragment.findPreference(resourceHelper.gs(R.string.key_ns_create_announcements_from_errors))?.isVisible = false + preferenceFragment.findPreference(resourceHelper.gs(R.string.key_ns_create_announcements_from_carbs_req))?.isVisible = false + preferenceFragment.findPreference(resourceHelper.gs(R.string.key_ns_sync_use_absolute))?.isVisible = false + } else { + // APS or pumpControl mode + preferenceFragment.findPreference(resourceHelper.gs(R.string.key_ns_receive_profile_switch))?.isVisible = buildHelper.isEngineeringMode() + preferenceFragment.findPreference(resourceHelper.gs(R.string.key_ns_receive_insulin))?.isVisible = buildHelper.isEngineeringMode() + preferenceFragment.findPreference(resourceHelper.gs(R.string.key_ns_receive_carbs))?.isVisible = buildHelper.isEngineeringMode() + preferenceFragment.findPreference(resourceHelper.gs(R.string.key_ns_receive_temp_target))?.isVisible = buildHelper.isEngineeringMode() + } + } + + private val mConnection: ServiceConnection = object : ServiceConnection { + override fun onServiceDisconnected(name: ComponentName) { + aapsLogger.debug(LTag.NSCLIENT, "Service is disconnected") + nsClientService = null + } + + override fun onServiceConnected(name: ComponentName, service: IBinder) { + aapsLogger.debug(LTag.NSCLIENT, "Service is connected") + val mLocalBinder = service as NSClientService.LocalBinder + @Suppress("UNNECESSARY_SAFE_CALL") + nsClientService = mLocalBinder?.serviceInstance // is null when running in roboelectric + } + } + + @Synchronized fun clearLog() { + handler?.post { + synchronized(listLog) { listLog.clear() } + rxBus.send(EventNSClientUpdateGUI()) + } + } + + @Synchronized private fun addToLog(ev: EventNSClientNewLog) { + handler?.post { + synchronized(listLog) { + listLog.add(ev) + // remove the first line if log is too large + if (listLog.size >= Constants.MAX_LOG_LINES) { + listLog.removeAt(0) + } + } + rxBus.send(EventNSClientUpdateGUI()) + } + } + + @Synchronized fun updateLog() { + try { + val newTextLog = StringBuilder() + synchronized(listLog) { + for (log in listLog) { + newTextLog.append(log.toPreparedHtml()) + } + } + textLog = fromHtml(newTextLog.toString()) + } catch (e: OutOfMemoryError) { + ToastUtils.showToastInUiThread(context, rxBus, "Out of memory!\nStop using this phone !!!", R.raw.error) + } + } + + fun resend(reason: String) { + nsClientService?.resend(reason) + } + + fun pause(newState: Boolean) { + sp.putBoolean(R.string.key_nsclientinternal_paused, newState) + paused = newState + rxBus.send(EventPreferenceChange(resourceHelper, R.string.key_nsclientinternal_paused)) + } + + fun url(): String = nsClientService?.nsURL ?: "" + fun hasWritePermission(): Boolean = nsClientService?.hasWriteAuth ?: false + + fun handleClearAlarm(originalAlarm: NSAlarm, silenceTimeInMilliseconds: Long) { + if (!isEnabled(PluginType.GENERAL)) return + if (!sp.getBoolean(R.string.key_ns_upload, false)) { + aapsLogger.debug(LTag.NSCLIENT, "Upload disabled. Message dropped") + return + } + nsClientService?.sendAlarmAck( + AlarmAck().also { ack -> + ack.level = originalAlarm.level() + ack.group = originalAlarm.group() + ack.silenceTime = silenceTimeInMilliseconds + }) + } + + fun updateLatestDateReceivedIfNewer(latestReceived: Long) { + nsClientService?.let { if (latestReceived > it.latestDateInReceivedData) it.latestDateInReceivedData = latestReceived } + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientRemoveWorker.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientRemoveWorker.kt index a80c71c803..ee8dac6c47 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientRemoveWorker.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientRemoveWorker.kt @@ -3,17 +3,16 @@ package info.nightscout.androidaps.plugins.general.nsclient import android.content.Context import androidx.work.Worker import androidx.work.WorkerParameters +import androidx.work.workDataOf import dagger.android.HasAndroidInjector import info.nightscout.androidaps.R import info.nightscout.androidaps.database.AppRepository -import info.nightscout.androidaps.database.entities.UserEntry -import info.nightscout.androidaps.database.entities.UserEntry.ValueWithUnit -import info.nightscout.androidaps.database.transactions.SyncTemporaryTargetTransaction -import info.nightscout.androidaps.database.transactions.SyncTherapyEventTransaction -import info.nightscout.androidaps.events.EventNsTreatment -import info.nightscout.androidaps.events.EventNsTreatment.Companion.REMOVE -import info.nightscout.androidaps.interfaces.ConfigInterface -import info.nightscout.androidaps.interfaces.DatabaseHelperInterface +import info.nightscout.androidaps.database.entities.UserEntry.Action +import info.nightscout.androidaps.database.entities.UserEntry.Sources +import info.nightscout.androidaps.database.entities.ValueWithUnit +import info.nightscout.androidaps.database.transactions.* +import info.nightscout.androidaps.extensions.* +import info.nightscout.androidaps.interfaces.Config import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.logging.UserEntryLogger @@ -21,9 +20,8 @@ import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.receivers.DataWorker import info.nightscout.androidaps.utils.JsonHelper import info.nightscout.androidaps.utils.buildHelper.BuildHelper -import info.nightscout.androidaps.utils.extensions.temporaryTargetFromNsIdForInvalidating -import info.nightscout.androidaps.utils.extensions.therapyEventFromNsIdForInvalidating import info.nightscout.androidaps.utils.sharedPreferences.SP +import java.util.concurrent.TimeUnit import javax.inject.Inject // This will not be needed fpr NS v3 @@ -38,20 +36,20 @@ class NSClientRemoveWorker( @Inject lateinit var aapsLogger: AAPSLogger @Inject lateinit var buildHelper: BuildHelper @Inject lateinit var sp: SP - @Inject lateinit var config: ConfigInterface + @Inject lateinit var config: Config @Inject lateinit var repository: AppRepository - @Inject lateinit var databaseHelper: DatabaseHelperInterface @Inject lateinit var rxBus: RxBusWrapper @Inject lateinit var uel: UserEntryLogger override fun doWork(): Result { - val acceptNSData = !sp.getBoolean(R.string.key_ns_upload_only, true) && buildHelper.isEngineeringMode() || config.NSCLIENT - if (!acceptNSData) return Result.failure() + // Do not accept removed data over WS. Only invalidated trough NSClient + @Suppress("ConstantConditionIf") + if (true) return Result.success() var ret = Result.success() val treatments = dataWorker.pickupJSONArray(inputData.getLong(DataWorker.STORE_KEY, -1)) - ?: return Result.failure() + ?: return Result.failure(workDataOf("Error" to "missing input data")) for (i in 0 until treatments.length()) { val json = treatments.getJSONObject(i) @@ -59,47 +57,120 @@ class NSClientRemoveWorker( // room Temporary target val temporaryTarget = temporaryTargetFromNsIdForInvalidating(nsId) - repository.runTransactionForResult(SyncTemporaryTargetTransaction(temporaryTarget)) + repository.runTransactionForResult(SyncNsTemporaryTargetTransaction(temporaryTarget, invalidateByNsOnly = true)) .doOnError { - aapsLogger.error(LTag.DATABASE, "Error while removing temporary target", it) - ret = Result.failure() + aapsLogger.error(LTag.DATABASE, "Error while invalidating temporary target", it) + ret = Result.failure(workDataOf("Error" to it.toString())) } .blockingGet() .also { result -> - result.invalidated.forEach { + result.invalidated.forEach { tt -> uel.log( - UserEntry.Action.TT_DELETED_FROM_NS, - ValueWithUnit(it.reason.text, UserEntry.Units.TherapyEvent), - ValueWithUnit(it.lowTarget, UserEntry.Units.Mg_Dl, true), - ValueWithUnit(it.highTarget, UserEntry.Units.Mg_Dl, it.lowTarget != it.highTarget), - ValueWithUnit(it.duration.toInt() / 60000, UserEntry.Units.M, it.duration != 0L) + Action.TT_REMOVED, Sources.NSClient, + ValueWithUnit.TherapyEventTTReason(tt.reason), + ValueWithUnit.Mgdl(tt.lowTarget), + ValueWithUnit.Mgdl(tt.highTarget).takeIf { tt.lowTarget != tt.highTarget }, + ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(tt.duration).toInt()).takeIf { tt.duration != 0L } ) } } // room Therapy Event val therapyEvent = therapyEventFromNsIdForInvalidating(nsId) - repository.runTransactionForResult(SyncTherapyEventTransaction(therapyEvent)) + repository.runTransactionForResult(SyncNsTherapyEventTransaction(therapyEvent, invalidateByNsOnly = true)) .doOnError { - aapsLogger.error(LTag.DATABASE, "Error while removing therapy event", it) - ret = Result.failure() + aapsLogger.error(LTag.DATABASE, "Error while invalidating therapy event", it) + ret = Result.failure(workDataOf("Error" to it.toString())) + } + .blockingGet() + .also { result -> + result.invalidated.forEach { + uel.log(Action.CAREPORTAL_REMOVED, Sources.NSClient, + (it.note ?: ""), + ValueWithUnit.Timestamp(it.timestamp), + ValueWithUnit.TherapyEventType(it.type)) + } + } + + // room Bolus + val bolus = bolusFromNsIdForInvalidating(nsId) + repository.runTransactionForResult(SyncNsBolusTransaction(bolus, invalidateByNsOnly = true)) + .doOnError { + aapsLogger.error(LTag.DATABASE, "Error while invalidating bolus", it) + ret = Result.failure(workDataOf("Error" to it.toString())) + } + .blockingGet() + .also { result -> + result.invalidated.forEach { + uel.log(Action.CAREPORTAL_REMOVED, Sources.NSClient, + ValueWithUnit.Timestamp(it.timestamp), + ValueWithUnit.Insulin(it.amount)) + } + } + + // room Carbs + val carbs = carbsFromNsIdForInvalidating(nsId) + repository.runTransactionForResult(SyncNsCarbsTransaction(carbs, invalidateByNsOnly = true)) + .doOnError { + aapsLogger.error(LTag.DATABASE, "Error while invalidating carbs", it) + ret = Result.failure(workDataOf("Error" to it.toString())) + } + .blockingGet() + .also { result -> + result.invalidated.forEach { + uel.log(Action.CAREPORTAL_REMOVED, Sources.NSClient, + ValueWithUnit.Timestamp(it.timestamp), + ValueWithUnit.Gram(it.amount.toInt())) + } + } + + // room TemporaryBasal + val temporaryBasal = temporaryBasalFromNsIdForInvalidating(nsId) + repository.runTransactionForResult(SyncNsTemporaryBasalTransaction(temporaryBasal, invalidateByNsOnly = true)) + .doOnError { + aapsLogger.error(LTag.DATABASE, "Error while invalidating temporary basal", it) + ret = Result.failure(workDataOf("Error" to it.toString())) } .blockingGet() .also { result -> result.invalidated.forEach { uel.log( - UserEntry.Action.CAREPORTAL_DELETED_FROM_NS, (it.note ?: ""), - ValueWithUnit(it.timestamp, UserEntry.Units.Timestamp, true), - ValueWithUnit(it.type.text, UserEntry.Units.TherapyEvent)) + Action.CAREPORTAL_REMOVED, Sources.NSClient, + ValueWithUnit.Timestamp(it.timestamp), + ValueWithUnit.UnitPerHour(it.rate)) + } + } + // room ExtendedBolus + val extendedBolus = extendedBolusFromNsIdForInvalidating(nsId) + repository.runTransactionForResult(SyncNsExtendedBolusTransaction(extendedBolus, invalidateByNsOnly = true)) + .doOnError { + aapsLogger.error(LTag.DATABASE, "Error while invalidating extended bolus", it) + ret = Result.failure(workDataOf("Error" to it.toString())) + } + .blockingGet() + .also { result -> + result.invalidated.forEach { + uel.log( + Action.CAREPORTAL_REMOVED, Sources.NSClient, + ValueWithUnit.Timestamp(it.timestamp), + ValueWithUnit.UnitPerHour(it.rate)) } } - // Insulin, carbs - rxBus.send(EventNsTreatment(REMOVE, json)) - // old DB model - databaseHelper.deleteTempBasalById(nsId) - databaseHelper.deleteExtendedBolusById(nsId) - databaseHelper.deleteProfileSwitchById(nsId) + // room ProfileSwitch + repository.runTransactionForResult(InvalidateNsIdProfileSwitchTransaction(nsId)) + .doOnError { + aapsLogger.error(LTag.DATABASE, "Error while invalidating ProfileSwitch", it) + ret = Result.failure(workDataOf("Error" to it.toString())) + } + .blockingGet() + .also { result -> + result.invalidated.forEach { + uel.log( + Action.CAREPORTAL_REMOVED, Sources.NSClient, + ValueWithUnit.Timestamp(it.timestamp)) + } + } } return ret diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientUpdateRemoveAckWorker.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientUpdateRemoveAckWorker.kt new file mode 100644 index 0000000000..a7f51cbc70 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSClientUpdateRemoveAckWorker.kt @@ -0,0 +1,135 @@ +package info.nightscout.androidaps.plugins.general.nsclient + +import android.content.Context +import androidx.work.Worker +import androidx.work.WorkerParameters +import androidx.work.workDataOf +import dagger.android.HasAndroidInjector +import info.nightscout.androidaps.database.AppRepository +import info.nightscout.androidaps.interfaces.DataSyncSelector +import info.nightscout.androidaps.interfaces.DataSyncSelector.* +import info.nightscout.androidaps.logging.AAPSLogger +import info.nightscout.androidaps.plugins.bus.RxBusWrapper +import info.nightscout.androidaps.plugins.general.nsclient.acks.NSUpdateAck +import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientNewLog +import info.nightscout.androidaps.receivers.DataWorker +import info.nightscout.androidaps.utils.rx.AapsSchedulers +import javax.inject.Inject + +class NSClientUpdateRemoveAckWorker( + context: Context, + params: WorkerParameters +) : Worker(context, params) { + + @Inject lateinit var dataWorker: DataWorker + @Inject lateinit var aapsLogger: AAPSLogger + @Inject lateinit var repository: AppRepository + @Inject lateinit var rxBus: RxBusWrapper + @Inject lateinit var dataSyncSelector: DataSyncSelector + @Inject lateinit var aapsSchedulers: AapsSchedulers + + override fun doWork(): Result { + var ret = Result.success() + + val ack = dataWorker.pickupObject(inputData.getLong(DataWorker.STORE_KEY, -1)) as NSUpdateAck? + ?: return Result.failure(workDataOf("Error" to "missing input data")) + + // new room way + when (ack.originalObject) { + is PairTemporaryTarget -> { + val pair = ack.originalObject + dataSyncSelector.confirmLastTempTargetsIdIfGreater(pair.updateRecordId) + rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked TemporaryTarget" + ack._id)) + // Send new if waiting + dataSyncSelector.processChangedTempTargetsCompat() + ret = Result.success(workDataOf("ProcessedData" to pair.toString())) + } + + is PairGlucoseValue -> { + val pair = ack.originalObject + dataSyncSelector.confirmLastGlucoseValueIdIfGreater(pair.updateRecordId) + rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked GlucoseValue " + ack._id)) + // Send new if waiting + dataSyncSelector.processChangedGlucoseValuesCompat() + ret = Result.success(workDataOf("ProcessedData" to pair.toString())) + } + + is PairFood -> { + val pair = ack.originalObject + dataSyncSelector.confirmLastFoodIdIfGreater(pair.updateRecordId) + rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked Food " + ack._id)) + // Send new if waiting + dataSyncSelector.processChangedFoodsCompat() + ret = Result.success(workDataOf("ProcessedData" to pair.toString())) + } + + is PairTherapyEvent -> { + val pair = ack.originalObject + dataSyncSelector.confirmLastTherapyEventIdIfGreater(pair.updateRecordId) + rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked TherapyEvent " + ack._id)) + // Send new if waiting + dataSyncSelector.processChangedTherapyEventsCompat() + ret = Result.success(workDataOf("ProcessedData" to pair.toString())) + } + + is PairBolus -> { + val pair = ack.originalObject + dataSyncSelector.confirmLastBolusIdIfGreater(pair.updateRecordId) + rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked Bolus " + ack._id)) + // Send new if waiting + dataSyncSelector.processChangedBolusesCompat() + ret = Result.success(workDataOf("ProcessedData" to pair.toString())) + } + + is PairCarbs -> { + val pair = ack.originalObject + dataSyncSelector.confirmLastCarbsIdIfGreater(pair.updateRecordId) + rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked Carbs " + ack._id)) + // Send new if waiting + dataSyncSelector.processChangedCarbsCompat() + ret = Result.success(workDataOf("ProcessedData" to pair.toString())) + } + + is PairBolusCalculatorResult -> { + val pair = ack.originalObject + dataSyncSelector.confirmLastBolusCalculatorResultsIdIfGreater(pair.updateRecordId) + rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked BolusCalculatorResult " + ack._id)) + // Send new if waiting + dataSyncSelector.processChangedBolusCalculatorResultsCompat() + ret = Result.success(workDataOf("ProcessedData" to pair.toString())) + } + + is PairTemporaryBasal -> { + val pair = ack.originalObject + dataSyncSelector.confirmLastTemporaryBasalIdIfGreater(pair.updateRecordId) + rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked TemporaryBasal " + ack._id)) + // Send new if waiting + dataSyncSelector.processChangedTemporaryBasalsCompat() + ret = Result.success(workDataOf("ProcessedData" to pair.toString())) + } + + is PairExtendedBolus -> { + val pair = ack.originalObject + dataSyncSelector.confirmLastExtendedBolusIdIfGreater(pair.updateRecordId) + rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked ExtendedBolus " + ack._id)) + // Send new if waiting + dataSyncSelector.processChangedExtendedBolusesCompat() + ret = Result.success(workDataOf("ProcessedData" to pair.toString())) + } + + is PairProfileSwitch -> { + val pair = ack.originalObject + dataSyncSelector.confirmLastProfileSwitchIdIfGreater(pair.updateRecordId) + rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked ProfileSwitch " + ack._id)) + // Send new if waiting + dataSyncSelector.processChangedProfileSwitchesCompat() + ret = Result.success(workDataOf("ProcessedData" to pair.toString())) + } + } + return ret + } + + init { + (context.applicationContext as HasAndroidInjector).androidInjector().inject(this) + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NsClientReceiverDelegate.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NsClientReceiverDelegate.java deleted file mode 100644 index d54f48d511..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NsClientReceiverDelegate.java +++ /dev/null @@ -1,117 +0,0 @@ -package info.nightscout.androidaps.plugins.general.nsclient; - -import java.util.Arrays; - -import javax.inject.Inject; -import javax.inject.Singleton; - -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.events.EventChargingState; -import info.nightscout.androidaps.events.EventNetworkChange; -import info.nightscout.androidaps.events.EventPreferenceChange; -import info.nightscout.androidaps.plugins.bus.RxBusWrapper; -import info.nightscout.androidaps.receivers.ReceiverStatusStore; -import info.nightscout.androidaps.utils.resources.ResourceHelper; -import info.nightscout.androidaps.utils.sharedPreferences.SP; - -@Singleton -class NsClientReceiverDelegate { - - private boolean allowedChargingState = true; - private boolean allowedNetworkState = true; - boolean allowed = true; - - private RxBusWrapper rxBus; - private ResourceHelper resourceHelper; - private SP sp; - private ReceiverStatusStore receiverStatusStore; - - @Inject - public NsClientReceiverDelegate( - RxBusWrapper rxBus, - ResourceHelper resourceHelper, - SP sp, - ReceiverStatusStore receiverStatusStore - ) { - this.rxBus = rxBus; - this.resourceHelper = resourceHelper; - this.sp = sp; - this.receiverStatusStore = receiverStatusStore; - } - - void grabReceiversState() { - - receiverStatusStore.updateNetworkStatus(); - } - - void onStatusEvent(EventPreferenceChange ev) { - if (ev.isChanged(resourceHelper, R.string.key_ns_wifionly) || - ev.isChanged(resourceHelper, R.string.key_ns_wifi_ssids) || - ev.isChanged(resourceHelper, R.string.key_ns_allowroaming) - ) { - receiverStatusStore.updateNetworkStatus(); - onStatusEvent(receiverStatusStore.getLastNetworkEvent()); - } else if (ev.isChanged(resourceHelper, R.string.key_ns_chargingonly)) { - receiverStatusStore.broadcastChargingState(); - } - } - - void onStatusEvent(final EventChargingState ev) { - boolean newChargingState = calculateStatus(ev); - - if (newChargingState != allowedChargingState) { - allowedChargingState = newChargingState; - processStateChange(); - } - } - - void onStatusEvent(final EventNetworkChange ev) { - boolean newNetworkState = calculateStatus(ev); - - if (newNetworkState != allowedNetworkState) { - allowedNetworkState = newNetworkState; - processStateChange(); - } - } - - private void processStateChange() { - boolean newAllowedState = allowedChargingState && allowedNetworkState; - if (newAllowedState != allowed) { - allowed = newAllowedState; - rxBus.send(new EventPreferenceChange(resourceHelper.gs(R.string.key_nsclientinternal_paused))); - } - } - - boolean calculateStatus(final EventChargingState ev) { - boolean chargingOnly = sp.getBoolean(R.string.key_ns_chargingonly, false); - boolean newAllowedState = true; - - if (!ev.isCharging() && chargingOnly) { - newAllowedState = false; - } - - return newAllowedState; - } - - boolean calculateStatus(final EventNetworkChange ev) { - boolean wifiOnly = sp.getBoolean(R.string.key_ns_wifionly, false); - String allowedSSIDstring = sp.getString(R.string.key_ns_wifi_ssids, ""); - String[] allowedSSIDs = allowedSSIDstring.split(";"); - if (allowedSSIDstring.isEmpty()) allowedSSIDs = new String[0]; - boolean allowRoaming = sp.getBoolean(R.string.key_ns_allowroaming, true); - - boolean newAllowedState = true; - - if (ev.getWifiConnected()) { - if (allowedSSIDs.length != 0 && !Arrays.asList(allowedSSIDs).contains(ev.getSsid())) { - newAllowedState = false; - } - } else { - if ((!allowRoaming && ev.getRoaming()) || wifiOnly) { - newAllowedState = false; - } - } - - return newAllowedState; - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NsClientReceiverDelegate.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NsClientReceiverDelegate.kt new file mode 100644 index 0000000000..fa67b25cda --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NsClientReceiverDelegate.kt @@ -0,0 +1,91 @@ +package info.nightscout.androidaps.plugins.general.nsclient + +import info.nightscout.androidaps.R +import info.nightscout.androidaps.events.EventChargingState +import info.nightscout.androidaps.events.EventNetworkChange +import info.nightscout.androidaps.events.EventPreferenceChange +import info.nightscout.androidaps.plugins.bus.RxBusWrapper +import info.nightscout.androidaps.receivers.ReceiverStatusStore +import info.nightscout.androidaps.utils.resources.ResourceHelper +import info.nightscout.androidaps.utils.sharedPreferences.SP +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class NsClientReceiverDelegate @Inject constructor( + private val rxBus: RxBusWrapper, + private val resourceHelper: ResourceHelper, + private val sp: SP, + private val receiverStatusStore: ReceiverStatusStore +) { + + private var allowedChargingState = true + private var allowedNetworkState = true + var allowed = true + + fun grabReceiversState() { + receiverStatusStore.updateNetworkStatus() + } + + fun onStatusEvent(ev: EventPreferenceChange) { + if (ev.isChanged(resourceHelper, R.string.key_ns_wifionly) || + ev.isChanged(resourceHelper, R.string.key_ns_wifi_ssids) || + ev.isChanged(resourceHelper, R.string.key_ns_allowroaming)) { + receiverStatusStore.updateNetworkStatus() + onStatusEvent(receiverStatusStore.lastNetworkEvent) + } else if (ev.isChanged(resourceHelper, R.string.key_ns_chargingonly)) { + receiverStatusStore.broadcastChargingState() + } + } + + fun onStatusEvent(ev: EventChargingState) { + val newChargingState = calculateStatus(ev) + if (newChargingState != allowedChargingState) { + allowedChargingState = newChargingState + processStateChange() + } + } + + fun onStatusEvent(ev: EventNetworkChange?) { + val newNetworkState = calculateStatus(ev) + if (newNetworkState != allowedNetworkState) { + allowedNetworkState = newNetworkState + processStateChange() + } + } + + private fun processStateChange() { + val newAllowedState = allowedChargingState && allowedNetworkState + if (newAllowedState != allowed) { + allowed = newAllowedState + rxBus.send(EventPreferenceChange(resourceHelper.gs(R.string.key_nsclientinternal_paused))) + } + } + + fun calculateStatus(ev: EventChargingState): Boolean { + val chargingOnly = sp.getBoolean(R.string.key_ns_chargingonly, false) + var newAllowedState = true + if (!ev.isCharging && chargingOnly) { + newAllowedState = false + } + return newAllowedState + } + + fun calculateStatus(ev: EventNetworkChange?): Boolean { + val wifiOnly = sp.getBoolean(R.string.key_ns_wifionly, false) + val allowedSsidString = sp.getString(R.string.key_ns_wifi_ssids, "") + val allowedSSIDs: List = if (allowedSsidString.isEmpty()) List(0) { "" } else allowedSsidString.split(";") + val allowRoaming = sp.getBoolean(R.string.key_ns_allowroaming, true) + var newAllowedState = true + if (ev?.wifiConnected == true) { + if (allowedSSIDs.isNotEmpty() && !allowedSSIDs.contains(ev.ssid)) { + newAllowedState = false + } + } else { + if (!allowRoaming && ev?.roaming == true || wifiOnly) { + newAllowedState = false + } + } + return newAllowedState + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/UploadQueue.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/UploadQueue.java deleted file mode 100644 index f7ba9c3c0a..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/UploadQueue.java +++ /dev/null @@ -1,146 +0,0 @@ -package info.nightscout.androidaps.plugins.general.nsclient; - -import android.content.Context; -import android.content.Intent; -import android.os.SystemClock; - -import com.j256.ormlite.dao.CloseableIterator; - -import org.json.JSONException; -import org.json.JSONObject; - -import java.sql.SQLException; - -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.db.DatabaseHelper; -import info.nightscout.androidaps.db.DbRequest; -import info.nightscout.androidaps.interfaces.DatabaseHelperInterface; -import info.nightscout.androidaps.interfaces.UploadQueueAdminInterface; -import info.nightscout.androidaps.interfaces.UploadQueueInterface; -import info.nightscout.androidaps.logging.AAPSLogger; -import info.nightscout.androidaps.logging.LTag; -import info.nightscout.androidaps.plugins.bus.RxBusWrapper; -import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientResend; -import info.nightscout.androidaps.plugins.general.nsclient.services.NSClientService; -import info.nightscout.androidaps.utils.sharedPreferences.SP; - -/** - * Created by mike on 21.02.2016. - */ -public class UploadQueue implements UploadQueueAdminInterface { - private final AAPSLogger aapsLogger; - private final DatabaseHelperInterface databaseHelper; - private final Context context; - private final SP sp; - private final RxBusWrapper rxBus; - - public UploadQueue( - AAPSLogger aapsLogger, - DatabaseHelperInterface databaseHelper, - Context context, - SP sp, - RxBusWrapper rxBus - ) { - this.aapsLogger = aapsLogger; - this.databaseHelper = databaseHelper; - this.context = context; - this.sp = sp; - this.rxBus = rxBus; - } - - public String status() { - return "QUEUE: " + databaseHelper.size(DatabaseHelper.DATABASE_DBREQUESTS); - } - - @Override - public long size() { - return databaseHelper.size(DatabaseHelper.DATABASE_DBREQUESTS); - } - - private void startService() { - if (NSClientService.handler == null) { - context.startService(new Intent(context, NSClientService.class)); - SystemClock.sleep(2000); - } - } - - public void add(final DbRequest dbr) { - if (sp.getBoolean(R.string.key_ns_noupload, false)) return; - aapsLogger.debug(LTag.NSCLIENT, "Adding to queue: " + dbr.log()); - try { - databaseHelper.create(dbr); - } catch (Exception e) { - aapsLogger.error("Unhandled exception", e); - } - rxBus.send(new EventNSClientResend("newdata")); - } - - @Override public void clearQueue() { - startService(); - if (NSClientService.handler != null) { - NSClientService.handler.post(() -> { - aapsLogger.debug(LTag.NSCLIENT, "ClearQueue"); - databaseHelper.deleteAllDbRequests(); - aapsLogger.debug(LTag.NSCLIENT, status()); - }); - } - } - - @Override - public void removeByNsClientIdIfExists(final JSONObject record) { - startService(); - if (NSClientService.handler != null) { - NSClientService.handler.post(() -> { - try { - String id; - if (record.has("NSCLIENT_ID")) { - id = record.getString("NSCLIENT_ID"); - } else { - return; - } - if (databaseHelper.deleteDbRequest(id) == 1) { - aapsLogger.debug(LTag.NSCLIENT, "Removed item from UploadQueue. " + status()); - } - } catch (JSONException e) { - aapsLogger.error("Unhandled exception", e); - } - }); - } - } - - @Override - public void removeByMongoId(final String action, final String _id) { - if (_id == null || _id.equals("")) - return; - startService(); - if (NSClientService.handler != null) { - NSClientService.handler.post(() -> { - databaseHelper.deleteDbRequestbyMongoId(action, _id); - aapsLogger.debug(LTag.NSCLIENT, "Removing " + _id + " from UploadQueue. " + status()); - }); - } - } - - @Override public String textList() { - String result = ""; - CloseableIterator iterator; - try { - iterator = databaseHelper.getDbRequestIterator(); - try { - while (iterator.hasNext()) { - DbRequest dbr = iterator.next(); - result += "
"; - result += dbr.action.toUpperCase() + " "; - result += dbr.collection + ": "; - result += dbr.data; - } - } finally { - iterator.close(); - } - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - } - return result; - } - -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/acks/NSAddAck.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/acks/NSAddAck.java deleted file mode 100644 index dbd9151938..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/acks/NSAddAck.java +++ /dev/null @@ -1,62 +0,0 @@ -package info.nightscout.androidaps.plugins.general.nsclient.acks; - -import org.json.JSONArray; -import org.json.JSONObject; - -import info.nightscout.androidaps.events.Event; -import info.nightscout.androidaps.logging.AAPSLogger; -import info.nightscout.androidaps.logging.LTag; -import info.nightscout.androidaps.plugins.bus.RxBusWrapper; -import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart; -import io.socket.client.Ack; - -/** - * Created by mike on 29.12.2015. - */ -public class NSAddAck extends Event implements Ack { - private final AAPSLogger aapsLogger; - private final RxBusWrapper rxBus; - - public String _id = null; - public String nsClientID = null; - public JSONObject json = null; - - public NSAddAck(AAPSLogger aapsLogger, RxBusWrapper rxBus) { - this.aapsLogger = aapsLogger; - this.rxBus = rxBus; - } - - public void call(Object... args) { - // Regular response - try { - JSONArray responsearray = (JSONArray) (args[0]); - JSONObject response; - if (responsearray.length() > 0) { - response = responsearray.getJSONObject(0); - _id = response.getString("_id"); - json = response; - if (response.has("NSCLIENT_ID")) { - nsClientID = response.getString("NSCLIENT_ID"); - } - } - rxBus.send(this); - return; - } catch (Exception e) { - aapsLogger.error("Unhandled exception", e); - } - // Check for not authorized - try { - JSONObject response = (JSONObject) (args[0]); - if (response.has("result")) { - _id = null; - if (response.getString("result").contains("Not")) { - rxBus.send(new EventNSClientRestart()); - return; - } - aapsLogger.debug(LTag.NSCLIENT, "DBACCESS " + response.getString("result")); - } - } catch (Exception e) { - aapsLogger.error("Unhandled exception", e); - } - } -} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/acks/NSAddAck.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/acks/NSAddAck.kt new file mode 100644 index 0000000000..930defad05 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/acks/NSAddAck.kt @@ -0,0 +1,54 @@ +package info.nightscout.androidaps.plugins.general.nsclient.acks + +import info.nightscout.androidaps.events.Event +import info.nightscout.androidaps.logging.AAPSLogger +import info.nightscout.androidaps.logging.LTag +import info.nightscout.androidaps.plugins.bus.RxBusWrapper +import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart +import io.socket.client.Ack +import org.json.JSONArray +import org.json.JSONObject + +class NSAddAck( + private val aapsLogger: AAPSLogger, + private val rxBus: RxBusWrapper, + val originalObject: Any? = null +) : Event(), Ack { + + var id: String? = null + @JvmField var nsClientID: String? = null + @JvmField var json: JSONObject? = null + override fun call(vararg args: Any) { + // Regular response + try { + val responseArray = args[0] as JSONArray + val response: JSONObject + if (responseArray.length() > 0) { + response = responseArray.getJSONObject(0) + id = response.getString("_id") + json = response + if (response.has("NSCLIENT_ID")) { + nsClientID = response.getString("NSCLIENT_ID") + } + } + rxBus.send(this) + return + } catch (e: Exception) { + aapsLogger.error("Unhandled exception", e) + } + // Check for not authorized + try { + val response = args[0] as JSONObject + if (response.has("result")) { + id = null + if (response.getString("result").contains("Not")) { + rxBus.send(EventNSClientRestart()) + return + } + aapsLogger.debug(LTag.NSCLIENT, "DBACCESS " + response.getString("result")) + } + } catch (e: Exception) { + aapsLogger.error("Unhandled exception", e) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/acks/NSUpdateAck.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/acks/NSUpdateAck.java deleted file mode 100644 index 95a00279cf..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/acks/NSUpdateAck.java +++ /dev/null @@ -1,46 +0,0 @@ -package info.nightscout.androidaps.plugins.general.nsclient.acks; - -import org.json.JSONException; -import org.json.JSONObject; - -import info.nightscout.androidaps.events.Event; -import info.nightscout.androidaps.logging.AAPSLogger; -import info.nightscout.androidaps.logging.LTag; -import info.nightscout.androidaps.plugins.bus.RxBusWrapper; -import io.socket.client.Ack; - -/** - * Created by mike on 21.02.2016. - */ -public class NSUpdateAck extends Event implements Ack { - private final AAPSLogger aapsLogger; - private final RxBusWrapper rxBus; - - public boolean result = false; - public String _id; - public String action; - - public void call(Object... args) { - JSONObject response = (JSONObject) args[0]; - if (response.has("result")) - try { - if (response.getString("result").equals("success")) - result = true; - else if (response.getString("result").equals("Missing _id")) { - result = true; - aapsLogger.debug(LTag.NSCLIENT, "Internal error: Missing _id returned on dbUpdate ack"); - } - rxBus.send(this); - } catch (JSONException e) { - aapsLogger.error("Unhandled exception", e); - } - } - - public NSUpdateAck(String action, String _id, AAPSLogger aapsLogger, RxBusWrapper rxBus) { - super(); - this.action = action; - this._id = _id; - this.aapsLogger = aapsLogger; - this.rxBus = rxBus; - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/acks/NSUpdateAck.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/acks/NSUpdateAck.kt new file mode 100644 index 0000000000..5d91178f82 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/acks/NSUpdateAck.kt @@ -0,0 +1,37 @@ +package info.nightscout.androidaps.plugins.general.nsclient.acks + +import info.nightscout.androidaps.events.Event +import info.nightscout.androidaps.logging.AAPSLogger +import info.nightscout.androidaps.logging.LTag +import info.nightscout.androidaps.plugins.bus.RxBusWrapper +import io.socket.client.Ack +import org.json.JSONException +import org.json.JSONObject + +/** + * Created by mike on 21.02.2016. + */ +class NSUpdateAck( + val action : String, + var _id: String, + private val aapsLogger: AAPSLogger, + private val rxBus: RxBusWrapper, + val originalObject: Any? = null +) : Event(), Ack { + + var result = false + override fun call(vararg args: Any) { + val response = args[0] as JSONObject + if (response.has("result")) try { + if (response.getString("result") == "success") { + result = true + } else if (response.getString("result") == "Missing _id") { + result = true + aapsLogger.debug(LTag.NSCLIENT, "Internal error: Missing _id returned on dbUpdate ack") + } + rxBus.send(this) + } catch (e: JSONException) { + aapsLogger.error("Unhandled exception", e) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/data/NSDeviceStatus.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/data/NSDeviceStatus.kt index 5657f1d48e..5faa6861c1 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/data/NSDeviceStatus.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/data/NSDeviceStatus.kt @@ -3,7 +3,7 @@ package info.nightscout.androidaps.plugins.general.nsclient.data import android.text.Spanned import dagger.android.HasAndroidInjector import info.nightscout.androidaps.R -import info.nightscout.androidaps.interfaces.ConfigInterface +import info.nightscout.androidaps.interfaces.Config import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.plugins.aps.loop.APSResult @@ -78,7 +78,7 @@ class NSDeviceStatus @Inject constructor( private val sp: SP, private val resourceHelper: ResourceHelper, private val nsSettingsStatus: NSSettingsStatus, - private val config: ConfigInterface, + private val config: Config, private val dateUtil: DateUtil, private val runningConfiguration: RunningConfiguration ) { @@ -164,12 +164,12 @@ class NSDeviceStatus @Inject constructor( // test warning level val level = when { - pumpData.clock + nsSettingsStatus.extendedPumpSettings("urgentClock") * 60 * 1000L < dateUtil._now() -> Levels.URGENT - pumpData.reservoir < nsSettingsStatus.extendedPumpSettings("urgentRes") -> Levels.URGENT + pumpData.clock + nsSettingsStatus.extendedPumpSettings("urgentClock") * 60 * 1000L < dateUtil.now() -> Levels.URGENT + pumpData.reservoir < nsSettingsStatus.extendedPumpSettings("urgentRes") -> Levels.URGENT pumpData.isPercent && pumpData.percent < nsSettingsStatus.extendedPumpSettings("urgentBattP") -> Levels.URGENT - !pumpData.isPercent && pumpData.voltage < nsSettingsStatus.extendedPumpSettings("urgentBattV") -> Levels.URGENT - pumpData.clock + nsSettingsStatus.extendedPumpSettings("warnClock") * 60 * 1000L < dateUtil._now() -> Levels.WARN - pumpData.reservoir < nsSettingsStatus.extendedPumpSettings("warnRes") -> Levels.WARN + !pumpData.isPercent && pumpData.voltage < nsSettingsStatus.extendedPumpSettings("urgentBattV") -> Levels.URGENT + pumpData.clock + nsSettingsStatus.extendedPumpSettings("warnClock") * 60 * 1000L < dateUtil.now() -> Levels.WARN + pumpData.reservoir < nsSettingsStatus.extendedPumpSettings("warnRes") -> Levels.WARN pumpData.isPercent && pumpData.percent < nsSettingsStatus.extendedPumpSettings("warnBattP") -> Levels.WARN !pumpData.isPercent && pumpData.voltage < nsSettingsStatus.extendedPumpSettings("warnBattV") -> Levels.WARN else -> Levels.INFO @@ -179,7 +179,7 @@ class NSDeviceStatus @Inject constructor( if (fields.contains("reservoir")) string.append(pumpData.reservoir.toInt()).append("U ") if (fields.contains("battery") && pumpData.isPercent) string.append(pumpData.percent).append("% ") if (fields.contains("battery") && !pumpData.isPercent) string.append(Round.roundTo(pumpData.voltage, 0.001)).append(" ") - if (fields.contains("clock")) string.append(DateUtil.minAgo(resourceHelper, pumpData.clock)).append(" ") + if (fields.contains("clock")) string.append(dateUtil.minAgo(resourceHelper, pumpData.clock)).append(" ") if (fields.contains("status")) string.append(pumpData.status).append(" ") if (fields.contains("device")) string.append(device).append(" ") string.append("") // color @@ -201,7 +201,7 @@ class NSDeviceStatus @Inject constructor( try { val data = this.data ?: return val pump = if (data.has("pump")) data.getJSONObject("pump") else JSONObject() - val clock = if (pump.has("clock")) DateUtil.fromISODateString(pump.getString("clock")).time else 0L + val clock = if (pump.has("clock")) dateUtil.fromISODateString(pump.getString("clock")) else 0L // check if this is new data if (clock == 0L || deviceStatusPumpData != null && clock < deviceStatusPumpData!!.clock) return @@ -247,13 +247,13 @@ class NSDeviceStatus @Inject constructor( val openAps = if (jsonObject.has("openaps")) jsonObject.getJSONObject("openaps") else JSONObject() val suggested = if (openAps.has("suggested")) openAps.getJSONObject("suggested") else JSONObject() val enacted = if (openAps.has("enacted")) openAps.getJSONObject("enacted") else JSONObject() - var clock = if (suggested.has("timestamp")) DateUtil.fromISODateString(suggested.getString("timestamp")).time else 0L + var clock = if (suggested.has("timestamp")) dateUtil.fromISODateString(suggested.getString("timestamp")) else 0L // check if this is new data if (clock != 0L && clock > deviceStatusOpenAPSData.clockSuggested) { deviceStatusOpenAPSData.suggested = suggested deviceStatusOpenAPSData.clockSuggested = clock } - clock = if (enacted.has("timestamp")) DateUtil.fromISODateString(enacted.getString("timestamp")).time else 0L + clock = if (enacted.has("timestamp")) dateUtil.fromISODateString(enacted.getString("timestamp")) else 0L // check if this is new data if (clock != 0L && clock > deviceStatusOpenAPSData.clockEnacted) { deviceStatusOpenAPSData.enacted = enacted @@ -273,12 +273,12 @@ class NSDeviceStatus @Inject constructor( // test warning level val level = when { - deviceStatusOpenAPSData.clockSuggested + T.mins(sp.getLong(R.string.key_nsalarm_urgent_staledatavalue, 31)).msecs() < dateUtil._now() -> Levels.URGENT - deviceStatusOpenAPSData.clockSuggested + T.mins(sp.getLong(R.string.key_nsalarm_staledatavalue, 16)).msecs() < dateUtil._now() -> Levels.WARN - else -> Levels.INFO + deviceStatusOpenAPSData.clockSuggested + T.mins(sp.getLong(R.string.key_nsalarm_urgent_staledatavalue, 31)).msecs() < dateUtil.now() -> Levels.URGENT + deviceStatusOpenAPSData.clockSuggested + T.mins(sp.getLong(R.string.key_nsalarm_staledatavalue, 16)).msecs() < dateUtil.now() -> Levels.WARN + else -> Levels.INFO } string.append("") - if (deviceStatusOpenAPSData.clockSuggested != 0L) string.append(DateUtil.minAgo(resourceHelper, deviceStatusOpenAPSData.clockSuggested)).append(" ") + if (deviceStatusOpenAPSData.clockSuggested != 0L) string.append(dateUtil.minAgo(resourceHelper, deviceStatusOpenAPSData.clockSuggested)).append(" ") string.append("") // color return fromHtml(string.toString()) } @@ -287,8 +287,8 @@ class NSDeviceStatus @Inject constructor( get() { val string = StringBuilder() try { - if (deviceStatusOpenAPSData.enacted != null && deviceStatusOpenAPSData.clockEnacted != deviceStatusOpenAPSData.clockSuggested) string.append("").append(DateUtil.minAgo(resourceHelper, deviceStatusOpenAPSData.clockEnacted)).append(" ").append(deviceStatusOpenAPSData.enacted!!.getString("reason")).append("
") - if (deviceStatusOpenAPSData.suggested != null) string.append("").append(DateUtil.minAgo(resourceHelper, deviceStatusOpenAPSData.clockSuggested)).append(" ").append(deviceStatusOpenAPSData.suggested!!.getString("reason")).append("
") + if (deviceStatusOpenAPSData.enacted != null && deviceStatusOpenAPSData.clockEnacted != deviceStatusOpenAPSData.clockSuggested) string.append("").append(dateUtil.minAgo(resourceHelper, deviceStatusOpenAPSData.clockEnacted)).append(" ").append(deviceStatusOpenAPSData.enacted!!.getString("reason")).append("
") + if (deviceStatusOpenAPSData.suggested != null) string.append("").append(dateUtil.minAgo(resourceHelper, deviceStatusOpenAPSData.clockSuggested)).append(" ").append(deviceStatusOpenAPSData.suggested!!.getString("reason")).append("
") return fromHtml(string.toString()) } catch (e: JSONException) { aapsLogger.error("Unhandled exception", e) @@ -307,7 +307,7 @@ class NSDeviceStatus @Inject constructor( val clock = when { jsonObject.has("mills") -> jsonObject.getLong("mills") - jsonObject.has("created_at") -> DateUtil.fromISODateString(jsonObject.getString("created_at")).time + jsonObject.has("created_at") -> dateUtil.fromISODateString(jsonObject.getString("created_at")) else -> 0L } val device = device diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/data/NSSettingsStatus.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/data/NSSettingsStatus.kt index e6aafce3c1..7efeb7c466 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/data/NSSettingsStatus.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/data/NSSettingsStatus.kt @@ -1,8 +1,9 @@ package info.nightscout.androidaps.plugins.general.nsclient.data import android.content.Context -import info.nightscout.androidaps.Config +import info.nightscout.androidaps.interfaces.Config import info.nightscout.androidaps.R +import info.nightscout.androidaps.database.entities.UserEntry import info.nightscout.androidaps.database.entities.UserEntry.Action import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag @@ -246,7 +247,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(Action.NS_SETTINGS_COPIED) + uel.log(Action.NS_SETTINGS_COPIED, UserEntry.Sources.NSClient) } if (context != null) OKDialog.showConfirmation(context, resourceHelper.gs(R.string.statuslights), resourceHelper.gs(R.string.copyexistingvalues), action) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/services/NSClientService.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/services/NSClientService.java deleted file mode 100644 index 809bbae38b..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/services/NSClientService.java +++ /dev/null @@ -1,894 +0,0 @@ -package info.nightscout.androidaps.plugins.general.nsclient.services; - -import android.content.Context; -import android.content.Intent; -import android.os.Binder; -import android.os.Bundle; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.IBinder; -import android.os.PowerManager; -import android.os.SystemClock; - -import androidx.localbroadcastmanager.content.LocalBroadcastManager; -import androidx.work.OneTimeWorkRequest; - -import com.google.common.base.Charsets; -import com.google.common.hash.Hashing; -import com.j256.ormlite.dao.CloseableIterator; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import java.net.URISyntaxException; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.List; - -import javax.inject.Inject; - -import dagger.android.DaggerService; -import dagger.android.HasAndroidInjector; -import info.nightscout.androidaps.Config; -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.db.DbRequest; -import info.nightscout.androidaps.events.EventAppExit; -import info.nightscout.androidaps.events.EventConfigBuilderChange; -import info.nightscout.androidaps.events.EventPreferenceChange; -import info.nightscout.androidaps.interfaces.DatabaseHelperInterface; -import info.nightscout.androidaps.interfaces.PluginType; -import info.nightscout.androidaps.interfaces.UploadQueueInterface; -import info.nightscout.androidaps.logging.AAPSLogger; -import info.nightscout.androidaps.logging.LTag; -import info.nightscout.androidaps.plugins.bus.RxBusWrapper; -import info.nightscout.androidaps.plugins.general.food.FoodPlugin; -import info.nightscout.androidaps.plugins.general.nsclient.NSClientAddUpdateWorker; -import info.nightscout.androidaps.plugins.general.nsclient.NSClientMbgWorker; -import info.nightscout.androidaps.plugins.general.nsclient.NSClientPlugin; -import info.nightscout.androidaps.plugins.general.nsclient.NSClientRemoveWorker; -import info.nightscout.androidaps.plugins.general.nsclient.acks.NSAddAck; -import info.nightscout.androidaps.plugins.general.nsclient.acks.NSAuthAck; -import info.nightscout.androidaps.plugins.general.nsclient.acks.NSUpdateAck; -import info.nightscout.androidaps.plugins.general.nsclient.data.AlarmAck; -import info.nightscout.androidaps.plugins.general.nsclient.data.NSAlarm; -import info.nightscout.androidaps.plugins.general.nsclient.data.NSDeviceStatus; -import info.nightscout.androidaps.plugins.general.nsclient.data.NSSettingsStatus; -import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientNewLog; -import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart; -import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientStatus; -import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientUpdateGUI; -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.overview.notifications.NotificationWithAction; -import info.nightscout.androidaps.plugins.profile.ns.NSProfilePlugin; -import info.nightscout.androidaps.plugins.source.NSClientSourcePlugin; -import info.nightscout.androidaps.receivers.DataWorker; -import info.nightscout.androidaps.services.Intents; -import info.nightscout.androidaps.utils.DateUtil; -import info.nightscout.androidaps.utils.FabricPrivacy; -import info.nightscout.androidaps.utils.JsonHelper; -import info.nightscout.androidaps.utils.T; -import info.nightscout.androidaps.utils.buildHelper.BuildHelper; -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.socket.client.IO; -import io.socket.client.Socket; -import io.socket.emitter.Emitter; - -public class NSClientService extends DaggerService { - @Inject HasAndroidInjector injector; - @Inject AAPSLogger aapsLogger; - @Inject AapsSchedulers aapsSchedulers; - @Inject NSSettingsStatus nsSettingsStatus; - @Inject NSDeviceStatus nsDeviceStatus; - @Inject DatabaseHelperInterface databaseHelper; - @Inject RxBusWrapper rxBus; - @Inject ResourceHelper resourceHelper; - @Inject SP sp; - @Inject FabricPrivacy fabricPrivacy; - @Inject NSClientPlugin nsClientPlugin; - @Inject BuildHelper buildHelper; - @Inject Config config; - @Inject DateUtil dateUtil; - @Inject UploadQueueInterface uploadQueue; - @Inject DataWorker dataWorker; - - private final CompositeDisposable disposable = new CompositeDisposable(); - - static public PowerManager.WakeLock mWakeLock; - private final IBinder mBinder = new NSClientService.LocalBinder(); - - static public Handler handler; - - public static Socket mSocket; - public static boolean isConnected = false; - public static boolean hasWriteAuth = false; - private static Integer dataCounter = 0; - private static Integer connectCounter = 0; - - - private boolean nsEnabled = false; - static public String nsURL = ""; - private String nsAPISecret = ""; - private String nsDevice = ""; - private final Integer nsHours = 48; - - public long lastResendTime = 0; - - public long latestDateInReceivedData = 0; - - private String nsAPIhashCode = ""; - - private final ArrayList reconnections = new ArrayList<>(); - private final int WATCHDOG_INTERVAL_MINUTES = 2; - private final int WATCHDOG_RECONNECT_IN = 15; - private final int WATCHDOG_MAXCONNECTIONS = 5; - - public NSClientService() { - super(); - if (handler == null) { - HandlerThread handlerThread = new HandlerThread(NSClientService.class.getSimpleName() + "Handler"); - handlerThread.start(); - handler = new Handler(handlerThread.getLooper()); - } - } - - @Override - public void onCreate() { - super.onCreate(); - PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE); - mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "AndroidAPS:NSClientService"); - mWakeLock.acquire(); - - initialize(); - - disposable.add(rxBus - .toObservable(EventConfigBuilderChange.class) - .observeOn(aapsSchedulers.getIo()) - .subscribe(event -> { - if (nsEnabled != nsClientPlugin.isEnabled(PluginType.GENERAL)) { - latestDateInReceivedData = 0; - destroy(); - initialize(); - } - }, fabricPrivacy::logException) - ); - disposable.add(rxBus - .toObservable(EventPreferenceChange.class) - .observeOn(aapsSchedulers.getIo()) - .subscribe(event -> { - if (event.isChanged(resourceHelper, R.string.key_nsclientinternal_url) || - event.isChanged(resourceHelper, R.string.key_nsclientinternal_api_secret) || - event.isChanged(resourceHelper, R.string.key_nsclientinternal_paused) - ) { - latestDateInReceivedData = 0; - destroy(); - initialize(); - } - }, fabricPrivacy::logException) - ); - disposable.add(rxBus - .toObservable(EventAppExit.class) - .observeOn(aapsSchedulers.getIo()) - .subscribe(event -> { - aapsLogger.debug(LTag.NSCLIENT, "EventAppExit received"); - destroy(); - stopSelf(); - }, fabricPrivacy::logException) - ); - disposable.add(rxBus - .toObservable(EventNSClientRestart.class) - .observeOn(aapsSchedulers.getIo()) - .subscribe(event -> { - latestDateInReceivedData = 0; - restart(); - }, fabricPrivacy::logException) - ); - disposable.add(rxBus - .toObservable(NSAuthAck.class) - .observeOn(aapsSchedulers.getIo()) - .subscribe(this::processAuthAck, fabricPrivacy::logException) - ); - disposable.add(rxBus - .toObservable(NSUpdateAck.class) - .observeOn(aapsSchedulers.getIo()) - .subscribe(this::processUpdateAck, fabricPrivacy::logException) - ); - disposable.add(rxBus - .toObservable(NSAddAck.class) - .observeOn(aapsSchedulers.getIo()) - .subscribe(this::processAddAck, fabricPrivacy::logException) - ); - } - - @Override - public void onDestroy() { - super.onDestroy(); - disposable.clear(); - if (mWakeLock.isHeld()) mWakeLock.release(); - } - - public void processAddAck(NSAddAck ack) { - if (ack.nsClientID != null) { - uploadQueue.removeByNsClientIdIfExists(ack.json); - rxBus.send(new EventNSClientNewLog("DBADD", "Acked " + ack.nsClientID)); - } else { - rxBus.send(new EventNSClientNewLog("ERROR", "DBADD Unknown response")); - } - } - - public void processUpdateAck(NSUpdateAck ack) { - if (ack.result) { - uploadQueue.removeByMongoId(ack.action, ack._id); - rxBus.send(new EventNSClientNewLog("DBUPDATE/DBREMOVE", "Acked " + ack._id)); - } else { - rxBus.send(new EventNSClientNewLog("ERROR", "DBUPDATE/DBREMOVE Unknown response")); - } - } - - public void processAuthAck(NSAuthAck ack) { - String connectionStatus = "Authenticated ("; - if (ack.read) connectionStatus += "R"; - if (ack.write) connectionStatus += "W"; - if (ack.write_treatment) connectionStatus += "T"; - connectionStatus += ')'; - isConnected = true; - hasWriteAuth = ack.write && ack.write_treatment; - rxBus.send(new EventNSClientStatus(connectionStatus)); - rxBus.send(new EventNSClientNewLog("AUTH", connectionStatus)); - if (!ack.write) { - rxBus.send(new EventNSClientNewLog("ERROR", "Write permission not granted !!!!")); - } - if (!ack.write_treatment) { - rxBus.send(new EventNSClientNewLog("ERROR", "Write treatment permission not granted !!!!")); - } - if (!hasWriteAuth) { - Notification noperm = new Notification(Notification.NSCLIENT_NO_WRITE_PERMISSION, resourceHelper.gs(R.string.nowritepermission), Notification.URGENT); - rxBus.send(new EventNewNotification(noperm)); - } else { - rxBus.send(new EventDismissNotification(Notification.NSCLIENT_NO_WRITE_PERMISSION)); - } - } - - public class LocalBinder extends Binder { - public NSClientService getServiceInstance() { - return NSClientService.this; - } - } - - @Override - public IBinder onBind(Intent intent) { - return mBinder; - } - - @Override - public int onStartCommand(Intent intent, int flags, int startId) { - - return START_STICKY; - } - - @SuppressWarnings("deprecation") - public void initialize() { - dataCounter = 0; - - readPreferences(); - - if (!nsAPISecret.equals("")) - nsAPIhashCode = Hashing.sha1().hashString(nsAPISecret, Charsets.UTF_8).toString(); - - rxBus.send(new EventNSClientStatus("Initializing")); - if (!nsClientPlugin.isAllowed()) { - rxBus.send(new EventNSClientNewLog("NSCLIENT", "not allowed")); - rxBus.send(new EventNSClientStatus("Not allowed")); - } else if (nsClientPlugin.paused) { - rxBus.send(new EventNSClientNewLog("NSCLIENT", "paused")); - rxBus.send(new EventNSClientStatus("Paused")); - } else if (!nsEnabled) { - rxBus.send(new EventNSClientNewLog("NSCLIENT", "disabled")); - rxBus.send(new EventNSClientStatus("Disabled")); - } else if (!nsURL.equals("") && (buildHelper.isEngineeringMode() || nsURL.toLowerCase().startsWith("https://"))) { - try { - rxBus.send(new EventNSClientStatus("Connecting ...")); - IO.Options opt = new IO.Options(); - opt.forceNew = true; - opt.reconnection = true; - mSocket = IO.socket(nsURL, opt); - mSocket.on(Socket.EVENT_CONNECT, onConnect); - mSocket.on(Socket.EVENT_DISCONNECT, onDisconnect); - mSocket.on(Socket.EVENT_ERROR, onError); - mSocket.on(Socket.EVENT_CONNECT_ERROR, onError); - mSocket.on(Socket.EVENT_CONNECT_TIMEOUT, onError); - mSocket.on(Socket.EVENT_PING, onPing); - rxBus.send(new EventNSClientNewLog("NSCLIENT", "do connect")); - mSocket.connect(); - mSocket.on("dataUpdate", onDataUpdate); - mSocket.on("announcement", onAnnouncement); - mSocket.on("alarm", onAlarm); - mSocket.on("urgent_alarm", onUrgentAlarm); - mSocket.on("clear_alarm", onClearAlarm); - } catch (URISyntaxException | RuntimeException e) { - rxBus.send(new EventNSClientNewLog("NSCLIENT", "Wrong URL syntax")); - rxBus.send(new EventNSClientStatus("Wrong URL syntax")); - } - } else if (nsURL.toLowerCase().startsWith("http://")) { - rxBus.send(new EventNSClientNewLog("NSCLIENT", "NS URL not encrypted")); - rxBus.send(new EventNSClientStatus("Not encrypted")); - } else { - rxBus.send(new EventNSClientNewLog("NSCLIENT", "No NS URL specified")); - rxBus.send(new EventNSClientStatus("Not configured")); - } - } - - private final Emitter.Listener onConnect = new Emitter.Listener() { - @Override - public void call(Object... args) { - connectCounter++; - String socketId = mSocket != null ? mSocket.id() : "NULL"; - rxBus.send(new EventNSClientNewLog("NSCLIENT", "connect #" + connectCounter + " event. ID: " + socketId)); - if (mSocket != null) - sendAuthMessage(new NSAuthAck(rxBus)); - watchdog(); - } - }; - - void watchdog() { - synchronized (reconnections) { - long now = DateUtil.now(); - reconnections.add(now); - for (int i = 0; i < reconnections.size(); i++) { - Long r = reconnections.get(i); - if (r < now - T.mins(WATCHDOG_INTERVAL_MINUTES).msecs()) { - reconnections.remove(r); - } - } - rxBus.send(new EventNSClientNewLog("WATCHDOG", "connections in last " + WATCHDOG_INTERVAL_MINUTES + " mins: " + reconnections.size() + "/" + WATCHDOG_MAXCONNECTIONS)); - if (reconnections.size() >= WATCHDOG_MAXCONNECTIONS) { - Notification n = new Notification(Notification.NS_MALFUNCTION, resourceHelper.gs(R.string.nsmalfunction), Notification.URGENT); - rxBus.send(new EventNewNotification(n)); - rxBus.send(new EventNSClientNewLog("WATCHDOG", "pausing for " + WATCHDOG_RECONNECT_IN + " mins")); - nsClientPlugin.pause(true); - rxBus.send(new EventNSClientUpdateGUI()); - new Thread(() -> { - SystemClock.sleep(T.mins(WATCHDOG_RECONNECT_IN).msecs()); - rxBus.send(new EventNSClientNewLog("WATCHDOG", "reenabling NSClient")); - nsClientPlugin.pause(false); - }).start(); - } - } - } - - private final Emitter.Listener onDisconnect = new Emitter.Listener() { - @Override - public void call(Object... args) { - aapsLogger.debug(LTag.NSCLIENT, "disconnect reason: {}", args); - rxBus.send(new EventNSClientNewLog("NSCLIENT", "disconnect event")); - } - }; - - public synchronized void destroy() { - if (mSocket != null) { - mSocket.off(Socket.EVENT_CONNECT); - mSocket.off(Socket.EVENT_DISCONNECT); - mSocket.off(Socket.EVENT_PING); - mSocket.off("dataUpdate"); - mSocket.off("announcement"); - mSocket.off("alarm"); - mSocket.off("urgent_alarm"); - mSocket.off("clear_alarm"); - - rxBus.send(new EventNSClientNewLog("NSCLIENT", "destroy")); - isConnected = false; - hasWriteAuth = false; - mSocket.disconnect(); - mSocket = null; - } - } - - - public void sendAuthMessage(NSAuthAck ack) { - JSONObject authMessage = new JSONObject(); - try { - authMessage.put("client", "Android_" + nsDevice); - authMessage.put("history", nsHours); - authMessage.put("status", true); // receive status - authMessage.put("from", latestDateInReceivedData); // send data newer than - authMessage.put("secret", nsAPIhashCode); - } catch (JSONException e) { - aapsLogger.error("Unhandled exception", e); - return; - } - rxBus.send(new EventNSClientNewLog("AUTH", "requesting auth")); - if (mSocket != null) - mSocket.emit("authorize", authMessage, ack); - } - - public void readPreferences() { - nsEnabled = nsClientPlugin.isEnabled(PluginType.GENERAL); - nsURL = sp.getString(R.string.key_nsclientinternal_url, ""); - nsAPISecret = sp.getString(R.string.key_nsclientinternal_api_secret, ""); - nsDevice = sp.getString("careportal_enteredby", ""); - } - - private final Emitter.Listener onError = new Emitter.Listener() { - @Override - public void call(final Object... args) { - String msg = "Unknown Error"; - if (args.length > 0 && args[0] != null) { - msg = args[0].toString(); - } - rxBus.send(new EventNSClientNewLog("ERROR", msg)); - } - }; - - private final Emitter.Listener onPing = new Emitter.Listener() { - @Override - public void call(final Object... args) { - rxBus.send(new EventNSClientNewLog("PING", "received")); - // send data if there is something waiting - resend("Ping received"); - } - }; - - private final Emitter.Listener onAnnouncement = new Emitter.Listener() { - /* - { - "level":0, - "title":"Announcement", - "message":"test", - "plugin":{"name":"treatmentnotify","label":"Treatment Notifications","pluginType":"notification","enabled":true}, - "group":"Announcement", - "isAnnouncement":true, - "key":"9ac46ad9a1dcda79dd87dae418fce0e7955c68da" - } - */ - @Override - public void call(final Object... args) { - JSONObject data; - try { - data = (JSONObject) args[0]; - handleAnnouncement(data); - } catch (Exception e) { - aapsLogger.error("Unhandled exception", e); - } - } - }; - - private final Emitter.Listener onAlarm = new Emitter.Listener() { - /* - { - "level":1, - "title":"Warning HIGH", - "message":"BG Now: 5 -0.2 → mmol\/L\nRaw BG: 4.8 mmol\/L Čistý\nBG 15m: 4.8 mmol\/L\nIOB: -0.02U\nCOB: 0g", - "eventName":"high", - "plugin":{"name":"simplealarms","label":"Simple Alarms","pluginType":"notification","enabled":true}, - "pushoverSound":"climb", - "debug":{"lastSGV":5,"thresholds":{"bgHigh":180,"bgTargetTop":75,"bgTargetBottom":72,"bgLow":70}}, - "group":"default", - "key":"simplealarms_1" - } - */ - @Override - public void call(final Object... args) { - JSONObject data; - try { - data = (JSONObject) args[0]; - handleAlarm(data); - } catch (Exception e) { - aapsLogger.error("Unhandled exception", e); - } - } - }; - - private final Emitter.Listener onUrgentAlarm = args -> { - JSONObject data; - try { - data = (JSONObject) args[0]; - handleUrgentAlarm(data); - } catch (Exception e) { - aapsLogger.error("Unhandled exception", e); - } - }; - - private final Emitter.Listener onClearAlarm = new Emitter.Listener() { - /* - { - "clear":true, - "title":"All Clear", - "message":"default - Urgent was ack'd", - "group":"default" - } - */ - @Override - public void call(final Object... args) { - JSONObject data; - try { - data = (JSONObject) args[0]; - rxBus.send(new EventNSClientNewLog("CLEARALARM", "received")); - rxBus.send(new EventDismissNotification(Notification.NS_ALARM)); - rxBus.send(new EventDismissNotification(Notification.NS_URGENT_ALARM)); - aapsLogger.debug(LTag.NSCLIENT, data.toString()); - } catch (Exception e) { - aapsLogger.error("Unhandled exception", e); - } - } - }; - - private final Emitter.Listener onDataUpdate = new Emitter.Listener() { - @Override - public void call(final Object... args) { - NSClientService.handler.post(() -> { - PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE); - PowerManager.WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, - "AndroidAPS:NSClientService_onDataUpdate"); - wakeLock.acquire(); - try { - - JSONObject data = (JSONObject) args[0]; - boolean broadcastProfile = false; - try { - // delta means only increment/changes are comming - boolean isDelta = data.has("delta"); - boolean isFull = !isDelta; - rxBus.send(new EventNSClientNewLog("DATA", "Data packet #" + dataCounter++ + (isDelta ? " delta" : " full"))); - - if (data.has("status")) { - JSONObject status = data.getJSONObject("status"); - nsSettingsStatus.handleNewData(status); - } else if (!isDelta) { - rxBus.send(new EventNSClientNewLog("ERROR", "Unsupported Nightscout version !!!!")); - } - - if (data.has("profiles")) { - JSONArray profiles = data.getJSONArray("profiles"); - if (profiles.length() > 0) { - // take the newest - JSONObject profileStoreJson = (JSONObject) profiles.get(profiles.length() - 1); - rxBus.send(new EventNSClientNewLog("PROFILE", "profile received")); - dataWorker.enqueue( - new OneTimeWorkRequest.Builder(NSProfilePlugin.NSProfileWorker.class) - .setInputData(dataWorker.storeInputData(profileStoreJson, null)) - .build()); - - if (sp.getBoolean(R.string.key_nsclient_localbroadcasts, false)) { - Bundle bundle = new Bundle(); - bundle.putString("profile", profileStoreJson.toString()); - bundle.putBoolean("delta", isDelta); - Intent intent = new Intent(Intents.ACTION_NEW_PROFILE); - intent.putExtras(bundle); - intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); - sendBroadcast(intent); - } - } - } - - if (data.has("treatments")) { - JSONArray treatments = data.getJSONArray("treatments"); - JSONArray removedTreatments = new JSONArray(); - JSONArray addedOrUpdatedTreatments = new JSONArray(); - if (treatments.length() > 0) - rxBus.send(new EventNSClientNewLog("DATA", "received " + treatments.length() + " treatments")); - for (Integer index = 0; index < treatments.length(); index++) { - JSONObject jsonTreatment = treatments.getJSONObject(index); - String action = JsonHelper.safeGetStringAllowNull(jsonTreatment, "action", null); - long mills = JsonHelper.safeGetLong(jsonTreatment, "mills"); - - if (action == null) addedOrUpdatedTreatments.put(jsonTreatment); - else if (action.equals("update")) - addedOrUpdatedTreatments.put(jsonTreatment); - else if (action.equals("remove") && mills > dateUtil._now() - T.days(1).msecs()) // handle 1 day old deletions only - removedTreatments.put(jsonTreatment); - } - if (removedTreatments.length() > 0) { - dataWorker.enqueue( - new OneTimeWorkRequest.Builder(NSClientRemoveWorker.class) - .setInputData(dataWorker.storeInputData(removedTreatments, null)) - .build()); - - if (sp.getBoolean(R.string.key_nsclient_localbroadcasts, false)) { - Bundle bundle = new Bundle(); - bundle.putString("treatments", removedTreatments.toString()); - bundle.putBoolean("delta", isDelta); - Intent intent = new Intent(Intents.ACTION_REMOVED_TREATMENT); - intent.putExtras(bundle); - intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); - sendBroadcast(intent); - } - } - if (addedOrUpdatedTreatments.length() > 0) { - dataWorker.enqueue( - new OneTimeWorkRequest.Builder(NSClientAddUpdateWorker.class) - .setInputData(dataWorker.storeInputData(addedOrUpdatedTreatments, null)) - .build()); - - if (sp.getBoolean(R.string.key_nsclient_localbroadcasts, false)) { - List splitted = splitArray(addedOrUpdatedTreatments); - for (JSONArray part : splitted) { - Bundle bundle = new Bundle(); - bundle.putString("treatments", part.toString()); - bundle.putBoolean("delta", isDelta); - Intent intent = new Intent(Intents.ACTION_CHANGED_TREATMENT); - intent.putExtras(bundle); - intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); - sendBroadcast(intent); - } - } - } - } - if (data.has("devicestatus")) { - JSONArray devicestatuses = data.getJSONArray("devicestatus"); - if (devicestatuses.length() > 0) { - rxBus.send(new EventNSClientNewLog("DATA", "received " + devicestatuses.length() + " device statuses")); - nsDeviceStatus.handleNewData(devicestatuses); - } - } - if (data.has("food")) { - JSONArray foods = data.getJSONArray("food"); - if (foods.length() > 0) - rxBus.send(new EventNSClientNewLog("DATA", "received " + foods.length() + " foods")); - dataWorker.enqueue( - new OneTimeWorkRequest.Builder(FoodPlugin.FoodWorker.class) - .setInputData(dataWorker.storeInputData(foods, null)) - .build()); - } - //noinspection SpellCheckingInspection - if (data.has("mbgs")) { - JSONArray mbgArray = data.getJSONArray("mbgs"); - if (mbgArray.length() > 0) - rxBus.send(new EventNSClientNewLog("DATA", "received " + mbgArray.length() + " mbgs")); - dataWorker.enqueue( - new OneTimeWorkRequest.Builder(NSClientMbgWorker.class) - .setInputData(dataWorker.storeInputData(mbgArray, null)) - .build()); - } - if (data.has("cals")) { - JSONArray cals = data.getJSONArray("cals"); - if (cals.length() > 0) - rxBus.send(new EventNSClientNewLog("DATA", "received " + cals.length() + " cals")); - // Calibrations ignored - } - if (data.has("sgvs")) { - JSONArray sgvs = data.getJSONArray("sgvs"); - if (sgvs.length() > 0) - rxBus.send(new EventNSClientNewLog("DATA", "received " + sgvs.length() + " sgvs")); - - dataWorker.enqueue(new OneTimeWorkRequest.Builder(NSClientSourcePlugin.NSClientSourceWorker.class) - .setInputData(dataWorker.storeInputData(sgvs, null)) - .build()); - - List splitted = splitArray(sgvs); - if (sp.getBoolean(R.string.key_nsclient_localbroadcasts, false)) { - for (JSONArray part : splitted) { - Bundle bundle = new Bundle(); - bundle.putString("sgvs", part.toString()); - bundle.putBoolean("delta", isDelta); - Intent intent = new Intent(Intents.ACTION_NEW_SGV); - intent.putExtras(bundle); - intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); - sendBroadcast(intent); - } - } - } - rxBus.send(new EventNSClientNewLog("LAST", dateUtil.dateAndTimeString(latestDateInReceivedData))); - } catch (JSONException e) { - aapsLogger.error("Unhandled exception", e); - } - //rxBus.send(new EventNSClientNewLog("NSCLIENT", "onDataUpdate end"); - } finally { - if (wakeLock.isHeld()) wakeLock.release(); - } - }); - } - }; - - public void dbUpdate(DbRequest dbr, NSUpdateAck ack) { - try { - if (!isConnected || !hasWriteAuth) return; - JSONObject message = new JSONObject(); - message.put("collection", dbr.collection); - message.put("_id", dbr._id); - message.put("data", new JSONObject(dbr.data)); - mSocket.emit("dbUpdate", message, ack); - rxBus.send(new EventNSClientNewLog("DBUPDATE " + dbr.collection, "Sent " + dbr._id)); - } catch (JSONException e) { - aapsLogger.error("Unhandled exception", e); - } - } - - public void dbUpdateUnset(DbRequest dbr, NSUpdateAck ack) { - try { - if (!isConnected || !hasWriteAuth) return; - JSONObject message = new JSONObject(); - message.put("collection", dbr.collection); - message.put("_id", dbr._id); - message.put("data", new JSONObject(dbr.data)); - mSocket.emit("dbUpdateUnset", message, ack); - rxBus.send(new EventNSClientNewLog("DBUPDATEUNSET " + dbr.collection, "Sent " + dbr._id)); - } catch (JSONException e) { - aapsLogger.error("Unhandled exception", e); - } - } - - public void dbRemove(DbRequest dbr, NSUpdateAck ack) { - try { - if (!isConnected || !hasWriteAuth) return; - JSONObject message = new JSONObject(); - message.put("collection", dbr.collection); - message.put("_id", dbr._id); - mSocket.emit("dbRemove", message, ack); - rxBus.send(new EventNSClientNewLog("DBREMOVE " + dbr.collection, "Sent " + dbr._id)); - } catch (JSONException e) { - aapsLogger.error("Unhandled exception", e); - } - } - - public void dbAdd(DbRequest dbr, NSAddAck ack) { - try { - if (!isConnected || !hasWriteAuth) return; - JSONObject message = new JSONObject(); - message.put("collection", dbr.collection); - message.put("data", new JSONObject(dbr.data)); - mSocket.emit("dbAdd", message, ack); - rxBus.send(new EventNSClientNewLog("DBADD " + dbr.collection, "Sent " + dbr.nsClientID)); - } catch (JSONException e) { - aapsLogger.error("Unhandled exception", e); - } - } - - public void sendAlarmAck(AlarmAck alarmAck) { - if (!isConnected || !hasWriteAuth) return; - mSocket.emit("ack", alarmAck.level, alarmAck.group, alarmAck.silenceTime); - rxBus.send(new EventNSClientNewLog("ALARMACK ", alarmAck.level + " " + alarmAck.group + " " + alarmAck.silenceTime)); - } - - public void resend(final String reason) { - if (uploadQueue.size() == 0) - return; - - if (!isConnected || !hasWriteAuth) return; - - handler.post(() -> { - if (mSocket == null || !mSocket.connected()) return; - - if (lastResendTime > System.currentTimeMillis() - 10 * 1000L) { - aapsLogger.debug(LTag.NSCLIENT, "Skipping resend by lastResendTime: " + ((System.currentTimeMillis() - lastResendTime) / 1000L) + " sec"); - return; - } - lastResendTime = System.currentTimeMillis(); - - rxBus.send(new EventNSClientNewLog("QUEUE", "Resend started: " + reason)); - - CloseableIterator iterator; - int maxcount = 30; - try { - iterator = databaseHelper.getDbRequestIterator(); - try { - while (iterator.hasNext() && maxcount > 0) { - DbRequest dbr = iterator.next(); - if (dbr.action.equals("dbAdd")) { - NSAddAck addAck = new NSAddAck(aapsLogger, rxBus); - dbAdd(dbr, addAck); - } else if (dbr.action.equals("dbRemove")) { - NSUpdateAck removeAck = new NSUpdateAck(dbr.action, dbr._id, aapsLogger, rxBus); - dbRemove(dbr, removeAck); - } else if (dbr.action.equals("dbUpdate")) { - NSUpdateAck updateAck = new NSUpdateAck(dbr.action, dbr._id, aapsLogger, rxBus); - dbUpdate(dbr, updateAck); - } else if (dbr.action.equals("dbUpdateUnset")) { - NSUpdateAck updateUnsetAck = new NSUpdateAck(dbr.action, dbr._id, aapsLogger, rxBus); - dbUpdateUnset(dbr, updateUnsetAck); - } - maxcount--; - } - } finally { - iterator.close(); - } - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - } - - rxBus.send(new EventNSClientNewLog("QUEUE", "Resend ended: " + reason)); - }); - } - - public void restart() { - destroy(); - initialize(); - } - - private void handleAnnouncement(JSONObject announcement) { - boolean defaultVal = config.getNSCLIENT(); - if (sp.getBoolean(R.string.key_ns_announcements, defaultVal)) { - NSAlarm nsAlarm = new NSAlarm(announcement); - Notification notification = new NotificationWithAction(injector, nsAlarm); - rxBus.send(new EventNewNotification(notification)); - rxBus.send(new EventNSClientNewLog("ANNOUNCEMENT", JsonHelper.safeGetString(announcement, "message", "received"))); - aapsLogger.debug(LTag.NSCLIENT, announcement.toString()); - } - } - - private void handleAlarm(JSONObject alarm) { - boolean defaultVal = config.getNSCLIENT(); - if (sp.getBoolean(R.string.key_ns_alarms, defaultVal)) { - long snoozedTo = sp.getLong(R.string.key_snoozedTo, 0L); - if (snoozedTo == 0L || System.currentTimeMillis() > snoozedTo) { - NSAlarm nsAlarm = new NSAlarm(alarm); - Notification notification = new NotificationWithAction(injector, nsAlarm); - rxBus.send(new EventNewNotification(notification)); - } - rxBus.send(new EventNSClientNewLog("ALARM", JsonHelper.safeGetString(alarm, "message", "received"))); - aapsLogger.debug(LTag.NSCLIENT, alarm.toString()); - } - } - - private void handleUrgentAlarm(JSONObject alarm) { - boolean defaultVal = config.getNSCLIENT(); - if (sp.getBoolean(R.string.key_ns_alarms, defaultVal)) { - long snoozedTo = sp.getLong(R.string.key_snoozedTo, 0L); - if (snoozedTo == 0L || System.currentTimeMillis() > snoozedTo) { - NSAlarm nsAlarm = new NSAlarm(alarm); - Notification notification = new NotificationWithAction(injector, nsAlarm); - rxBus.send(new EventNewNotification(notification)); - } - rxBus.send(new EventNSClientNewLog("URGENTALARM", JsonHelper.safeGetString(alarm, "message", "received"))); - aapsLogger.debug(LTag.NSCLIENT, alarm.toString()); - } - } - - public void handleNewTreatment(JSONArray treatments, boolean isDelta) { - List splitted = splitArray(treatments); - for (JSONArray part : splitted) { - Bundle bundle = new Bundle(); - bundle.putString("treatments", part.toString()); - bundle.putBoolean("delta", isDelta); - Intent intent = new Intent(Intents.ACTION_NEW_TREATMENT); - intent.putExtras(bundle); - intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); - LocalBroadcastManager.getInstance(this).sendBroadcast(intent); - } - - if (sp.getBoolean(R.string.key_nsclient_localbroadcasts, false)) { - splitted = splitArray(treatments); - for (JSONArray part : splitted) { - Bundle bundle = new Bundle(); - bundle.putString("treatments", part.toString()); - bundle.putBoolean("delta", isDelta); - Intent intent = new Intent(Intents.ACTION_NEW_TREATMENT); - intent.putExtras(bundle); - intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); - this.getApplicationContext().sendBroadcast(intent); - } - } - } - - public List splitArray(JSONArray array) { - List ret = new ArrayList<>(); - try { - int size = array.length(); - int count = 0; - JSONArray newarr = null; - for (int i = 0; i < size; i++) { - if (count == 0) { - if (newarr != null) { - ret.add(newarr); - } - newarr = new JSONArray(); - count = 20; - } - newarr.put(array.get(i)); - --count; - } - if (newarr != null && newarr.length() > 0) { - ret.add(newarr); - } - } catch (JSONException e) { - aapsLogger.error("Unhandled exception", e); - ret = new ArrayList<>(); - ret.add(array); - } - return ret; - } -} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/services/NSClientService.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/services/NSClientService.kt new file mode 100644 index 0000000000..4a765e49e6 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/services/NSClientService.kt @@ -0,0 +1,728 @@ +package info.nightscout.androidaps.plugins.general.nsclient.services + +import android.annotation.SuppressLint +import android.content.Context +import android.content.Intent +import android.os.* +import androidx.work.OneTimeWorkRequest +import com.google.common.base.Charsets +import com.google.common.hash.Hashing +import dagger.android.DaggerService +import dagger.android.HasAndroidInjector +import info.nightscout.androidaps.R +import info.nightscout.androidaps.database.AppRepository +import info.nightscout.androidaps.events.EventAppExit +import info.nightscout.androidaps.events.EventConfigBuilderChange +import info.nightscout.androidaps.events.EventPreferenceChange +import info.nightscout.androidaps.interfaces.Config +import info.nightscout.androidaps.interfaces.DataSyncSelector +import info.nightscout.androidaps.interfaces.DatabaseHelperInterface +import info.nightscout.androidaps.interfaces.PluginType +import info.nightscout.androidaps.logging.AAPSLogger +import info.nightscout.androidaps.logging.LTag +import info.nightscout.androidaps.plugins.bus.RxBusWrapper +import info.nightscout.androidaps.plugins.general.food.FoodPlugin.FoodWorker +import info.nightscout.androidaps.plugins.general.nsclient.* +import info.nightscout.androidaps.plugins.general.nsclient.acks.NSAddAck +import info.nightscout.androidaps.plugins.general.nsclient.acks.NSAuthAck +import info.nightscout.androidaps.plugins.general.nsclient.acks.NSUpdateAck +import info.nightscout.androidaps.plugins.general.nsclient.data.AlarmAck +import info.nightscout.androidaps.plugins.general.nsclient.data.NSAlarm +import info.nightscout.androidaps.plugins.general.nsclient.data.NSDeviceStatus +import info.nightscout.androidaps.plugins.general.nsclient.data.NSSettingsStatus +import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientNewLog +import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart +import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientStatus +import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientUpdateGUI +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.overview.notifications.NotificationWithAction +import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin +import info.nightscout.androidaps.plugins.source.NSClientSourcePlugin.NSClientSourceWorker +import info.nightscout.androidaps.receivers.DataWorker +import info.nightscout.androidaps.services.Intents +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.FabricPrivacy +import info.nightscout.androidaps.utils.JsonHelper.safeGetLong +import info.nightscout.androidaps.utils.JsonHelper.safeGetString +import info.nightscout.androidaps.utils.JsonHelper.safeGetStringAllowNull +import info.nightscout.androidaps.utils.T.Companion.days +import info.nightscout.androidaps.utils.T.Companion.mins +import info.nightscout.androidaps.utils.buildHelper.BuildHelper +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.socket.client.IO +import io.socket.client.Socket +import io.socket.emitter.Emitter +import org.json.JSONArray +import org.json.JSONException +import org.json.JSONObject +import java.net.URISyntaxException +import java.util.* +import javax.inject.Inject + +class NSClientService : DaggerService() { + + @Inject lateinit var injector: HasAndroidInjector + @Inject lateinit var aapsLogger: AAPSLogger + @Inject lateinit var aapsSchedulers: AapsSchedulers + @Inject lateinit var nsSettingsStatus: NSSettingsStatus + @Inject lateinit var nsDeviceStatus: NSDeviceStatus + @Inject lateinit var databaseHelper: DatabaseHelperInterface + @Inject lateinit var rxBus: RxBusWrapper + @Inject lateinit var resourceHelper: ResourceHelper + @Inject lateinit var sp: SP + @Inject lateinit var fabricPrivacy: FabricPrivacy + @Inject lateinit var nsClientPlugin: NSClientPlugin + @Inject lateinit var buildHelper: BuildHelper + @Inject lateinit var config: Config + @Inject lateinit var dateUtil: DateUtil + @Inject lateinit var dataWorker: DataWorker + @Inject lateinit var dataSyncSelector: DataSyncSelector + @Inject lateinit var repository: AppRepository + + companion object { + + private const val WATCHDOG_INTERVAL_MINUTES = 2 + private const val WATCHDOG_RECONNECT_IN = 15 + private const val WATCHDOG_MAX_CONNECTIONS = 5 + } + + private val disposable = CompositeDisposable() + + private var wakeLock: PowerManager.WakeLock? = null + private val binder: IBinder = LocalBinder() + private var handler: Handler? = null + private var socket: Socket? = null + private var dataCounter = 0 + private var connectCounter = 0 + private var nsEnabled = false + private var nsAPISecret = "" + private var nsDevice = "" + private val nsHours = 48 + private var lastAckTime: Long = 0 + private var nsApiHashCode = "" + private val reconnections = ArrayList() + + var isConnected = false + var hasWriteAuth = false + var nsURL = "" + var latestDateInReceivedData: Long = 0 + + @SuppressLint("WakelockTimeout") + override fun onCreate() { + super.onCreate() + wakeLock = (getSystemService(Context.POWER_SERVICE) as PowerManager).newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "AndroidAPS:NSClientService") + wakeLock?.acquire() + initialize() + disposable.add(rxBus + .toObservable(EventConfigBuilderChange::class.java) + .observeOn(aapsSchedulers.io) + .subscribe({ + if (nsEnabled != nsClientPlugin.isEnabled(PluginType.GENERAL)) { + latestDateInReceivedData = 0 + destroy() + initialize() + } + }, fabricPrivacy::logException) + ) + disposable.add(rxBus + .toObservable(EventPreferenceChange::class.java) + .observeOn(aapsSchedulers.io) + .subscribe({ event: EventPreferenceChange -> + if (event.isChanged(resourceHelper, R.string.key_nsclientinternal_url) || + event.isChanged(resourceHelper, R.string.key_nsclientinternal_api_secret) || + event.isChanged(resourceHelper, R.string.key_nsclientinternal_paused)) { + latestDateInReceivedData = 0 + destroy() + initialize() + } + }, fabricPrivacy::logException) + ) + disposable.add(rxBus + .toObservable(EventAppExit::class.java) + .observeOn(aapsSchedulers.io) + .subscribe({ + aapsLogger.debug(LTag.NSCLIENT, "EventAppExit received") + destroy() + stopSelf() + }, fabricPrivacy::logException) + ) + disposable.add(rxBus + .toObservable(EventNSClientRestart::class.java) + .observeOn(aapsSchedulers.io) + .subscribe({ + latestDateInReceivedData = 0 + restart() + }, fabricPrivacy::logException) + ) + disposable.add(rxBus + .toObservable(NSAuthAck::class.java) + .observeOn(aapsSchedulers.io) + .subscribe({ ack -> processAuthAck(ack) }, fabricPrivacy::logException) + ) + disposable.add(rxBus + .toObservable(NSUpdateAck::class.java) + .observeOn(aapsSchedulers.io) + .subscribe({ ack -> processUpdateAck(ack) }, fabricPrivacy::logException) + ) + disposable.add(rxBus + .toObservable(NSAddAck::class.java) + .observeOn(aapsSchedulers.io) + .subscribe({ ack -> processAddAck(ack) }, fabricPrivacy::logException) + ) + } + + override fun onDestroy() { + super.onDestroy() + disposable.clear() + if (wakeLock?.isHeld == true) wakeLock?.release() + } + + private fun processAddAck(ack: NSAddAck) { + lastAckTime = dateUtil.now() + dataWorker.enqueue( + OneTimeWorkRequest.Builder(NSClientAddAckWorker::class.java) + .setInputData(dataWorker.storeInputData(ack, null)) + .build()) + } + + private fun processUpdateAck(ack: NSUpdateAck) { + lastAckTime = dateUtil.now() + dataWorker.enqueue( + OneTimeWorkRequest.Builder(NSClientUpdateRemoveAckWorker::class.java) + .setInputData(dataWorker.storeInputData(ack, null)) + .build()) + } + + fun processAuthAck(ack: NSAuthAck) { + var connectionStatus = "Authenticated (" + if (ack.read) connectionStatus += "R" + if (ack.write) connectionStatus += "W" + if (ack.write_treatment) connectionStatus += "T" + connectionStatus += ')' + isConnected = true + hasWriteAuth = ack.write && ack.write_treatment + rxBus.send(EventNSClientStatus(connectionStatus)) + rxBus.send(EventNSClientNewLog("AUTH", connectionStatus)) + if (!ack.write) { + rxBus.send(EventNSClientNewLog("ERROR", "Write permission not granted ")) + } + if (!ack.write_treatment) { + rxBus.send(EventNSClientNewLog("ERROR", "Write treatment permission not granted ")) + } + if (!hasWriteAuth) { + val noWritePerm = Notification(Notification.NSCLIENT_NO_WRITE_PERMISSION, resourceHelper.gs(R.string.nowritepermission), Notification.URGENT) + rxBus.send(EventNewNotification(noWritePerm)) + } else { + rxBus.send(EventDismissNotification(Notification.NSCLIENT_NO_WRITE_PERMISSION)) + } + } + + inner class LocalBinder : Binder() { + + val serviceInstance: NSClientService + get() = this@NSClientService + } + + override fun onBind(intent: Intent): IBinder { + return binder + } + + override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int { + return START_STICKY + } + + fun initialize() { + dataCounter = 0 + readPreferences() + @Suppress("UnstableApiUsage", "DEPRECATION") + if (nsAPISecret != "") nsApiHashCode = Hashing.sha1().hashString(nsAPISecret, Charsets.UTF_8).toString() + rxBus.send(EventNSClientStatus("Initializing")) + if (!nsClientPlugin.isAllowed) { + rxBus.send(EventNSClientNewLog("NSCLIENT", "not allowed")) + rxBus.send(EventNSClientStatus("Not allowed")) + } else if (nsClientPlugin.paused) { + rxBus.send(EventNSClientNewLog("NSCLIENT", "paused")) + rxBus.send(EventNSClientStatus("Paused")) + } else if (!nsEnabled) { + rxBus.send(EventNSClientNewLog("NSCLIENT", "disabled")) + rxBus.send(EventNSClientStatus("Disabled")) + } else if (nsURL != "" && (buildHelper.isEngineeringMode() || nsURL.toLowerCase(Locale.getDefault()).startsWith("https://"))) { + try { + rxBus.send(EventNSClientStatus("Connecting ...")) + val opt = IO.Options() + opt.forceNew = true + opt.reconnection = true + socket = IO.socket(nsURL, opt).also { socket -> + socket.on(Socket.EVENT_CONNECT, onConnect) + socket.on(Socket.EVENT_DISCONNECT, onDisconnect) + socket.on(Socket.EVENT_ERROR, onError) + socket.on(Socket.EVENT_CONNECT_ERROR, onError) + socket.on(Socket.EVENT_CONNECT_TIMEOUT, onError) + socket.on(Socket.EVENT_PING, onPing) + rxBus.send(EventNSClientNewLog("NSCLIENT", "do connect")) + socket.connect() + socket.on("dataUpdate", onDataUpdate) + socket.on("announcement", onAnnouncement) + socket.on("alarm", onAlarm) + socket.on("urgent_alarm", onUrgentAlarm) + socket.on("clear_alarm", onClearAlarm) + } + } catch (e: URISyntaxException) { + rxBus.send(EventNSClientNewLog("NSCLIENT", "Wrong URL syntax")) + rxBus.send(EventNSClientStatus("Wrong URL syntax")) + } catch (e: RuntimeException) { + rxBus.send(EventNSClientNewLog("NSCLIENT", "Wrong URL syntax")) + rxBus.send(EventNSClientStatus("Wrong URL syntax")) + } + } else if (nsURL.toLowerCase(Locale.getDefault()).startsWith("http://")) { + rxBus.send(EventNSClientNewLog("NSCLIENT", "NS URL not encrypted")) + rxBus.send(EventNSClientStatus("Not encrypted")) + } else { + rxBus.send(EventNSClientNewLog("NSCLIENT", "No NS URL specified")) + rxBus.send(EventNSClientStatus("Not configured")) + } + } + + private val onConnect = Emitter.Listener { + connectCounter++ + val socketId = socket?.id() ?: "NULL" + rxBus.send(EventNSClientNewLog("NSCLIENT", "connect #$connectCounter event. ID: $socketId")) + if (socket != null) sendAuthMessage(NSAuthAck(rxBus)) + watchdog() + } + + private fun watchdog() { + synchronized(reconnections) { + val now = dateUtil.now() + reconnections.add(now) + for (r in reconnections.reversed()) { + if (r < now - mins(WATCHDOG_INTERVAL_MINUTES.toLong()).msecs()) { + reconnections.remove(r) + } + } + rxBus.send(EventNSClientNewLog("WATCHDOG", "connections in last " + WATCHDOG_INTERVAL_MINUTES + " minutes: " + reconnections.size + "/" + WATCHDOG_MAX_CONNECTIONS)) + if (reconnections.size >= WATCHDOG_MAX_CONNECTIONS) { + val n = Notification(Notification.NS_MALFUNCTION, resourceHelper.gs(R.string.nsmalfunction), Notification.URGENT) + rxBus.send(EventNewNotification(n)) + rxBus.send(EventNSClientNewLog("WATCHDOG", "pausing for $WATCHDOG_RECONNECT_IN minutes")) + nsClientPlugin.pause(true) + rxBus.send(EventNSClientUpdateGUI()) + Thread { + SystemClock.sleep(mins(WATCHDOG_RECONNECT_IN.toLong()).msecs()) + rxBus.send(EventNSClientNewLog("WATCHDOG", "re-enabling NSClient")) + nsClientPlugin.pause(false) + }.start() + } + } + } + + private val onDisconnect = Emitter.Listener { args -> + aapsLogger.debug(LTag.NSCLIENT, "disconnect reason: {}", *args) + rxBus.send(EventNSClientNewLog("NSCLIENT", "disconnect event")) + } + + @Synchronized fun destroy() { + socket?.off(Socket.EVENT_CONNECT) + socket?.off(Socket.EVENT_DISCONNECT) + socket?.off(Socket.EVENT_PING) + socket?.off("dataUpdate") + socket?.off("announcement") + socket?.off("alarm") + socket?.off("urgent_alarm") + socket?.off("clear_alarm") + rxBus.send(EventNSClientNewLog("NSCLIENT", "destroy")) + isConnected = false + hasWriteAuth = false + socket?.disconnect() + socket = null + } + + private fun sendAuthMessage(ack: NSAuthAck?) { + val authMessage = JSONObject() + try { + authMessage.put("client", "Android_$nsDevice") + authMessage.put("history", nsHours) + authMessage.put("status", true) // receive status + authMessage.put("from", latestDateInReceivedData) // send data newer than + authMessage.put("secret", nsApiHashCode) + } catch (e: JSONException) { + aapsLogger.error("Unhandled exception", e) + return + } + rxBus.send(EventNSClientNewLog("AUTH", "requesting auth")) + socket?.emit("authorize", authMessage, ack) + } + + fun readPreferences() { + nsEnabled = nsClientPlugin.isEnabled(PluginType.GENERAL) + nsURL = sp.getString(R.string.key_nsclientinternal_url, "") + nsAPISecret = sp.getString(R.string.key_nsclientinternal_api_secret, "") + nsDevice = sp.getString("careportal_enteredby", "") + } + + private val onError = Emitter.Listener { args -> + var msg = "Unknown Error" + if (args.isNotEmpty() && args[0] != null) { + msg = args[0].toString() + } + rxBus.send(EventNSClientNewLog("ERROR", msg)) + } + private val onPing = Emitter.Listener { + rxBus.send(EventNSClientNewLog("PING", "received")) + // send data if there is something waiting + resend("Ping received") + } + private val onAnnouncement = Emitter.Listener { args -> + + /* + { + "level":0, + "title":"Announcement", + "message":"test", + "plugin":{"name":"treatmentnotify","label":"Treatment Notifications","pluginType":"notification","enabled":true}, + "group":"Announcement", + "isAnnouncement":true, + "key":"9ac46ad9a1dcda79dd87dae418fce0e7955c68da" + } + */ + val data: JSONObject + try { + data = args[0] as JSONObject + handleAnnouncement(data) + } catch (e: Exception) { + aapsLogger.error("Unhandled exception", e) + } + } + private val onAlarm = Emitter.Listener { args -> + + /* + { + "level":1, + "title":"Warning HIGH", + "message":"BG Now: 5 -0.2 → mmol\/L\nRaw BG: 4.8 mmol\/L Čistý\nBG 15m: 4.8 mmol\/L\nIOB: -0.02U\nCOB: 0g", + "eventName":"high", + "plugin":{"name":"simplealarms","label":"Simple Alarms","pluginType":"notification","enabled":true}, + "pushoverSound":"climb", + "debug":{"lastSGV":5,"thresholds":{"bgHigh":180,"bgTargetTop":75,"bgTargetBottom":72,"bgLow":70}}, + "group":"default", + "key":"simplealarms_1" + } + */ + val data: JSONObject + try { + data = args[0] as JSONObject + handleAlarm(data) + } catch (e: Exception) { + aapsLogger.error("Unhandled exception", e) + } + } + private val onUrgentAlarm = Emitter.Listener { args: Array -> + val data: JSONObject + try { + data = args[0] as JSONObject + handleUrgentAlarm(data) + } catch (e: Exception) { + aapsLogger.error("Unhandled exception", e) + } + } + private val onClearAlarm = Emitter.Listener { args -> + + /* + { + "clear":true, + "title":"All Clear", + "message":"default - Urgent was ack'd", + "group":"default" + } + */ + val data: JSONObject + try { + data = args[0] as JSONObject + rxBus.send(EventNSClientNewLog("CLEARALARM", "received")) + rxBus.send(EventDismissNotification(Notification.NS_ALARM)) + rxBus.send(EventDismissNotification(Notification.NS_URGENT_ALARM)) + aapsLogger.debug(LTag.NSCLIENT, data.toString()) + } catch (e: Exception) { + aapsLogger.error("Unhandled exception", e) + } + } + private val onDataUpdate = Emitter.Listener { args -> + handler?.post { + // val powerManager = getSystemService(POWER_SERVICE) as PowerManager + // val wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, + // "AndroidAPS:NSClientService_onDataUpdate") + // wakeLock.acquire(3000) + try { + val data = args[0] as JSONObject + try { + // delta means only increment/changes are coming + val isDelta = data.has("delta") + rxBus.send(EventNSClientNewLog("DATA", "Data packet #" + dataCounter++ + if (isDelta) " delta" else " full")) + if (data.has("status")) { + val status = data.getJSONObject("status") + nsSettingsStatus.handleNewData(status) + } else if (!isDelta) { + rxBus.send(EventNSClientNewLog("ERROR", "Unsupported Nightscout version ")) + } + if (data.has("profiles")) { + val profiles = data.getJSONArray("profiles") + if (profiles.length() > 0) { + // take the newest + val profileStoreJson = profiles[profiles.length() - 1] as JSONObject + rxBus.send(EventNSClientNewLog("PROFILE", "profile received")) + dataWorker.enqueue( + OneTimeWorkRequest.Builder(LocalProfilePlugin.NSProfileWorker::class.java) + .setInputData(dataWorker.storeInputData(profileStoreJson, null)) + .build()) + if (sp.getBoolean(R.string.key_nsclient_localbroadcasts, false)) { + val bundle = Bundle() + bundle.putString("profile", profileStoreJson.toString()) + bundle.putBoolean("delta", isDelta) + val intent = Intent(Intents.ACTION_NEW_PROFILE) + intent.putExtras(bundle) + intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES) + sendBroadcast(intent) + } + } + } + if (data.has("treatments")) { + val treatments = data.getJSONArray("treatments") + val removedTreatments = JSONArray() + val addedOrUpdatedTreatments = JSONArray() + if (treatments.length() > 0) rxBus.send(EventNSClientNewLog("DATA", "received " + treatments.length() + " treatments")) + for (index in 0 until treatments.length()) { + val jsonTreatment = treatments.getJSONObject(index) + val action = safeGetStringAllowNull(jsonTreatment, "action", null) + val mills = safeGetLong(jsonTreatment, "mills") + if (action == null) addedOrUpdatedTreatments.put(jsonTreatment) else if (action == "update") addedOrUpdatedTreatments.put(jsonTreatment) else if (action == "remove" && mills > dateUtil.now() - days(1).msecs()) // handle 1 day old deletions only + removedTreatments.put(jsonTreatment) + } + if (removedTreatments.length() > 0) { + dataWorker.enqueue( + OneTimeWorkRequest.Builder(NSClientRemoveWorker::class.java) + .setInputData(dataWorker.storeInputData(removedTreatments, null)) + .build()) + if (sp.getBoolean(R.string.key_nsclient_localbroadcasts, false)) { + val bundle = Bundle() + bundle.putString("treatments", removedTreatments.toString()) + bundle.putBoolean("delta", isDelta) + val intent = Intent(Intents.ACTION_REMOVED_TREATMENT) + intent.putExtras(bundle) + intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES) + sendBroadcast(intent) + } + } + if (addedOrUpdatedTreatments.length() > 0) { + dataWorker.enqueue( + OneTimeWorkRequest.Builder(NSClientAddUpdateWorker::class.java) + .setInputData(dataWorker.storeInputData(addedOrUpdatedTreatments, null)) + .build()) + if (sp.getBoolean(R.string.key_nsclient_localbroadcasts, false)) { + val splitted = splitArray(addedOrUpdatedTreatments) + for (part in splitted) { + val bundle = Bundle() + bundle.putString("treatments", part.toString()) + bundle.putBoolean("delta", isDelta) + val intent = Intent(Intents.ACTION_CHANGED_TREATMENT) + intent.putExtras(bundle) + intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES) + sendBroadcast(intent) + } + } + } + } + if (data.has("devicestatus")) { + val devicestatuses = data.getJSONArray("devicestatus") + if (devicestatuses.length() > 0) { + rxBus.send(EventNSClientNewLog("DATA", "received " + devicestatuses.length() + " device statuses")) + nsDeviceStatus.handleNewData(devicestatuses) + } + } + if (data.has("food")) { + val foods = data.getJSONArray("food") + if (foods.length() > 0) rxBus.send(EventNSClientNewLog("DATA", "received " + foods.length() + " foods")) + dataWorker.enqueue( + OneTimeWorkRequest.Builder(FoodWorker::class.java) + .setInputData(dataWorker.storeInputData(foods, null)) + .build()) + } + if (data.has("mbgs")) { + val mbgArray = data.getJSONArray("mbgs") + if (mbgArray.length() > 0) rxBus.send(EventNSClientNewLog("DATA", "received " + mbgArray.length() + " mbgs")) + dataWorker.enqueue( + OneTimeWorkRequest.Builder(NSClientMbgWorker::class.java) + .setInputData(dataWorker.storeInputData(mbgArray, null)) + .build()) + } + if (data.has("cals")) { + val cals = data.getJSONArray("cals") + if (cals.length() > 0) rxBus.send(EventNSClientNewLog("DATA", "received " + cals.length() + " cals")) + // Calibrations ignored + } + if (data.has("sgvs")) { + val sgvs = data.getJSONArray("sgvs") + if (sgvs.length() > 0) rxBus.send(EventNSClientNewLog("DATA", "received " + sgvs.length() + " sgvs")) + dataWorker.enqueue(OneTimeWorkRequest.Builder(NSClientSourceWorker::class.java) + .setInputData(dataWorker.storeInputData(sgvs, null)) + .build()) + val splitted = splitArray(sgvs) + if (sp.getBoolean(R.string.key_nsclient_localbroadcasts, false)) { + for (part in splitted) { + val bundle = Bundle() + bundle.putString("sgvs", part.toString()) + bundle.putBoolean("delta", isDelta) + val intent = Intent(Intents.ACTION_NEW_SGV) + intent.putExtras(bundle) + intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES) + sendBroadcast(intent) + } + } + } + rxBus.send(EventNSClientNewLog("LAST", dateUtil.dateAndTimeString(latestDateInReceivedData))) + } catch (e: JSONException) { + aapsLogger.error("Unhandled exception", e) + } + //rxBus.send(new EventNSClientNewLog("NSCLIENT", "onDataUpdate end"); + } finally { + // if (wakeLock.isHeld) wakeLock.release() + } + } + } + + fun dbUpdate(collection: String, _id: String?, data: JSONObject?, originalObject: Any, progress: String) { + try { + if (_id == null) return + if (!isConnected || !hasWriteAuth) return + val message = JSONObject() + message.put("collection", collection) + message.put("_id", _id) + message.put("data", data) + socket?.emit("dbUpdate", message, NSUpdateAck("dbUpdate", _id, aapsLogger, rxBus, originalObject)) + rxBus.send(EventNSClientNewLog("DBUPDATE $collection", "Sent " + originalObject.javaClass.simpleName + " " + _id + " " + progress)) + } catch (e: JSONException) { + aapsLogger.error("Unhandled exception", e) + } + } + + fun dbAdd(collection: String, data: JSONObject, originalObject: Any, progress: String) { + try { + if (!isConnected || !hasWriteAuth) return + val message = JSONObject() + message.put("collection", collection) + message.put("data", data) + socket?.emit("dbAdd", message, NSAddAck(aapsLogger, rxBus, originalObject)) + rxBus.send(EventNSClientNewLog("DBADD $collection", "Sent " + originalObject.javaClass.simpleName + " " + data + " " + progress)) + } catch (e: JSONException) { + aapsLogger.error("Unhandled exception", e) + } + } + + fun sendAlarmAck(alarmAck: AlarmAck) { + if (!isConnected || !hasWriteAuth) return + socket?.emit("ack", alarmAck.level, alarmAck.group, alarmAck.silenceTime) + rxBus.send(EventNSClientNewLog("ALARMACK ", alarmAck.level.toString() + " " + alarmAck.group + " " + alarmAck.silenceTime)) + } + + fun resend(reason: String) { + if (!isConnected || !hasWriteAuth) return + handler?.post { + if (socket?.connected() != true) return@post + if (lastAckTime > System.currentTimeMillis() - 10 * 1000L) { + aapsLogger.debug(LTag.NSCLIENT, "Skipping resend by lastAckTime: " + (System.currentTimeMillis() - lastAckTime) / 1000L + " sec") + return@post + } + // val powerManager = getSystemService(POWER_SERVICE) as PowerManager + // val wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, + // "AndroidAPS:NSClientService_onDataUpdate") + // wakeLock.acquire(mins(10).msecs()) + try { + rxBus.send(EventNSClientNewLog("QUEUE", "Resend started: $reason")) + dataSyncSelector.doUpload() + rxBus.send(EventNSClientNewLog("QUEUE", "Resend ended: $reason")) + } finally { + // if (wakeLock.isHeld) wakeLock.release() + } + } + } + + fun restart() { + destroy() + initialize() + } + + private fun handleAnnouncement(announcement: JSONObject) { + val defaultVal = config.NSCLIENT + if (sp.getBoolean(R.string.key_ns_announcements, defaultVal)) { + val nsAlarm = NSAlarm(announcement) + val notification: Notification = NotificationWithAction(injector, nsAlarm) + rxBus.send(EventNewNotification(notification)) + rxBus.send(EventNSClientNewLog("ANNOUNCEMENT", safeGetString(announcement, "message", "received"))) + aapsLogger.debug(LTag.NSCLIENT, announcement.toString()) + } + } + + private fun handleAlarm(alarm: JSONObject) { + val defaultVal = config.NSCLIENT + if (sp.getBoolean(R.string.key_ns_alarms, defaultVal)) { + val snoozedTo = sp.getLong(R.string.key_snoozedTo, 0L) + if (snoozedTo == 0L || System.currentTimeMillis() > snoozedTo) { + val nsAlarm = NSAlarm(alarm) + val notification: Notification = NotificationWithAction(injector, nsAlarm) + rxBus.send(EventNewNotification(notification)) + } + rxBus.send(EventNSClientNewLog("ALARM", safeGetString(alarm, "message", "received"))) + aapsLogger.debug(LTag.NSCLIENT, alarm.toString()) + } + } + + private fun handleUrgentAlarm(alarm: JSONObject) { + val defaultVal = config.NSCLIENT + if (sp.getBoolean(R.string.key_ns_alarms, defaultVal)) { + val snoozedTo = sp.getLong(R.string.key_snoozedTo, 0L) + if (snoozedTo == 0L || System.currentTimeMillis() > snoozedTo) { + val nsAlarm = NSAlarm(alarm) + val notification: Notification = NotificationWithAction(injector, nsAlarm) + rxBus.send(EventNewNotification(notification)) + } + rxBus.send(EventNSClientNewLog("URGENTALARM", safeGetString(alarm, "message", "received"))) + aapsLogger.debug(LTag.NSCLIENT, alarm.toString()) + } + } + + private fun splitArray(array: JSONArray): List { + var ret: MutableList = ArrayList() + try { + val size = array.length() + var count = 0 + var newarr: JSONArray? = null + for (i in 0 until size) { + if (count == 0) { + if (newarr != null) ret.add(newarr) + newarr = JSONArray() + count = 20 + } + newarr?.put(array[i]) + --count + } + if (newarr != null && newarr.length() > 0) ret.add(newarr) + } catch (e: JSONException) { + aapsLogger.error("Unhandled exception", e) + ret = ArrayList() + ret.add(array) + } + return ret + } + + init { + if (handler == null) { + val handlerThread = HandlerThread(NSClientService::class.java.simpleName + "Handler") + handlerThread.start() + handler = Handler(handlerThread.looper) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/openhumans/OpenHumansUploader.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/openhumans/OpenHumansUploader.kt index b1de3bab2e..a331fcd88d 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/openhumans/OpenHumansUploader.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/openhumans/OpenHumansUploader.kt @@ -28,8 +28,7 @@ import info.nightscout.androidaps.interfaces.PluginType 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.toConstant +import info.nightscout.androidaps.extensions.toConstant import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.utils.sharedPreferences.SP @@ -61,7 +60,6 @@ class OpenHumansUploader @Inject constructor( private val sp: SP, private val rxBus: RxBusWrapper, private val context: Context, - private val treatmentsPlugin: TreatmentsPlugin, private val databaseHelper: DatabaseHelperInterface, val repository: AppRepository ) : PluginBase( @@ -180,24 +178,24 @@ class OpenHumansUploader @Inject constructor( } } - @JvmOverloads - fun enqueueTreatment(treatment: Treatment?, deleted: Boolean = false) = treatment?.let { - insertQueueItem("Treatments") { - put("date", treatment.date) - put("isValid", treatment.isValid) - put("source", treatment.source) - put("nsId", treatment._id) - put("boluscalc", treatment.boluscalc) - put("carbs", treatment.carbs) - put("dia", treatment.dia) - put("insulin", treatment.insulin) - put("insulinInterfaceID", treatment.insulinInterfaceID) - put("isSMB", treatment.isSMB) - put("mealBolus", treatment.mealBolus) - put("bolusCalcJson", treatment.getBoluscalc()) - put("isDeletion", deleted) - } - } + // @JvmOverloads + // fun enqueueTreatment(treatment: Treatment?, deleted: Boolean = false) = treatment?.let { + // insertQueueItem("Treatments") { + // put("date", treatment.date) + // put("isValid", treatment.isValid) + // put("source", treatment.source) + // put("nsId", treatment._id) + // put("boluscalc", treatment.boluscalc) + // put("carbs", treatment.carbs) + // put("dia", treatment.dia) + // put("insulin", treatment.insulin) + // put("insulinInterfaceID", treatment.insulinInterfaceID) + // put("isSMB", treatment.isSMB) + // put("mealBolus", treatment.mealBolus) + // put("bolusCalcJson", treatment.getBoluscalc()) + // put("isDeletion", deleted) + // } + // } @JvmOverloads fun enqueueTherapyEvent(therapyEvent: TherapyEvent, deleted: Boolean = false) = insertQueueItem("TherapyEvents") { @@ -212,56 +210,56 @@ class OpenHumansUploader @Inject constructor( put("isDeletion", deleted) } - @JvmOverloads - fun enqueueExtendedBolus(extendedBolus: ExtendedBolus, deleted: Boolean = false) = insertQueueItem("ExtendedBoluses") { - put("date", extendedBolus.date) - put("isValid", extendedBolus.isValid) - put("source", extendedBolus.source) - put("nsId", extendedBolus._id) - put("pumpId", extendedBolus.pumpId) - put("insulin", extendedBolus.insulin) - put("durationInMinutes", extendedBolus.durationInMinutes) - put("isDeletion", deleted) - } + // @JvmOverloads + // fun enqueueExtendedBolus(extendedBolus: ExtendedBolus, deleted: Boolean = false) = insertQueueItem("ExtendedBoluses") { + // put("date", extendedBolus.date) + // put("isValid", extendedBolus.isValid) + // put("source", extendedBolus.source) + // put("nsId", extendedBolus._id) + // put("pumpId", extendedBolus.pumpId) + // put("insulin", extendedBolus.insulin) + // put("durationInMinutes", extendedBolus.durationInMinutes) + // put("isDeletion", deleted) + // } - @JvmOverloads - fun enqueueProfileSwitch(profileSwitch: ProfileSwitch, deleted: Boolean = false) = insertQueueItem("ProfileSwitches") { - put("date", profileSwitch.date) - put("isValid", profileSwitch.isValid) - put("source", profileSwitch.source) - put("nsId", profileSwitch._id) - put("isCPP", profileSwitch.isCPP) - put("timeshift", profileSwitch.timeshift) - put("percentage", profileSwitch.percentage) - put("profile", JSONObject(profileSwitch.profileJson)) - put("profilePlugin", profileSwitch.profilePlugin) - put("durationInMinutes", profileSwitch.durationInMinutes) - put("isDeletion", deleted) - } + // @JvmOverloads + // fun enqueueProfileSwitch(profileSwitch: ProfileSwitch, deleted: Boolean = false) = insertQueueItem("ProfileSwitches") { + // put("date", profileSwitch.date) + // put("isValid", profileSwitch.isValid) + // put("source", profileSwitch.source) + // put("nsId", profileSwitch._id) + // put("isCPP", profileSwitch.isCPP) + // put("timeshift", profileSwitch.timeshift) + // put("percentage", profileSwitch.percentage) + // put("profile", JSONObject(profileSwitch.profileJson)) + // put("profilePlugin", profileSwitch.profilePlugin) + // put("durationInMinutes", profileSwitch.durationInMinutes) + // put("isDeletion", deleted) + // } - fun enqueueTotalDailyDose(tdd: TDD) = insertQueueItem("TotalDailyDoses") { - put("double", tdd.date) - put("double", tdd.bolus) - put("double", tdd.basal) - put("double", tdd.total) - } + // fun enqueueTotalDailyDose(tdd: TDD) = insertQueueItem("TotalDailyDoses") { + // put("double", tdd.date) + // put("double", tdd.bolus) + // put("double", tdd.basal) + // put("double", tdd.total) + // } - @JvmOverloads - fun enqueueTemporaryBasal(temporaryBasal: TemporaryBasal?, deleted: Boolean = false) = temporaryBasal?.let { - insertQueueItem("TemporaryBasals") { - put("date", temporaryBasal.date) - put("isValid", temporaryBasal.isValid) - put("source", temporaryBasal.source) - put("nsId", temporaryBasal._id) - put("pumpId", temporaryBasal.pumpId) - put("durationInMinutes", temporaryBasal.durationInMinutes) - put("durationInMinutes", temporaryBasal.durationInMinutes) - put("isAbsolute", temporaryBasal.isAbsolute) - put("percentRate", temporaryBasal.percentRate) - put("absoluteRate", temporaryBasal.absoluteRate) - put("isDeletion", deleted) - } - } + // @JvmOverloads + // fun enqueueTemporaryBasal(temporaryBasal: TemporaryBasal?, deleted: Boolean = false) = temporaryBasal?.let { + // insertQueueItem("TemporaryBasals") { + // put("date", temporaryBasal.date) + // put("isValid", temporaryBasal.isValid) + // put("source", temporaryBasal.source) + // put("nsId", temporaryBasal._id) + // put("pumpId", temporaryBasal.pumpId) + // put("durationInMinutes", temporaryBasal.durationInMinutes) + // put("durationInMinutes", temporaryBasal.durationInMinutes) + // put("isAbsolute", temporaryBasal.isAbsolute) + // put("percentRate", temporaryBasal.percentRate) + // put("absoluteRate", temporaryBasal.absoluteRate) + // put("isDeletion", deleted) + // } + // } @JvmOverloads fun enqueueTempTarget(tempTarget: TemporaryTarget?, deleted: Boolean = false) = tempTarget?.let { @@ -356,29 +354,29 @@ class OpenHumansUploader @Inject constructor( if (currentProgress % 1000L == 0L) showOngoingNotification(maxProgress, currentProgress) } copyDisposable = Completable.fromCallable { databaseHelper.clearOpenHumansQueue() } - .andThen(Single.defer { Single.just(databaseHelper.getCountOfAllRows() + treatmentsPlugin.service.count()) }) - .doOnSuccess { maxProgress = it } - .flatMapObservable { Observable.defer { Observable.fromIterable(treatmentsPlugin.service.getTreatmentData()) } } - .map { enqueueTreatment(it); increaseCounter() } - .ignoreElements() +// .andThen(Single.defer { Single.just(databaseHelper.getCountOfAllRows() + treatmentsPlugin.service.count()) }) +// .doOnSuccess { maxProgress = it } +// .flatMapObservable { Observable.defer { Observable.fromIterable(treatmentsPlugin.service.getTreatmentData()) } } +// .map { enqueueTreatment(it); increaseCounter() } +// .ignoreElements() .andThen(Observable.defer { Observable.fromIterable(repository.compatGetBgReadingsDataFromTime(0, true).blockingGet()) }) .map { enqueueBGReading(it); increaseCounter() } .ignoreElements() .andThen(Observable.defer { Observable.fromIterable(repository.compatGetTherapyEventDataFromTime(0, true).blockingGet()) }) .map { enqueueTherapyEvent(it); increaseCounter() } .ignoreElements() - .andThen(Observable.defer { Observable.fromIterable(databaseHelper.getAllExtendedBoluses()) }) - .map { enqueueExtendedBolus(it); increaseCounter() } - .ignoreElements() - .andThen(Observable.defer { Observable.fromIterable(databaseHelper.getAllProfileSwitches()) }) - .map { enqueueProfileSwitch(it); increaseCounter() } - .ignoreElements() - .andThen(Observable.defer { Observable.fromIterable(databaseHelper.getAllTDDs()) }) - .map { enqueueTotalDailyDose(it); increaseCounter() } - .ignoreElements() - .andThen(Observable.defer { Observable.fromIterable(databaseHelper.getAllTemporaryBasals()) }) - .map { enqueueTemporaryBasal(it); increaseCounter() } - .ignoreElements() +// .andThen(Observable.defer { Observable.fromIterable(databaseHelper.getAllExtendedBoluses()) }) +// .map { enqueueExtendedBolus(it); increaseCounter() } +// .ignoreElements() +// .andThen(Observable.defer { Observable.fromIterable(databaseHelper.getAllProfileSwitches()) }) +// .map { enqueueProfileSwitch(it); increaseCounter() } +// .ignoreElements() + // .andThen(Observable.defer { Observable.fromIterable(databaseHelper.getAllTDDs()) }) + // .map { enqueueTotalDailyDose(it); increaseCounter() } + // .ignoreElements() + // .andThen(Observable.defer { Observable.fromIterable(databaseHelper.getAllTemporaryBasals()) }) + // .map { enqueueTemporaryBasal(it); increaseCounter() } + // .ignoreElements() .andThen(Observable.defer { Observable.fromIterable(repository.compatGetTemporaryTargetData().blockingGet()) }) .map { enqueueTempTarget(it); increaseCounter() } .ignoreElements() diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewData.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewData.kt new file mode 100644 index 0000000000..d5f64cc96e --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewData.kt @@ -0,0 +1,281 @@ +package info.nightscout.androidaps.plugins.general.overview + +import com.jjoe64.graphview.series.BarGraphSeries +import com.jjoe64.graphview.series.DataPoint +import com.jjoe64.graphview.series.LineGraphSeries +import info.nightscout.androidaps.R +import info.nightscout.androidaps.data.IobTotal +import info.nightscout.androidaps.database.entities.ExtendedBolus +import info.nightscout.androidaps.database.entities.GlucoseValue +import info.nightscout.androidaps.database.entities.TemporaryBasal +import info.nightscout.androidaps.database.entities.TemporaryTarget +import info.nightscout.androidaps.extensions.convertedToPercent +import info.nightscout.androidaps.extensions.toStringFull +import info.nightscout.androidaps.extensions.toStringShort +import info.nightscout.androidaps.extensions.valueToUnits +import info.nightscout.androidaps.interfaces.ActivePlugin +import info.nightscout.androidaps.interfaces.Profile +import info.nightscout.androidaps.interfaces.ProfileFunction +import info.nightscout.androidaps.plugins.general.overview.graphExtensions.DataPointWithLabelInterface +import info.nightscout.androidaps.plugins.general.overview.graphExtensions.FixedLineGraphSeries +import info.nightscout.androidaps.plugins.general.overview.graphExtensions.PointsWithLabelGraphSeries +import info.nightscout.androidaps.plugins.general.overview.graphExtensions.Scale +import info.nightscout.androidaps.plugins.general.overview.graphExtensions.ScaledDataPoint +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.CobInfo +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.data.AutosensData +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.DefaultValueHelper +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 javax.inject.Singleton +import kotlin.collections.ArrayList + +@Singleton +class OverviewData @Inject constructor( + private val resourceHelper: ResourceHelper, + private val dateUtil: DateUtil, + private val sp: SP, + private val activePlugin: ActivePlugin, + private val defaultValueHelper: DefaultValueHelper, + private val profileFunction: ProfileFunction +) { + + enum class Property { + TIME, + CALC_PROGRESS, + PROFILE, + TEMPORARY_BASAL, + EXTENDED_BOLUS, + TEMPORARY_TARGET, + BG, + IOB_COB, + SENSITIVITY, + GRAPH + } + + var rangeToDisplay = 6 // for graph + var toTime: Long = 0 + var fromTime: Long = 0 + var endTime: Long = 0 + + fun initRange() { + rangeToDisplay = sp.getInt(R.string.key_rangetodisplay, 6) + + val calendar = Calendar.getInstance().also { + it.timeInMillis = System.currentTimeMillis() + it[Calendar.MILLISECOND] = 0 + it[Calendar.SECOND] = 0 + it[Calendar.MINUTE] = 0 + it.add(Calendar.HOUR, 1) + } + + toTime = calendar.timeInMillis + 100000 // little bit more to avoid wrong rounding - GraphView specific + fromTime = toTime - T.hours(rangeToDisplay.toLong()).msecs() + endTime = toTime + } + + /* + * PROFILE + */ + var profile: Profile? = null + var profileName: String? = null + var profileNameWithRemainingTime: String? = null + + val profileBackgroudColor: Int + get() = + profile?.let { profile -> + if (profile.percentage != 100 || profile.timeshift != 0) resourceHelper.gc(R.color.ribbonWarning) + else resourceHelper.gc(R.color.ribbonDefault) + } ?: resourceHelper.gc(R.color.ribbonTextDefault) + + val profileTextColor: Int + get() = + profile?.let { profile -> + if (profile.percentage != 100 || profile.timeshift != 0) resourceHelper.gc(R.color.ribbonTextWarning) + else resourceHelper.gc(R.color.ribbonTextDefault) + } ?: resourceHelper.gc(R.color.ribbonTextDefault) + + /* + * CALC PROGRESS + */ + + var calcProgress: String = "" + + /* + * BG + */ + + var lastBg: GlucoseValue? = null + + val lastBgColor: Int + get() = lastBg?.let { lastBg -> + when { + lastBg.valueToUnits(profileFunction.getUnits()) < defaultValueHelper.determineLowLine() -> resourceHelper.gc(R.color.low) + lastBg.valueToUnits(profileFunction.getUnits()) > defaultValueHelper.determineHighLine() -> resourceHelper.gc(R.color.high) + else -> resourceHelper.gc(R.color.inrange) + } + } ?: resourceHelper.gc(R.color.inrange) + + val isActualBg: Boolean + get() = + lastBg?.let { lastBg -> + lastBg.timestamp > dateUtil.now() - T.mins(9).msecs() + } ?: false + + /* + * TEMPORARY BASAL + */ + + var temporaryBasal: TemporaryBasal? = null + + val temporaryBasalText: String + get() = + profile?.let { profile -> + temporaryBasal?.let { "T:" + it.toStringShort() } + ?: resourceHelper.gs(R.string.pump_basebasalrate, profile.getBasal()) + } ?: resourceHelper.gs(R.string.notavailable) + + val temporaryBasalDialogText: String + get() = profile?.let { profile -> + temporaryBasal?.let { temporaryBasal -> + "${resourceHelper.gs(R.string.basebasalrate_label)}: ${resourceHelper.gs(R.string.pump_basebasalrate, profile.getBasal())}" + + "\n" + resourceHelper.gs(R.string.tempbasal_label) + ": " + temporaryBasal.toStringFull(profile, dateUtil) + } + ?: "${resourceHelper.gs(R.string.basebasalrate_label)}: ${resourceHelper.gs(R.string.pump_basebasalrate, profile.getBasal())}" + } ?: resourceHelper.gs(R.string.notavailable) + + val temporaryBasalIcon: Int + get() = + profile?.let { profile -> + temporaryBasal?.let { temporaryBasal -> + val percentRate = temporaryBasal.convertedToPercent(dateUtil.now(), profile) + when { + percentRate > 100 -> R.drawable.ic_cp_basal_tbr_high + percentRate < 100 -> R.drawable.ic_cp_basal_tbr_low + else -> R.drawable.ic_cp_basal_no_tbr + } + } + } ?: R.drawable.ic_cp_basal_no_tbr + + val temporaryBasalColor: Int + get() = temporaryBasal?.let { resourceHelper.gc(R.color.basal) } + ?: resourceHelper.gc(R.color.defaulttextcolor) + + /* + * EXTENDED BOLUS + */ + + var extendedBolus: ExtendedBolus? = null + + val extendedBolusText: String + get() = + extendedBolus?.let { extendedBolus -> + if (activePlugin.activePump.isFakingTempsByExtendedBoluses) resourceHelper.gs(R.string.pump_basebasalrate, extendedBolus.rate) + else "" + } ?: "" + + val extendedBolusDialogText: String + get() = extendedBolus?.toStringFull(dateUtil) ?: "" + + /* + * IOB, COB + */ + + var bolusIob: IobTotal? = null + var basalIob: IobTotal? = null + var cobInfo: CobInfo? = null + var lastCarbsTime: Long = 0L + + val iobText: String + get() = + bolusIob?.let { bolusIob -> + basalIob?.let { basalIob -> + resourceHelper.gs(R.string.formatinsulinunits, bolusIob.iob + basalIob.basaliob) + } ?: resourceHelper.gs(R.string.value_unavailable_short) + } ?: resourceHelper.gs(R.string.value_unavailable_short) + + val iobDialogText: String + get() = + bolusIob?.let { bolusIob -> + basalIob?.let { basalIob -> + resourceHelper.gs(R.string.formatinsulinunits, bolusIob.iob + basalIob.basaliob) + "\n" + + resourceHelper.gs(R.string.bolus) + ": " + resourceHelper.gs(R.string.formatinsulinunits, bolusIob.iob) + "\n" + + resourceHelper.gs(R.string.basal) + ": " + resourceHelper.gs(R.string.formatinsulinunits, basalIob.basaliob) + } ?: resourceHelper.gs(R.string.value_unavailable_short) + } ?: resourceHelper.gs(R.string.value_unavailable_short) + + /* + * TEMP TARGET + */ + + var temporarytarget: TemporaryTarget? = null + + /* + * SENSITIVITY + */ + + var lastAutosensData: AutosensData? = null + /* + * Graphs + */ + + var bgReadingsArray: List = ArrayList() + var maxBgValue = Double.MIN_VALUE + var bucketedGraphSeries: PointsWithLabelGraphSeries = PointsWithLabelGraphSeries() + var bgReadingGraphSeries: PointsWithLabelGraphSeries = PointsWithLabelGraphSeries() + var predictionsGraphSeries: PointsWithLabelGraphSeries = PointsWithLabelGraphSeries() + + var maxBasalValueFound = 0.0 + val basalScale = Scale() + var baseBasalGraphSeries: LineGraphSeries = LineGraphSeries() + var tempBasalGraphSeries: LineGraphSeries = LineGraphSeries() + var basalLineGraphSeries: LineGraphSeries = LineGraphSeries() + var absoluteBasalGraphSeries: LineGraphSeries = LineGraphSeries() + + var temporaryTargetSeries: LineGraphSeries = LineGraphSeries() + + var maxIAValue = 0.0 + val actScale = Scale() + var activitySeries: FixedLineGraphSeries = FixedLineGraphSeries() + var activityPredictionSeries: FixedLineGraphSeries = FixedLineGraphSeries() + + var maxTreatmentsValue = 0.0 + var treatmentsSeries: PointsWithLabelGraphSeries = PointsWithLabelGraphSeries() + + var maxIobValueFound = Double.MIN_VALUE + val iobScale = Scale() + var iobSeries: FixedLineGraphSeries = FixedLineGraphSeries() + var absIobSeries: FixedLineGraphSeries = FixedLineGraphSeries() + var iobPredictions1Series: PointsWithLabelGraphSeries = PointsWithLabelGraphSeries() + var iobPredictions2Series: PointsWithLabelGraphSeries = PointsWithLabelGraphSeries() + + var maxBGIValue = Double.MIN_VALUE + val bgiScale = Scale() + var minusBgiSeries: FixedLineGraphSeries = FixedLineGraphSeries() + var minusBgiHistSeries: FixedLineGraphSeries = FixedLineGraphSeries() + + var maxCobValueFound = Double.MIN_VALUE + val cobScale = Scale() + var cobSeries: FixedLineGraphSeries = FixedLineGraphSeries() + var cobMinFailOverSeries: PointsWithLabelGraphSeries = PointsWithLabelGraphSeries() + + var maxDevValueFound = Double.MIN_VALUE + val devScale = Scale() + var deviationsSeries: BarGraphSeries = BarGraphSeries() + + var maxRatioValueFound = 5.0 //even if sens data equals 0 for all the period, minimum scale is between 95% and 105% + var minRatioValueFound = -maxRatioValueFound + val ratioScale = Scale() + var ratioSeries: LineGraphSeries = LineGraphSeries() + + var maxFromMaxValueFound = Double.MIN_VALUE + var maxFromMinValueFound = Double.MIN_VALUE + val dsMaxScale = Scale() + val dsMinScale = Scale() + var dsMaxSeries: LineGraphSeries = LineGraphSeries() + var dsMinSeries: LineGraphSeries = LineGraphSeries() + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.kt index c7fd5f2a1a..d4b9d90774 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.kt @@ -10,6 +10,7 @@ import android.graphics.Paint import android.graphics.drawable.AnimationDrawable import android.os.Bundle import android.os.Handler +import android.os.HandlerThread import android.util.DisplayMetrics import android.view.LayoutInflater import android.view.View @@ -19,39 +20,40 @@ import android.widget.LinearLayout import android.widget.RelativeLayout import android.widget.TextView import androidx.core.text.toSpanned -import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager import com.jjoe64.graphview.GraphView import dagger.android.HasAndroidInjector import dagger.android.support.DaggerFragment -import info.nightscout.androidaps.Config import info.nightscout.androidaps.Constants import info.nightscout.androidaps.R -import info.nightscout.androidaps.data.Profile import info.nightscout.androidaps.database.AppRepository -import info.nightscout.androidaps.database.ValueWrapper -import info.nightscout.androidaps.database.entities.TemporaryTarget -import info.nightscout.androidaps.database.entities.UserEntry.* +import info.nightscout.androidaps.database.entities.UserEntry.Action +import info.nightscout.androidaps.database.entities.UserEntry.Sources import info.nightscout.androidaps.database.interfaces.end import info.nightscout.androidaps.databinding.OverviewFragmentBinding import info.nightscout.androidaps.dialogs.* -import info.nightscout.androidaps.events.* +import info.nightscout.androidaps.events.EventAcceptOpenLoopChange +import info.nightscout.androidaps.events.EventInitializationChanged +import info.nightscout.androidaps.events.EventPreferenceChange +import info.nightscout.androidaps.events.EventPumpStatusChanged +import info.nightscout.androidaps.events.EventRefreshOverview +import info.nightscout.androidaps.extensions.directionToIcon +import info.nightscout.androidaps.extensions.toVisibility +import info.nightscout.androidaps.extensions.valueToUnitsString 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 -import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker 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.events.EventUpdateOverview 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.events.EventWearInitiateAction import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatusProvider -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventIobCalculationProgress import info.nightscout.androidaps.plugins.pump.common.defs.PumpType import info.nightscout.androidaps.plugins.source.DexcomPlugin import info.nightscout.androidaps.plugins.source.XdripPlugin @@ -61,10 +63,6 @@ import info.nightscout.androidaps.skins.SkinProvider import info.nightscout.androidaps.utils.* import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.buildHelper.BuildHelper -import info.nightscout.androidaps.utils.extensions.directionToIcon -import info.nightscout.androidaps.utils.extensions.toVisibility -import info.nightscout.androidaps.utils.extensions.valueToUnits -import info.nightscout.androidaps.utils.extensions.valueToUnitsString import info.nightscout.androidaps.utils.protection.ProtectionCheck import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers @@ -72,15 +70,11 @@ import info.nightscout.androidaps.utils.sharedPreferences.SP import info.nightscout.androidaps.utils.ui.UIRunnable import info.nightscout.androidaps.utils.wizard.QuickWizard import io.reactivex.disposables.CompositeDisposable -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext +import io.reactivex.rxkotlin.plusAssign import java.util.* import javax.inject.Inject import kotlin.collections.ArrayList import kotlin.math.abs -import kotlin.math.ceil -import kotlin.math.max import kotlin.math.min class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickListener { @@ -97,10 +91,9 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList @Inject lateinit var statusLightHandler: StatusLightHandler @Inject lateinit var nsDeviceStatus: NSDeviceStatus @Inject lateinit var loopPlugin: LoopPlugin - @Inject lateinit var configBuilderPlugin: ConfigBuilderPlugin - @Inject lateinit var activePlugin: ActivePluginProvider + @Inject lateinit var activePlugin: ActivePlugin @Inject lateinit var treatmentsPlugin: TreatmentsPlugin - @Inject lateinit var iobCobCalculatorPlugin: IobCobCalculatorPlugin + @Inject lateinit var iobCobCalculator: IobCobCalculator @Inject lateinit var dexcomPlugin: DexcomPlugin @Inject lateinit var dexcomMediator: DexcomPlugin.DexcomMediator @Inject lateinit var xdripPlugin: XdripPlugin @@ -119,6 +112,8 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList @Inject lateinit var uel: UserEntryLogger @Inject lateinit var repository: AppRepository @Inject lateinit var glucoseStatusProvider: GlucoseStatusProvider + @Inject lateinit var overviewData: OverviewData + @Inject lateinit var overviewPlugin: OverviewPlugin private val disposable = CompositeDisposable() @@ -126,17 +121,14 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList private var smallHeight = false private lateinit var dm: DisplayMetrics private var axisWidth: Int = 0 - private var rangeToDisplay = 6 // for graph - private var loopHandler = Handler() private var refreshLoop: Runnable? = null + private lateinit var handler: Handler private val secondaryGraphs = ArrayList() private val secondaryGraphsLabel = ArrayList() private var carbAnimation: AnimationDrawable? = null - private val graphLock = Object() - private var _binding: OverviewFragmentBinding? = null // This property is only valid between onCreateView and @@ -153,6 +145,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + handler = Handler(HandlerThread(this::class.simpleName + "Handler").also { it.start() }.looper) // pre-process landscape mode val screenWidth = dm.widthPixels @@ -178,13 +171,14 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList carbAnimation?.setEnterFadeDuration(1200) carbAnimation?.setExitFadeDuration(1200) - rangeToDisplay = sp.getInt(R.string.key_rangetodisplay, 6) binding.graphsLayout.bgGraph.setOnLongClickListener { - rangeToDisplay += 6 - rangeToDisplay = if (rangeToDisplay > 24) 6 else rangeToDisplay - sp.putInt(R.string.key_rangetodisplay, rangeToDisplay) - updateGUI("rangeChange") + overviewData.rangeToDisplay += 6 + overviewData.rangeToDisplay = if (overviewData.rangeToDisplay > 24) 6 else overviewData.rangeToDisplay + sp.putInt(R.string.key_rangetodisplay, overviewData.rangeToDisplay) + overviewData.initRange() + updateGUI("rangeChange", OverviewData.Property.GRAPH) + rxBus.send(EventPreferenceChange(resourceHelper, R.string.key_rangetodisplay)) sp.putBoolean(R.string.key_objectiveusescale, true) false } @@ -213,55 +207,32 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList override fun onPause() { super.onPause() disposable.clear() - loopHandler.removeCallbacksAndMessages(null) + handler.removeCallbacksAndMessages(null) } @Synchronized override fun onResume() { super.onResume() + disposable += activePlugin.activeOverview.overviewBus + .toObservable(EventUpdateOverview::class.java) + .observeOn(aapsSchedulers.main) + .subscribe({ updateGUI(it.from, it.what) }, fabricPrivacy::logException) + disposable.add(rxBus .toObservable(EventRefreshOverview::class.java) - .observeOn(aapsSchedulers.main) + .observeOn(aapsSchedulers.io) .subscribe({ - if (it.now) updateGUI(it.from) + if (it.now) overviewPlugin.refreshLoop(it.from) else scheduleUpdateGUI(it.from) }, fabricPrivacy::logException)) - disposable.add(rxBus - .toObservable(EventExtendedBolusChange::class.java) - .observeOn(aapsSchedulers.io) - .subscribe({ scheduleUpdateGUI("EventExtendedBolusChange") }, fabricPrivacy::logException)) - disposable.add(rxBus - .toObservable(EventTempBasalChange::class.java) - .observeOn(aapsSchedulers.io) - .subscribe({ scheduleUpdateGUI("EventTempBasalChange") }, fabricPrivacy::logException)) - disposable.add(rxBus - .toObservable(EventTreatmentChange::class.java) - .observeOn(aapsSchedulers.io) - .subscribe({ scheduleUpdateGUI("EventTreatmentChange") }, fabricPrivacy::logException)) - disposable.add(rxBus - .toObservable(EventTempTargetChange::class.java) - .observeOn(aapsSchedulers.io) - .subscribe({ scheduleUpdateGUI("EventTempTargetChange") }, fabricPrivacy::logException)) disposable.add(rxBus .toObservable(EventAcceptOpenLoopChange::class.java) .observeOn(aapsSchedulers.io) .subscribe({ scheduleUpdateGUI("EventAcceptOpenLoopChange") }, fabricPrivacy::logException)) - disposable.add(rxBus - .toObservable(EventTherapyEventChange::class.java) - .observeOn(aapsSchedulers.io) - .subscribe({ scheduleUpdateGUI("EventCareportalEventChange") }, fabricPrivacy::logException)) disposable.add(rxBus .toObservable(EventInitializationChanged::class.java) - .observeOn(aapsSchedulers.io) - .subscribe({ scheduleUpdateGUI("EventInitializationChanged") }, fabricPrivacy::logException)) - disposable.add(rxBus - .toObservable(EventAutosensCalculationFinished::class.java) - .observeOn(aapsSchedulers.io) - .subscribe({ scheduleUpdateGUI("EventAutosensCalculationFinished") }, fabricPrivacy::logException)) - disposable.add(rxBus - .toObservable(EventProfileNeedsUpdate::class.java) - .observeOn(aapsSchedulers.io) - .subscribe({ scheduleUpdateGUI("EventProfileNeedsUpdate") }, fabricPrivacy::logException)) + .observeOn(aapsSchedulers.main) + .subscribe({ updateGUI("EventInitializationChanged", OverviewData.Property.TIME) }, fabricPrivacy::logException)) disposable.add(rxBus .toObservable(EventPreferenceChange::class.java) .observeOn(aapsSchedulers.io) @@ -274,18 +245,14 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList .toObservable(EventPumpStatusChanged::class.java) .observeOn(aapsSchedulers.main) .subscribe({ updatePumpStatus(it) }, fabricPrivacy::logException)) - disposable.add(rxBus - .toObservable(EventIobCalculationProgress::class.java) - .observeOn(aapsSchedulers.main) - .subscribe({ binding.graphsLayout.iobCalculationProgress.text = it.progress }, fabricPrivacy::logException)) refreshLoop = Runnable { - scheduleUpdateGUI("refreshLoop") - loopHandler.postDelayed(refreshLoop, 60 * 1000L) + overviewPlugin.refreshLoop("refreshLoop") + handler.postDelayed(refreshLoop, 60 * 1000L) } - loopHandler.postDelayed(refreshLoop, 60 * 1000L) + handler.postDelayed(refreshLoop, 60 * 1000L) - updateGUI("onResume") + for (p in OverviewData.Property.values()) updateGUI("onResume", p) } @Synchronized @@ -300,23 +267,23 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList if (childFragmentManager.isStateSaved) return activity?.let { activity -> when (v.id) { - R.id.treatment_button -> protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, UIRunnable { if (isAdded) TreatmentDialog().show(childFragmentManager, "Overview") }) - R.id.wizard_button -> protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, UIRunnable { if (isAdded) WizardDialog().show(childFragmentManager, "Overview") }) - R.id.insulin_button -> protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, UIRunnable { if (isAdded) InsulinDialog().show(childFragmentManager, "Overview") }) + R.id.treatment_button -> protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, UIRunnable { if (isAdded) TreatmentDialog().show(childFragmentManager, "Overview") }) + R.id.wizard_button -> protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, UIRunnable { if (isAdded) WizardDialog().show(childFragmentManager, "Overview") }) + R.id.insulin_button -> protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, UIRunnable { if (isAdded) InsulinDialog().show(childFragmentManager, "Overview") }) R.id.quick_wizard_button -> protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, UIRunnable { if (isAdded) onClickQuickWizard() }) - R.id.carbs_button -> protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, UIRunnable { if (isAdded) CarbsDialog().show(childFragmentManager, "Overview") }) - R.id.temp_target -> protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, UIRunnable { if (isAdded) TempTargetDialog().show(childFragmentManager, "Overview") }) + R.id.carbs_button -> protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, UIRunnable { if (isAdded) CarbsDialog().show(childFragmentManager, "Overview") }) + R.id.temp_target -> protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, UIRunnable { if (isAdded) TempTargetDialog().show(childFragmentManager, "Overview") }) - R.id.active_profile -> { + R.id.active_profile -> { ProfileViewerDialog().also { pvd -> pvd.arguments = Bundle().also { - it.putLong("time", DateUtil.now()) + it.putLong("time", dateUtil.now()) it.putInt("mode", ProfileViewerDialog.Mode.RUNNING_PROFILE.ordinal) } }.show(childFragmentManager, "ProfileViewDialog") } - R.id.cgm_button -> { + R.id.cgm_button -> { if (xdripPlugin.isEnabled(PluginType.BGSOURCE)) openCgmApp("com.eveningoutpost.dexdrip") else if (dexcomPlugin.isEnabled(PluginType.BGSOURCE)) { @@ -327,7 +294,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList } } - R.id.calibration_button -> { + R.id.calibration_button -> { if (xdripPlugin.isEnabled(PluginType.BGSOURCE)) { CalibrationDialog().show(childFragmentManager, "CalibrationDialog") } else if (dexcomPlugin.isEnabled(PluginType.BGSOURCE)) { @@ -342,27 +309,29 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList } } - R.id.accept_temp_button -> { + R.id.accept_temp_button -> { profileFunction.getProfile() ?: return if (loopPlugin.isEnabled(PluginType.LOOP)) { - val lastRun = loopPlugin.lastRun - loopPlugin.invoke("Accept temp button", false) - if (lastRun?.lastAPSRun != null && lastRun.constraintsProcessed?.isChangeRequested == true) { - protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, UIRunnable { - OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.tempbasal_label), lastRun.constraintsProcessed?.toSpanned() - ?: "".toSpanned(), { - uel.log(Action.ACCEPTS_TEMP_BASAL) - binding.buttonsLayout.acceptTempButton.visibility = View.GONE - (context?.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager).cancel(Constants.notificationID) - rxBus.send(EventWearInitiateAction("cancelChangeRequest")) - loopPlugin.acceptChangeRequest() + handler.post { + val lastRun = loopPlugin.lastRun + loopPlugin.invoke("Accept temp button", false) + if (lastRun?.lastAPSRun != null && lastRun.constraintsProcessed?.isChangeRequested == true) { + protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, UIRunnable { + OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.tempbasal_label), lastRun.constraintsProcessed?.toSpanned() + ?: "".toSpanned(), { + uel.log(Action.ACCEPTS_TEMP_BASAL, Sources.Overview) + binding.buttonsLayout.acceptTempButton.visibility = View.GONE + (context?.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager).cancel(Constants.notificationID) + rxBus.send(EventWearInitiateAction("cancelChangeRequest")) + Thread { loopPlugin.acceptChangeRequest() }.run() + }) }) - }) + } } } } - R.id.aps_mode -> { + R.id.aps_mode -> { protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, UIRunnable { if (isAdded) LoopDialog().also { dialog -> dialog.arguments = Bundle().also { it.putInt("showOkCancel", 1) } @@ -394,7 +363,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList return true } - R.id.aps_mode -> { + R.id.aps_mode -> { activity?.let { activity -> protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, UIRunnable { LoopDialog().also { dialog -> @@ -404,15 +373,15 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList } } - R.id.temp_target -> v.performClick() - R.id.active_profile -> activity?.let { activity -> protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, UIRunnable { ProfileSwitchDialog().show(childFragmentManager, "Overview") }) } + R.id.temp_target -> v.performClick() + R.id.active_profile -> activity?.let { activity -> protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, UIRunnable { ProfileSwitchDialog().show(childFragmentManager, "Overview") }) } } return false } private fun onClickQuickWizard() { - val actualBg = iobCobCalculatorPlugin.actualBg() + val actualBg = iobCobCalculator.ads.actualBg() val profile = profileFunction.getProfile() val profileName = profileFunction.getProfileName() val pump = activePlugin.activePump @@ -447,11 +416,11 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList @SuppressLint("SetTextI18n") private fun processButtonsVisibility() { - val lastBG = iobCobCalculatorPlugin.lastBg() + val lastBG = iobCobCalculator.ads.lastBg() val pump = activePlugin.activePump val profile = profileFunction.getProfile() val profileName = profileFunction.getProfileName() - val actualBG = iobCobCalculatorPlugin.actualBg() + val actualBG = iobCobCalculator.ads.actualBg() // QuickWizard button val quickWizardEntry = quickWizard.getActive() @@ -493,157 +462,35 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList } - private fun prepareGraphsIfNeeded(numOfGraphs: Int) { - synchronized(graphLock) { - if (numOfGraphs != secondaryGraphs.size - 1) { - //aapsLogger.debug("New secondary graph count ${numOfGraphs-1}") - // rebuild needed - secondaryGraphs.clear() - secondaryGraphsLabel.clear() - binding.graphsLayout.iobGraph.removeAllViews() - for (i in 1 until numOfGraphs) { - val relativeLayout = RelativeLayout(context) - relativeLayout.layoutParams = RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) - - val graph = GraphView(context) - graph.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, resourceHelper.dpToPx(skinProvider.activeSkin().secondaryGraphHeight)).also { it.setMargins(0, resourceHelper.dpToPx(15), 0, resourceHelper.dpToPx(10)) } - graph.gridLabelRenderer?.gridColor = resourceHelper.gc(R.color.graphgrid) - graph.gridLabelRenderer?.reloadStyles() - graph.gridLabelRenderer?.isHorizontalLabelsVisible = false - graph.gridLabelRenderer?.labelVerticalWidth = axisWidth - graph.gridLabelRenderer?.numVerticalLabels = 3 - graph.viewport.backgroundColor = Color.argb(20, 255, 255, 255) // 8% of gray - relativeLayout.addView(graph) - - val label = TextView(context) - val layoutParams = RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT).also { it.setMargins(resourceHelper.dpToPx(30), resourceHelper.dpToPx(25), 0, 0) } - layoutParams.addRule(RelativeLayout.ALIGN_PARENT_TOP) - layoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT) - label.layoutParams = layoutParams - relativeLayout.addView(label) - secondaryGraphsLabel.add(label) - - binding.graphsLayout.iobGraph.addView(relativeLayout) - secondaryGraphs.add(graph) - } - } - } - } - - var task: Runnable? = null - - private fun scheduleUpdateGUI(from: String) { - class UpdateRunnable : Runnable { - - override fun run() { - updateGUI(from) - task = null - } - } - view?.removeCallbacks(task) - task = UpdateRunnable() - view?.postDelayed(task, 500) - } - - @SuppressLint("SetTextI18n") - fun updateGUI(from: String) { - if (_binding == null) return - aapsLogger.debug("UpdateGUI from $from") - - binding.infoLayout.time.text = dateUtil.timeString(Date()) - - if (!profileFunction.isProfileValid("Overview")) { - binding.loopPumpStatusLayout.pumpStatus.setText(R.string.noprofileset) - binding.loopPumpStatusLayout.pumpStatusLayout.visibility = View.VISIBLE - binding.loopPumpStatusLayout.loopLayout.visibility = View.GONE - return - } - binding.notifications.let { notificationStore.updateNotifications(it) } - binding.loopPumpStatusLayout.pumpStatusLayout.visibility = View.GONE - binding.loopPumpStatusLayout.loopLayout.visibility = View.VISIBLE - - val profile = profileFunction.getProfile() ?: return - val actualBG = iobCobCalculatorPlugin.actualBg() - val lastBG = iobCobCalculatorPlugin.lastBg() + private fun processAps() { val pump = activePlugin.activePump - val units = profileFunction.getUnits() - val lowLine = defaultValueHelper.determineLowLine() - val highLine = defaultValueHelper.determineHighLine() - val lastRun = loopPlugin.lastRun - val predictionsAvailable = if (config.APS) lastRun?.request?.hasPredictions == true else config.NSCLIENT - - try { - updateGraph(lastRun, predictionsAvailable, lowLine, highLine, pump, profile) - } catch (e: IllegalStateException) { - return // view no longer exists - } - - //Start with updating the BG as it is unaffected by loop. - // **** BG value **** - if (lastBG != null) { - val color = when { - lastBG.valueToUnits(units) < lowLine -> resourceHelper.gc(R.color.low) - lastBG.valueToUnits(units) > highLine -> resourceHelper.gc(R.color.high) - else -> resourceHelper.gc(R.color.inrange) - } - - binding.infoLayout.bg.text = lastBG.valueToUnitsString(units) - binding.infoLayout.bg.setTextColor(color) - binding.infoLayout.arrow.setImageResource(trendCalculator.getTrendArrow(lastBG).directionToIcon()) - binding.infoLayout.arrow.setColorFilter(color) - - val glucoseStatus = glucoseStatusProvider.glucoseStatusData - if (glucoseStatus != null) { - 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.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 = "" - binding.infoLayout.longAvgDelta.text = "" - } - - // strike through if BG is old - binding.infoLayout.bg.let { overview_bg -> - var flag = overview_bg.paintFlags - flag = if (actualBG == null) { - flag or Paint.STRIKE_THRU_TEXT_FLAG - } else flag and Paint.STRIKE_THRU_TEXT_FLAG.inv() - overview_bg.paintFlags = flag - } - binding.infoLayout.timeAgo.text = DateUtil.minAgo(resourceHelper, lastBG.timestamp) - binding.infoLayout.timeAgoShort.text = "(" + DateUtil.minAgoShort(lastBG.timestamp) + ")" - - } - val closedLoopEnabled = constraintChecker.isClosedLoopAllowed() // aps mode + val closedLoopEnabled = constraintChecker.isClosedLoopAllowed() if (config.APS && pump.pumpDescription.isTempBasalCapable) { binding.infoLayout.apsMode.visibility = View.VISIBLE binding.infoLayout.timeLayout.visibility = View.GONE when { loopPlugin.isEnabled() && loopPlugin.isSuperBolus -> { binding.infoLayout.apsMode.setImageResource(R.drawable.ic_loop_superbolus) - binding.infoLayout.apsModeText.text = DateUtil.age(loopPlugin.minutesToEndOfSuspend() * 60000L, true, resourceHelper) + binding.infoLayout.apsModeText.text = dateUtil.age(loopPlugin.minutesToEndOfSuspend() * 60000L, true, resourceHelper) binding.infoLayout.apsModeText.visibility = View.VISIBLE } loopPlugin.isDisconnected -> { binding.infoLayout.apsMode.setImageResource(R.drawable.ic_loop_disconnected) - binding.infoLayout.apsModeText.text = DateUtil.age(loopPlugin.minutesToEndOfSuspend() * 60000L, true, resourceHelper) + binding.infoLayout.apsModeText.text = dateUtil.age(loopPlugin.minutesToEndOfSuspend() * 60000L, true, resourceHelper) binding.infoLayout.apsModeText.visibility = View.VISIBLE } loopPlugin.isEnabled() && loopPlugin.isSuspended -> { binding.infoLayout.apsMode.setImageResource(R.drawable.ic_loop_paused) - binding.infoLayout.apsModeText.text = DateUtil.age(loopPlugin.minutesToEndOfSuspend() * 60000L, true, resourceHelper) + binding.infoLayout.apsModeText.text = dateUtil.age(loopPlugin.minutesToEndOfSuspend() * 60000L, true, resourceHelper) binding.infoLayout.apsModeText.visibility = View.VISIBLE } pump.isSuspended() -> { - binding.infoLayout.apsMode.setImageResource(if (pump.model() == PumpType.Omnipod_Eros || pump.model() == PumpType.Omnipod_Dash) { + binding.infoLayout.apsMode.setImageResource(if (pump.model() == PumpType.OMNIPOD_EROS || pump.model() == PumpType.OMNIPOD_DASH) { // For Omnipod, indicate the pump as disconnected when it's suspended. // The only way to 'reconnect' it, is through the Omnipod tab R.drawable.ic_loop_disconnected @@ -680,119 +527,6 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList binding.infoLayout.timeLayout.visibility = View.VISIBLE } - // temp target - val tempTarget: ValueWrapper = repository.getTemporaryTargetActiveAt(dateUtil._now()).blockingGet() - if (tempTarget is ValueWrapper.Existing) { - binding.loopPumpStatusLayout.tempTarget.setTextColor(resourceHelper.gc(R.color.ribbonTextWarning)) - binding.loopPumpStatusLayout.tempTarget.setBackgroundColor(resourceHelper.gc(R.color.ribbonWarning)) - binding.loopPumpStatusLayout.tempTarget.text = Profile.toTargetRangeString(tempTarget.value.lowTarget, tempTarget.value.highTarget, Constants.MGDL, units) + " " + DateUtil.untilString(tempTarget.value.end, resourceHelper) - } else { - // If the target is not the same as set in the profile then oref has overridden it - val targetUsed = lastRun?.constraintsProcessed?.targetBG ?: 0.0 - - if (targetUsed != 0.0 && abs(profile.targetMgdl - targetUsed) > 0.01) { - aapsLogger.debug("Adjusted target. Profile: ${profile.targetMgdl} APS: $targetUsed") - binding.loopPumpStatusLayout.tempTarget.text = Profile.toTargetRangeString(targetUsed, targetUsed, Constants.MGDL, units) - binding.loopPumpStatusLayout.tempTarget.setTextColor(resourceHelper.gc(R.color.ribbonTextWarning)) - binding.loopPumpStatusLayout.tempTarget.setBackgroundColor(resourceHelper.gc(R.color.tempTargetBackground)) - } else { - binding.loopPumpStatusLayout.tempTarget.setTextColor(resourceHelper.gc(R.color.ribbonTextDefault)) - binding.loopPumpStatusLayout.tempTarget.setBackgroundColor(resourceHelper.gc(R.color.ribbonDefault)) - binding.loopPumpStatusLayout.tempTarget.text = Profile.toTargetRangeString(profile.targetLowMgdl, profile.targetHighMgdl, Constants.MGDL, units) - } - } - - // Basal, TBR - val activeTemp = treatmentsPlugin.getTempBasalFromHistory(System.currentTimeMillis()) - binding.infoLayout.baseBasal.text = activeTemp?.let { "T:" + activeTemp.toStringVeryShort() } - ?: resourceHelper.gs(R.string.pump_basebasalrate, profile.basal) - binding.infoLayout.basalLayout.setOnClickListener { - var fullText = "${resourceHelper.gs(R.string.basebasalrate_label)}: ${resourceHelper.gs(R.string.pump_basebasalrate, profile.basal)}" - if (activeTemp != null) - fullText += "\n" + resourceHelper.gs(R.string.tempbasal_label) + ": " + activeTemp.toStringFull() - activity?.let { - OKDialog.show(it, resourceHelper.gs(R.string.basal), fullText) - } - } - binding.infoLayout.baseBasal.setTextColor(activeTemp?.let { resourceHelper.gc(R.color.basal) } - ?: resourceHelper.gc(R.color.defaulttextcolor)) - - binding.infoLayout.baseBasalIcon.setImageResource(R.drawable.ic_cp_basal_no_tbr) - val percentRate = activeTemp?.tempBasalConvertedToPercent(System.currentTimeMillis(), profile) - ?: 100 - if (percentRate > 100) binding.infoLayout.baseBasalIcon.setImageResource(R.drawable.ic_cp_basal_tbr_high) - if (percentRate < 100) binding.infoLayout.baseBasalIcon.setImageResource(R.drawable.ic_cp_basal_tbr_low) - - // Extended bolus - val extendedBolus = treatmentsPlugin.getExtendedBolusFromHistory(System.currentTimeMillis()) - binding.infoLayout.extendedBolus.text = - if (extendedBolus != null && !pump.isFakingTempsByExtendedBoluses) - resourceHelper.gs(R.string.pump_basebasalrate, extendedBolus.absoluteRate()) - else "" - binding.infoLayout.extendedBolus.setOnClickListener { - if (extendedBolus != null) activity?.let { - OKDialog.show(it, resourceHelper.gs(R.string.extended_bolus), extendedBolus.toString()) - } - } - binding.infoLayout.extendedLayout.visibility = (extendedBolus != null && !pump.isFakingTempsByExtendedBoluses).toVisibility() - - // Active profile - binding.loopPumpStatusLayout.activeProfile.text = profileFunction.getProfileNameWithDuration() - if (profile.percentage != 100 || profile.timeshift != 0) { - binding.loopPumpStatusLayout.activeProfile.setBackgroundColor(resourceHelper.gc(R.color.ribbonWarning)) - binding.loopPumpStatusLayout.activeProfile.setTextColor(resourceHelper.gc(R.color.ribbonTextWarning)) - } else { - binding.loopPumpStatusLayout.activeProfile.setBackgroundColor(resourceHelper.gc(R.color.ribbonDefault)) - binding.loopPumpStatusLayout.activeProfile.setTextColor(resourceHelper.gc(R.color.ribbonTextDefault)) - } - - processButtonsVisibility() - - // iob - treatmentsPlugin.updateTotalIOBTreatments() - treatmentsPlugin.updateTotalIOBTempBasals() - val bolusIob = treatmentsPlugin.lastCalculationTreatments.round() - val basalIob = treatmentsPlugin.lastCalculationTempBasals.round() - binding.infoLayout.iob.text = resourceHelper.gs(R.string.formatinsulinunits, bolusIob.iob + basalIob.basaliob) - - binding.infoLayout.iobLayout.setOnClickListener { - activity?.let { - OKDialog.show(it, resourceHelper.gs(R.string.iob), - resourceHelper.gs(R.string.formatinsulinunits, bolusIob.iob + basalIob.basaliob) + "\n" + - resourceHelper.gs(R.string.bolus) + ": " + resourceHelper.gs(R.string.formatinsulinunits, bolusIob.iob) + "\n" + - resourceHelper.gs(R.string.basal) + ": " + resourceHelper.gs(R.string.formatinsulinunits, basalIob.basaliob) - ) - } - } - - // Status lights - binding.statusLightsLayout.statusLights.visibility = (sp.getBoolean(R.string.key_show_statuslights, true) || config.NSCLIENT).toVisibility() - statusLightHandler.updateStatusLights(binding.statusLightsLayout.cannulaAge, binding.statusLightsLayout.insulinAge, binding.statusLightsLayout.reservoirLevel, binding.statusLightsLayout.sensorAge, null, binding.statusLightsLayout.pbAge, binding.statusLightsLayout.batteryLevel) - - // cob - var cobText: String = resourceHelper.gs(R.string.value_unavailable_short) - val cobInfo = iobCobCalculatorPlugin.getCobInfo(false, "Overview COB") - if (cobInfo.displayCob != null) { - cobText = resourceHelper.gs(R.string.format_carbs, cobInfo.displayCob!!.toInt()) - if (cobInfo.futureCarbs > 0) cobText += "(" + DecimalFormatter.to0Decimal(cobInfo.futureCarbs) + ")" - } - - if (config.APS && lastRun?.constraintsProcessed != null) { - if (lastRun.constraintsProcessed!!.carbsReq > 0) { - //only display carbsreq when carbs have not been entered recently - if (treatmentsPlugin.lastCarbTime < lastRun.lastAPSRun) { - cobText = cobText + " | " + lastRun.constraintsProcessed!!.carbsReq + " " + resourceHelper.gs(R.string.required) - } - binding.infoLayout.cob.text = cobText - if (carbAnimation?.isRunning == false) - carbAnimation?.start() - } else { - binding.infoLayout.cob.text = cobText - carbAnimation?.stop() - carbAnimation?.selectDrawable(0) - } - } else binding.infoLayout.cob.text = cobText - // pump status from ns binding.pump.text = nsDeviceStatus.pumpStatus binding.pump.setOnClickListener { activity?.let { OKDialog.show(it, resourceHelper.gs(R.string.pump), nsDeviceStatus.extendedPumpStatus) } } @@ -804,130 +538,255 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList // Uploader status from ns binding.uploader.text = nsDeviceStatus.uploaderStatusSpanned binding.uploader.setOnClickListener { activity?.let { OKDialog.show(it, resourceHelper.gs(R.string.uploader), nsDeviceStatus.extendedUploaderStatus) } } - - // Sensitivity - if (sp.getBoolean(R.string.key_openapsama_useautosens, false) && constraintChecker.isAutosensModeEnabled().value()) { - binding.infoLayout.sensitivityIcon.setImageResource(R.drawable.ic_swap_vert_black_48dp_green) - } else { - binding.infoLayout.sensitivityIcon.setImageResource(R.drawable.ic_x_swap_vert) - } - - binding.infoLayout.sensitivity.text = - iobCobCalculatorPlugin.getLastAutosensData("Overview")?.let { autosensData -> - String.format(Locale.ENGLISH, "%.0f%%", autosensData.autosensResult.ratio * 100) - } ?: "" } - private fun updateGraph(lastRun: LoopInterface.LastRun?, predictionsAvailable: Boolean, lowLine: Double, highLine: Double, pump: PumpInterface, profile: Profile) { - viewLifecycleOwner.lifecycleScope.launch(Dispatchers.Main) { - if (_binding == null) return@launch - val menuChartSettings = overviewMenus.setting - prepareGraphsIfNeeded(menuChartSettings.size) - val graphData = GraphData(injector, binding.graphsLayout.bgGraph, iobCobCalculatorPlugin, treatmentsPlugin) - val secondaryGraphsData: ArrayList = ArrayList() + private fun prepareGraphsIfNeeded(numOfGraphs: Int) { + if (numOfGraphs != secondaryGraphs.size - 1) { + //aapsLogger.debug("New secondary graph count ${numOfGraphs-1}") + // rebuild needed + secondaryGraphs.clear() + secondaryGraphsLabel.clear() + binding.graphsLayout.iobGraph.removeAllViews() + for (i in 1 until numOfGraphs) { + val relativeLayout = RelativeLayout(context) + relativeLayout.layoutParams = RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) - // do preparation in different thread - withContext(Dispatchers.Default) { - // align to hours - val calendar = Calendar.getInstance() - calendar.timeInMillis = System.currentTimeMillis() - calendar[Calendar.MILLISECOND] = 0 - calendar[Calendar.SECOND] = 0 - calendar[Calendar.MINUTE] = 0 - calendar.add(Calendar.HOUR, 1) - val hoursToFetch: Int - val toTime: Long - val fromTime: Long - val endTime: Long - val apsResult = if (config.APS) lastRun?.constraintsProcessed else nsDeviceStatus.getAPSResult(injector) - if (predictionsAvailable && apsResult != null && menuChartSettings[0][OverviewMenus.CharType.PRE.ordinal]) { - var predictionHours = (ceil(apsResult.latestPredictionsTime - System.currentTimeMillis().toDouble()) / (60 * 60 * 1000)).toInt() - predictionHours = min(2, predictionHours) - predictionHours = max(0, predictionHours) - hoursToFetch = rangeToDisplay - predictionHours - toTime = calendar.timeInMillis + 100000 // little bit more to avoid wrong rounding - GraphView specific - fromTime = toTime - T.hours(hoursToFetch.toLong()).msecs() - endTime = toTime + T.hours(predictionHours.toLong()).msecs() + val graph = GraphView(context) + graph.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, resourceHelper.dpToPx(skinProvider.activeSkin().secondaryGraphHeight)).also { it.setMargins(0, resourceHelper.dpToPx(15), 0, resourceHelper.dpToPx(10)) } + graph.gridLabelRenderer?.gridColor = resourceHelper.gc(R.color.graphgrid) + graph.gridLabelRenderer?.reloadStyles() + graph.gridLabelRenderer?.isHorizontalLabelsVisible = false + graph.gridLabelRenderer?.labelVerticalWidth = axisWidth + graph.gridLabelRenderer?.numVerticalLabels = 3 + graph.viewport.backgroundColor = Color.argb(20, 255, 255, 255) // 8% of gray + relativeLayout.addView(graph) + + val label = TextView(context) + val layoutParams = RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT).also { it.setMargins(resourceHelper.dpToPx(30), resourceHelper.dpToPx(25), 0, 0) } + layoutParams.addRule(RelativeLayout.ALIGN_PARENT_TOP) + layoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT) + label.layoutParams = layoutParams + relativeLayout.addView(label) + secondaryGraphsLabel.add(label) + + binding.graphsLayout.iobGraph.addView(relativeLayout) + secondaryGraphs.add(graph) + } + } + } + + var task: Runnable? = null + + private fun scheduleUpdateGUI(from: String) { + class UpdateRunnable : Runnable { + + override fun run() { + overviewPlugin.refreshLoop(from) + task = null + } + } + handler.removeCallbacks(task) + task = UpdateRunnable() + handler.postDelayed(task, 500) + } + + @Suppress("UNUSED_PARAMETER") + @SuppressLint("SetTextI18n") + fun updateGUI(from: String, what: OverviewData.Property) { +// if (what != OverviewData.Property.CALC_PROGRESS) +// aapsLogger.debug(LTag.UI, "UpdateGui $from $what") + if (overviewData.profile == null) { + binding.loopPumpStatusLayout.pumpStatus.setText(R.string.noprofileset) + binding.loopPumpStatusLayout.pumpStatusLayout.visibility = View.VISIBLE + binding.loopPumpStatusLayout.loopLayout.visibility = View.GONE + return + } + binding.notifications.let { notificationStore.updateNotifications(it) } + binding.loopPumpStatusLayout.pumpStatusLayout.visibility = View.GONE + binding.loopPumpStatusLayout.loopLayout.visibility = View.VISIBLE + + val units = profileFunction.getUnits() + val pump = activePlugin.activePump + when (what) { + OverviewData.Property.BG -> { + binding.infoLayout.bg.text = overviewData.lastBg?.valueToUnitsString(units) + ?: resourceHelper.gs(R.string.notavailable) + binding.infoLayout.bg.setTextColor(overviewData.lastBgColor) + binding.infoLayout.arrow.setImageResource(trendCalculator.getTrendArrow(overviewData.lastBg).directionToIcon()) + binding.infoLayout.arrow.setColorFilter(overviewData.lastBgColor) + + val glucoseStatus = glucoseStatusProvider.glucoseStatusData + if (glucoseStatus != null) { + binding.infoLayout.deltaLarge.text = Profile.toSignedUnitsString(glucoseStatus.delta, glucoseStatus.delta * Constants.MGDL_TO_MMOLL, units) + binding.infoLayout.deltaLarge.setTextColor(overviewData.lastBgColor) + binding.infoLayout.delta.text = Profile.toSignedUnitsString(glucoseStatus.delta, glucoseStatus.delta * 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 { - hoursToFetch = rangeToDisplay - toTime = calendar.timeInMillis + 100000 // little bit more to avoid wrong rounding - GraphView specific - fromTime = toTime - T.hours(hoursToFetch.toLong()).msecs() - endTime = toTime + binding.infoLayout.deltaLarge.text = "Δ " + resourceHelper.gs(R.string.notavailable) + binding.infoLayout.delta.text = "Δ " + resourceHelper.gs(R.string.notavailable) + binding.infoLayout.avgDelta.text = "" + binding.infoLayout.longAvgDelta.text = "" } - val now = System.currentTimeMillis() - // ------------------ 1st graph + // strike through if BG is old + binding.infoLayout.bg.paintFlags = + if (!overviewData.isActualBg) binding.infoLayout.bg.paintFlags or Paint.STRIKE_THRU_TEXT_FLAG + else binding.infoLayout.bg.paintFlags and Paint.STRIKE_THRU_TEXT_FLAG.inv() + binding.infoLayout.timeAgo.text = dateUtil.minAgo(resourceHelper, overviewData.lastBg?.timestamp) + binding.infoLayout.timeAgoShort.text = "(" + dateUtil.minAgoShort(overviewData.lastBg?.timestamp) + ")" + } - // **** In range Area **** - graphData.addInRangeArea(fromTime, endTime, lowLine, highLine) + OverviewData.Property.PROFILE -> { + binding.loopPumpStatusLayout.activeProfile.text = overviewData.profileNameWithRemainingTime + ?: "" + binding.loopPumpStatusLayout.activeProfile.setBackgroundColor(overviewData.profileBackgroudColor) + binding.loopPumpStatusLayout.activeProfile.setTextColor(overviewData.profileTextColor) + } - // **** BG **** - if (predictionsAvailable && menuChartSettings[0][OverviewMenus.CharType.PRE.ordinal]) - graphData.addBgReadings(fromTime, toTime, lowLine, highLine, apsResult?.predictions) - else graphData.addBgReadings(fromTime, toTime, lowLine, highLine, null) + OverviewData.Property.TEMPORARY_BASAL -> { + binding.infoLayout.baseBasal.text = overviewData.temporaryBasalText + binding.infoLayout.baseBasal.setTextColor(overviewData.temporaryBasalColor) + binding.infoLayout.baseBasalIcon.setImageResource(overviewData.temporaryBasalIcon) + binding.infoLayout.basalLayout.setOnClickListener { + activity?.let { OKDialog.show(it, resourceHelper.gs(R.string.basal), overviewData.temporaryBasalDialogText) } + } + } - // Treatments - graphData.addTreatments(fromTime, endTime) + OverviewData.Property.EXTENDED_BOLUS -> { + binding.infoLayout.extendedBolus.text = overviewData.extendedBolusText + binding.infoLayout.extendedBolus.setOnClickListener { + activity?.let { OKDialog.show(it, resourceHelper.gs(R.string.extended_bolus), overviewData.extendedBolusDialogText) } + } + binding.infoLayout.extendedLayout.visibility = (overviewData.extendedBolus != null && !pump.isFakingTempsByExtendedBoluses).toVisibility() - // set manual x bounds to have nice steps - graphData.setNumVerticalLabels() - graphData.formatAxis(fromTime, endTime) + } + OverviewData.Property.TIME -> { + binding.infoLayout.time.text = dateUtil.timeString(dateUtil.now()) + // Status lights + binding.statusLightsLayout.statusLights.visibility = (sp.getBoolean(R.string.key_show_statuslights, true) || config.NSCLIENT).toVisibility() + statusLightHandler.updateStatusLights(binding.statusLightsLayout.cannulaAge, binding.statusLightsLayout.insulinAge, binding.statusLightsLayout.reservoirLevel, binding.statusLightsLayout.sensorAge, null, binding.statusLightsLayout.pbAge, binding.statusLightsLayout.batteryLevel) + processButtonsVisibility() + processAps() + } - if (menuChartSettings[0][OverviewMenus.CharType.ACT.ordinal]) - graphData.addActivity(fromTime, endTime, false, 0.8) + OverviewData.Property.IOB_COB -> { + binding.infoLayout.iob.text = overviewData.iobText + binding.infoLayout.iobLayout.setOnClickListener { + activity?.let { OKDialog.show(it, resourceHelper.gs(R.string.iob), overviewData.iobDialogText) } + } + // cob + var cobText: String = resourceHelper.gs(R.string.value_unavailable_short) + overviewData.cobInfo?.let { cobInfo -> + if (cobInfo.displayCob != null) { + cobText = resourceHelper.gs(R.string.format_carbs, cobInfo.displayCob!!.toInt()) + if (cobInfo.futureCarbs > 0) cobText += "(" + DecimalFormatter.to0Decimal(cobInfo.futureCarbs) + ")" + } + } + binding.infoLayout.cob.text = cobText - // add basal data - if (pump.pumpDescription.isTempBasalCapable && menuChartSettings[0][OverviewMenus.CharType.BAS.ordinal]) - graphData.addBasals(fromTime, now, lowLine / graphData.maxY / 1.2) - - // add target line - graphData.addTargetLine(fromTime, toTime, profile, loopPlugin.lastRun) - - // **** NOW line **** - graphData.addNowLine(now) - - // ------------------ 2nd graph - synchronized(graphLock) { - for (g in 0 until min(secondaryGraphs.size, menuChartSettings.size + 1)) { - val secondGraphData = GraphData(injector, secondaryGraphs[g], iobCobCalculatorPlugin, treatmentsPlugin) - var useABSForScale = false - var useIobForScale = false - var useCobForScale = false - var useDevForScale = false - var useRatioForScale = false - var useDSForScale = 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.BGI.ordinal] -> useBGIForScale = true - menuChartSettings[g + 1][OverviewMenus.CharType.SEN.ordinal] -> useRatioForScale = true - menuChartSettings[g + 1][OverviewMenus.CharType.DEVSLOPE.ordinal] -> useDSForScale = true + val constraintsProcessed = loopPlugin.lastRun?.constraintsProcessed + val lastRun = loopPlugin.lastRun + if (config.APS && constraintsProcessed != null && lastRun != null) { + if (constraintsProcessed.carbsReq > 0) { + //only display carbsreq when carbs have not been entered recently + if (overviewData.lastCarbsTime < lastRun.lastAPSRun) { + cobText = cobText + " | " + constraintsProcessed.carbsReq + " " + resourceHelper.gs(R.string.required) } - 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], 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, alignDevBgiScale) - 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.SEN.ordinal]) secondGraphData.addRatio(fromTime, now, useRatioForScale, if (useRatioForScale) 1.0 else 0.8) - 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 - secondGraphData.formatAxis(fromTime, endTime) - secondGraphData.addNowLine(now) - secondaryGraphsData.add(secondGraphData) + if (carbAnimation?.isRunning == false) + carbAnimation?.start() + } else { + carbAnimation?.stop() + carbAnimation?.selectDrawable(0) } } } - // finally enforce drawing of graphs in UI thread - graphData.performUpdate() - synchronized(graphLock) { + + OverviewData.Property.TEMPORARY_TARGET -> { + // temp target + val tempTarget = overviewData.temporarytarget + if (tempTarget != null) { + binding.loopPumpStatusLayout.tempTarget.setTextColor(resourceHelper.gc(R.color.ribbonTextWarning)) + binding.loopPumpStatusLayout.tempTarget.setBackgroundColor(resourceHelper.gc(R.color.ribbonWarning)) + binding.loopPumpStatusLayout.tempTarget.text = Profile.toTargetRangeString(tempTarget.lowTarget, tempTarget.highTarget, GlucoseUnit.MGDL, units) + " " + dateUtil.untilString(tempTarget.end, resourceHelper) + } else { + // If the target is not the same as set in the profile then oref has overridden it + overviewData.profile?.let { profile -> + val targetUsed = loopPlugin.lastRun?.constraintsProcessed?.targetBG ?: 0.0 + + if (targetUsed != 0.0 && abs(profile.getTargetMgdl() - targetUsed) > 0.01) { + aapsLogger.debug("Adjusted target. Profile: ${profile.getTargetMgdl()} APS: $targetUsed") + binding.loopPumpStatusLayout.tempTarget.text = Profile.toTargetRangeString(targetUsed, targetUsed, GlucoseUnit.MGDL, units) + binding.loopPumpStatusLayout.tempTarget.setTextColor(resourceHelper.gc(R.color.ribbonTextWarning)) + binding.loopPumpStatusLayout.tempTarget.setBackgroundColor(resourceHelper.gc(R.color.tempTargetBackground)) + } else { + binding.loopPumpStatusLayout.tempTarget.setTextColor(resourceHelper.gc(R.color.ribbonTextDefault)) + binding.loopPumpStatusLayout.tempTarget.setBackgroundColor(resourceHelper.gc(R.color.ribbonDefault)) + binding.loopPumpStatusLayout.tempTarget.text = Profile.toTargetRangeString(profile.getTargetLowMgdl(), profile.getTargetHighMgdl(), GlucoseUnit.MGDL, units) + } + } + } + } + + OverviewData.Property.GRAPH -> { + val graphData = GraphData(injector, binding.graphsLayout.bgGraph) + val menuChartSettings = overviewMenus.setting + graphData.addInRangeArea(overviewData.fromTime, overviewData.endTime, defaultValueHelper.determineLowLine(), defaultValueHelper.determineHighLine()) + graphData.addBgReadings(menuChartSettings[0][OverviewMenus.CharType.PRE.ordinal]) + if (buildHelper.isDev()) graphData.addBucketedData() + graphData.addTreatments() + if (menuChartSettings[0][OverviewMenus.CharType.ACT.ordinal]) + graphData.addActivity(0.8) + if (pump.pumpDescription.isTempBasalCapable && menuChartSettings[0][OverviewMenus.CharType.BAS.ordinal]) + graphData.addBasals() + graphData.addTargetLine() + graphData.addNowLine(dateUtil.now()) + + // set manual x bounds to have nice steps + graphData.setNumVerticalLabels() + graphData.formatAxis(overviewData.fromTime, overviewData.endTime) + + graphData.performUpdate() + + // 2nd graphs + prepareGraphsIfNeeded(menuChartSettings.size) + val secondaryGraphsData: ArrayList = ArrayList() + + val now = System.currentTimeMillis() + for (g in 0 until min(secondaryGraphs.size, menuChartSettings.size + 1)) { + val secondGraphData = GraphData(injector, secondaryGraphs[g]) + var useABSForScale = false + var useIobForScale = false + var useCobForScale = false + var useDevForScale = false + var useRatioForScale = false + var useDSForScale = 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.BGI.ordinal] -> useBGIForScale = true + menuChartSettings[g + 1][OverviewMenus.CharType.SEN.ordinal] -> useRatioForScale = true + menuChartSettings[g + 1][OverviewMenus.CharType.DEVSLOPE.ordinal] -> useDSForScale = true + } + 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(useABSForScale, 1.0) + if (menuChartSettings[g + 1][OverviewMenus.CharType.IOB.ordinal]) secondGraphData.addIob(useIobForScale, 1.0) + if (menuChartSettings[g + 1][OverviewMenus.CharType.COB.ordinal]) secondGraphData.addCob(useCobForScale, if (useCobForScale) 1.0 else 0.5) + if (menuChartSettings[g + 1][OverviewMenus.CharType.DEV.ordinal]) secondGraphData.addDeviations(useDevForScale, 1.0) + if (menuChartSettings[g + 1][OverviewMenus.CharType.BGI.ordinal]) secondGraphData.addMinusBGI(useBGIForScale, if (alignDevBgiScale) 1.0 else 0.8) + if (menuChartSettings[g + 1][OverviewMenus.CharType.SEN.ordinal]) secondGraphData.addRatio(useRatioForScale, if (useRatioForScale) 1.0 else 0.8) + if (menuChartSettings[g + 1][OverviewMenus.CharType.DEVSLOPE.ordinal] && buildHelper.isDev()) secondGraphData.addDeviationSlope(useDSForScale, 1.0) + + // set manual x bounds to have nice steps + secondGraphData.formatAxis(overviewData.fromTime, overviewData.endTime) + secondGraphData.addNowLine(now) + secondaryGraphsData.add(secondGraphData) + } for (g in 0 until min(secondaryGraphs.size, menuChartSettings.size + 1)) { secondaryGraphsLabel[g].text = overviewMenus.enabledTypes(g + 1) secondaryGraphs[g].visibility = ( @@ -942,6 +801,23 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList secondaryGraphsData[g].performUpdate() } } + + OverviewData.Property.CALC_PROGRESS -> { + binding.graphsLayout.iobCalculationProgress.text = overviewData.calcProgress + } + + OverviewData.Property.SENSITIVITY -> { + if (sp.getBoolean(R.string.key_openapsama_useautosens, false) && constraintChecker.isAutosensModeEnabled().value()) { + binding.infoLayout.sensitivityIcon.setImageResource(R.drawable.ic_swap_vert_black_48dp_green) + } else { + binding.infoLayout.sensitivityIcon.setImageResource(R.drawable.ic_x_swap_vert) + } + + binding.infoLayout.sensitivity.text = + overviewData.lastAutosensData?.let { autosensData -> + String.format(Locale.ENGLISH, "%.0f%%", autosensData.autosensResult.ratio * 100) + } ?: "" + } } } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewMenus.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewMenus.kt index 3bbeac5404..70fa9ef620 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewMenus.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewMenus.kt @@ -9,7 +9,7 @@ import androidx.annotation.ColorRes import androidx.annotation.StringRes import androidx.appcompat.widget.PopupMenu import com.google.gson.Gson -import info.nightscout.androidaps.Config +import info.nightscout.androidaps.interfaces.Config import info.nightscout.androidaps.R import info.nightscout.androidaps.events.EventRefreshOverview import info.nightscout.androidaps.logging.AAPSLogger @@ -34,18 +34,19 @@ class OverviewMenus @Inject constructor( enum class CharType(@StringRes val nameId: Int, @ColorRes val colorId: Int, val primary: Boolean, val secondary: Boolean, @StringRes val shortnameId: Int) { PRE(R.string.overview_show_predictions, R.color.prediction, primary = true, secondary = false, shortnameId = R.string.prediction_shortname), - BAS(R.string.overview_show_basals, R.color.basal, primary = true, secondary = false,shortnameId = R.string.basal_shortname), - ABS(R.string.overview_show_absinsulin, R.color.iob, primary = false, secondary = true,shortnameId = R.string.abs_insulin_shortname), - IOB(R.string.overview_show_iob, R.color.iob, primary = false, secondary = true,shortnameId = R.string.iob), - 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.bgi, primary = false, secondary = true,shortnameId = R.string.deviation_shortname), - BGI(R.string.overview_show_bgi, R.color.bgi, primary = false, secondary = true,shortnameId = R.string.bgi_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 = false,shortnameId = R.string.activity_shortname), - DEVSLOPE(R.string.overview_show_deviationslope, R.color.devslopepos, primary = false, secondary = true,shortnameId = R.string.devslope_shortname) + BAS(R.string.overview_show_basals, R.color.basal, primary = true, secondary = false, shortnameId = R.string.basal_shortname), + ABS(R.string.overview_show_absinsulin, R.color.iob, primary = false, secondary = true, shortnameId = R.string.abs_insulin_shortname), + IOB(R.string.overview_show_iob, R.color.iob, primary = false, secondary = true, shortnameId = R.string.iob), + 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.bgi, primary = false, secondary = true, shortnameId = R.string.deviation_shortname), + BGI(R.string.overview_show_bgi, R.color.bgi, primary = false, secondary = true, shortnameId = R.string.bgi_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 = false, shortnameId = R.string.activity_shortname), + DEVSLOPE(R.string.overview_show_deviationslope, R.color.devslopepos, primary = false, secondary = true, shortnameId = R.string.devslope_shortname) } companion object { + const val MAX_GRAPHS = 5 // including main } @@ -58,12 +59,10 @@ class OverviewMenus @Inject constructor( return r.toString() } - - private var _setting: MutableList> = ArrayList() val setting: List> - get() = _setting.toMutableList() // implicitly does a list copy + get() = _setting.toMutableList() // implicitly does a list copy private fun storeGraphConfig() { val sts = Gson().toJson(_setting) @@ -71,7 +70,7 @@ class OverviewMenus @Inject constructor( aapsLogger.debug(sts) } - private fun loadGraphConfig() { + fun loadGraphConfig() { val sts = sp.getString(R.string.key_graphconfig, "") if (sts.isNotEmpty()) { _setting = Gson().fromJson(sts, Array>::class.java).toMutableList() @@ -88,7 +87,6 @@ class OverviewMenus @Inject constructor( } fun setupChartMenu(chartButton: ImageButton) { - loadGraphConfig() val settingsCopy = setting val numOfGraphs = settingsCopy.size // 1 main + x secondary @@ -100,6 +98,8 @@ class OverviewMenus @Inject constructor( } val popup = PopupMenu(v.context, v) + val used = arrayListOf() + for (g in 0 until numOfGraphs) { if (g != 0 && g < numOfGraphs) { val dividerItem = popup.menu.add(Menu.NONE, g, Menu.NONE, "------- ${resourceHelper.gs(R.string.graph_menu_divider_header)} $g -------") @@ -112,6 +112,7 @@ class OverviewMenus @Inject constructor( var insert = true if (m == CharType.PRE) insert = predictionsAvailable if (m == CharType.DEVSLOPE) insert = buildHelper.isDev() + if (used.contains(m.ordinal)) insert = false if (insert) { val item = popup.menu.add(Menu.NONE, m.ordinal + 100 * (g + 1), Menu.NONE, resourceHelper.gs(m.nameId)) val title = item.title @@ -120,6 +121,7 @@ class OverviewMenus @Inject constructor( item.title = s item.isCheckable = true item.isChecked = settingsCopy[g][m.ordinal] + if (settingsCopy[g][m.ordinal]) used.add(m.ordinal) } } } @@ -131,16 +133,20 @@ class OverviewMenus @Inject constructor( popup.setOnMenuItemClickListener { // id < 100 graph header - divider 1, 2, 3 ..... - if (it.itemId == numOfGraphs) { - // add new empty - _setting.add(Array(CharType.values().size) { false }) - } else if (it.itemId < 100) { - // remove graph - _setting.removeAt(it.itemId) - } else { - val graphNumber = it.itemId / 100 - 1 - val item = it.itemId % 100 - _setting[graphNumber][item] = !it.isChecked + when { + it.itemId == numOfGraphs -> { + // add new empty + _setting.add(Array(CharType.values().size) { false }) + } + it.itemId < 100 -> { + // remove graph + _setting.removeAt(it.itemId) + } + else -> { + val graphNumber = it.itemId / 100 - 1 + val item = it.itemId % 100 + _setting[graphNumber][item] = !it.isChecked + } } storeGraphConfig() setupChartMenu(chartButton) @@ -153,4 +159,11 @@ class OverviewMenus @Inject constructor( } } + fun isEnabledIn(type: CharType): Int { + val settingsCopy = setting + val numOfGraphs = settingsCopy.size // 1 main + x secondary + for (g in 0 until numOfGraphs) if (settingsCopy[g][type.ordinal]) return g + return -1 + } + } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewPlugin.kt index d8308f346e..99da457276 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewPlugin.kt @@ -1,30 +1,50 @@ package info.nightscout.androidaps.plugins.general.overview +import android.graphics.DashPathEffect +import android.graphics.Paint import androidx.preference.PreferenceFragmentCompat import androidx.preference.SwitchPreference +import com.jjoe64.graphview.series.BarGraphSeries +import com.jjoe64.graphview.series.DataPoint +import com.jjoe64.graphview.series.LineGraphSeries import dagger.android.HasAndroidInjector -import info.nightscout.androidaps.Config import info.nightscout.androidaps.R -import info.nightscout.androidaps.events.EventRefreshOverview -import info.nightscout.androidaps.interfaces.OverviewInterface -import info.nightscout.androidaps.interfaces.PluginBase -import info.nightscout.androidaps.interfaces.PluginDescription -import info.nightscout.androidaps.interfaces.PluginType +import info.nightscout.androidaps.data.IobTotal +import info.nightscout.androidaps.database.AppRepository +import info.nightscout.androidaps.database.ValueWrapper +import info.nightscout.androidaps.database.entities.Bolus +import info.nightscout.androidaps.events.* +import info.nightscout.androidaps.extensions.* +import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.logging.AAPSLogger +import info.nightscout.androidaps.logging.LTag +import info.nightscout.androidaps.plugins.aps.events.EventLoopInvoked +import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin +import info.nightscout.androidaps.plugins.aps.openAPSSMB.SMBDefaults import info.nightscout.androidaps.plugins.bus.RxBusWrapper +import info.nightscout.androidaps.plugins.general.nsclient.data.NSDeviceStatus 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.events.EventUpdateOverview +import info.nightscout.androidaps.plugins.general.overview.graphExtensions.* import info.nightscout.androidaps.plugins.general.overview.notifications.NotificationStore -import info.nightscout.androidaps.utils.FabricPrivacy -import info.nightscout.androidaps.utils.extensions.* +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventBucketedDataCreated +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventIobCalculationProgress +import info.nightscout.androidaps.utils.* 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 java.util.* import javax.inject.Inject import javax.inject.Singleton +import kotlin.math.abs +import kotlin.math.ceil +import kotlin.math.max +import kotlin.math.min @Singleton class OverviewPlugin @Inject constructor( @@ -36,7 +56,19 @@ class OverviewPlugin @Inject constructor( aapsLogger: AAPSLogger, private val aapsSchedulers: AapsSchedulers, resourceHelper: ResourceHelper, - private val config: Config + private val config: Config, + private val dateUtil: DateUtil, + private val translator: Translator, +// private val profiler: Profiler, + private val profileFunction: ProfileFunction, + private val iobCobCalculator: IobCobCalculator, + private val repository: AppRepository, + private val defaultValueHelper: DefaultValueHelper, + private val loopPlugin: LoopPlugin, + private val activePlugin: ActivePlugin, + private val nsDeviceStatus: NSDeviceStatus, + private val overviewData: OverviewData, + private val overviewMenus: OverviewMenus ) : PluginBase(PluginDescription() .mainType(PluginType.GENERAL) .fragmentClass(OverviewFragment::class.qualifiedName) @@ -48,12 +80,19 @@ class OverviewPlugin @Inject constructor( .preferencesId(R.xml.pref_overview) .description(R.string.description_overview), aapsLogger, resourceHelper, injector -), OverviewInterface { +), Overview { private var disposable: CompositeDisposable = CompositeDisposable() + override val overviewBus = RxBusWrapper(aapsSchedulers) + + class DeviationDataPoint(x: Double, y: Double, var color: Int, scale: Scale) : ScaledDataPoint(x, y, scale) + override fun onStart() { super.onStart() + overviewMenus.loadGraphConfig() + overviewData.initRange() + notificationStore.createNotificationChannel() disposable += rxBus .toObservable(EventNewNotification::class.java) @@ -69,6 +108,63 @@ class OverviewPlugin @Inject constructor( if (notificationStore.remove(n.id)) rxBus.send(EventRefreshOverview("EventDismissNotification")) }, fabricPrivacy::logException) + disposable += rxBus + .toObservable(EventIobCalculationProgress::class.java) + .observeOn(aapsSchedulers.main) + .subscribe({ overviewData.calcProgress = it.progress; overviewBus.send(EventUpdateOverview("EventIobCalculationProgress", OverviewData.Property.CALC_PROGRESS)) }, fabricPrivacy::logException) + disposable += rxBus + .toObservable(EventTempBasalChange::class.java) + .observeOn(aapsSchedulers.io) + .subscribe({ loadTemporaryBasal("EventTempBasalChange") }, fabricPrivacy::logException) + disposable += rxBus + .toObservable(EventExtendedBolusChange::class.java) + .observeOn(aapsSchedulers.io) + .subscribe({ loadExtendedBolus("EventExtendedBolusChange") }, fabricPrivacy::logException) + disposable += rxBus + .toObservable(EventNewBG::class.java) + .observeOn(aapsSchedulers.io) + .subscribe({ loadBg("EventNewBG") }, fabricPrivacy::logException) + disposable += rxBus + .toObservable(EventTempTargetChange::class.java) + .observeOn(aapsSchedulers.io) + .subscribe({ loadTemporaryTarget("EventTempTargetChange") }, fabricPrivacy::logException) + disposable += rxBus + .toObservable(EventTreatmentChange::class.java) + .observeOn(aapsSchedulers.io) + .subscribe({ + loadIobCobResults("EventTreatmentChange") + prepareTreatmentsData("EventTreatmentChange") + overviewBus.send(EventUpdateOverview("EventTreatmentChange", OverviewData.Property.GRAPH)) + }, fabricPrivacy::logException) + disposable += rxBus + .toObservable(EventTherapyEventChange::class.java) + .observeOn(aapsSchedulers.io) + .subscribe({ + prepareTreatmentsData("EventTherapyEventChange") + overviewBus.send(EventUpdateOverview("EventTherapyEventChange", OverviewData.Property.GRAPH)) + }, fabricPrivacy::logException) + disposable += rxBus + .toObservable(EventBucketedDataCreated::class.java) + .observeOn(aapsSchedulers.io) + .subscribe({ + prepareBucketedData("EventBucketedDataCreated") + prepareBgData("EventBucketedDataCreated") + overviewBus.send(EventUpdateOverview("EventBucketedDataCreated", OverviewData.Property.GRAPH)) + }, fabricPrivacy::logException) + disposable += rxBus + .toObservable(EventLoopInvoked::class.java) + .observeOn(aapsSchedulers.io) + .subscribe({ preparePredictions("EventLoopInvoked") }, fabricPrivacy::logException) + disposable.add(rxBus + .toObservable(EventProfileSwitchChanged::class.java) + .observeOn(aapsSchedulers.io) + .subscribe({ loadProfile("EventProfileSwitchChanged") }, fabricPrivacy::logException)) + disposable.add(rxBus + .toObservable(EventAutosensCalculationFinished::class.java) + .observeOn(aapsSchedulers.io) + .subscribe({ refreshLoop("EventAutosensCalculationFinished") }, fabricPrivacy::logException)) + + Thread { loadAll("onResume") }.start() } override fun onStop() { @@ -142,4 +238,601 @@ class OverviewPlugin @Inject constructor( .storeDouble(R.string.key_statuslights_bat_warning, sp, resourceHelper) .storeDouble(R.string.key_statuslights_bat_critical, sp, resourceHelper) } + + @Volatile var runningRefresh = false + override fun refreshLoop(from: String) { + if (runningRefresh) return + runningRefresh = true + overviewBus.send(EventUpdateOverview(from, OverviewData.Property.BG)) + overviewBus.send(EventUpdateOverview(from, OverviewData.Property.TIME)) + overviewBus.send(EventUpdateOverview(from, OverviewData.Property.TEMPORARY_BASAL)) + overviewBus.send(EventUpdateOverview(from, OverviewData.Property.EXTENDED_BOLUS)) + overviewBus.send(EventUpdateOverview(from, OverviewData.Property.IOB_COB)) + overviewBus.send(EventUpdateOverview(from, OverviewData.Property.TEMPORARY_TARGET)) + overviewBus.send(EventUpdateOverview(from, OverviewData.Property.SENSITIVITY)) + loadAsData(from) + preparePredictions(from) + prepareBasalData(from) + prepareTemporaryTargetData(from) + prepareTreatmentsData(from) + prepareIobAutosensData(from) + overviewBus.send(EventUpdateOverview(from, OverviewData.Property.GRAPH)) + aapsLogger.debug(LTag.UI, "refreshLoop finished") + runningRefresh = false + } + + @Suppress("SameParameterValue") + private fun loadAll(from: String) { + loadBg(from) + loadProfile(from) + loadTemporaryBasal(from) + loadExtendedBolus(from) + loadTemporaryTarget(from) + loadIobCobResults(from) + loadAsData(from) + prepareBasalData(from) + prepareTemporaryTargetData(from) + prepareTreatmentsData(from) +// prepareIobAutosensData(from) +// preparePredictions(from) + overviewBus.send(EventUpdateOverview(from, OverviewData.Property.GRAPH)) + aapsLogger.debug(LTag.UI, "loadAll finished") + } + + private fun loadProfile(from: String) { + overviewData.profile = profileFunction.getProfile() + overviewData.profileName = profileFunction.getProfileName() + overviewData.profileNameWithRemainingTime = profileFunction.getProfileNameWithRemainingTime() + overviewBus.send(EventUpdateOverview(from, OverviewData.Property.PROFILE)) + } + + private fun loadTemporaryBasal(from: String) { + overviewData.temporaryBasal = iobCobCalculator.getTempBasalIncludingConvertedExtended(dateUtil.now()) + overviewBus.send(EventUpdateOverview(from, OverviewData.Property.TEMPORARY_BASAL)) + } + + private fun loadExtendedBolus(from: String) { + overviewData.extendedBolus = iobCobCalculator.getExtendedBolus(dateUtil.now()) + overviewBus.send(EventUpdateOverview(from, OverviewData.Property.EXTENDED_BOLUS)) + } + + private fun loadTemporaryTarget(from: String) { + val tempTarget = repository.getTemporaryTargetActiveAt(dateUtil.now()).blockingGet() + if (tempTarget is ValueWrapper.Existing) overviewData.temporarytarget = tempTarget.value + else overviewData.temporarytarget = null + overviewBus.send(EventUpdateOverview(from, OverviewData.Property.TEMPORARY_TARGET)) + } + + private fun loadAsData(from: String) { + overviewData.lastAutosensData = iobCobCalculator.ads.getLastAutosensData("Overview", aapsLogger, dateUtil) + overviewBus.send(EventUpdateOverview(from, OverviewData.Property.SENSITIVITY)) + } + + private fun loadBg(from: String) { + val gvWrapped = repository.getLastGlucoseValueWrapped().blockingGet() + if (gvWrapped is ValueWrapper.Existing) overviewData.lastBg = gvWrapped.value + else overviewData.lastBg = null + overviewBus.send(EventUpdateOverview(from, OverviewData.Property.BG)) + } + + private fun loadIobCobResults(from: String) { + overviewData.bolusIob = iobCobCalculator.calculateIobFromBolus().round() + overviewData.basalIob = iobCobCalculator.calculateIobFromTempBasalsIncludingConvertedExtended().round() + overviewData.cobInfo = iobCobCalculator.getCobInfo(false, "Overview COB") + val lastCarbs = repository.getLastCarbsRecordWrapped().blockingGet() + overviewData.lastCarbsTime = if (lastCarbs is ValueWrapper.Existing) lastCarbs.value.timestamp else 0L + + overviewBus.send(EventUpdateOverview(from, OverviewData.Property.IOB_COB)) + } + + @Synchronized + @Suppress("SameParameterValue", "UNUSED_PARAMETER") + private fun prepareBgData(from: String) { +// val start = dateUtil.now() + var maxBgValue = Double.MIN_VALUE + overviewData.bgReadingsArray = repository.compatGetBgReadingsDataFromTime(overviewData.fromTime, overviewData.toTime, false).blockingGet() + val bgListArray: MutableList = ArrayList() + for (bg in overviewData.bgReadingsArray) { + if (bg.timestamp < overviewData.fromTime || bg.timestamp > overviewData.toTime) continue + if (bg.value > maxBgValue) maxBgValue = bg.value + bgListArray.add(GlucoseValueDataPoint(bg, defaultValueHelper, profileFunction, resourceHelper)) + } + overviewData.bgReadingGraphSeries = PointsWithLabelGraphSeries(Array(bgListArray.size) { i -> bgListArray[i] }) + overviewData.maxBgValue = Profile.fromMgdlToUnits(maxBgValue, profileFunction.getUnits()) + if (defaultValueHelper.determineHighLine() > maxBgValue) overviewData.maxBgValue = defaultValueHelper.determineHighLine() + overviewData.maxBgValue = addUpperChartMargin(overviewData.maxBgValue) +// profiler.log(LTag.UI, "prepareBgData() $from", start) + } + + @Suppress("UNUSED_PARAMETER") + @Synchronized + private fun preparePredictions(from: String) { +// val start = dateUtil.now() + val apsResult = if (config.APS) loopPlugin.lastRun?.constraintsProcessed else nsDeviceStatus.getAPSResult(injector) + val predictionsAvailable = if (config.APS) loopPlugin.lastRun?.request?.hasPredictions == true else config.NSCLIENT + val menuChartSettings = overviewMenus.setting + // align to hours + val calendar = Calendar.getInstance().also { + it.timeInMillis = System.currentTimeMillis() + it[Calendar.MILLISECOND] = 0 + it[Calendar.SECOND] = 0 + it[Calendar.MINUTE] = 0 + it.add(Calendar.HOUR, 1) + } + if (predictionsAvailable && apsResult != null && menuChartSettings[0][OverviewMenus.CharType.PRE.ordinal]) { + var predictionHours = (ceil(apsResult.latestPredictionsTime - System.currentTimeMillis().toDouble()) / (60 * 60 * 1000)).toInt() + predictionHours = min(2, predictionHours) + predictionHours = max(0, predictionHours) + val hoursToFetch = overviewData.rangeToDisplay - predictionHours + overviewData.toTime = calendar.timeInMillis + 100000 // little bit more to avoid wrong rounding - GraphView specific + overviewData.fromTime = overviewData.toTime - T.hours(hoursToFetch.toLong()).msecs() + overviewData.endTime = overviewData.toTime + T.hours(predictionHours.toLong()).msecs() + } else { + overviewData.toTime = calendar.timeInMillis + 100000 // little bit more to avoid wrong rounding - GraphView specific + overviewData.fromTime = overviewData.toTime - T.hours(overviewData.rangeToDisplay.toLong()).msecs() + overviewData.endTime = overviewData.toTime + } + + val bgListArray: MutableList = ArrayList() + val predictions: MutableList? = apsResult?.predictions + ?.map { bg -> GlucoseValueDataPoint(bg, defaultValueHelper, profileFunction, resourceHelper) } + ?.toMutableList() + if (predictions != null) { + predictions.sortWith { o1: GlucoseValueDataPoint, o2: GlucoseValueDataPoint -> o1.x.compareTo(o2.x) } + for (prediction in predictions) if (prediction.data.value >= 40) bgListArray.add(prediction) + } + overviewData.predictionsGraphSeries = PointsWithLabelGraphSeries(Array(bgListArray.size) { i -> bgListArray[i] }) +// profiler.log(LTag.UI, "preparePredictions() $from", start) + } + + @Synchronized + @Suppress("SameParameterValue", "UNUSED_PARAMETER") + private fun prepareBucketedData(from: String) { +// val start = dateUtil.now() + val bucketedData = iobCobCalculator.ads.getBucketedDataTableCopy() ?: return + if (bucketedData.isEmpty()) { + aapsLogger.debug("No bucketed data.") + return + } + val bucketedListArray: MutableList = ArrayList() + for (inMemoryGlucoseValue in bucketedData) { + if (inMemoryGlucoseValue.timestamp < overviewData.fromTime || inMemoryGlucoseValue.timestamp > overviewData.toTime) continue + bucketedListArray.add(InMemoryGlucoseValueDataPoint(inMemoryGlucoseValue, profileFunction, resourceHelper)) + } + overviewData.bucketedGraphSeries = PointsWithLabelGraphSeries(Array(bucketedListArray.size) { i -> bucketedListArray[i] }) +// profiler.log(LTag.UI, "prepareBucketedData() $from", start) + } + + @Suppress("UNUSED_PARAMETER") + @Synchronized + private fun prepareBasalData(from: String) { +// val start = dateUtil.now() + overviewData.maxBasalValueFound = 0.0 + val baseBasalArray: MutableList = ArrayList() + val tempBasalArray: MutableList = ArrayList() + val basalLineArray: MutableList = ArrayList() + val absoluteBasalLineArray: MutableList = ArrayList() + var lastLineBasal = 0.0 + var lastAbsoluteLineBasal = -1.0 + var lastBaseBasal = 0.0 + var lastTempBasal = 0.0 + var time = overviewData.fromTime + while (time < overviewData.toTime) { + val profile = profileFunction.getProfile(time) + if (profile == null) { + time += 60 * 1000L + continue + } + val basalData = iobCobCalculator.getBasalData(profile, time) + val baseBasalValue = basalData.basal + var absoluteLineValue = baseBasalValue + var tempBasalValue = 0.0 + var basal = 0.0 + if (basalData.isTempBasalRunning) { + tempBasalValue = basalData.tempBasalAbsolute + absoluteLineValue = tempBasalValue + if (tempBasalValue != lastTempBasal) { + tempBasalArray.add(ScaledDataPoint(time, lastTempBasal, overviewData.basalScale)) + tempBasalArray.add(ScaledDataPoint(time, tempBasalValue.also { basal = it }, overviewData.basalScale)) + } + if (lastBaseBasal != 0.0) { + baseBasalArray.add(ScaledDataPoint(time, lastBaseBasal, overviewData.basalScale)) + baseBasalArray.add(ScaledDataPoint(time, 0.0, overviewData.basalScale)) + lastBaseBasal = 0.0 + } + } else { + if (baseBasalValue != lastBaseBasal) { + baseBasalArray.add(ScaledDataPoint(time, lastBaseBasal, overviewData.basalScale)) + baseBasalArray.add(ScaledDataPoint(time, baseBasalValue.also { basal = it }, overviewData.basalScale)) + lastBaseBasal = baseBasalValue + } + if (lastTempBasal != 0.0) { + tempBasalArray.add(ScaledDataPoint(time, lastTempBasal, overviewData.basalScale)) + tempBasalArray.add(ScaledDataPoint(time, 0.0, overviewData.basalScale)) + } + } + if (baseBasalValue != lastLineBasal) { + basalLineArray.add(ScaledDataPoint(time, lastLineBasal, overviewData.basalScale)) + basalLineArray.add(ScaledDataPoint(time, baseBasalValue, overviewData.basalScale)) + } + if (absoluteLineValue != lastAbsoluteLineBasal) { + absoluteBasalLineArray.add(ScaledDataPoint(time, lastAbsoluteLineBasal, overviewData.basalScale)) + absoluteBasalLineArray.add(ScaledDataPoint(time, basal, overviewData.basalScale)) + } + lastAbsoluteLineBasal = absoluteLineValue + lastLineBasal = baseBasalValue + lastTempBasal = tempBasalValue + overviewData.maxBasalValueFound = max(overviewData.maxBasalValueFound, max(tempBasalValue, baseBasalValue)) + time += 60 * 1000L + } + + // final points + basalLineArray.add(ScaledDataPoint(overviewData.toTime, lastLineBasal, overviewData.basalScale)) + baseBasalArray.add(ScaledDataPoint(overviewData.toTime, lastBaseBasal, overviewData.basalScale)) + tempBasalArray.add(ScaledDataPoint(overviewData.toTime, lastTempBasal, overviewData.basalScale)) + absoluteBasalLineArray.add(ScaledDataPoint(overviewData.toTime, lastAbsoluteLineBasal, overviewData.basalScale)) + + // create series + overviewData.baseBasalGraphSeries = LineGraphSeries(Array(baseBasalArray.size) { i -> baseBasalArray[i] }).also { + it.isDrawBackground = true + it.backgroundColor = resourceHelper.gc(R.color.basebasal) + it.thickness = 0 + } + overviewData.tempBasalGraphSeries = LineGraphSeries(Array(tempBasalArray.size) { i -> tempBasalArray[i] }).also { + it.isDrawBackground = true + it.backgroundColor = resourceHelper.gc(R.color.tempbasal) + it.thickness = 0 + } + overviewData.basalLineGraphSeries = LineGraphSeries(Array(basalLineArray.size) { i -> basalLineArray[i] }).also { + it.setCustomPaint(Paint().also { paint -> + paint.style = Paint.Style.STROKE + paint.strokeWidth = resourceHelper.getDisplayMetrics().scaledDensity * 2 + paint.pathEffect = DashPathEffect(floatArrayOf(2f, 4f), 0f) + paint.color = resourceHelper.gc(R.color.basal) + }) + } + overviewData.absoluteBasalGraphSeries = LineGraphSeries(Array(absoluteBasalLineArray.size) { i -> absoluteBasalLineArray[i] }).also { + it.setCustomPaint(Paint().also { absolutePaint -> + absolutePaint.style = Paint.Style.STROKE + absolutePaint.strokeWidth = resourceHelper.getDisplayMetrics().scaledDensity * 2 + absolutePaint.color = resourceHelper.gc(R.color.basal) + }) + } +// profiler.log(LTag.UI, "prepareBasalData() $from", start) + } + + @Suppress("UNUSED_PARAMETER") + @Synchronized + private fun prepareTemporaryTargetData(from: String) { +// val start = dateUtil.now() + val profile = overviewData.profile ?: return + val units = profileFunction.getUnits() + var toTime = overviewData.toTime + val targetsSeriesArray: MutableList = ArrayList() + var lastTarget = -1.0 + loopPlugin.lastRun?.constraintsProcessed?.let { toTime = max(it.latestPredictionsTime, toTime) } + var time = overviewData.fromTime + while (time < toTime) { + val tt = repository.getTemporaryTargetActiveAt(time).blockingGet() + val value: Double = if (tt is ValueWrapper.Existing) { + Profile.fromMgdlToUnits(tt.value.target(), units) + } else { + Profile.fromMgdlToUnits((profile.getTargetLowMgdl(time) + profile.getTargetHighMgdl(time)) / 2, units) + } + if (lastTarget != value) { + if (lastTarget != -1.0) targetsSeriesArray.add(DataPoint(time.toDouble(), lastTarget)) + targetsSeriesArray.add(DataPoint(time.toDouble(), value)) + } + lastTarget = value + time += 5 * 60 * 1000L + } + // final point + targetsSeriesArray.add(DataPoint(toTime.toDouble(), lastTarget)) + // create series + overviewData.temporaryTargetSeries = LineGraphSeries(Array(targetsSeriesArray.size) { i -> targetsSeriesArray[i] }).also { + it.isDrawBackground = false + it.color = resourceHelper.gc(R.color.tempTargetBackground) + it.thickness = 2 + } +// profiler.log(LTag.UI, "prepareTemporaryTargetData() $from", start) + } + + @Suppress("UNUSED_PARAMETER") + @Synchronized + private fun prepareTreatmentsData(from: String) { +// val start = dateUtil.now() + overviewData.maxTreatmentsValue = 0.0 + val filteredTreatments: MutableList = ArrayList() + repository.getBolusesIncludingInvalidFromTimeToTime(overviewData.fromTime, overviewData.endTime, true).blockingGet() + .map { BolusDataPoint(it, resourceHelper, activePlugin, defaultValueHelper) } + .filter { it.data.type != Bolus.Type.SMB || it.data.isValid } + .forEach { + it.y = getNearestBg(it.x.toLong()) + filteredTreatments.add(it) + } + repository.getCarbsIncludingInvalidFromTimeToTimeExpanded(overviewData.fromTime, overviewData.endTime, true).blockingGet() + .map { CarbsDataPoint(it, resourceHelper) } + .forEach { + it.y = getNearestBg(it.x.toLong()) + filteredTreatments.add(it) + } + + // ProfileSwitch + repository.getEffectiveProfileSwitchDataFromTimeToTime(overviewData.fromTime, overviewData.endTime, true).blockingGet() + .map { EffectiveProfileSwitchDataPoint(it) } + .forEach(filteredTreatments::add) + + // Extended bolus + if (!activePlugin.activePump.isFakingTempsByExtendedBoluses) { + repository.getExtendedBolusDataFromTimeToTime(overviewData.fromTime, overviewData.endTime, true).blockingGet() + .map { ExtendedBolusDataPoint(it) } + .filter { it.duration != 0L } + .forEach { + it.y = getNearestBg(it.x.toLong()) + filteredTreatments.add(it) + } + } + + // Careportal + repository.compatGetTherapyEventDataFromToTime(overviewData.fromTime - T.hours(6).msecs(), overviewData.endTime).blockingGet() + .map { TherapyEventDataPoint(it, resourceHelper, profileFunction, translator) } + .filterTimeframe(overviewData.fromTime, overviewData.endTime) + .forEach { + if (it.y == 0.0) it.y = getNearestBg(it.x.toLong()) + filteredTreatments.add(it) + } + + // increase maxY if a treatment forces it's own height that's higher than a BG value + filteredTreatments.map { it.y } + .maxOrNull() + ?.let(::addUpperChartMargin) + ?.let { overviewData.maxTreatmentsValue = maxOf(overviewData.maxTreatmentsValue, it) } + + overviewData.treatmentsSeries = PointsWithLabelGraphSeries(filteredTreatments.toTypedArray()) +// profiler.log(LTag.UI, "prepareTreatmentsData() $from", start) + } + + @Suppress("UNUSED_PARAMETER") + @Synchronized + private fun prepareIobAutosensData(from: String) { +// val start = dateUtil.now() + val iobArray: MutableList = ArrayList() + val absIobArray: MutableList = ArrayList() + overviewData.maxIobValueFound = Double.MIN_VALUE + var lastIob = 0.0 + var absLastIob = 0.0 + var time = overviewData.fromTime + + val minFailOverActiveList: MutableList = ArrayList() + val cobArray: MutableList = ArrayList() + overviewData.maxCobValueFound = Double.MIN_VALUE + var lastCob = 0 + + val actArrayHist: MutableList = ArrayList() + val actArrayPrediction: MutableList = ArrayList() + val now = dateUtil.now().toDouble() + overviewData.maxIAValue = 0.0 + + val bgiArrayHist: MutableList = ArrayList() + val bgiArrayPrediction: MutableList = ArrayList() + overviewData.maxBGIValue = Double.MIN_VALUE + + val devArray: MutableList = ArrayList() + overviewData.maxDevValueFound = Double.MIN_VALUE + + val ratioArray: MutableList = ArrayList() + overviewData.maxRatioValueFound = 5.0 //even if sens data equals 0 for all the period, minimum scale is between 95% and 105% + overviewData.minRatioValueFound = -5.0 + + val dsMaxArray: MutableList = ArrayList() + val dsMinArray: MutableList = ArrayList() + overviewData.maxFromMaxValueFound = Double.MIN_VALUE + overviewData.maxFromMinValueFound = Double.MIN_VALUE + + val adsData = iobCobCalculator.ads.clone() + + while (time <= overviewData.toTime) { + val profile = profileFunction.getProfile(time) + if (profile == null) { + time += 5 * 60 * 1000L + continue + } + // IOB + val iob = iobCobCalculator.calculateFromTreatmentsAndTemps(time, profile) + val baseBasalIob = iobCobCalculator.calculateAbsoluteIobFromBaseBasals(time) + val absIob = IobTotal.combine(iob, baseBasalIob) + val autosensData = adsData.getAutosensDataAtTime(time) + if (abs(lastIob - iob.iob) > 0.02) { + if (abs(lastIob - iob.iob) > 0.2) iobArray.add(ScaledDataPoint(time, lastIob, overviewData.iobScale)) + iobArray.add(ScaledDataPoint(time, iob.iob, overviewData.iobScale)) + overviewData.maxIobValueFound = maxOf(overviewData.maxIobValueFound, abs(iob.iob)) + lastIob = iob.iob + } + if (abs(absLastIob - absIob.iob) > 0.02) { + if (abs(absLastIob - absIob.iob) > 0.2) absIobArray.add(ScaledDataPoint(time, absLastIob, overviewData.iobScale)) + absIobArray.add(ScaledDataPoint(time, absIob.iob, overviewData.iobScale)) + overviewData.maxIobValueFound = maxOf(overviewData.maxIobValueFound, abs(absIob.iob)) + absLastIob = absIob.iob + } + + // COB + if (autosensData != null) { + val cob = autosensData.cob.toInt() + if (cob != lastCob) { + if (autosensData.carbsFromBolus > 0) cobArray.add(ScaledDataPoint(time, lastCob.toDouble(), overviewData.cobScale)) + cobArray.add(ScaledDataPoint(time, cob.toDouble(), overviewData.cobScale)) + overviewData.maxCobValueFound = max(overviewData.maxCobValueFound, cob.toDouble()) + lastCob = cob + } + if (autosensData.failoverToMinAbsorbtionRate) { + autosensData.setScale(overviewData.cobScale) + autosensData.setChartTime(time) + minFailOverActiveList.add(autosensData) + } + } + + // ACTIVITY + if (time <= now) actArrayHist.add(ScaledDataPoint(time, iob.activity, overviewData.actScale)) + else actArrayPrediction.add(ScaledDataPoint(time, iob.activity, overviewData.actScale)) + overviewData.maxIAValue = max(overviewData.maxIAValue, abs(iob.activity)) + + // BGI + val devBgiScale = overviewMenus.isEnabledIn(OverviewMenus.CharType.DEV) == overviewMenus.isEnabledIn(OverviewMenus.CharType.BGI) + val deviation = if (devBgiScale) autosensData?.deviation ?: 0.0 else 0.0 + val bgi: Double = iob.activity * profile.getIsfMgdl(time) * 5.0 + if (time <= now) bgiArrayHist.add(ScaledDataPoint(time, bgi, overviewData.bgiScale)) + else bgiArrayPrediction.add(ScaledDataPoint(time, bgi, overviewData.bgiScale)) + overviewData.maxBGIValue = max(overviewData.maxBGIValue, max(abs(bgi), deviation)) + + // DEVIATIONS + if (autosensData != null) { + var color = resourceHelper.gc(R.color.deviationblack) // "=" + if (autosensData.type == "" || autosensData.type == "non-meal") { + if (autosensData.pastSensitivity == "C") color = resourceHelper.gc(R.color.deviationgrey) + if (autosensData.pastSensitivity == "+") color = resourceHelper.gc(R.color.deviationgreen) + if (autosensData.pastSensitivity == "-") color = resourceHelper.gc(R.color.deviationred) + } else if (autosensData.type == "uam") { + color = resourceHelper.gc(R.color.uam) + } else if (autosensData.type == "csf") { + color = resourceHelper.gc(R.color.deviationgrey) + } + devArray.add(DeviationDataPoint(time.toDouble(), autosensData.deviation, color, overviewData.devScale)) + overviewData.maxDevValueFound = maxOf(overviewData.maxDevValueFound, abs(autosensData.deviation), abs(bgi)) + } + + // RATIO + if (autosensData != null) { + ratioArray.add(ScaledDataPoint(time, 100.0 * (autosensData.autosensResult.ratio - 1), overviewData.ratioScale)) + overviewData.maxRatioValueFound = max(overviewData.maxRatioValueFound, 100.0 * (autosensData.autosensResult.ratio - 1)) + overviewData.minRatioValueFound = min(overviewData.minRatioValueFound, 100.0 * (autosensData.autosensResult.ratio - 1)) + } + + // DEV SLOPE + if (autosensData != null) { + dsMaxArray.add(ScaledDataPoint(time, autosensData.slopeFromMaxDeviation, overviewData.dsMaxScale)) + dsMinArray.add(ScaledDataPoint(time, autosensData.slopeFromMinDeviation, overviewData.dsMinScale)) + overviewData.maxFromMaxValueFound = max(overviewData.maxFromMaxValueFound, abs(autosensData.slopeFromMaxDeviation)) + overviewData.maxFromMinValueFound = max(overviewData.maxFromMinValueFound, abs(autosensData.slopeFromMinDeviation)) + } + + time += 5 * 60 * 1000L + } + // IOB + overviewData.iobSeries = FixedLineGraphSeries(Array(iobArray.size) { i -> iobArray[i] }).also { + it.isDrawBackground = true + it.backgroundColor = -0x7f000001 and resourceHelper.gc(R.color.iob) //50% + it.color = resourceHelper.gc(R.color.iob) + it.thickness = 3 + } + overviewData.absIobSeries = FixedLineGraphSeries(Array(absIobArray.size) { i -> absIobArray[i] }).also { + it.isDrawBackground = true + it.backgroundColor = -0x7f000001 and resourceHelper.gc(R.color.iob) //50% + it.color = resourceHelper.gc(R.color.iob) + it.thickness = 3 + } + + if (overviewMenus.setting[0][OverviewMenus.CharType.PRE.ordinal]) { + val autosensData = adsData.getLastAutosensData("GraphData", aapsLogger, dateUtil) + val lastAutosensResult = autosensData?.autosensResult ?: AutosensResult() + val isTempTarget = repository.getTemporaryTargetActiveAt(dateUtil.now()).blockingGet() is ValueWrapper.Existing + val iobPrediction: MutableList = ArrayList() + val iobPredictionArray = iobCobCalculator.calculateIobArrayForSMB(lastAutosensResult, SMBDefaults.exercise_mode, SMBDefaults.half_basal_exercise_target, isTempTarget) + for (i in iobPredictionArray) { + iobPrediction.add(i.setColor(resourceHelper.gc(R.color.iobPredAS))) + overviewData.maxIobValueFound = max(overviewData.maxIobValueFound, abs(i.iob)) + } + overviewData.iobPredictions1Series = PointsWithLabelGraphSeries(Array(iobPrediction.size) { i -> iobPrediction[i] }) + val iobPrediction2: MutableList = ArrayList() + val iobPredictionArray2 = iobCobCalculator.calculateIobArrayForSMB(AutosensResult(), SMBDefaults.exercise_mode, SMBDefaults.half_basal_exercise_target, isTempTarget) + for (i in iobPredictionArray2) { + iobPrediction2.add(i.setColor(resourceHelper.gc(R.color.iobPred))) + overviewData.maxIobValueFound = max(overviewData.maxIobValueFound, abs(i.iob)) + } + overviewData.iobPredictions2Series = PointsWithLabelGraphSeries(Array(iobPrediction2.size) { i -> iobPrediction2[i] }) + aapsLogger.debug(LTag.AUTOSENS, "IOB prediction for AS=" + DecimalFormatter.to2Decimal(lastAutosensResult.ratio) + ": " + iobCobCalculator.iobArrayToString(iobPredictionArray)) + aapsLogger.debug(LTag.AUTOSENS, "IOB prediction for AS=" + DecimalFormatter.to2Decimal(1.0) + ": " + iobCobCalculator.iobArrayToString(iobPredictionArray2)) + } else { + overviewData.iobPredictions1Series = PointsWithLabelGraphSeries() + overviewData.iobPredictions2Series = PointsWithLabelGraphSeries() + } + + // COB + overviewData.cobSeries = FixedLineGraphSeries(Array(cobArray.size) { i -> cobArray[i] }).also { + it.isDrawBackground = true + it.backgroundColor = -0x7f000001 and resourceHelper.gc(R.color.cob) //50% + it.color = resourceHelper.gc(R.color.cob) + it.thickness = 3 + } + overviewData.cobMinFailOverSeries = PointsWithLabelGraphSeries(Array(minFailOverActiveList.size) { i -> minFailOverActiveList[i] }) + + // ACTIVITY + overviewData.activitySeries = FixedLineGraphSeries(Array(actArrayHist.size) { i -> actArrayHist[i] }).also { + it.isDrawBackground = false + it.color = resourceHelper.gc(R.color.activity) + it.thickness = 3 + } + overviewData.activityPredictionSeries = FixedLineGraphSeries(Array(actArrayPrediction.size) { i -> actArrayPrediction[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.activity) + }) + } + + // BGI + overviewData.minusBgiSeries = FixedLineGraphSeries(Array(bgiArrayHist.size) { i -> bgiArrayHist[i] }).also { + it.isDrawBackground = false + it.color = resourceHelper.gc(R.color.bgi) + it.thickness = 3 + } + overviewData.minusBgiHistSeries = 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) + }) + } + + // DEVIATIONS + overviewData.deviationsSeries = BarGraphSeries(Array(devArray.size) { i -> devArray[i] }).also { + it.setValueDependentColor { data: DeviationDataPoint -> data.color } + } + + // RATIO + overviewData.ratioSeries = LineGraphSeries(Array(ratioArray.size) { i -> ratioArray[i] }).also { + it.color = resourceHelper.gc(R.color.ratio) + it.thickness = 3 + } + + // DEV SLOPE + overviewData.dsMaxSeries = LineGraphSeries(Array(dsMaxArray.size) { i -> dsMaxArray[i] }).also { + it.color = resourceHelper.gc(R.color.devslopepos) + it.thickness = 3 + } + overviewData.dsMinSeries = LineGraphSeries(Array(dsMinArray.size) { i -> dsMinArray[i] }).also { + it.color = resourceHelper.gc(R.color.devslopeneg) + it.thickness = 3 + } + +// profiler.log(LTag.UI, "prepareIobAutosensData() $from", start) + } + + private fun addUpperChartMargin(maxBgValue: Double) = + if (profileFunction.getUnits() == GlucoseUnit.MGDL) Round.roundTo(maxBgValue, 40.0) + 80 else Round.roundTo(maxBgValue, 2.0) + 4 + + private fun getNearestBg(date: Long): Double { + overviewData.bgReadingsArray.let { bgReadingsArray -> + for (reading in bgReadingsArray) { + if (reading.timestamp > date) continue + return Profile.fromMgdlToUnits(reading.value, profileFunction.getUnits()) + } + return if (bgReadingsArray.isNotEmpty()) Profile.fromMgdlToUnits(bgReadingsArray[0].value, profileFunction.getUnits()) + else Profile.fromMgdlToUnits(100.0, profileFunction.getUnits()) + } + } + + private fun List.filterTimeframe(fromTime: Long, endTime: Long): List = + filter { it.x + it.duration >= fromTime && it.x <= endTime } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/StatusLightHandler.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/StatusLightHandler.kt index e4f5074afe..917ff84630 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/StatusLightHandler.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/StatusLightHandler.kt @@ -3,18 +3,19 @@ package info.nightscout.androidaps.plugins.general.overview import android.graphics.Color import android.widget.TextView import androidx.annotation.StringRes -import info.nightscout.androidaps.Config +import info.nightscout.androidaps.interfaces.Config import info.nightscout.androidaps.R import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.ValueWrapper import info.nightscout.androidaps.database.entities.TherapyEvent -import info.nightscout.androidaps.interfaces.ActivePluginProvider +import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.plugins.pump.common.defs.PumpType import info.nightscout.androidaps.plugins.pump.omnipod.eros.OmnipodErosPumpPlugin import info.nightscout.androidaps.plugins.pump.omnipod.eros.driver.definition.OmnipodConstants import info.nightscout.androidaps.utils.DecimalFormatter import info.nightscout.androidaps.utils.WarnColors -import info.nightscout.androidaps.utils.extensions.age +import info.nightscout.androidaps.extensions.age +import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.sharedPreferences.SP import javax.inject.Inject @@ -24,7 +25,8 @@ import javax.inject.Singleton class StatusLightHandler @Inject constructor( private val resourceHelper: ResourceHelper, private val sp: SP, - private val activePlugin: ActivePluginProvider, + private val dateUtil: DateUtil, + private val activePlugin: ActivePlugin, private val warnColors: WarnColors, private val config: Config, private val repository: AppRepository @@ -43,7 +45,7 @@ class StatusLightHandler @Inject constructor( handleAge(careportal_pb_age, TherapyEvent.Type.PUMP_BATTERY_CHANGE, R.string.key_statuslights_bage_warning, 216.0, R.string.key_statuslights_bage_critical, 240.0) } if (!config.NSCLIENT) { - if (pump.model() == PumpType.Omnipod_Eros || pump.model() == PumpType.Omnipod_Dash) { + if (pump.model() == PumpType.OMNIPOD_EROS || pump.model() == PumpType.OMNIPOD_DASH) { handleOmnipodReservoirLevel(careportal_reservoir_level, R.string.key_statuslights_res_critical, 10.0, R.string.key_statuslights_res_warning, 80.0, pump.reservoirLevel, "U") } else { handleLevel(careportal_reservoir_level, R.string.key_statuslights_res_critical, 10.0, R.string.key_statuslights_res_warning, 80.0, pump.reservoirLevel, "U") @@ -55,15 +57,15 @@ class StatusLightHandler @Inject constructor( } if (!config.NSCLIENT) { - if (pump.model() == PumpType.Omnipod_Dash) { + if (pump.model() == PumpType.OMNIPOD_DASH) { // Omnipod Dash does not report its battery level careportal_battery_level?.text = resourceHelper.gs(R.string.notavailable) careportal_battery_level?.setTextColor(Color.WHITE) - } else if (pump.model() == PumpType.Omnipod_Eros && pump is OmnipodErosPumpPlugin) { // instance of check is needed because at startup, pump can still be VirtualPumpPlugin and that will cause a crash because of the class cast below + } else if (pump.model() == PumpType.OMNIPOD_EROS && pump is OmnipodErosPumpPlugin) { // instance of check is needed because at startup, pump can still be VirtualPumpPlugin and that will cause a crash because of the class cast below // The Omnipod Eros does not report its battery level. However, some RileyLink alternatives do. // Depending on the user's configuration, we will either show the battery level reported by the RileyLink or "n/a" handleOmnipodErosBatteryLevel(careportal_battery_level, R.string.key_statuslights_bat_critical, 26.0, R.string.key_statuslights_bat_warning, 51.0, pump.batteryLevel.toDouble(), "%", pump.isUseRileyLinkBatteryLevel) - } else if (pump.model() != PumpType.AccuChekCombo) { + } else if (pump.model() != PumpType.ACCU_CHEK_COMBO) { handleLevel(careportal_battery_level, R.string.key_statuslights_bat_critical, 26.0, R.string.key_statuslights_bat_warning, 51.0, pump.batteryLevel.toDouble(), "%") } } @@ -75,7 +77,7 @@ class StatusLightHandler @Inject constructor( val therapyEvent = repository.getLastTherapyRecord(type).blockingGet() if (therapyEvent is ValueWrapper.Existing) { warnColors.setColorByAge(view, therapyEvent.value, warn, urgent) - view?.text = therapyEvent.value.age(resourceHelper.shortTextMode(), resourceHelper) + view?.text = therapyEvent.value.age(resourceHelper.shortTextMode(), resourceHelper, dateUtil) } else { view?.text = if (resourceHelper.shortTextMode()) "-" else resourceHelper.gs(R.string.notavailable) } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/dialogs/EditQuickWizardDialog.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/dialogs/EditQuickWizardDialog.kt index 237afba172..366850607f 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/dialogs/EditQuickWizardDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/dialogs/EditQuickWizardDialog.kt @@ -83,7 +83,7 @@ class EditQuickWizardDialog : DaggerDialogFragment(), View.OnClickListener { // create an OnTimeSetListener val fromTimeSetListener = TimePickerDialog.OnTimeSetListener { _, hour, minute -> fromSeconds = (T.hours(hour.toLong()).secs() + T.mins(minute.toLong()).secs()).toInt() - binding.from.text = dateUtil.timeString(DateUtil.toDate(fromSeconds)) + binding.from.text = dateUtil.timeString(dateUtil.secondsOfTheDayToMilliseconds(fromSeconds)) } binding.from.setOnClickListener { @@ -96,11 +96,11 @@ class EditQuickWizardDialog : DaggerDialogFragment(), View.OnClickListener { } } fromSeconds = entry.validFrom() - binding.from.text = dateUtil.timeString(DateUtil.toDate(fromSeconds)) + binding.from.text = dateUtil.timeString(dateUtil.secondsOfTheDayToMilliseconds(fromSeconds)) val toTimeSetListener = TimePickerDialog.OnTimeSetListener { _, hour, minute -> toSeconds = (T.hours(hour.toLong()).secs() + T.mins(minute.toLong()).secs()).toInt() - binding.from.text = dateUtil.timeString(DateUtil.toDate(toSeconds)) + binding.from.text = dateUtil.timeString(dateUtil.secondsOfTheDayToMilliseconds(toSeconds)) } binding.to.setOnClickListener { @@ -113,7 +113,7 @@ class EditQuickWizardDialog : DaggerDialogFragment(), View.OnClickListener { } } toSeconds = entry.validFrom() - binding.to.text = dateUtil.timeString(DateUtil.toDate(toSeconds)) + binding.to.text = dateUtil.timeString(dateUtil.secondsOfTheDayToMilliseconds(toSeconds)) binding.buttonEdit.setText(entry.buttonText()) binding.carbsEdit.setText(entry.carbs().toString()) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/events/EventUpdateOverview.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/events/EventUpdateOverview.kt new file mode 100644 index 0000000000..969ed4feed --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/events/EventUpdateOverview.kt @@ -0,0 +1,6 @@ +package info.nightscout.androidaps.plugins.general.overview.events + +import info.nightscout.androidaps.events.Event +import info.nightscout.androidaps.plugins.general.overview.OverviewData + +class EventUpdateOverview(val from: String, val what: OverviewData.Property) : Event() \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphData/GraphData.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphData/GraphData.kt index 3722fd1eaa..c2055e61e8 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphData/GraphData.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphData/GraphData.kt @@ -4,62 +4,40 @@ import android.graphics.Color import android.graphics.DashPathEffect import android.graphics.Paint import com.jjoe64.graphview.GraphView -import com.jjoe64.graphview.series.BarGraphSeries import com.jjoe64.graphview.series.DataPoint import com.jjoe64.graphview.series.LineGraphSeries import com.jjoe64.graphview.series.Series import dagger.android.HasAndroidInjector -import info.nightscout.androidaps.Constants import info.nightscout.androidaps.R -import info.nightscout.androidaps.data.GlucoseValueDataPoint -import info.nightscout.androidaps.data.IobTotal -import info.nightscout.androidaps.data.Profile -import info.nightscout.androidaps.data.TherapyEventDataPoint -import info.nightscout.androidaps.database.AppRepository -import info.nightscout.androidaps.database.ValueWrapper -import info.nightscout.androidaps.database.entities.GlucoseValue -import info.nightscout.androidaps.interfaces.ActivePluginProvider -import info.nightscout.androidaps.interfaces.DatabaseHelperInterface -import info.nightscout.androidaps.interfaces.LoopInterface +import info.nightscout.androidaps.interfaces.GlucoseUnit import info.nightscout.androidaps.interfaces.ProfileFunction -import info.nightscout.androidaps.interfaces.TreatmentsInterface import info.nightscout.androidaps.logging.AAPSLogger -import info.nightscout.androidaps.logging.LTag -import info.nightscout.androidaps.plugins.aps.openAPSSMB.SMBDefaults -import info.nightscout.androidaps.plugins.general.overview.graphExtensions.* -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin -import info.nightscout.androidaps.utils.DateUtil -import info.nightscout.androidaps.utils.DecimalFormatter +import info.nightscout.androidaps.plugins.general.overview.OverviewData +import info.nightscout.androidaps.plugins.general.overview.graphExtensions.AreaGraphSeries +import info.nightscout.androidaps.plugins.general.overview.graphExtensions.DoubleDataPoint +import info.nightscout.androidaps.plugins.general.overview.graphExtensions.TimeAsXAxisLabelFormatter +import info.nightscout.androidaps.utils.DefaultValueHelper import info.nightscout.androidaps.utils.Round -import info.nightscout.androidaps.utils.extensions.target import info.nightscout.androidaps.utils.resources.ResourceHelper import java.util.* import javax.inject.Inject import kotlin.math.abs import kotlin.math.max -import kotlin.math.min class GraphData( - private val injector: HasAndroidInjector, - private val graph: GraphView, - private val iobCobCalculatorPlugin: IobCobCalculatorPlugin, - private val treatmentsPlugin: TreatmentsInterface + injector: HasAndroidInjector, + private val graph: GraphView ) { - // IobCobCalculatorPlugin Cannot be injected: HistoryBrowser @Inject lateinit var aapsLogger: AAPSLogger @Inject lateinit var profileFunction: ProfileFunction @Inject lateinit var resourceHelper: ResourceHelper - @Inject lateinit var activePlugin: ActivePluginProvider - @Inject lateinit var databaseHelper: DatabaseHelperInterface - @Inject lateinit var repository: AppRepository - @Inject lateinit var dateUtil: DateUtil + @Inject lateinit var defaultValueHelper: DefaultValueHelper + @Inject lateinit var overviewData: OverviewData var maxY = Double.MIN_VALUE private var minY = Double.MAX_VALUE - private var bgReadingsArray: List? = null - private val units: String + private val units: GlucoseUnit private val series: MutableList> = ArrayList() init { @@ -67,564 +45,130 @@ class GraphData( units = profileFunction.getUnits() } - @Suppress("UNUSED_PARAMETER") - fun addBgReadings(fromTime: Long, toTime: Long, lowLine: Double, highLine: Double, predictions: MutableList?) { - var maxBgValue = Double.MIN_VALUE - bgReadingsArray = iobCobCalculatorPlugin.bgReadings - if (bgReadingsArray?.isEmpty() != false) { - aapsLogger.debug("No BG data.") - maxY = if (units == Constants.MGDL) 180.0 else 10.0 - minY = 0.0 - return - } - val bgListArray: MutableList = ArrayList() - for (bg in bgReadingsArray!!) { - if (bg.timestamp < fromTime || bg.timestamp > toTime) continue - if (bg.value > maxBgValue) maxBgValue = bg.value - bgListArray.add(GlucoseValueDataPoint(injector, bg)) - } - if (predictions != null) { - predictions.sortWith(Comparator { o1: GlucoseValueDataPoint, o2: GlucoseValueDataPoint -> o1.x.compareTo(o2.x) }) - for (prediction in predictions) if (prediction.data.value >= 40) bgListArray.add(prediction) - } - maxBgValue = Profile.fromMgdlToUnits(maxBgValue, units) - maxBgValue = addUpperChartMargin(maxBgValue) - if (highLine > maxBgValue) maxBgValue = highLine - maxY = maxBgValue + fun addBucketedData() { + addSeries(overviewData.bucketedGraphSeries) + } + + fun addBgReadings(addPredictions: Boolean) { + maxY = if (overviewData.bgReadingsArray.isEmpty()) { + if (units == GlucoseUnit.MGDL) 180.0 else 10.0 + } else overviewData.maxBgValue minY = 0.0 - addSeries(PointsWithLabelGraphSeries(Array(bgListArray.size) { i -> bgListArray[i] })) + addSeries(overviewData.bgReadingGraphSeries) + if (addPredictions) addSeries(overviewData.predictionsGraphSeries) } - internal fun setNumVerticalLabels() { - graph.gridLabelRenderer.numVerticalLabels = if (units == Constants.MGDL) (maxY / 40 + 1).toInt() else (maxY / 2 + 1).toInt() - } - - private fun addUpperChartMargin(maxBgValue: Double) = - if (units == Constants.MGDL) Round.roundTo(maxBgValue, 40.0) + 80 else Round.roundTo(maxBgValue, 2.0) + 4 - fun addInRangeArea(fromTime: Long, toTime: Long, lowLine: Double, highLine: Double) { - val inRangeAreaSeries: AreaGraphSeries val inRangeAreaDataPoints = arrayOf( DoubleDataPoint(fromTime.toDouble(), lowLine, highLine), DoubleDataPoint(toTime.toDouble(), lowLine, highLine) ) - inRangeAreaSeries = AreaGraphSeries(inRangeAreaDataPoints) - inRangeAreaSeries.color = 0 - inRangeAreaSeries.isDrawBackground = true - inRangeAreaSeries.backgroundColor = resourceHelper.gc(R.color.inrangebackground) - addSeries(inRangeAreaSeries) - } - - // scale in % of vertical size (like 0.3) - fun addBasals(fromTime: Long, toTime: Long, scale: Double) { - var maxBasalValueFound = 0.0 - val basalScale = Scale() - val baseBasalArray: MutableList = ArrayList() - val tempBasalArray: MutableList = ArrayList() - val basalLineArray: MutableList = ArrayList() - val absoluteBasalLineArray: MutableList = ArrayList() - var lastLineBasal = 0.0 - var lastAbsoluteLineBasal = -1.0 - var lastBaseBasal = 0.0 - var lastTempBasal = 0.0 - var time = fromTime - while (time < toTime) { - val profile = profileFunction.getProfile(time) - if (profile == null) { - time += 60 * 1000L - continue - } - val basalData = iobCobCalculatorPlugin.getBasalData(profile, time) - val baseBasalValue = basalData.basal - var absoluteLineValue = baseBasalValue - var tempBasalValue = 0.0 - var basal = 0.0 - if (basalData.isTempBasalRunning) { - tempBasalValue = basalData.tempBasalAbsolute - absoluteLineValue = tempBasalValue - if (tempBasalValue != lastTempBasal) { - tempBasalArray.add(ScaledDataPoint(time, lastTempBasal, basalScale)) - tempBasalArray.add(ScaledDataPoint(time, tempBasalValue.also { basal = it }, basalScale)) - } - if (lastBaseBasal != 0.0) { - baseBasalArray.add(ScaledDataPoint(time, lastBaseBasal, basalScale)) - baseBasalArray.add(ScaledDataPoint(time, 0.0, basalScale)) - lastBaseBasal = 0.0 - } - } else { - if (baseBasalValue != lastBaseBasal) { - baseBasalArray.add(ScaledDataPoint(time, lastBaseBasal, basalScale)) - baseBasalArray.add(ScaledDataPoint(time, baseBasalValue.also { basal = it }, basalScale)) - lastBaseBasal = baseBasalValue - } - if (lastTempBasal != 0.0) { - tempBasalArray.add(ScaledDataPoint(time, lastTempBasal, basalScale)) - tempBasalArray.add(ScaledDataPoint(time, 0.0, basalScale)) - } - } - if (baseBasalValue != lastLineBasal) { - basalLineArray.add(ScaledDataPoint(time, lastLineBasal, basalScale)) - basalLineArray.add(ScaledDataPoint(time, baseBasalValue, basalScale)) - } - if (absoluteLineValue != lastAbsoluteLineBasal) { - absoluteBasalLineArray.add(ScaledDataPoint(time, lastAbsoluteLineBasal, basalScale)) - absoluteBasalLineArray.add(ScaledDataPoint(time, basal, basalScale)) - } - lastAbsoluteLineBasal = absoluteLineValue - lastLineBasal = baseBasalValue - lastTempBasal = tempBasalValue - maxBasalValueFound = max(maxBasalValueFound, max(tempBasalValue, baseBasalValue)) - time += 60 * 1000L - } - - // final points - basalLineArray.add(ScaledDataPoint(toTime, lastLineBasal, basalScale)) - baseBasalArray.add(ScaledDataPoint(toTime, lastBaseBasal, basalScale)) - tempBasalArray.add(ScaledDataPoint(toTime, lastTempBasal, basalScale)) - absoluteBasalLineArray.add(ScaledDataPoint(toTime, lastAbsoluteLineBasal, basalScale)) - - // create series - addSeries(LineGraphSeries(Array(baseBasalArray.size) { i -> baseBasalArray[i] }).also { + addSeries(AreaGraphSeries(inRangeAreaDataPoints).also { + it.color = 0 it.isDrawBackground = true - it.backgroundColor = resourceHelper.gc(R.color.basebasal) - it.thickness = 0 - }) - addSeries(LineGraphSeries(Array(tempBasalArray.size) { i -> tempBasalArray[i] }).also { - it.isDrawBackground = true - it.backgroundColor = resourceHelper.gc(R.color.tempbasal) - it.thickness = 0 - }) - addSeries(LineGraphSeries(Array(basalLineArray.size) { i -> basalLineArray[i] }).also { - it.setCustomPaint(Paint().also { paint -> - paint.style = Paint.Style.STROKE - paint.strokeWidth = resourceHelper.getDisplayMetrics().scaledDensity * 2 - paint.pathEffect = DashPathEffect(floatArrayOf(2f, 4f), 0f) - paint.color = resourceHelper.gc(R.color.basal) - }) - }) - addSeries(LineGraphSeries(Array(absoluteBasalLineArray.size) { i -> absoluteBasalLineArray[i] }).also { - it.setCustomPaint(Paint().also { absolutePaint -> - absolutePaint.style = Paint.Style.STROKE - absolutePaint.strokeWidth = resourceHelper.getDisplayMetrics().scaledDensity * 2 - absolutePaint.color = resourceHelper.gc(R.color.basal) - }) - }) - basalScale.setMultiplier(maxY * scale / maxBasalValueFound) - } - - fun addTargetLine(fromTime: Long, toTimeParam: Long, profile: Profile, lastRun: LoopInterface.LastRun?) { - var toTime = toTimeParam - val targetsSeriesArray: MutableList = ArrayList() - var lastTarget = -1.0 - lastRun?.constraintsProcessed?.let { toTime = max(it.latestPredictionsTime, toTime) } - var time = fromTime - while (time < toTime) { - val tt = repository.getTemporaryTargetActiveAt(time).blockingGet() - val value: Double = if (tt is ValueWrapper.Existing) { - Profile.fromMgdlToUnits(tt.value.target(), units) - } else { - Profile.fromMgdlToUnits((profile.getTargetLowMgdl(time) + profile.getTargetHighMgdl(time)) / 2, units) - } - if (lastTarget != value) { - if (lastTarget != -1.0) targetsSeriesArray.add(DataPoint(time.toDouble(), lastTarget)) - targetsSeriesArray.add(DataPoint(time.toDouble(), value)) - } - lastTarget = value - time += 5 * 60 * 1000L - } - // final point - targetsSeriesArray.add(DataPoint(toTime.toDouble(), lastTarget)) - // create series - addSeries(LineGraphSeries(Array(targetsSeriesArray.size) { i -> targetsSeriesArray[i] }).also { - it.isDrawBackground = false - it.color = resourceHelper.gc(R.color.tempTargetBackground) - it.thickness = 2 + it.backgroundColor = resourceHelper.gc(R.color.inrangebackground) }) } - fun addTreatments(fromTime: Long, endTime: Long) { - val filteredTreatments: MutableList = ArrayList() - treatmentsPlugin.treatmentsFromHistory - .filterTimeframe(fromTime, endTime) - .filter { !it.isSMB || it.isValid } - .forEach { - it.y = getNearestBg(it.x.toLong()) - filteredTreatments.add(it) - } - - // ProfileSwitch - treatmentsPlugin.profileSwitchesFromHistory.list - .filterTimeframe(fromTime, endTime) - .forEach(filteredTreatments::add) - - // Extended bolus - if (!activePlugin.activePump.isFakingTempsByExtendedBoluses) { - treatmentsPlugin.extendedBolusesFromHistory.list - .filterTimeframe(fromTime, endTime) - .filter { it.duration != 0L } - .forEach { - it.y = getNearestBg(it.x.toLong()) - filteredTreatments.add(it) - } - } - - // Careportal -// databaseHelper.getCareportalEventsFromTime(fromTime - 6 * 60 * 60 * 1000, true) - repository.compatGetTherapyEventDataFromToTime(fromTime - 6 * 60 * 60 * 1000, endTime).blockingGet() - .map { TherapyEventDataPoint(injector, it) } - .filterTimeframe(fromTime, endTime) - .forEach { - if (it.y == 0.0) it.y = getNearestBg(it.x.toLong()) - filteredTreatments.add(it) - } - - // increase maxY if a treatment forces it's own height that's higher than a BG value - filteredTreatments.map { it.y } - .maxOrNull() - ?.let(::addUpperChartMargin) - ?.let { maxY = maxOf(maxY, it) } - - addSeries(PointsWithLabelGraphSeries(filteredTreatments.toTypedArray())) + fun addBasals() { + val scale = defaultValueHelper.determineLowLine() / maxY / 1.2 + addSeries(overviewData.baseBasalGraphSeries) + addSeries(overviewData.tempBasalGraphSeries) + addSeries(overviewData.basalLineGraphSeries) + addSeries(overviewData.absoluteBasalGraphSeries) + overviewData.basalScale.setMultiplier(maxY * scale / overviewData.maxBasalValueFound) } - private fun getNearestBg(date: Long): Double { - bgReadingsArray?.let { bgReadingsArray -> - for (r in bgReadingsArray.indices) { - val reading = bgReadingsArray[r] - if (reading.timestamp > date) continue - return Profile.fromMgdlToUnits(reading.value, units) - } - return if (bgReadingsArray.isNotEmpty()) Profile.fromMgdlToUnits(bgReadingsArray[0].value, units) else Profile.fromMgdlToUnits(100.0, units) - } ?: return Profile.fromMgdlToUnits(100.0, units) + fun addTargetLine() { + addSeries(overviewData.temporaryTargetSeries) } - fun addActivity(fromTime: Long, toTime: Long, useForScale: Boolean, scale: Double) { - val actArrayHist: MutableList = ArrayList() - val actArrayPrediction: MutableList = ArrayList() - val now = System.currentTimeMillis().toDouble() - val actScale = Scale() - var total: IobTotal - var maxIAValue = 0.0 - var time = fromTime - while (time <= toTime) { - val profile = profileFunction.getProfile(time) - if (profile == null) { - time += 5 * 60 * 1000L - continue - } - total = iobCobCalculatorPlugin.calculateFromTreatmentsAndTempsSynchronized(time, profile) - val act: Double = total.activity - 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 - } - addSeries(FixedLineGraphSeries(Array(actArrayHist.size) { i -> actArrayHist[i] }).also { - it.isDrawBackground = false - it.color = resourceHelper.gc(R.color.activity) - it.thickness = 3 - }) - addSeries(FixedLineGraphSeries(Array(actArrayPrediction.size) { i -> actArrayPrediction[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.activity) - }) - }) - if (useForScale) { - maxY = maxIAValue - minY = -maxIAValue - } - actScale.setMultiplier(maxY * scale / maxIAValue) + fun addTreatments() { + maxY = maxOf(maxY, overviewData.maxTreatmentsValue) + addSeries(overviewData.treatmentsSeries) + } + + fun addActivity(scale: Double) { + addSeries(overviewData.activitySeries) + addSeries(overviewData.activityPredictionSeries) + overviewData.actScale.setMultiplier(maxY * scale / overviewData.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 = ArrayList() - val bgiArrayPrediction: MutableList = 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) - }) - }) + fun addMinusBGI(useForScale: Boolean, scale: Double) { if (useForScale) { - maxY = maxBGIValue - minY = -maxBGIValue + maxY = overviewData.maxBGIValue + minY = -overviewData.maxBGIValue } - bgiScale.setMultiplier(maxY * scale / maxBGIValue) + overviewData.bgiScale.setMultiplier(maxY * scale / overviewData.maxBGIValue) + addSeries(overviewData.minusBgiSeries) + addSeries(overviewData.minusBgiHistSeries) } // scale in % of vertical size (like 0.3) - fun addIob(fromTime: Long, toTime: Long, useForScale: Boolean, scale: Double, showPrediction: Boolean, absScale: Boolean) { - val iobSeries: FixedLineGraphSeries - val iobArray: MutableList = ArrayList() - var maxIobValueFound = Double.MIN_VALUE - var lastIob = 0.0 - val iobScale = Scale() - var time = fromTime - while (time <= toTime) { - val profile = profileFunction.getProfile(time) - var iob = 0.0 - 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 = if (absScale) max(maxIobValueFound, abs(absIob)) else max(maxIobValueFound, abs(iob)) - lastIob = iob - } - time += 5 * 60 * 1000L - } - iobSeries = FixedLineGraphSeries(Array(iobArray.size) { i -> iobArray[i] }).also { - it.isDrawBackground = true - it.backgroundColor = -0x7f000001 and resourceHelper.gc(R.color.iob) //50% - it.color = resourceHelper.gc(R.color.iob) - it.thickness = 3 - } - if (showPrediction) { - val autosensData = iobCobCalculatorPlugin.getLastAutosensDataSynchronized("GraphData") - val lastAutosensResult = autosensData?.autosensResult ?: AutosensResult() - val isTempTarget = repository.getTemporaryTargetActiveAt(dateUtil._now()).blockingGet() is ValueWrapper.Existing - val iobPrediction: MutableList = 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(iobPrediction.size) { i -> iobPrediction[i] })) - val iobPrediction2: MutableList = 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(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)) - } + fun addIob(useForScale: Boolean, scale: Double) { if (useForScale) { - maxY = maxIobValueFound - minY = -maxIobValueFound + maxY = overviewData.maxIobValueFound + minY = -overviewData.maxIobValueFound } - iobScale.setMultiplier(maxY * scale / maxIobValueFound) - addSeries(iobSeries) + overviewData.iobScale.setMultiplier(maxY * scale / overviewData.maxIobValueFound) + addSeries(overviewData.iobSeries) + addSeries(overviewData.iobPredictions1Series) + addSeries(overviewData.iobPredictions2Series) } // scale in % of vertical size (like 0.3) - fun addAbsIob(fromTime: Long, toTime: Long, useForScale: Boolean, scale: Double) { - val iobSeries: FixedLineGraphSeries - val iobArray: MutableList = ArrayList() - var maxIobValueFound = Double.MIN_VALUE - var lastIob = 0.0 - val iobScale = Scale() - var time = fromTime - while (time <= toTime) { - val profile = profileFunction.getProfile(time) - var iob = 0.0 - 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)) - maxIobValueFound = max(maxIobValueFound, abs(iob)) - lastIob = iob - } - time += 5 * 60 * 1000L - } - iobSeries = FixedLineGraphSeries(Array(iobArray.size) { i -> iobArray[i] }).also { - it.isDrawBackground = true - it.backgroundColor = -0x7f000001 and resourceHelper.gc(R.color.iob) //50% - it.color = resourceHelper.gc(R.color.iob) - it.thickness = 3 - } + fun addAbsIob(useForScale: Boolean, scale: Double) { if (useForScale) { - maxY = maxIobValueFound - minY = -maxIobValueFound + maxY = overviewData.maxIobValueFound + minY = -overviewData.maxIobValueFound } - iobScale.setMultiplier(maxY * scale / maxIobValueFound) - addSeries(iobSeries) + overviewData.iobScale.setMultiplier(maxY * scale / overviewData.maxIobValueFound) + addSeries(overviewData.absIobSeries) } // scale in % of vertical size (like 0.3) - fun addCob(fromTime: Long, toTime: Long, useForScale: Boolean, scale: Double) { - val minFailOverActiveList: MutableList = ArrayList() - val cobArray: MutableList = ArrayList() - var maxCobValueFound = 0.0 - var lastCob = 0 - val cobScale = Scale() - var time = fromTime - while (time <= toTime) { - iobCobCalculatorPlugin.getAutosensData(time)?.let { autosensData -> - val cob = autosensData.cob.toInt() - if (cob != lastCob) { - if (autosensData.carbsFromBolus > 0) cobArray.add(ScaledDataPoint(time, lastCob.toDouble(), cobScale)) - cobArray.add(ScaledDataPoint(time, cob.toDouble(), cobScale)) - maxCobValueFound = max(maxCobValueFound, cob.toDouble()) - lastCob = cob - } - if (autosensData.failoverToMinAbsorbtionRate) { - autosensData.setScale(cobScale) - autosensData.setChartTime(time) - minFailOverActiveList.add(autosensData) - } - } - time += 5 * 60 * 1000L - } - - // COB - addSeries(FixedLineGraphSeries(Array(cobArray.size) { i -> cobArray[i] }).also { - it.isDrawBackground = true - it.backgroundColor = -0x7f000001 and resourceHelper.gc(R.color.cob) //50% - it.color = resourceHelper.gc(R.color.cob) - it.thickness = 3 - }) + fun addCob(useForScale: Boolean, scale: Double) { if (useForScale) { - maxY = maxCobValueFound + maxY = overviewData.maxCobValueFound minY = 0.0 } - cobScale.setMultiplier(maxY * scale / maxCobValueFound) - addSeries(PointsWithLabelGraphSeries(Array(minFailOverActiveList.size) { i -> minFailOverActiveList[i] })) + overviewData.cobScale.setMultiplier(maxY * scale / overviewData.maxCobValueFound) + addSeries(overviewData.cobSeries) + addSeries(overviewData.cobMinFailOverSeries) } // scale in % of vertical size (like 0.3) - 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 = 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") { - if (autosensData.pastSensitivity == "C") color = resourceHelper.gc(R.color.deviationgrey) - if (autosensData.pastSensitivity == "+") color = resourceHelper.gc(R.color.deviationgreen) - if (autosensData.pastSensitivity == "-") color = resourceHelper.gc(R.color.deviationred) - } else if (autosensData.type == "uam") { - color = resourceHelper.gc(R.color.uam) - } else if (autosensData.type == "csf") { - color = resourceHelper.gc(R.color.deviationgrey) - } - devArray.add(DeviationDataPoint(time.toDouble(), autosensData.deviation, color, devScale)) - maxDevValueFound = max(maxDevValueFound, max(abs(autosensData.deviation), abs(bgi))) - } - time += 5 * 60 * 1000L - } - - // DEVIATIONS - addSeries(BarGraphSeries(Array(devArray.size) { i -> devArray[i] }).also { - it.setValueDependentColor { data: DeviationDataPoint -> data.color } - }) + fun addDeviations(useForScale: Boolean, scale: Double) { if (useForScale) { - maxY = maxDevValueFound + maxY = overviewData.maxDevValueFound minY = -maxY } - devScale.setMultiplier(maxY * scale / maxDevValueFound) + overviewData.devScale.setMultiplier(maxY * scale / overviewData.maxDevValueFound) + addSeries(overviewData.deviationsSeries) } // scale in % of vertical size (like 0.3) - fun addRatio(fromTime: Long, toTime: Long, useForScale: Boolean, scale: Double) { - val ratioArray: MutableList = ArrayList() - var maxRatioValueFound = 5.0 //even if sens data equals 0 for all the period, minimum scale is between 95% and 105% - var minRatioValueFound = - maxRatioValueFound - val ratioScale = if (useForScale) Scale(100.0) else Scale() - var time = fromTime - while (time <= toTime) { - iobCobCalculatorPlugin.getAutosensData(time)?.let { autosensData -> - ratioArray.add(ScaledDataPoint(time, 100.0 * (autosensData.autosensResult.ratio - 1 ), ratioScale)) - maxRatioValueFound = max(maxRatioValueFound, 100.0 * (autosensData.autosensResult.ratio - 1)) - minRatioValueFound = min(minRatioValueFound, 100.0 * (autosensData.autosensResult.ratio - 1)) - } - time += 5 * 60 * 1000L - } - - // RATIOS - addSeries(LineGraphSeries(Array(ratioArray.size) { i -> ratioArray[i] }).also { - it.color = resourceHelper.gc(R.color.ratio) - it.thickness = 3 - }) + fun addRatio(useForScale: Boolean, scale: Double) { if (useForScale) { - maxY = 100.0 + max(maxRatioValueFound, abs(minRatioValueFound)) - minY = 100.0 - max(maxRatioValueFound, abs(minRatioValueFound)) - ratioScale.setMultiplier(1.0) + maxY = 100.0 + max(overviewData.maxRatioValueFound, abs(overviewData.minRatioValueFound)) + minY = 100.0 - max(overviewData.maxRatioValueFound, abs(overviewData.minRatioValueFound)) + overviewData.ratioScale.setMultiplier(1.0) } else - ratioScale.setMultiplier(maxY * scale / max(maxRatioValueFound, abs(minRatioValueFound))) + overviewData.ratioScale.setMultiplier(maxY * scale / max(overviewData.maxRatioValueFound, abs(overviewData.minRatioValueFound))) + addSeries(overviewData.ratioSeries) } // scale in % of vertical size (like 0.3) - fun addDeviationSlope(fromTime: Long, toTime: Long, useForScale: Boolean, scale: Double) { - val dsMaxArray: MutableList = ArrayList() - val dsMinArray: MutableList = ArrayList() - var maxFromMaxValueFound = 0.0 - var maxFromMinValueFound = 0.0 - val dsMaxScale = Scale() - val dsMinScale = Scale() - var time = fromTime - while (time <= toTime) { - iobCobCalculatorPlugin.getAutosensData(time)?.let { autosensData -> - dsMaxArray.add(ScaledDataPoint(time, autosensData.slopeFromMaxDeviation, dsMaxScale)) - dsMinArray.add(ScaledDataPoint(time, autosensData.slopeFromMinDeviation, dsMinScale)) - maxFromMaxValueFound = max(maxFromMaxValueFound, abs(autosensData.slopeFromMaxDeviation)) - maxFromMinValueFound = max(maxFromMinValueFound, abs(autosensData.slopeFromMinDeviation)) - } - time += 5 * 60 * 1000L - } - - // Slopes - addSeries(LineGraphSeries(Array(dsMaxArray.size) { i -> dsMaxArray[i] }).also { - it.color = resourceHelper.gc(R.color.devslopepos) - it.thickness = 3 - }) - addSeries(LineGraphSeries(Array(dsMinArray.size) { i -> dsMinArray[i] }).also { - it.color = resourceHelper.gc(R.color.devslopeneg) - it.thickness = 3 - }) + fun addDeviationSlope(useForScale: Boolean, scale: Double) { if (useForScale) { - maxY = max(maxFromMaxValueFound, maxFromMinValueFound) + maxY = max(overviewData.maxFromMaxValueFound, overviewData.maxFromMinValueFound) minY = -maxY } - dsMaxScale.setMultiplier(maxY * scale / maxFromMaxValueFound) - dsMinScale.setMultiplier(maxY * scale / maxFromMinValueFound) + overviewData.dsMaxScale.setMultiplier(maxY * scale / overviewData.maxFromMaxValueFound) + overviewData.dsMinScale.setMultiplier(maxY * scale / overviewData.maxFromMinValueFound) + addSeries(overviewData.dsMaxSeries) + addSeries(overviewData.dsMinSeries) } // scale in % of vertical size (like 0.3) @@ -645,6 +189,10 @@ class GraphData( }) } + fun setNumVerticalLabels() { + graph.gridLabelRenderer.numVerticalLabels = if (units == GlucoseUnit.MGDL) (maxY / 40 + 1).toInt() else (maxY / 2 + 1).toInt() + } + fun formatAxis(fromTime: Long, endTime: Long) { graph.viewport.setMaxX(endTime.toDouble()) graph.viewport.setMinX(fromTime.toDouble()) @@ -653,7 +201,7 @@ class GraphData( graph.gridLabelRenderer.numHorizontalLabels = 7 // only 7 because of the space } - private fun addSeries(s: Series<*>) = series.add(s) + internal fun addSeries(s: Series<*>) = series.add(s) fun performUpdate() { // clear old data @@ -677,5 +225,3 @@ class GraphData( } } -private fun List.filterTimeframe(fromTime: Long, endTime: Long): List = - filter { it.x + it.duration >= fromTime && it.x <= endTime } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphExtensions/BolusDataPoint.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphExtensions/BolusDataPoint.kt new file mode 100644 index 0000000000..89136b5b69 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphExtensions/BolusDataPoint.kt @@ -0,0 +1,39 @@ +package info.nightscout.androidaps.plugins.general.overview.graphExtensions + +import android.graphics.Color +import info.nightscout.androidaps.core.R +import info.nightscout.androidaps.database.entities.Bolus +import info.nightscout.androidaps.interfaces.ActivePlugin +import info.nightscout.androidaps.utils.DecimalFormatter +import info.nightscout.androidaps.utils.DefaultValueHelper +import info.nightscout.androidaps.utils.resources.ResourceHelper +import javax.inject.Inject + +class BolusDataPoint @Inject constructor( + val data: Bolus, + private val resourceHelper: ResourceHelper, + private val activePlugin: ActivePlugin, + private val defaultValueHelper: DefaultValueHelper +) : DataPointWithLabelInterface { + + private var yValue = 0.0 + + override fun getX(): Double = data.timestamp.toDouble() + override fun getY(): Double = if (data.type == Bolus.Type.SMB) defaultValueHelper.determineLowLine() else yValue + override fun getLabel(): String = DecimalFormatter.toPumpSupportedBolus(data.amount, activePlugin.activePump, resourceHelper) + override fun getDuration(): Long = 0 + override fun getSize(): Float = 2f + + override fun getShape(): PointsWithLabelGraphSeries.Shape = + if (data.type == Bolus.Type.SMB) PointsWithLabelGraphSeries.Shape.SMB + else PointsWithLabelGraphSeries.Shape.BOLUS + + override fun getColor(): Int = + if (data.type == Bolus.Type.SMB) resourceHelper.gc(R.color.tempbasal) + else if (data.isValid) Color.CYAN + else resourceHelper.gc(android.R.color.holo_red_light) + + override fun setY(y: Double) { + yValue = y + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphExtensions/CarbsDataPoint.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphExtensions/CarbsDataPoint.kt new file mode 100644 index 0000000000..719533da98 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphExtensions/CarbsDataPoint.kt @@ -0,0 +1,30 @@ +package info.nightscout.androidaps.plugins.general.overview.graphExtensions + +import info.nightscout.androidaps.core.R +import info.nightscout.androidaps.database.entities.Carbs +import info.nightscout.androidaps.utils.resources.ResourceHelper +import javax.inject.Inject + +class CarbsDataPoint @Inject constructor( + val data: Carbs, + private val resourceHelper: ResourceHelper +) : DataPointWithLabelInterface { + + private var yValue = 0.0 + + override fun getX(): Double = data.timestamp.toDouble() + override fun getY(): Double = yValue + override fun getLabel(): String = resourceHelper.gs(R.string.format_carbs, data.amount.toInt()) + override fun getDuration(): Long = 0 + override fun getSize(): Float = 2f + + override fun getShape(): PointsWithLabelGraphSeries.Shape = PointsWithLabelGraphSeries.Shape.CARBS + + override fun getColor(): Int = + if (data.isValid) resourceHelper.gc(R.color.carbs) + else resourceHelper.gc(android.R.color.holo_red_light) + + override fun setY(y: Double) { + yValue = y + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphExtensions/EffectiveProfileSwitchDataPoint.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphExtensions/EffectiveProfileSwitchDataPoint.kt new file mode 100644 index 0000000000..7c82b3d9bc --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphExtensions/EffectiveProfileSwitchDataPoint.kt @@ -0,0 +1,25 @@ +package info.nightscout.androidaps.plugins.general.overview.graphExtensions + +import android.graphics.Color +import info.nightscout.androidaps.database.entities.EffectiveProfileSwitch +import javax.inject.Inject + +class EffectiveProfileSwitchDataPoint @Inject constructor( + val data: EffectiveProfileSwitch +) : DataPointWithLabelInterface { + + private var yValue = 0.0 + + override fun getX(): Double = data.timestamp.toDouble() + override fun getY(): Double = yValue + + override fun setY(y: Double) { + yValue = y + } + + override fun getLabel(): String = data.originalCustomizedName + override fun getDuration(): Long = 0 + override fun getShape(): PointsWithLabelGraphSeries.Shape = PointsWithLabelGraphSeries.Shape.PROFILE + override fun getSize(): Float = 10f + override fun getColor(): Int = Color.CYAN +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphExtensions/ExtendedBolusDataPoint.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphExtensions/ExtendedBolusDataPoint.kt new file mode 100644 index 0000000000..d286f83a45 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphExtensions/ExtendedBolusDataPoint.kt @@ -0,0 +1,25 @@ +package info.nightscout.androidaps.plugins.general.overview.graphExtensions + +import android.graphics.Color +import info.nightscout.androidaps.database.entities.ExtendedBolus +import info.nightscout.androidaps.extensions.toStringTotal +import javax.inject.Inject + +class ExtendedBolusDataPoint @Inject constructor( + val data: ExtendedBolus +) : DataPointWithLabelInterface { + + private var yValue = 0.0 + + override fun getX(): Double = data.timestamp.toDouble() + override fun getY(): Double = yValue + override fun getLabel(): String = data.toStringTotal() + override fun getDuration(): Long = data.duration + override fun getSize(): Float = 10f + override fun getShape(): PointsWithLabelGraphSeries.Shape = PointsWithLabelGraphSeries.Shape.EXTENDEDBOLUS + override fun getColor(): Int = Color.CYAN + + override fun setY(y: Double) { + yValue = y + } +} \ No newline at end of file diff --git a/core/src/main/java/info/nightscout/androidaps/data/GlucoseValueDataPoint.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphExtensions/GlucoseValueDataPoint.kt similarity index 61% rename from core/src/main/java/info/nightscout/androidaps/data/GlucoseValueDataPoint.kt rename to app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphExtensions/GlucoseValueDataPoint.kt index 9c884b5315..3c4c6aa367 100644 --- a/core/src/main/java/info/nightscout/androidaps/data/GlucoseValueDataPoint.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphExtensions/GlucoseValueDataPoint.kt @@ -1,31 +1,23 @@ -package info.nightscout.androidaps.data +package info.nightscout.androidaps.plugins.general.overview.graphExtensions -import dagger.android.HasAndroidInjector import info.nightscout.androidaps.Constants import info.nightscout.androidaps.core.R import info.nightscout.androidaps.database.entities.GlucoseValue +import info.nightscout.androidaps.interfaces.GlucoseUnit import info.nightscout.androidaps.interfaces.ProfileFunction -import info.nightscout.androidaps.plugins.general.overview.graphExtensions.DataPointWithLabelInterface -import info.nightscout.androidaps.plugins.general.overview.graphExtensions.PointsWithLabelGraphSeries import info.nightscout.androidaps.utils.DefaultValueHelper import info.nightscout.androidaps.utils.resources.ResourceHelper import javax.inject.Inject class GlucoseValueDataPoint @Inject constructor( - val injector: HasAndroidInjector, - val data: GlucoseValue + val data: GlucoseValue, + private val defaultValueHelper: DefaultValueHelper, + private val profileFunction: ProfileFunction, + private val resourceHelper: ResourceHelper ) : DataPointWithLabelInterface { - @Inject lateinit var defaultValueHelper: DefaultValueHelper - @Inject lateinit var profileFunction: ProfileFunction - @Inject lateinit var resourceHelper: ResourceHelper - - init { - injector.androidInjector().inject(this) - } - - fun valueToUnits(units: String): Double = - if (units == Constants.MGDL) data.value else data.value * Constants.MGDL_TO_MMOLL + fun valueToUnits(units: GlucoseUnit): Double = + if (units == GlucoseUnit.MGDL) data.value else data.value * Constants.MGDL_TO_MMOLL override fun getX(): Double { return data.timestamp.toDouble() @@ -59,11 +51,11 @@ class GlucoseValueDataPoint @Inject constructor( val predictionColor: Int get() { return when (data.sourceSensor) { - GlucoseValue.SourceSensor.IOB_PREDICTION -> resourceHelper.gc(R.color.iob) - GlucoseValue.SourceSensor.COB_PREDICTION -> resourceHelper.gc(R.color.cob) - GlucoseValue.SourceSensor.aCOB_PREDICTION -> -0x7f000001 and resourceHelper.gc(R.color.cob) - GlucoseValue.SourceSensor.UAM_PREDICTION -> resourceHelper.gc(R.color.uam) - GlucoseValue.SourceSensor.ZT_PREDICTION -> resourceHelper.gc(R.color.zt) + GlucoseValue.SourceSensor.IOB_PREDICTION -> resourceHelper.gc(R.color.iob) + GlucoseValue.SourceSensor.COB_PREDICTION -> resourceHelper.gc(R.color.cob) + GlucoseValue.SourceSensor.A_COB_PREDICTION -> -0x7f000001 and resourceHelper.gc(R.color.cob) + GlucoseValue.SourceSensor.UAM_PREDICTION -> resourceHelper.gc(R.color.uam) + GlucoseValue.SourceSensor.ZT_PREDICTION -> resourceHelper.gc(R.color.zt) else -> R.color.white } } @@ -71,7 +63,7 @@ class GlucoseValueDataPoint @Inject constructor( private val isPrediction: Boolean get() = data.sourceSensor == GlucoseValue.SourceSensor.IOB_PREDICTION || data.sourceSensor == GlucoseValue.SourceSensor.COB_PREDICTION || - data.sourceSensor == GlucoseValue.SourceSensor.aCOB_PREDICTION || + data.sourceSensor == GlucoseValue.SourceSensor.A_COB_PREDICTION || data.sourceSensor == GlucoseValue.SourceSensor.UAM_PREDICTION || data.sourceSensor == GlucoseValue.SourceSensor.ZT_PREDICTION diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphExtensions/InMemoryGlucoseValueDataPoint.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphExtensions/InMemoryGlucoseValueDataPoint.kt new file mode 100644 index 0000000000..04540d9131 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphExtensions/InMemoryGlucoseValueDataPoint.kt @@ -0,0 +1,28 @@ +package info.nightscout.androidaps.plugins.general.overview.graphExtensions + +import info.nightscout.androidaps.Constants +import info.nightscout.androidaps.core.R +import info.nightscout.androidaps.data.InMemoryGlucoseValue +import info.nightscout.androidaps.interfaces.GlucoseUnit +import info.nightscout.androidaps.interfaces.ProfileFunction +import info.nightscout.androidaps.utils.resources.ResourceHelper +import javax.inject.Inject + +class InMemoryGlucoseValueDataPoint @Inject constructor( + val data: InMemoryGlucoseValue, + private val profileFunction: ProfileFunction, + private val resourceHelper: ResourceHelper +) : DataPointWithLabelInterface { + + fun valueToUnits(units: GlucoseUnit): Double = + if (units == GlucoseUnit.MGDL) data.value else data.value * Constants.MGDL_TO_MMOLL + + override fun getX(): Double = data.timestamp.toDouble() + override fun getY(): Double = valueToUnits(profileFunction.getUnits()) + override fun setY(y: Double) {} + override fun getLabel(): String? = null + override fun getDuration(): Long = 0 + override fun getShape(): PointsWithLabelGraphSeries.Shape = PointsWithLabelGraphSeries.Shape.BUCKETED_BG + override fun getSize(): Float = 0.3f + override fun getColor(): Int = resourceHelper.gc(R.color.white) +} \ No newline at end of file diff --git a/core/src/main/java/info/nightscout/androidaps/data/TherapyEventDataPoint.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphExtensions/TherapyEventDataPoint.kt similarity index 81% rename from core/src/main/java/info/nightscout/androidaps/data/TherapyEventDataPoint.kt rename to app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphExtensions/TherapyEventDataPoint.kt index 7c884db7f1..d273f4e7f2 100644 --- a/core/src/main/java/info/nightscout/androidaps/data/TherapyEventDataPoint.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphExtensions/TherapyEventDataPoint.kt @@ -1,35 +1,26 @@ -package info.nightscout.androidaps.data +package info.nightscout.androidaps.plugins.general.overview.graphExtensions import android.graphics.Color -import dagger.android.HasAndroidInjector import info.nightscout.androidaps.Constants import info.nightscout.androidaps.core.R +import info.nightscout.androidaps.interfaces.Profile import info.nightscout.androidaps.database.entities.TherapyEvent +import info.nightscout.androidaps.interfaces.GlucoseUnit import info.nightscout.androidaps.interfaces.Interval import info.nightscout.androidaps.interfaces.ProfileFunction -import info.nightscout.androidaps.plugins.general.overview.graphExtensions.DataPointWithLabelInterface -import info.nightscout.androidaps.plugins.general.overview.graphExtensions.PointsWithLabelGraphSeries -import info.nightscout.androidaps.utils.DefaultValueHelper import info.nightscout.androidaps.utils.Translator import info.nightscout.androidaps.utils.resources.ResourceHelper import javax.inject.Inject class TherapyEventDataPoint @Inject constructor( - val injector: HasAndroidInjector, - val data: TherapyEvent + val data: TherapyEvent, + private val resourceHelper: ResourceHelper, + private val profileFunction: ProfileFunction, + private val translator: Translator ) : DataPointWithLabelInterface, Interval { - @Inject lateinit var defaultValueHelper: DefaultValueHelper - @Inject lateinit var profileFunction: ProfileFunction - @Inject lateinit var resourceHelper: ResourceHelper - @Inject lateinit var translator: Translator - private var yValue = 0.0 - init { - injector.androidInjector().inject(this) - } - override fun getX(): Double { return data.timestamp.toDouble() } @@ -40,11 +31,11 @@ class TherapyEventDataPoint @Inject constructor( if (data.glucose != null && data.glucose != 0.0) { var mmol = 0.0 var mgdl = 0.0 - if (units == Constants.MGDL) { + if (units == GlucoseUnit.MGDL) { mgdl = data.glucose!! mmol = data.glucose!! * Constants.MGDL_TO_MMOLL } - if (units == Constants.MMOL) { + if (units == GlucoseUnit.MMOL) { mmol = data.glucose!! mgdl = data.glucose!! * Constants.MMOLL_TO_MGDL } @@ -59,7 +50,7 @@ class TherapyEventDataPoint @Inject constructor( override fun getLabel(): String? = if (data.note != null) data.note - else translator.translate(data.type.text) + else translator.translate(data.type) override fun getDuration(): Long = end() - start() override fun getShape(): PointsWithLabelGraphSeries.Shape = diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/notifications/NotificationStore.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/notifications/NotificationStore.kt index a732aaffd9..2ac420ade8 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/notifications/NotificationStore.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/notifications/NotificationStore.kt @@ -14,14 +14,14 @@ import androidx.core.app.NotificationCompat import androidx.recyclerview.widget.RecyclerView import info.nightscout.androidaps.R import info.nightscout.androidaps.databinding.OverviewNotificationItemBinding -import info.nightscout.androidaps.interfaces.NotificationHolderInterface +import info.nightscout.androidaps.interfaces.IconsProvider +import info.nightscout.androidaps.interfaces.NotificationHolder import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification import info.nightscout.androidaps.services.AlarmSoundServiceHelper import info.nightscout.androidaps.utils.DateUtil -import info.nightscout.androidaps.utils.resources.IconsProvider import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.sharedPreferences.SP import java.util.* @@ -38,7 +38,7 @@ class NotificationStore @Inject constructor( private val iconsProvider: IconsProvider, private val alarmSoundServiceHelper: AlarmSoundServiceHelper, private val dateUtil: DateUtil, - private val notificationHolder: NotificationHolderInterface + private val notificationHolder: NotificationHolder ) { private var store: MutableList = ArrayList() diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/persistentNotification/DummyService.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/persistentNotification/DummyService.kt index 3534146fe0..85675df350 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/persistentNotification/DummyService.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/persistentNotification/DummyService.kt @@ -7,11 +7,11 @@ import android.os.Binder import android.os.IBinder import dagger.android.DaggerService import info.nightscout.androidaps.events.EventAppExit +import info.nightscout.androidaps.interfaces.NotificationHolder import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.utils.FabricPrivacy -import info.nightscout.androidaps.utils.androidNotification.NotificationHolder import info.nightscout.androidaps.utils.rx.AapsSchedulers import io.reactivex.disposables.CompositeDisposable import javax.inject.Inject diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/persistentNotification/DummyServiceHelper.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/persistentNotification/DummyServiceHelper.kt index 5f52035bac..f5373abec0 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/persistentNotification/DummyServiceHelper.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/persistentNotification/DummyServiceHelper.kt @@ -5,7 +5,7 @@ import android.content.Context import android.content.Intent import android.content.ServiceConnection import android.os.IBinder -import info.nightscout.androidaps.interfaces.NotificationHolderInterface +import info.nightscout.androidaps.interfaces.NotificationHolder import javax.inject.Inject import javax.inject.Singleton @@ -21,7 +21,7 @@ import javax.inject.Singleton */ @Singleton class DummyServiceHelper @Inject constructor( - private val notificationHolder: NotificationHolderInterface + private val notificationHolder: NotificationHolder ) { fun startService(context: Context) { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/persistentNotification/PersistentNotificationPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/persistentNotification/PersistentNotificationPlugin.kt index 8c028c89be..7a7edb678b 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/persistentNotification/PersistentNotificationPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/persistentNotification/PersistentNotificationPlugin.kt @@ -10,20 +10,18 @@ import androidx.core.app.RemoteInput import dagger.android.HasAndroidInjector import info.nightscout.androidaps.Constants import info.nightscout.androidaps.R -import info.nightscout.androidaps.data.Profile +import info.nightscout.androidaps.interfaces.Profile import info.nightscout.androidaps.events.* import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.plugins.bus.RxBusWrapper -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatusProvider -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin import info.nightscout.androidaps.utils.DecimalFormatter import info.nightscout.androidaps.utils.FabricPrivacy -import info.nightscout.androidaps.utils.resources.IconsProvider +import info.nightscout.androidaps.extensions.toStringShort import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers -import info.nightscout.androidaps.utils.extensions.valueToUnitsString +import info.nightscout.androidaps.extensions.valueToUnitsString import io.reactivex.disposables.CompositeDisposable import javax.inject.Inject import javax.inject.Singleton @@ -37,11 +35,11 @@ class PersistentNotificationPlugin @Inject constructor( private val aapsSchedulers: AapsSchedulers, private val profileFunction: ProfileFunction, private val fabricPrivacy: FabricPrivacy, - private val activePlugins: ActivePluginProvider, - private val iobCobCalculatorPlugin: IobCobCalculatorPlugin, + private val activePlugins: ActivePlugin, + private val iobCobCalculator: IobCobCalculator, private val rxBus: RxBusWrapper, private val context: Context, - private val notificationHolder: NotificationHolderInterface, + private val notificationHolder: NotificationHolder, private val dummyServiceHelper: DummyServiceHelper, private val iconsProvider: IconsProvider, private val glucoseStatusProvider: GlucoseStatusProvider @@ -131,7 +129,7 @@ class PersistentNotificationPlugin @Inject constructor( if (profileFunction.isProfileValid("Notification")) { var line1aa: String val units = profileFunction.getUnits() - val lastBG = iobCobCalculatorPlugin.lastBg() + val lastBG = iobCobCalculator.ads.lastBg() val glucoseStatus = glucoseStatusProvider.glucoseStatusData if (lastBG != null) { line1aa = lastBG.valueToUnitsString(units) @@ -150,18 +148,16 @@ class PersistentNotificationPlugin @Inject constructor( line1aa = resourceHelper.gs(R.string.missed_bg_readings) line1 = line1aa } - val activeTemp = activePlugins.activeTreatments.getTempBasalFromHistory(System.currentTimeMillis()) + val activeTemp = iobCobCalculator.getTempBasalIncludingConvertedExtended(System.currentTimeMillis()) if (activeTemp != null) { line1 += " " + activeTemp.toStringShort() line1aa += " " + activeTemp.toStringShort() + "." } //IOB - activePlugins.activeTreatments.updateTotalIOBTreatments() - activePlugins.activeTreatments.updateTotalIOBTempBasals() - val bolusIob = activePlugins.activeTreatments.lastCalculationTreatments.round() - val basalIob = activePlugins.activeTreatments.lastCalculationTempBasals.round() - line2 = resourceHelper.gs(R.string.treatments_iob_label_string) + " " + DecimalFormatter.to2Decimal(bolusIob.iob + basalIob.basaliob) + "U " + resourceHelper.gs(R.string.cob) + ": " + iobCobCalculatorPlugin.getCobInfo(false, "PersistentNotificationPlugin").generateCOBString() - val line2aa = resourceHelper.gs(R.string.treatments_iob_label_string) + " " + DecimalFormatter.to2Decimal(bolusIob.iob + basalIob.basaliob) + "U. " + resourceHelper.gs(R.string.cob) + ": " + iobCobCalculatorPlugin.getCobInfo(false, "PersistentNotificationPlugin").generateCOBString() + "." + val bolusIob = iobCobCalculator.calculateIobFromBolus().round() + val basalIob = iobCobCalculator.calculateIobFromTempBasalsIncludingConvertedExtended().round() + line2 = resourceHelper.gs(R.string.treatments_iob_label_string) + " " + DecimalFormatter.to2Decimal(bolusIob.iob + basalIob.basaliob) + "U " + resourceHelper.gs(R.string.cob) + ": " + iobCobCalculator.getCobInfo(false, "PersistentNotificationPlugin").generateCOBString() + val line2aa = resourceHelper.gs(R.string.treatments_iob_label_string) + " " + DecimalFormatter.to2Decimal(bolusIob.iob + basalIob.basaliob) + "U. " + resourceHelper.gs(R.string.cob) + ": " + iobCobCalculator.getCobInfo(false, "PersistentNotificationPlugin").generateCOBString() + "." line3 = DecimalFormatter.to2Decimal(pump.baseBasalRate) + " U/h" var line3aa = DecimalFormatter.to2Decimal(pump.baseBasalRate) + " U/h." line3 += " - " + profileFunction.getProfileName() diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/AuthRequest.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/AuthRequest.kt index ae454e1f2b..7d19bed23d 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/AuthRequest.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/AuthRequest.kt @@ -22,12 +22,14 @@ class AuthRequest internal constructor( @Inject lateinit var smsCommunicatorPlugin: SmsCommunicatorPlugin @Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var otp: OneTimePassword + @Inject lateinit var dateUtil: DateUtil - private val date = DateUtil.now() + private var date = 0L private var processed = false init { injector.androidInjector().inject(this) + date = dateUtil.now() smsCommunicatorPlugin.sendSMS(Sms(requester.phoneNumber, requestText)) } @@ -45,7 +47,7 @@ class AuthRequest internal constructor( smsCommunicatorPlugin.sendSMS(Sms(requester.phoneNumber, resourceHelper.gs(R.string.sms_wrongcode))) return } - if (DateUtil.now() - date < Constants.SMS_CONFIRM_TIMEOUT) { + if (dateUtil.now() - date < Constants.SMS_CONFIRM_TIMEOUT) { processed = true aapsLogger.debug(LTag.SMS, "Processing confirmed SMS: " + requester.text) action.run() diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/Sms.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/Sms.kt index bab25dfe3e..6efac44dbb 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/Sms.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/Sms.kt @@ -1,10 +1,9 @@ package info.nightscout.androidaps.plugins.general.smsCommunicator import android.telephony.SmsMessage -import info.nightscout.androidaps.MainApp -import info.nightscout.androidaps.utils.DateUtil class Sms { + var phoneNumber: String var text: String var date: Long @@ -23,7 +22,7 @@ class Sms { internal constructor(phoneNumber: String, text: String) { this.phoneNumber = phoneNumber this.text = text - date = DateUtil.now() + date = System.currentTimeMillis() sent = true } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt index e8e490bcf5..b6136e1445 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt @@ -9,18 +9,20 @@ import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat import androidx.work.Worker import androidx.work.WorkerParameters +import androidx.work.workDataOf import dagger.android.HasAndroidInjector -import info.nightscout.androidaps.Config +import info.nightscout.androidaps.interfaces.Config import info.nightscout.androidaps.Constants import info.nightscout.androidaps.R import info.nightscout.androidaps.data.DetailedBolusInfo -import info.nightscout.androidaps.data.Profile +import info.nightscout.androidaps.interfaces.Profile import info.nightscout.androidaps.database.AppRepository +import info.nightscout.androidaps.database.entities.ValueWithUnit import info.nightscout.androidaps.database.entities.TemporaryTarget -import info.nightscout.androidaps.database.entities.UserEntry.* +import info.nightscout.androidaps.database.entities.UserEntry.Action +import info.nightscout.androidaps.database.entities.UserEntry.Sources import info.nightscout.androidaps.database.transactions.CancelCurrentTemporaryTargetIfAnyTransaction import info.nightscout.androidaps.database.transactions.InsertTemporaryTargetAndCancelCurrentTransaction -import info.nightscout.androidaps.db.Source import info.nightscout.androidaps.events.EventPreferenceChange import info.nightscout.androidaps.events.EventRefreshOverview import info.nightscout.androidaps.interfaces.* @@ -30,19 +32,16 @@ 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.ConstraintChecker -import info.nightscout.androidaps.plugins.general.nsclient.NSUpload import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification import info.nightscout.androidaps.plugins.general.overview.notifications.Notification import info.nightscout.androidaps.plugins.general.smsCommunicator.events.EventSmsCommunicatorUpdateGui import info.nightscout.androidaps.plugins.general.smsCommunicator.otp.OneTimePassword import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatusProvider -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin import info.nightscout.androidaps.queue.Callback import info.nightscout.androidaps.receivers.DataWorker -import info.nightscout.androidaps.receivers.DataReceiver import info.nightscout.androidaps.utils.* -import info.nightscout.androidaps.utils.extensions.valueToUnitsString +import info.nightscout.androidaps.extensions.valueToUnitsString import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.utils.sharedPreferences.SP @@ -50,9 +49,11 @@ import info.nightscout.androidaps.utils.textValidator.ValidatingEditTextPreferen import io.reactivex.disposables.CompositeDisposable import io.reactivex.rxkotlin.plusAssign import org.apache.commons.lang3.StringUtils +import org.joda.time.DateTime import java.text.Normalizer import java.util.* import java.util.concurrent.TimeUnit +import java.util.regex.Pattern import javax.inject.Inject import javax.inject.Singleton import kotlin.math.max @@ -69,16 +70,15 @@ class SmsCommunicatorPlugin @Inject constructor( private val rxBus: RxBusWrapper, private val profileFunction: ProfileFunction, private val fabricPrivacy: FabricPrivacy, - private val activePlugin: ActivePluginProvider, + private val activePlugin: ActivePlugin, private val commandQueue: CommandQueueProvider, private val loopPlugin: LoopPlugin, - private val iobCobCalculatorPlugin: IobCobCalculatorPlugin, + private val iobCobCalculator: IobCobCalculator, private val xdripCalibrations: XdripCalibrations, private var otp: OneTimePassword, private val config: Config, private val dateUtil: DateUtil, private val uel: UserEntryLogger, - private val nsUpload: NSUpload, private val glucoseStatusProvider: GlucoseStatusProvider, private val repository: AppRepository ) : PluginBase(PluginDescription() @@ -90,7 +90,7 @@ class SmsCommunicatorPlugin @Inject constructor( .preferencesId(R.xml.pref_smscommunicator) .description(R.string.description_sms_communicator), aapsLogger, resourceHelper, injector -), SmsCommunicatorInterface { +), SmsCommunicator { private val disposable = CompositeDisposable() var allowedNumbers: MutableList = ArrayList() @@ -101,7 +101,6 @@ class SmsCommunicatorPlugin @Inject constructor( val commands = mapOf( "BG" to "BG", "LOOP" to "LOOP STOP/DISABLE/START/ENABLE/RESUME/STATUS\nLOOP SUSPEND 20", - "TREATMENTS" to "TREATMENTS REFRESH", "NSCLIENT" to "NSCLIENT RESTART", "PUMP" to "PUMP\nPUMP CONNECT\nPUMP DISCONNECT 30\n", "BASAL" to "BASAL STOP/CANCEL\nBASAL 0.3\nBASAL 0.3 20\nBASAL 30%\nBASAL 30% 20\n", @@ -184,8 +183,9 @@ class SmsCommunicatorPlugin @Inject constructor( @Suppress("SpellCheckingInspection") override fun doWork(): Result { val bundle = dataWorker.pickupBundle(inputData.getLong(DataWorker.STORE_KEY, -1)) - ?: return Result.failure() - val format = bundle.getString("format") ?: return Result.failure() + ?: return Result.failure(workDataOf("Error" to "missing input data")) + val format = bundle.getString("format") + ?: return Result.failure(workDataOf("Error" to "missing format in input data")) val pdus = bundle["pdus"] as Array<*> for (pdu in pdus) { val message = SmsMessage.createFromPdu(pdu as ByteArray, format) @@ -248,61 +248,58 @@ class SmsCommunicatorPlugin @Inject constructor( if (divided.isNotEmpty() && isCommand(divided[0].toUpperCase(Locale.getDefault()), receivedSms.phoneNumber)) { when (divided[0].toUpperCase(Locale.getDefault())) { - "BG" -> + "BG" -> if (divided.size == 1) processBG(receivedSms) else sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.wrongformat))) - "LOOP" -> + "LOOP" -> if (!remoteCommandsAllowed) sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.smscommunicator_remotecommandnotallowed))) else if (divided.size == 2 || divided.size == 3) processLOOP(divided, receivedSms) else sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.wrongformat))) - "TREATMENTS" -> - if (divided.size == 2) processTREATMENTS(divided, receivedSms) - else sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.wrongformat))) - "NSCLIENT" -> + "NSCLIENT" -> if (divided.size == 2) processNSCLIENT(divided, receivedSms) else sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.wrongformat))) - "PUMP" -> + "PUMP" -> if (!remoteCommandsAllowed && divided.size > 1) sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.smscommunicator_remotecommandnotallowed))) else if (divided.size <= 3) processPUMP(divided, receivedSms) else sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.wrongformat))) - "PROFILE" -> + "PROFILE" -> if (!remoteCommandsAllowed) sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.smscommunicator_remotecommandnotallowed))) else if (divided.size == 2 || divided.size == 3) processPROFILE(divided, receivedSms) else sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.wrongformat))) - "BASAL" -> + "BASAL" -> if (!remoteCommandsAllowed) sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.smscommunicator_remotecommandnotallowed))) else if (divided.size == 2 || divided.size == 3) processBASAL(divided, receivedSms) else sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.wrongformat))) - "EXTENDED" -> + "EXTENDED" -> if (!remoteCommandsAllowed) sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.smscommunicator_remotecommandnotallowed))) else if (divided.size == 2 || divided.size == 3) processEXTENDED(divided, receivedSms) else sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.wrongformat))) - "BOLUS" -> + "BOLUS" -> if (!remoteCommandsAllowed) sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.smscommunicator_remotecommandnotallowed))) - else if (divided.size == 2 && DateUtil.now() - lastRemoteBolusTime < minDistance) sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.smscommunicator_remotebolusnotallowed))) + else if (divided.size == 2 && dateUtil.now() - lastRemoteBolusTime < minDistance) sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.smscommunicator_remotebolusnotallowed))) else if (divided.size == 2 && pump.isSuspended()) sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.pumpsuspended))) else if (divided.size == 2 || divided.size == 3) processBOLUS(divided, receivedSms) else sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.wrongformat))) - "CARBS" -> + "CARBS" -> if (!remoteCommandsAllowed) sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.smscommunicator_remotecommandnotallowed))) else if (divided.size == 2 || divided.size == 3) processCARBS(divided, receivedSms) else sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.wrongformat))) - "CAL" -> + "CAL" -> if (!remoteCommandsAllowed) sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.smscommunicator_remotecommandnotallowed))) else if (divided.size == 2) processCAL(divided, receivedSms) else sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.wrongformat))) - "TARGET" -> + "TARGET" -> if (!remoteCommandsAllowed) sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.smscommunicator_remotecommandnotallowed))) else if (divided.size == 2) processTARGET(divided, receivedSms) else sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.wrongformat))) - "SMS" -> + "SMS" -> if (!remoteCommandsAllowed) sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.smscommunicator_remotecommandnotallowed))) else if (divided.size == 2) processSMS(divided, receivedSms) else sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.wrongformat))) - "HELP" -> + "HELP" -> if (divided.size == 1 || divided.size == 2) processHELP(divided, receivedSms) else sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.wrongformat))) - else -> + else -> if (messageToConfirm?.requester?.phoneNumber == receivedSms.phoneNumber) { messageToConfirm?.action(divided[0]) messageToConfirm = null @@ -313,24 +310,22 @@ class SmsCommunicatorPlugin @Inject constructor( } private fun processBG(receivedSms: Sms) { - val actualBG = iobCobCalculatorPlugin.actualBg() - val lastBG = iobCobCalculatorPlugin.lastBg() + val actualBG = iobCobCalculator.ads.actualBg() + val lastBG = iobCobCalculator.ads.lastBg() var reply = "" val units = profileFunction.getUnits() if (actualBG != null) { reply = resourceHelper.gs(R.string.sms_actualbg) + " " + actualBG.valueToUnitsString(units) + ", " } else if (lastBG != null) { - val agoMilliseconds = System.currentTimeMillis() - lastBG.timestamp + val agoMilliseconds = dateUtil.now() - lastBG.timestamp val agoMin = (agoMilliseconds / 60.0 / 1000.0).toInt() reply = resourceHelper.gs(R.string.sms_lastbg) + " " + lastBG.valueToUnitsString(units) + " " + String.format(resourceHelper.gs(R.string.sms_minago), agoMin) + ", " } val glucoseStatus = glucoseStatusProvider.glucoseStatusData if (glucoseStatus != null) reply += resourceHelper.gs(R.string.sms_delta) + " " + Profile.toUnitsString(glucoseStatus.delta, glucoseStatus.delta * Constants.MGDL_TO_MMOLL, units) + " " + units + ", " - activePlugin.activeTreatments.updateTotalIOBTreatments() - val bolusIob = activePlugin.activeTreatments.lastCalculationTreatments.round() - activePlugin.activeTreatments.updateTotalIOBTempBasals() - val basalIob = activePlugin.activeTreatments.lastCalculationTempBasals.round() - val cobInfo = iobCobCalculatorPlugin.getCobInfo(false, "SMS COB") + val bolusIob = iobCobCalculator.calculateIobFromBolus().round() + val basalIob = iobCobCalculator.calculateIobFromTempBasalsIncludingConvertedExtended().round() + val cobInfo = iobCobCalculator.getCobInfo(false, "SMS COB") reply += (resourceHelper.gs(R.string.sms_iob) + " " + DecimalFormatter.to2Decimal(bolusIob.iob + basalIob.basaliob) + "U (" + resourceHelper.gs(R.string.sms_bolus) + " " + DecimalFormatter.to2Decimal(bolusIob.iob) + "U " + resourceHelper.gs(R.string.sms_basal) + " " + DecimalFormatter.to2Decimal(basalIob.basaliob) + "U), " @@ -348,7 +343,7 @@ class SmsCommunicatorPlugin @Inject constructor( receivedSms.processed = true messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction() { override fun run() { - uel.log(Action.SMS_LOOP_DISABLED) + uel.log(Action.LOOP_DISABLED, Sources.SMS) loopPlugin.setPluginEnabled(PluginType.LOOP, false) commandQueue.cancelTempBasal(true, object : Callback() { override fun run() { @@ -372,7 +367,7 @@ class SmsCommunicatorPlugin @Inject constructor( receivedSms.processed = true messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction() { override fun run() { - uel.log(Action.SMS_LOOP_ENABLED) + uel.log(Action.LOOP_ENABLED, Sources.SMS) loopPlugin.setPluginEnabled(PluginType.LOOP, true) sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.smscommunicator_loophasbeenenabled))) rxBus.send(EventRefreshOverview("SMS_LOOP_START")) @@ -399,7 +394,7 @@ class SmsCommunicatorPlugin @Inject constructor( receivedSms.processed = true messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction() { override fun run() { - uel.log(Action.SMS_LOOP_RESUME) + uel.log(Action.RESUME, Sources.SMS) loopPlugin.suspendTo(0L) rxBus.send(EventRefreshOverview("SMS_LOOP_RESUME")) commandQueue.cancelTempBasal(true, object : Callback() { @@ -432,11 +427,11 @@ class SmsCommunicatorPlugin @Inject constructor( receivedSms.processed = true messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(duration) { override fun run() { - uel.log(Action.SMS_LOOP_SUSPEND) + uel.log(Action.SUSPEND, Sources.SMS) commandQueue.cancelTempBasal(true, object : Callback() { override fun run() { if (result.success) { - loopPlugin.suspendTo(System.currentTimeMillis() + anInteger() * 60L * 1000) + loopPlugin.suspendTo(dateUtil.now() + anInteger() * 60L * 1000) loopPlugin.createOfflineEvent(anInteger() * 60) rxBus.send(EventRefreshOverview("SMS_LOOP_SUSPENDED")) val replyText = resourceHelper.gs(R.string.smscommunicator_loopsuspended) + " " + @@ -458,16 +453,6 @@ class SmsCommunicatorPlugin @Inject constructor( } } - private fun processTREATMENTS(divided: Array, receivedSms: Sms) { - if (divided[1].toUpperCase(Locale.getDefault()) == "REFRESH") { - activePlugin.activeTreatments.service.resetTreatments() - rxBus.send(EventNSClientRestart()) - sendSMS(Sms(receivedSms.phoneNumber, "TREATMENTS REFRESH SENT")) - receivedSms.processed = true - } else - sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.wrongformat))) - } - private fun processNSCLIENT(divided: Array, receivedSms: Sms) { if (divided[1].toUpperCase(Locale.getDefault()) == "RESTART") { rxBus.send(EventNSClientRestart()) @@ -516,7 +501,7 @@ class SmsCommunicatorPlugin @Inject constructor( receivedSms.processed = true messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction() { override fun run() { - uel.log(Action.SMS_PUMP_CONNECT) + uel.log(Action.RECONNECT, Sources.SMS) commandQueue.cancelTempBasal(true, object : Callback() { override fun run() { if (!result.success) { @@ -545,7 +530,7 @@ class SmsCommunicatorPlugin @Inject constructor( receivedSms.processed = true messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction() { override fun run() { - uel.log(Action.SMS_PUMP_DISCONNECT) + uel.log(Action.DISCONNECT, Sources.SMS) val profile = profileFunction.getProfile() loopPlugin.disconnectPump(duration, profile) rxBus.send(EventRefreshOverview("SMS_PUMP_DISCONNECT")) @@ -560,7 +545,7 @@ class SmsCommunicatorPlugin @Inject constructor( } private fun processPROFILE(divided: Array, receivedSms: Sms) { // load profiles - val anInterface = activePlugin.activeProfileInterface + val anInterface = activePlugin.activeProfileSource val store = anInterface.profile if (store == null) { sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.notconfigured))) @@ -599,10 +584,11 @@ class SmsCommunicatorPlugin @Inject constructor( val finalPercentage = percentage messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(list[pIndex - 1] as String, finalPercentage) { override fun run() { - activePlugin.activeTreatments.doProfileSwitch(store, list[pIndex - 1] as String, 0, finalPercentage, 0, DateUtil.now()) + profileFunction.createProfileSwitch(store, list[pIndex - 1] as String, 0, finalPercentage, 0, dateUtil.now()) val replyText = resourceHelper.gs(R.string.profileswitchcreated) sendSMS(Sms(receivedSms.phoneNumber, replyText)) - uel.log(Action.SMS_PROFILE, ValueWithUnit(R.string.profileswitchcreated, Units.R_String)) + uel.log(Action.PROFILE_SWITCH, Sources.SMS, resourceHelper.gs(R.string.profileswitchcreated), + ValueWithUnit.SimpleString(resourceHelper.gsNotLocalised(R.string.profileswitchcreated))) } }) } @@ -624,12 +610,14 @@ class SmsCommunicatorPlugin @Inject constructor( var replyText = resourceHelper.gs(R.string.smscommunicator_tempbasalcanceled) replyText += "\n" + activePlugin.activePump.shortStatus(true) sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText)) - uel.log(Action.SMS_BASAL, activePlugin.activePump.shortStatus(true), ValueWithUnit(R.string.smscommunicator_tempbasalcanceled, Units.R_String)) + uel.log(Action.TEMP_BASAL, Sources.SMS, activePlugin.activePump.shortStatus(true) + "\n" + resourceHelper.gs(R.string.smscommunicator_tempbasalcanceled), + ValueWithUnit.SimpleString(resourceHelper.gsNotLocalised(R.string.smscommunicator_tempbasalcanceled))) } else { var replyText = resourceHelper.gs(R.string.smscommunicator_tempbasalcancelfailed) replyText += "\n" + activePlugin.activePump.shortStatus(true) sendSMS(Sms(receivedSms.phoneNumber, replyText)) - uel.log(Action.SMS_BASAL, activePlugin.activePump.shortStatus(true), ValueWithUnit(R.string.smscommunicator_tempbasalcancelfailed, Units.R_String)) + uel.log(Action.TEMP_BASAL, Sources.SMS, activePlugin.activePump.shortStatus(true) + "\n" + resourceHelper.gs(R.string.smscommunicator_tempbasalcancelfailed), + ValueWithUnit.SimpleString(resourceHelper.gsNotLocalised(R.string.smscommunicator_tempbasalcancelfailed))) } } }) @@ -637,7 +625,7 @@ class SmsCommunicatorPlugin @Inject constructor( }) } else if (divided[1].endsWith("%")) { var tempBasalPct = SafeParse.stringToInt(StringUtils.removeEnd(divided[1], "%")) - val durationStep = activePlugin.activePump.model().tbrSettings.durationStep + val durationStep = activePlugin.activePump.model().tbrSettings?.durationStep ?: 60 var duration = 30 if (divided.size > 2) duration = SafeParse.stringToInt(divided[2]) val profile = profileFunction.getProfile() @@ -651,21 +639,28 @@ class SmsCommunicatorPlugin @Inject constructor( receivedSms.processed = true messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(tempBasalPct, duration) { override fun run() { - commandQueue.tempBasalPercent(anInteger(), secondInteger(), true, profile, object : Callback() { + commandQueue.tempBasalPercent(anInteger(), secondInteger(), true, profile, PumpSync.TemporaryBasalType.NORMAL, object : Callback() { override fun run() { if (result.success) { var replyText = if (result.isPercent) String.format(resourceHelper.gs(R.string.smscommunicator_tempbasalset_percent), result.percent, result.duration) else String.format(resourceHelper.gs(R.string.smscommunicator_tempbasalset), result.absolute, result.duration) replyText += "\n" + activePlugin.activePump.shortStatus(true) sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText)) if (result.isPercent) - uel.log(Action.SMS_BASAL, activePlugin.activePump.shortStatus(true), ValueWithUnit(R.string.smscommunicator_tempbasalset_percent, 2), ValueWithUnit(result.percent, Units.Percent), ValueWithUnit(result.duration, Units.M)) + uel.log(Action.TEMP_BASAL, Sources.SMS, + activePlugin.activePump.shortStatus(true) + "\n" + resourceHelper.gs(R.string.smscommunicator_tempbasalset_percent, result.percent, result.duration), + ValueWithUnit.Percent(result.percent), + ValueWithUnit.Minute(result.duration)) else - uel.log(Action.SMS_BASAL, activePlugin.activePump.shortStatus(true), ValueWithUnit(R.string.smscommunicator_tempbasalset, 2), ValueWithUnit(result.absolute, Units.U_H), ValueWithUnit(result.duration, Units.M)) + uel.log(Action.TEMP_BASAL, Sources.SMS, + activePlugin.activePump.shortStatus(true) + "\n" + resourceHelper.gs(R.string.smscommunicator_tempbasalset, result.absolute, result.duration), + ValueWithUnit.UnitPerHour(result.absolute), + ValueWithUnit.Minute(result.duration)) } else { var replyText = resourceHelper.gs(R.string.smscommunicator_tempbasalfailed) replyText += "\n" + activePlugin.activePump.shortStatus(true) sendSMS(Sms(receivedSms.phoneNumber, replyText)) - uel.log(Action.SMS_BASAL, activePlugin.activePump.shortStatus(true), ValueWithUnit(R.string.smscommunicator_tempbasalfailed, Units.R_String)) + uel.log(Action.TEMP_BASAL, Sources.SMS, activePlugin.activePump.shortStatus(true) + "\n" + resourceHelper.gs(R.string.smscommunicator_tempbasalfailed), + ValueWithUnit.SimpleString(resourceHelper.gsNotLocalised(R.string.smscommunicator_tempbasalfailed))) } } }) @@ -674,7 +669,7 @@ class SmsCommunicatorPlugin @Inject constructor( } } else { var tempBasal = SafeParse.stringToDouble(divided[1]) - val durationStep = activePlugin.activePump.model().tbrSettings.durationStep + val durationStep = activePlugin.activePump.model().tbrSettings?.durationStep ?: 60 var duration = 30 if (divided.size > 2) duration = SafeParse.stringToInt(divided[2]) val profile = profileFunction.getProfile() @@ -688,7 +683,7 @@ class SmsCommunicatorPlugin @Inject constructor( receivedSms.processed = true messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(tempBasal, duration) { override fun run() { - commandQueue.tempBasalAbsolute(aDouble(), secondInteger(), true, profile, object : Callback() { + commandQueue.tempBasalAbsolute(aDouble(), secondInteger(), true, profile, PumpSync.TemporaryBasalType.NORMAL, object : Callback() { override fun run() { if (result.success) { var replyText = if (result.isPercent) String.format(resourceHelper.gs(R.string.smscommunicator_tempbasalset_percent), result.percent, result.duration) @@ -696,14 +691,19 @@ class SmsCommunicatorPlugin @Inject constructor( replyText += "\n" + activePlugin.activePump.shortStatus(true) sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText)) if (result.isPercent) - uel.log(Action.SMS_BASAL, activePlugin.activePump.shortStatus(true), ValueWithUnit(R.string.smscommunicator_tempbasalset_percent, 2), ValueWithUnit(result.percent, Units.Percent), ValueWithUnit(result.duration, Units.M)) + uel.log(Action.TEMP_BASAL, Sources.SMS, activePlugin.activePump.shortStatus(true) + "\n" + resourceHelper.gs(R.string.smscommunicator_tempbasalset_percent, result.percent, result.duration), + ValueWithUnit.Percent(result.percent), + ValueWithUnit.Minute(result.duration)) else - uel.log(Action.SMS_BASAL, activePlugin.activePump.shortStatus(true), ValueWithUnit(R.string.smscommunicator_tempbasalset, 2), ValueWithUnit(result.absolute, Units.U_H), ValueWithUnit(result.duration, Units.M)) + uel.log(Action.TEMP_BASAL, Sources.SMS, activePlugin.activePump.shortStatus(true) + "\n" + resourceHelper.gs(R.string.smscommunicator_tempbasalset, result.absolute, result.duration), + ValueWithUnit.UnitPerHour(result.absolute), + ValueWithUnit.Minute(result.duration)) } else { var replyText = resourceHelper.gs(R.string.smscommunicator_tempbasalfailed) replyText += "\n" + activePlugin.activePump.shortStatus(true) sendSMS(Sms(receivedSms.phoneNumber, replyText)) - uel.log(Action.SMS_BASAL, activePlugin.activePump.shortStatus(true), ValueWithUnit(R.string.smscommunicator_tempbasalfailed, Units.R_String)) + uel.log(Action.TEMP_BASAL, Sources.SMS, activePlugin.activePump.shortStatus(true) + "\n" + resourceHelper.gs(R.string.smscommunicator_tempbasalfailed), + ValueWithUnit.SimpleString(resourceHelper.gsNotLocalised(R.string.smscommunicator_tempbasalfailed))) } } }) @@ -730,7 +730,8 @@ class SmsCommunicatorPlugin @Inject constructor( var replyText = resourceHelper.gs(R.string.smscommunicator_extendedcancelfailed) replyText += "\n" + activePlugin.activePump.shortStatus(true) sendSMS(Sms(receivedSms.phoneNumber, replyText)) - uel.log(Action.SMS_EXTENDED_BOLUS, activePlugin.activePump.shortStatus(true), ValueWithUnit(R.string.smscommunicator_extendedcanceled, Units.R_String)) + uel.log(Action.EXTENDED_BOLUS, Sources.SMS, activePlugin.activePump.shortStatus(true) + "\n" + resourceHelper.gs(R.string.smscommunicator_extendedcanceled), + ValueWithUnit.SimpleString(resourceHelper.gsNotLocalised(R.string.smscommunicator_extendedcanceled))) } } }) @@ -757,16 +758,20 @@ class SmsCommunicatorPlugin @Inject constructor( replyText += "\n" + activePlugin.activePump.shortStatus(true) sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText)) if (config.APS) - uel.log(Action.SMS_EXTENDED_BOLUS, activePlugin.activePump.shortStatus(true), ValueWithUnit(R.string.smscommunicator_extendedset, 2), ValueWithUnit(aDouble - ?: 0.0, Units.U), ValueWithUnit(duration, Units.M), ValueWithUnit(R.string.loopsuspended, Units.R_String)) + uel.log(Action.EXTENDED_BOLUS, Sources.SMS, activePlugin.activePump.shortStatus(true) + "\n" + resourceHelper.gs(R.string.smscommunicator_extendedset, aDouble, duration) + " / " + resourceHelper.gs(R.string.loopsuspended), + ValueWithUnit.Insulin(aDouble ?: 0.0), + ValueWithUnit.Minute(duration), + ValueWithUnit.SimpleString(resourceHelper.gsNotLocalised(R.string.loopsuspended))) else - uel.log(Action.SMS_EXTENDED_BOLUS, activePlugin.activePump.shortStatus(true), ValueWithUnit(R.string.smscommunicator_extendedset, 2), ValueWithUnit(aDouble - ?: 0.0, Units.U), ValueWithUnit(duration, Units.M)) + uel.log(Action.EXTENDED_BOLUS, Sources.SMS, activePlugin.activePump.shortStatus(true) + "\n" + resourceHelper.gs(R.string.smscommunicator_extendedset, aDouble, duration), + ValueWithUnit.Insulin(aDouble ?: 0.0), + ValueWithUnit.Minute(duration)) } else { var replyText = resourceHelper.gs(R.string.smscommunicator_extendedfailed) replyText += "\n" + activePlugin.activePump.shortStatus(true) sendSMS(Sms(receivedSms.phoneNumber, replyText)) - uel.log(Action.SMS_EXTENDED_BOLUS, activePlugin.activePump.shortStatus(true), ValueWithUnit(R.string.smscommunicator_extendedfailed, Units.R_String)) + uel.log(Action.EXTENDED_BOLUS, Sources.SMS, activePlugin.activePump.shortStatus(true) + "\n" + resourceHelper.gs(R.string.smscommunicator_extendedfailed), + ValueWithUnit.SimpleString(resourceHelper.gsNotLocalised(R.string.smscommunicator_extendedfailed))) } } }) @@ -793,7 +798,6 @@ class SmsCommunicatorPlugin @Inject constructor( override fun run() { val detailedBolusInfo = DetailedBolusInfo() detailedBolusInfo.insulin = aDouble() - detailedBolusInfo.source = Source.USER commandQueue.bolus(detailedBolusInfo, object : Callback() { override fun run() { val resultSuccess = result.success @@ -806,45 +810,46 @@ class SmsCommunicatorPlugin @Inject constructor( else String.format(resourceHelper.gs(R.string.smscommunicator_bolusdelivered), resultBolusDelivered) replyText += "\n" + activePlugin.activePump.shortStatus(true) - lastRemoteBolusTime = DateUtil.now() + lastRemoteBolusTime = dateUtil.now() if (isMeal) { profileFunction.getProfile()?.let { currentProfile -> var eatingSoonTTDuration = sp.getInt(R.string.key_eatingsoon_duration, Constants.defaultEatingSoonTTDuration) eatingSoonTTDuration = if (eatingSoonTTDuration > 0) eatingSoonTTDuration else Constants.defaultEatingSoonTTDuration - var eatingSoonTT = sp.getDouble(R.string.key_eatingsoon_target, if (currentProfile.units == Constants.MMOL) Constants.defaultEatingSoonTTmmol else Constants.defaultEatingSoonTTmgdl) + var eatingSoonTT = sp.getDouble(R.string.key_eatingsoon_target, if (currentProfile.units == GlucoseUnit.MMOL) Constants.defaultEatingSoonTTmmol else Constants.defaultEatingSoonTTmgdl) eatingSoonTT = when { - eatingSoonTT > 0 -> eatingSoonTT - currentProfile.units == Constants.MMOL -> Constants.defaultEatingSoonTTmmol - else -> Constants.defaultEatingSoonTTmgdl + eatingSoonTT > 0 -> eatingSoonTT + currentProfile.units == GlucoseUnit.MMOL -> Constants.defaultEatingSoonTTmmol + else -> Constants.defaultEatingSoonTTmgdl } disposable += repository.runTransactionForResult(InsertTemporaryTargetAndCancelCurrentTransaction( - timestamp = System.currentTimeMillis(), + timestamp = dateUtil.now(), duration = TimeUnit.MINUTES.toMillis(eatingSoonTTDuration.toLong()), reason = TemporaryTarget.Reason.EATING_SOON, lowTarget = Profile.toMgdl(eatingSoonTT, profileFunction.getUnits()), highTarget = Profile.toMgdl(eatingSoonTT, profileFunction.getUnits()) )).subscribe({ result -> - result.inserted.forEach { nsUpload.uploadTempTarget(it) } - result.updated.forEach { nsUpload.updateTempTarget(it) } + result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted temp target $it") } + result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated temp target $it") } }, { - aapsLogger.error(LTag.BGSOURCE, "Error while saving temporary target", it) + aapsLogger.error(LTag.DATABASE, "Error while saving temporary target", it) }) - val tt = if (currentProfile.units == Constants.MMOL) { + val tt = if (currentProfile.units == GlucoseUnit.MMOL) { DecimalFormatter.to1Decimal(eatingSoonTT) } else DecimalFormatter.to0Decimal(eatingSoonTT) replyText += "\n" + String.format(resourceHelper.gs(R.string.smscommunicator_mealbolusdelivered_tt), tt, eatingSoonTTDuration) } } sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText)) - uel.log(Action.SMS_BOLUS, replyText) + uel.log(Action.BOLUS, Sources.SMS, replyText) } else { var replyText = resourceHelper.gs(R.string.smscommunicator_bolusfailed) replyText += "\n" + activePlugin.activePump.shortStatus(true) sendSMS(Sms(receivedSms.phoneNumber, replyText)) - uel.log(Action.SMS_BOLUS, activePlugin.activePump.shortStatus(true), ValueWithUnit(R.string.smscommunicator_bolusfailed, Units.R_String)) + uel.log(Action.BOLUS, Sources.SMS, activePlugin.activePump.shortStatus(true) + "\n" + resourceHelper.gs(R.string.smscommunicator_bolusfailed), + ValueWithUnit.SimpleString(resourceHelper.gsNotLocalised(R.string.smscommunicator_bolusfailed))) } } }) @@ -855,11 +860,30 @@ class SmsCommunicatorPlugin @Inject constructor( } else sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.wrongformat))) } + private fun toTodayTime(hh_colon_mm: String): Long { + val p = Pattern.compile("(\\d+):(\\d+)( a.m.| p.m.| AM| PM|AM|PM|)") + val m = p.matcher(hh_colon_mm) + var retVal: Long = 0 + if (m.find()) { + var hours = SafeParse.stringToInt(m.group(1)) + val minutes = SafeParse.stringToInt(m.group(2)) + if ((m.group(3) == " a.m." || m.group(3) == " AM" || m.group(3) == "AM") && m.group(1) == "12") hours -= 12 + if ((m.group(3) == " p.m." || m.group(3) == " PM" || m.group(3) == "PM") && m.group(1) != "12") hours += 12 + val t = DateTime() + .withHourOfDay(hours) + .withMinuteOfHour(minutes) + .withSecondOfMinute(0) + .withMillisOfSecond(0) + retVal = t.millis + } + return retVal + } + private fun processCARBS(divided: Array, receivedSms: Sms) { var grams = SafeParse.stringToInt(divided[1]) - var time = DateUtil.now() + var time = dateUtil.now() if (divided.size > 2) { - time = DateUtil.toTodayTime(divided[2].toUpperCase(Locale.getDefault())) + time = toTodayTime(divided[2].toUpperCase(Locale.getDefault())) if (time == 0L) { sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.wrongformat))) return @@ -875,34 +899,24 @@ class SmsCommunicatorPlugin @Inject constructor( override fun run() { val detailedBolusInfo = DetailedBolusInfo() detailedBolusInfo.carbs = anInteger().toDouble() - detailedBolusInfo.source = Source.USER - detailedBolusInfo.date = secondLong() - if (activePlugin.activePump.pumpDescription.storesCarbInfo) { - commandQueue.bolus(detailedBolusInfo, object : Callback() { - override fun run() { - if (result.success) { - var replyText = String.format(resourceHelper.gs(R.string.smscommunicator_carbsset), anInteger) - replyText += "\n" + activePlugin.activePump.shortStatus(true) - sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText)) - uel.log(Action.SMS_CARBS, activePlugin.activePump.shortStatus(true), ValueWithUnit(R.string.smscommunicator_carbsset, 1), ValueWithUnit(anInteger - ?: 0, Units.G)) - } else { - var replyText = resourceHelper.gs(R.string.smscommunicator_carbsfailed, anInteger) - replyText += "\n" + activePlugin.activePump.shortStatus(true) - sendSMS(Sms(receivedSms.phoneNumber, replyText)) - uel.log(Action.SMS_CARBS, activePlugin.activePump.shortStatus(true), ValueWithUnit(R.string.smscommunicator_carbsfailed, 1), ValueWithUnit(anInteger - ?: 0, Units.G)) - } + detailedBolusInfo.timestamp = secondLong() + commandQueue.bolus(detailedBolusInfo, object : Callback() { + override fun run() { + if (result.success) { + var replyText = String.format(resourceHelper.gs(R.string.smscommunicator_carbsset), anInteger) + replyText += "\n" + activePlugin.activePump.shortStatus(true) + sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText)) + uel.log(Action.CARBS, Sources.SMS, activePlugin.activePump.shortStatus(true) + ": " + resourceHelper.gs(R.string.smscommunicator_carbsset, anInteger), + ValueWithUnit.Gram(anInteger ?: 0)) + } else { + var replyText = resourceHelper.gs(R.string.smscommunicator_carbsfailed, anInteger) + replyText += "\n" + activePlugin.activePump.shortStatus(true) + sendSMS(Sms(receivedSms.phoneNumber, replyText)) + uel.log(Action.CARBS, Sources.SMS, activePlugin.activePump.shortStatus(true) + ": " + resourceHelper.gs(R.string.smscommunicator_carbsfailed, anInteger), + ValueWithUnit.Gram(anInteger ?: 0)) } - }) - } else { - activePlugin.activeTreatments.addToHistoryTreatment(detailedBolusInfo, true) - var replyText = String.format(resourceHelper.gs(R.string.smscommunicator_carbsset), anInteger) - replyText += "\n" + activePlugin.activePump.shortStatus(true) - sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText)) - uel.log(Action.SMS_CARBS, activePlugin.activePump.shortStatus(true), ValueWithUnit(R.string.smscommunicator_carbsset, 1), ValueWithUnit(anInteger - ?: 0, Units.G)) - } + } + }) } }) } @@ -952,26 +966,27 @@ class SmsCommunicatorPlugin @Inject constructor( } var ttDuration = sp.getInt(keyDuration, defaultTargetDuration) ttDuration = if (ttDuration > 0) ttDuration else defaultTargetDuration - var tt = sp.getDouble(keyTarget, if (units == Constants.MMOL) defaultTargetMMOL else defaultTargetMGDL) + var tt = sp.getDouble(keyTarget, if (units == GlucoseUnit.MMOL) defaultTargetMMOL else defaultTargetMGDL) tt = Profile.toCurrentUnits(profileFunction, tt) - tt = if (tt > 0) tt else if (units == Constants.MMOL) defaultTargetMMOL else defaultTargetMGDL + tt = if (tt > 0) tt else if (units == GlucoseUnit.MMOL) defaultTargetMMOL else defaultTargetMGDL disposable += repository.runTransactionForResult(InsertTemporaryTargetAndCancelCurrentTransaction( - timestamp = System.currentTimeMillis(), + timestamp = dateUtil.now(), duration = TimeUnit.MINUTES.toMillis(ttDuration.toLong()), reason = TemporaryTarget.Reason.EATING_SOON, lowTarget = Profile.toMgdl(tt, profileFunction.getUnits()), highTarget = Profile.toMgdl(tt, profileFunction.getUnits()) )).subscribe({ result -> - result.inserted.forEach { nsUpload.uploadTempTarget(it) } - result.updated.forEach { nsUpload.updateTempTarget(it) } + result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted temp target $it") } + result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated temp target $it") } }, { - aapsLogger.error(LTag.BGSOURCE, "Error while saving temporary target", it) + aapsLogger.error(LTag.DATABASE, "Error while saving temporary target", it) }) - val ttString = if (units == Constants.MMOL) DecimalFormatter.to1Decimal(tt) else DecimalFormatter.to0Decimal(tt) + val ttString = if (units == GlucoseUnit.MMOL) DecimalFormatter.to1Decimal(tt) else DecimalFormatter.to0Decimal(tt) val replyText = String.format(resourceHelper.gs(R.string.smscommunicator_tt_set), ttString, ttDuration) sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText)) - //uel.log(Action.SMS_TT, ValueWithUnit(R.string.smscommunicator_tt_set, 2), ValueWithUnit(tt, units), ValueWithUnit(ttDuration, Units.M)) - uel.log(Action.SMS_TT, ValueWithUnit(tt, units), ValueWithUnit(ttDuration, Units.M)) + uel.log(Action.TT, Sources.SMS, + ValueWithUnit.fromGlucoseUnit(tt, units.asText), + ValueWithUnit.Minute(ttDuration)) } }) } else if (isStop) { @@ -980,15 +995,16 @@ class SmsCommunicatorPlugin @Inject constructor( receivedSms.processed = true messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction() { override fun run() { - disposable += repository.runTransactionForResult(CancelCurrentTemporaryTargetIfAnyTransaction(dateUtil._now())) + disposable += repository.runTransactionForResult(CancelCurrentTemporaryTargetIfAnyTransaction(dateUtil.now())) .subscribe({ result -> - result.updated.forEach { nsUpload.updateTempTarget(it) } + result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated temp target $it") } }, { - aapsLogger.error(LTag.BGSOURCE, "Error while saving temporary target", it) + aapsLogger.error(LTag.DATABASE, "Error while saving temporary target", it) }) val replyText = String.format(resourceHelper.gs(R.string.smscommunicator_tt_canceled)) sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText)) - uel.log(Action.SMS_TT, ValueWithUnit(R.string.smscommunicator_tt_canceled, Units.R_String)) + uel.log(Action.CANCEL_TT, Sources.SMS, resourceHelper.gs(R.string.smscommunicator_tt_canceled), + ValueWithUnit.SimpleString(resourceHelper.gsNotLocalised(R.string.smscommunicator_tt_canceled))) } }) } else @@ -1007,7 +1023,8 @@ class SmsCommunicatorPlugin @Inject constructor( sp.putBoolean(R.string.key_smscommunicator_remotecommandsallowed, false) val replyText = String.format(resourceHelper.gs(R.string.smscommunicator_stoppedsms)) sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText)) - uel.log(Action.SMS_SMS, ValueWithUnit(R.string.smscommunicator_stoppedsms, Units.R_String)) + uel.log(Action.STOP_SMS, Sources.SMS, resourceHelper.gs(R.string.smscommunicator_stoppedsms), + ValueWithUnit.SimpleString(resourceHelper.gsNotLocalised(R.string.smscommunicator_stoppedsms))) } }) } else sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.wrongformat))) @@ -1026,9 +1043,11 @@ class SmsCommunicatorPlugin @Inject constructor( if (result) resourceHelper.gs(R.string.smscommunicator_calibrationsent) else resourceHelper.gs(R.string.smscommunicator_calibrationfailed) sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText)) if (result) - uel.log(Action.SMS_CAL, ValueWithUnit(R.string.smscommunicator_calibrationsent, Units.R_String)) + uel.log(Action.CALIBRATION, Sources.SMS, resourceHelper.gs(R.string.smscommunicator_calibrationsent), + ValueWithUnit.SimpleString(resourceHelper.gsNotLocalised(R.string.smscommunicator_calibrationsent))) else - uel.log(Action.SMS_CAL, ValueWithUnit(R.string.smscommunicator_calibrationfailed, Units.R_String)) + uel.log(Action.CALIBRATION, Sources.SMS, resourceHelper.gs(R.string.smscommunicator_calibrationfailed), + ValueWithUnit.SimpleString(resourceHelper.gsNotLocalised(R.string.smscommunicator_calibrationfailed))) } }) } else sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.wrongformat))) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/activities/SmsCommunicatorOtpActivity.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/activities/SmsCommunicatorOtpActivity.kt index 848dc05faf..bc81bc1f7d 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/activities/SmsCommunicatorOtpActivity.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/activities/SmsCommunicatorOtpActivity.kt @@ -14,7 +14,8 @@ import com.google.common.primitives.Ints.min import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel import info.nightscout.androidaps.R import info.nightscout.androidaps.activities.NoSplashAppCompatActivity -import info.nightscout.androidaps.database.entities.UserEntry.* +import info.nightscout.androidaps.database.entities.UserEntry.Action +import info.nightscout.androidaps.database.entities.UserEntry.Sources import info.nightscout.androidaps.databinding.ActivitySmscommunicatorOtpBinding import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.plugins.bus.RxBusWrapper @@ -73,7 +74,7 @@ class SmsCommunicatorOtpActivity : NoSplashAppCompatActivity() { resourceHelper.gs(R.string.smscommunicator_otp_reset_title), resourceHelper.gs(R.string.smscommunicator_otp_reset_prompt), Runnable { - uel.log(Action.OTP_RESET) + uel.log(Action.OTP_RESET, Sources.SMS) otp.ensureKey(true) updateGui() ToastUtils.Long.infoToast(this, resourceHelper.gs(R.string.smscommunicator_otp_reset_successful)) @@ -89,7 +90,7 @@ class SmsCommunicatorOtpActivity : NoSplashAppCompatActivity() { val clip = ClipData.newPlainText("OTP Secret", otp.provisioningSecret()) clipboard.primaryClip = clip ToastUtils.Long.infoToast(this, resourceHelper.gs(R.string.smscommunicator_otp_export_successful)) - uel.log(Action.OTP_EXPORT) + uel.log(Action.OTP_EXPORT, Sources.SMS) }) true diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/otp/OneTimePassword.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/otp/OneTimePassword.kt index 68018f8f9f..77470340a1 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/otp/OneTimePassword.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/otp/OneTimePassword.kt @@ -18,7 +18,8 @@ import javax.inject.Singleton @Singleton class OneTimePassword @Inject constructor( private val sp: SP, - private val resourceHelper: ResourceHelper + private val resourceHelper: ResourceHelper, + private val dateUtil: DateUtil ) { private var key: SecretKey? = null @@ -85,7 +86,7 @@ class OneTimePassword @Inject constructor( return OneTimePasswordValidationResult.ERROR_WRONG_PIN } - val counter: Long = DateUtil.now() / 30000L + val counter: Long = dateUtil.now() / 30000L val acceptableTokens: MutableList = mutableListOf(generateOneTimePassword(counter)) for (i in 0 until Constants.OTP_ACCEPT_OLD_TOKENS_COUNT) { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/comm/TidepoolUploader.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/comm/TidepoolUploader.kt index faaa1b40d6..c7bb146343 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/comm/TidepoolUploader.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/comm/TidepoolUploader.kt @@ -4,9 +4,8 @@ import android.content.Context import android.os.PowerManager import android.os.SystemClock import info.nightscout.androidaps.BuildConfig -import info.nightscout.androidaps.MainApp import info.nightscout.androidaps.R -import info.nightscout.androidaps.interfaces.ActivePluginProvider +import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.plugins.bus.RxBusWrapper @@ -17,8 +16,8 @@ import info.nightscout.androidaps.plugins.general.tidepool.messages.DatasetReply import info.nightscout.androidaps.plugins.general.tidepool.messages.OpenDatasetRequestMessage import info.nightscout.androidaps.plugins.general.tidepool.messages.UploadReplyMessage import info.nightscout.androidaps.utils.DateUtil -import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.T +import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.sharedPreferences.SP import okhttp3.MediaType.Companion.toMediaTypeOrNull @@ -34,17 +33,18 @@ import javax.inject.Singleton class TidepoolUploader @Inject constructor( private val aapsLogger: AAPSLogger, private val rxBus: RxBusWrapper, - private val mainApp: MainApp, + private val ctx: Context, private val resourceHelper: ResourceHelper, private val sp: SP, private val uploadChunk: UploadChunk, - private val activePlugin: ActivePluginProvider, + private val activePlugin: ActivePlugin, private val dateUtil: DateUtil ) { private var wl: PowerManager.WakeLock? = null companion object { + private const val INTEGRATION_BASE_URL = "https://int-api.tidepool.org" private const val PRODUCTION_BASE_URL = "https://api.tidepool.org" internal const val VERSION = "0.0.1" @@ -151,7 +151,7 @@ class TidepoolUploader @Inject constructor( if (session.datasetReply == null) { rxBus.send(EventTidepoolStatus(("Creating new dataset"))) val call = session.service.openDataSet(session.token!!, session.authReply!!.userid!!, - OpenDatasetRequestMessage(activePlugin.activePump.serialNumber()).getBody()) + OpenDatasetRequestMessage(activePlugin.activePump.serialNumber(), dateUtil).getBody()) call.enqueue(TidepoolCallback(aapsLogger, rxBus, session, "Open New Dataset", { connectionStatus = ConnectionStatus.CONNECTED rxBus.send(EventTidepoolStatus(("New dataset OK"))) @@ -232,7 +232,7 @@ class TidepoolUploader @Inject constructor( } private fun uploadNext() { - if (uploadChunk.getLastEnd() < DateUtil.now() - T.mins(1).msecs()) { + if (uploadChunk.getLastEnd() < dateUtil.now() - T.mins(1).msecs()) { SystemClock.sleep(3000) aapsLogger.debug(LTag.TIDEPOOL, "Restarting doUpload. Last: " + dateUtil.dateAndTimeString(uploadChunk.getLastEnd())) doUpload() @@ -285,7 +285,7 @@ class TidepoolUploader @Inject constructor( @Synchronized private fun extendWakeLock(ms: Long) { if (wl == null) { - val pm = mainApp.getSystemService(Context.POWER_SERVICE) as PowerManager + val pm = ctx.getSystemService(Context.POWER_SERVICE) as PowerManager wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "AndroidAPS:TidepoolUploader") wl?.acquire(ms) } else { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/comm/UploadChunk.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/comm/UploadChunk.kt index 74a7f29a5f..f48d07d617 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/comm/UploadChunk.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/comm/UploadChunk.kt @@ -1,12 +1,10 @@ package info.nightscout.androidaps.plugins.general.tidepool.comm import info.nightscout.androidaps.R -import info.nightscout.androidaps.data.Intervals import info.nightscout.androidaps.database.AppRepository -import info.nightscout.androidaps.db.ProfileSwitch -import info.nightscout.androidaps.db.TemporaryBasal -import info.nightscout.androidaps.interfaces.ActivePluginProvider -import info.nightscout.androidaps.interfaces.DatabaseHelperInterface +import info.nightscout.androidaps.database.entities.EffectiveProfileSwitch +import info.nightscout.androidaps.database.entities.TemporaryBasal +import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag @@ -14,7 +12,6 @@ import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.general.tidepool.elements.* import info.nightscout.androidaps.plugins.general.tidepool.events.EventTidepoolStatus import info.nightscout.androidaps.plugins.general.tidepool.utils.GsonInstance -import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.sharedPreferences.SP @@ -30,9 +27,7 @@ class UploadChunk @Inject constructor( private val rxBus: RxBusWrapper, private val aapsLogger: AAPSLogger, private val profileFunction: ProfileFunction, - private val treatmentsPlugin: TreatmentsPlugin, - private val activePlugin: ActivePluginProvider, - private val databaseHelper: DatabaseHelperInterface, + private val activePlugin: ActivePlugin, private val repository: AppRepository, private val dateUtil: DateUtil ) { @@ -44,7 +39,7 @@ class UploadChunk @Inject constructor( return null session.start = getLastEnd() - session.end = min(session.start + maxUploadSize, DateUtil.now()) + session.end = min(session.start + maxUploadSize, dateUtil.now()) val result = get(session.start, session.end) if (result.length < 3) { @@ -84,7 +79,7 @@ class UploadChunk @Inject constructor( fun getLastEnd(): Long { val result = sp.getLong(R.string.key_tidepool_last_end, 0) - return max(result, DateUtil.now() - T.months(2).msecs()) + return max(result, dateUtil.now() - T.months(2).msecs()) } fun setLastEnd(time: Long) { @@ -104,7 +99,7 @@ class UploadChunk @Inject constructor( // TODO we could make sure we include records older than the first bg record for completeness val start: Long = 0 - val end = DateUtil.now() + val end = dateUtil.now() val bgReadingList = repository.compatGetBgReadingsDataFromTime(start, end, true) .blockingGet() @@ -115,20 +110,22 @@ class UploadChunk @Inject constructor( private fun getTreatments(start: Long, end: Long): List { val result = LinkedList() - val treatments = treatmentsPlugin.service.getTreatmentDataFromTime(start, end, true) - for (treatment in treatments) { - if (treatment.carbs > 0) { - result.add(WizardElement(treatment)) - } else if (treatment.insulin > 0) { - result.add(BolusElement(treatment)) + repository.getBolusesDataFromTimeToTime(start, end, true) + .blockingGet() + .forEach { bolus -> + result.add(BolusElement(bolus, dateUtil)) + } + repository.getCarbsDataFromTimeToTimeExpanded(start, end, true) + .blockingGet() + .forEach { carb -> + result.add(WizardElement(carb, dateUtil)) } - } return result } private fun getBloodTests(start: Long, end: Long): List { val readings = repository.compatGetTherapyEventDataFromToTime(start, end).blockingGet() - val selection = BloodGlucoseElement.fromCareportalEvents(readings) + val selection = BloodGlucoseElement.fromCareportalEvents(readings, dateUtil) if (selection.isNotEmpty()) rxBus.send(EventTidepoolStatus("${selection.size} BGs selected for upload")) return selection @@ -138,38 +135,39 @@ class UploadChunk @Inject constructor( private fun getBgReadings(start: Long, end: Long): List { val readings = repository.compatGetBgReadingsDataFromTime(start, end, true) .blockingGet() - val selection = SensorGlucoseElement.fromBgReadings(readings) + val selection = SensorGlucoseElement.fromBgReadings(readings, dateUtil) if (selection.isNotEmpty()) rxBus.send(EventTidepoolStatus("${selection.size} CGMs selected for upload")) return selection } - private fun fromTemporaryBasals(tbrList: Intervals, start: Long, end: Long): List { + private fun fromTemporaryBasals(tbrList: List, start: Long, end: Long): List { val results = LinkedList() - for (tbr in tbrList.list) { - if (tbr.date in start..end && tbr.durationInMinutes != 0) - results.add(BasalElement(tbr, profileFunction)) + for (tbr in tbrList) { + if (tbr.timestamp in start..end) + profileFunction.getProfile(tbr.timestamp)?.let { + results.add(BasalElement(tbr, it, dateUtil)) + } } return results } private fun getBasals(start: Long, end: Long): List { - val temporaryBasals = treatmentsPlugin.temporaryBasalsFromHistory - temporaryBasals.merge() + val temporaryBasals = repository.getTemporaryBasalsDataFromTimeToTime(start, end, true).blockingGet() val selection = fromTemporaryBasals(temporaryBasals, start, end) // TODO do not upload running TBR if (selection.isNotEmpty()) rxBus.send(EventTidepoolStatus("${selection.size} TBRs selected for upload")) return selection } - private fun newInstanceOrNull(ps: ProfileSwitch): ProfileElement? = try { - ProfileElement(ps, activePlugin.activePump.serialNumber()) + private fun newInstanceOrNull(ps: EffectiveProfileSwitch): ProfileElement? = try { + ProfileElement(ps, activePlugin.activePump.serialNumber(), dateUtil) } catch (e: Throwable) { null } private fun getProfiles(start: Long, end: Long): List { - val pss = databaseHelper.getProfileSwitchEventsFromTime(start, end, true) + val pss = repository.getEffectiveProfileSwitchDataFromTimeToTime(start, end, true).blockingGet() val selection = LinkedList() for (ps in pss) { newInstanceOrNull(ps)?.let { selection.add(it) } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/elements/BasalElement.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/elements/BasalElement.kt index 50e07a4096..272a039300 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/elements/BasalElement.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/elements/BasalElement.kt @@ -1,32 +1,39 @@ package info.nightscout.androidaps.plugins.general.tidepool.elements import com.google.gson.annotations.Expose -import info.nightscout.androidaps.db.TemporaryBasal -import info.nightscout.androidaps.interfaces.ProfileFunction +import info.nightscout.androidaps.interfaces.Profile +import info.nightscout.androidaps.database.entities.TemporaryBasal +import info.nightscout.androidaps.extensions.convertedToAbsolute +import info.nightscout.androidaps.utils.DateUtil import java.util.* -class BasalElement(tbr: TemporaryBasal, private val profileFunction: ProfileFunction) - : BaseElement(tbr.date, UUID.nameUUIDFromBytes(("AAPS-basal" + tbr.date).toByteArray()).toString()) { +class BasalElement(tbr: TemporaryBasal, private val profile: Profile, dateUtil: DateUtil) + : BaseElement(tbr.timestamp, UUID.nameUUIDFromBytes(("AAPS-basal" + tbr.timestamp).toByteArray()).toString(), dateUtil) { internal var timestamp: Long = 0 // not exposed @Expose internal var deliveryType = "automated" + @Expose internal var duration: Long = 0 + @Expose internal var rate = -1.0 + @Expose internal var scheduleName = "AAPS" + @Expose internal var clockDriftOffset: Long = 0 + @Expose internal var conversionOffset: Long = 0 init { type = "basal" - timestamp = tbr.date - rate = tbr.tempBasalConvertedToAbsolute(tbr.date, profileFunction.getProfile(tbr.date)) - duration = tbr.end() - tbr.start() + timestamp = tbr.timestamp + rate = tbr.convertedToAbsolute(tbr.timestamp, profile) + duration = tbr.duration } } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/elements/BaseElement.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/elements/BaseElement.kt index 96900e62d6..49aa6eae18 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/elements/BaseElement.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/elements/BaseElement.kt @@ -3,7 +3,7 @@ package info.nightscout.androidaps.plugins.general.tidepool.elements import com.google.gson.annotations.Expose import info.nightscout.androidaps.utils.DateUtil -open class BaseElement(timestamp: Long, uuid: String) { +open class BaseElement(timestamp: Long, uuid: String, dateUtil: DateUtil) { @Expose var deviceTime: String = "" @Expose @@ -16,9 +16,9 @@ open class BaseElement(timestamp: Long, uuid: String) { var origin: Origin? = null init { - deviceTime = DateUtil.toISONoZone(timestamp) - time = DateUtil.toISOAsUTC(timestamp) - timezoneOffset = DateUtil.getTimeZoneOffsetMinutes(timestamp) // TODO + deviceTime = dateUtil.toISONoZone(timestamp) + time = dateUtil.toISOAsUTC(timestamp) + timezoneOffset = dateUtil.getTimeZoneOffsetMinutes(timestamp) // TODO origin = Origin(uuid) } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/elements/BloodGlucoseElement.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/elements/BloodGlucoseElement.kt index a181a4a768..2d1f575138 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/elements/BloodGlucoseElement.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/elements/BloodGlucoseElement.kt @@ -1,13 +1,15 @@ package info.nightscout.androidaps.plugins.general.tidepool.elements import com.google.gson.annotations.Expose -import info.nightscout.androidaps.data.Profile +import info.nightscout.androidaps.interfaces.Profile import info.nightscout.androidaps.database.entities.TherapyEvent -import info.nightscout.androidaps.utils.extensions.toConstant +import info.nightscout.androidaps.extensions.toConstant +import info.nightscout.androidaps.extensions.toMainUnit +import info.nightscout.androidaps.utils.DateUtil import java.util.* -class BloodGlucoseElement(therapyEvent: TherapyEvent) - : BaseElement(therapyEvent.timestamp, UUID.nameUUIDFromBytes(("AAPS-bg" + therapyEvent.timestamp).toByteArray()).toString()) { +class BloodGlucoseElement(therapyEvent: TherapyEvent, dateUtil: DateUtil) + : BaseElement(therapyEvent.timestamp, UUID.nameUUIDFromBytes(("AAPS-bg" + therapyEvent.timestamp).toByteArray()).toString(), dateUtil) { @Expose var subType: String = "manual" @@ -22,19 +24,19 @@ class BloodGlucoseElement(therapyEvent: TherapyEvent) type = "cbg" subType = "manual" // TODO value = if (therapyEvent.glucose != null) - Profile.toMgdl(therapyEvent.glucose!!, therapyEvent.glucoseUnit.toConstant()).toInt() + Profile.toMgdl(therapyEvent.glucose!!, therapyEvent.glucoseUnit.toMainUnit()).toInt() else 0 } companion object { - fun fromCareportalEvents(careportalList: List): List { + fun fromCareportalEvents(careportalList: List, dateUtil: DateUtil): List { val results = LinkedList() for (bt in careportalList) { if (bt.type == TherapyEvent.Type.NS_MBG || bt.type == TherapyEvent.Type.FINGER_STICK_BG_VALUE) { - val bge = BloodGlucoseElement(bt) + val bge = BloodGlucoseElement(bt, dateUtil) if (bge.value > 0) - results.add(BloodGlucoseElement(bt)) + results.add(BloodGlucoseElement(bt, dateUtil)) } } return results diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/elements/BolusElement.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/elements/BolusElement.kt index f437018994..6c3a05b0eb 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/elements/BolusElement.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/elements/BolusElement.kt @@ -1,22 +1,20 @@ package info.nightscout.androidaps.plugins.general.tidepool.elements import com.google.gson.annotations.Expose -import info.nightscout.androidaps.db.Treatment +import info.nightscout.androidaps.database.entities.Bolus +import info.nightscout.androidaps.utils.DateUtil import java.util.* -class BolusElement(treatment: Treatment) - : BaseElement(treatment.date, UUID.nameUUIDFromBytes(("AAPS-bolus" + treatment.date).toByteArray()).toString()) { +class BolusElement(bolus: Bolus, dateUtil: DateUtil) + : BaseElement(bolus.timestamp, UUID.nameUUIDFromBytes(("AAPS-bolus" + bolus.timestamp).toByteArray()).toString(), dateUtil) { - @Expose - var subType = "normal" - @Expose - var normal: Double = 0.0 - @Expose - var expectedNormal: Double = 0.0 + @Expose var subType = "normal" + @Expose var normal: Double = 0.0 + @Expose var expectedNormal: Double = 0.0 init { type = "bolus" - normal = treatment.insulin - expectedNormal = treatment.insulin + normal = bolus.amount + expectedNormal = bolus.amount } } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/elements/ProfileElement.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/elements/ProfileElement.kt index dbc9888e3e..c4f47e5aa3 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/elements/ProfileElement.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/elements/ProfileElement.kt @@ -1,14 +1,16 @@ package info.nightscout.androidaps.plugins.general.tidepool.elements import com.google.gson.annotations.Expose -import info.nightscout.androidaps.data.Profile -import info.nightscout.androidaps.db.ProfileSwitch +import info.nightscout.androidaps.data.ProfileSealed +import info.nightscout.androidaps.database.entities.EffectiveProfileSwitch +import info.nightscout.androidaps.interfaces.Profile import info.nightscout.androidaps.plugins.general.tidepool.comm.TidepoolUploader +import info.nightscout.androidaps.utils.DateUtil import java.util.* import kotlin.collections.ArrayList -class ProfileElement(ps: ProfileSwitch, serialNumber: String) - : BaseElement(ps.date, UUID.nameUUIDFromBytes(("AAPS-profile" + ps.date).toByteArray()).toString()) { +class ProfileElement(ps: EffectiveProfileSwitch, serialNumber: String, dateUtil: DateUtil) + : BaseElement(ps.timestamp, UUID.nameUUIDFromBytes(("AAPS-profile" + ps.timestamp).toByteArray()).toString(), dateUtil) { @Expose internal var activeSchedule = "Normal" @@ -33,15 +35,15 @@ class ProfileElement(ps: ProfileSwitch, serialNumber: String) init { type = "pumpSettings" - val profile: Profile? = ps.profileObject + val profile: Profile? = ProfileSealed.EPS(ps) checkNotNull(profile) - for (br in profile.basalValues) + for (br in profile.getBasalValues()) basalSchedules.Normal.add(BasalRate(br.timeAsSeconds * 1000, br.value)) - for (target in profile.singleTargetsMgdl) + for (target in profile.getSingleTargetsMgdl()) bgTargets.Normal.add(Target(target.timeAsSeconds * 1000, target.value)) - for (ic in profile.ics) + for (ic in profile.getIcsValues()) carbRatios.Normal.add(Ratio(ic.timeAsSeconds * 1000, ic.value)) - for (isf in profile.isfsMgdl) + for (isf in profile.getIsfsMgdlValues()) insulinSensitivities.Normal.add(Ratio(isf.timeAsSeconds * 1000, isf.value)) } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/elements/SensorGlucoseElement.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/elements/SensorGlucoseElement.kt index da769f902e..cd9538018a 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/elements/SensorGlucoseElement.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/elements/SensorGlucoseElement.kt @@ -2,10 +2,11 @@ package info.nightscout.androidaps.plugins.general.tidepool.elements import com.google.gson.annotations.Expose import info.nightscout.androidaps.database.entities.GlucoseValue +import info.nightscout.androidaps.utils.DateUtil import java.util.* -class SensorGlucoseElement(bgReading: GlucoseValue) - : BaseElement(bgReading.timestamp, UUID.nameUUIDFromBytes(("AAPS-cgm" + bgReading.timestamp).toByteArray()).toString()) { +class SensorGlucoseElement(bgReading: GlucoseValue, private val dateUtil: DateUtil) + : BaseElement(bgReading.timestamp, UUID.nameUUIDFromBytes(("AAPS-cgm" + bgReading.timestamp).toByteArray()).toString(), dateUtil) { @Expose internal var units: String = "mg/dL" @@ -20,10 +21,10 @@ class SensorGlucoseElement(bgReading: GlucoseValue) companion object { - internal fun fromBgReadings(bgReadingList: List): List { + internal fun fromBgReadings(bgReadingList: List, dateUtil: DateUtil): List { val results = LinkedList() for (bgReading in bgReadingList) { - results.add(SensorGlucoseElement(bgReading)) + results.add(SensorGlucoseElement(bgReading, dateUtil)) } return results } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/elements/WizardElement.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/elements/WizardElement.kt index 87aba24d1c..a6615e45a3 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/elements/WizardElement.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/elements/WizardElement.kt @@ -1,33 +1,28 @@ package info.nightscout.androidaps.plugins.general.tidepool.elements import com.google.gson.annotations.Expose -import info.nightscout.androidaps.db.Treatment +import info.nightscout.androidaps.database.entities.Bolus +import info.nightscout.androidaps.database.entities.Carbs +import info.nightscout.androidaps.utils.DateUtil import java.util.* -class WizardElement(treatment: Treatment) - : BaseElement(treatment.date, UUID.nameUUIDFromBytes(("AAPS-wizard" + treatment.date).toByteArray()).toString()) { +class WizardElement(carbs: Carbs, dateUtil: DateUtil) + : BaseElement(carbs.timestamp, UUID.nameUUIDFromBytes(("AAPS-wizard" + carbs.timestamp).toByteArray()).toString(), dateUtil) { - @Expose - var units = "mg/dL" - @Expose - var carbInput: Double = 0.toDouble() - @Expose - var insulinCarbRatio: Double = 0.toDouble() - @Expose - var bolus: BolusElement? = null + @Expose var units = "mg/dL" + @Expose var carbInput: Double = 0.toDouble() + @Expose var insulinCarbRatio: Double = 0.toDouble() + @Expose var bolus: BolusElement? = null init { type = "wizard" - carbInput = treatment.carbs - insulinCarbRatio = treatment.ic - if (treatment.insulin > 0) { - bolus = BolusElement(treatment) - } else { - val fake = Treatment() - fake.insulin = 0.0001 - fake.date = treatment.date - bolus = BolusElement(fake) // fake insulin record - } + carbInput = carbs.amount + val fake = Bolus( + amount = 0.0001, + timestamp = carbs.timestamp, + type = Bolus.Type.NORMAL + ) + bolus = BolusElement(fake, dateUtil) // fake insulin record } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/events/EventTidepoolStatus.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/events/EventTidepoolStatus.kt index aff358ba08..e2e0caf8ae 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/events/EventTidepoolStatus.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/events/EventTidepoolStatus.kt @@ -1,12 +1,12 @@ package info.nightscout.androidaps.plugins.general.tidepool.events import info.nightscout.androidaps.events.Event -import info.nightscout.androidaps.utils.DateUtil import java.text.SimpleDateFormat import java.util.* class EventTidepoolStatus(val status: String) : Event() { - var date: Long = DateUtil.now() + + var date: Long = System.currentTimeMillis() private var timeFormat = SimpleDateFormat("HH:mm:ss", Locale.getDefault()) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/messages/OpenDatasetRequestMessage.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/messages/OpenDatasetRequestMessage.kt index e6fd2c63dd..631a0259bd 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/messages/OpenDatasetRequestMessage.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/messages/OpenDatasetRequestMessage.kt @@ -7,46 +7,62 @@ import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.T import java.util.* -class OpenDatasetRequestMessage (val serialNumber : String): BaseMessage() { +class OpenDatasetRequestMessage(serialNumber: String, dateUtil: DateUtil) : BaseMessage() { @Expose var deviceId: String = TidepoolUploader.PUMP_TYPE + ":" + serialNumber + @Expose - var time: String = DateUtil.toISOAsUTC(DateUtil.now()) + var time: String = dateUtil.toISOAsUTC(System.currentTimeMillis()) + @Expose - var timezoneOffset = (DateUtil.getTimeZoneOffsetMs() / T.mins(1).msecs()).toInt() + var timezoneOffset = (dateUtil.getTimeZoneOffsetMs() / T.mins(1).msecs()).toInt() + @Expose var type = "upload" + //public String byUser; @Expose var client = ClientInfo() + @Expose - var computerTime: String = DateUtil.toISONoZone(DateUtil.now()) + var computerTime: String = dateUtil.toISONoZone(System.currentTimeMillis()) + @Expose var dataSetType = "continuous" + @Expose var deviceManufacturers = arrayOf(TidepoolUploader.PUMP_TYPE) + @Expose var deviceModel = TidepoolUploader.PUMP_TYPE + @Expose var deviceTags = arrayOf("bgm", "cgm", "insulin-pump") + @Expose var deduplicator = Deduplicator() + @Expose var timeProcessing = "none" + @Expose var timezone: String = TimeZone.getDefault().id + @Expose var version = BuildConfig.VERSION_NAME inner class ClientInfo { + @Expose val name = BuildConfig.APPLICATION_ID + @Expose val version = TidepoolUploader.VERSION } inner class Deduplicator { + @Expose val name = "org.tidepool.deduplicator.dataset.delete.origin" } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/utils/RateLimit.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/utils/RateLimit.kt index 9e48d0b90f..020e6714f3 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/utils/RateLimit.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/utils/RateLimit.kt @@ -10,7 +10,8 @@ import javax.inject.Singleton @Singleton class RateLimit @Inject constructor( - val aapsLogger: AAPSLogger + private val aapsLogger: AAPSLogger, + private val dateUtil: DateUtil ) { private val rateLimits = HashMap() @@ -20,13 +21,13 @@ class RateLimit @Inject constructor( fun rateLimit(name: String, seconds: Int): Boolean { // check if over limit rateLimits[name]?.let { - if (DateUtil.now() - it < T.secs(seconds.toLong()).msecs()) { + if (dateUtil.now() - it < T.secs(seconds.toLong()).msecs()) { aapsLogger.debug(LTag.TIDEPOOL, "$name rate limited: $seconds seconds") return false } } // not over limit - rateLimits[name] = DateUtil.now() + rateLimits[name] = dateUtil.now() return true } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/ActionStringHandler.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/ActionStringHandler.kt index 1fa4210b9a..0385823728 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/ActionStringHandler.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/ActionStringHandler.kt @@ -3,7 +3,6 @@ package info.nightscout.androidaps.plugins.general.wear import android.app.NotificationManager import android.content.Context import dagger.android.HasAndroidInjector -import info.nightscout.androidaps.Config import info.nightscout.androidaps.Constants import info.nightscout.androidaps.R import info.nightscout.androidaps.dana.DanaPump @@ -12,31 +11,31 @@ import info.nightscout.androidaps.danaRv2.DanaRv2Plugin import info.nightscout.androidaps.danar.DanaRPlugin import info.nightscout.androidaps.danars.DanaRSPlugin import info.nightscout.androidaps.data.DetailedBolusInfo -import info.nightscout.androidaps.data.Profile import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.ValueWrapper import info.nightscout.androidaps.database.entities.TemporaryTarget -import info.nightscout.androidaps.database.entities.TherapyEvent +import info.nightscout.androidaps.database.entities.TotalDailyDose +import info.nightscout.androidaps.database.entities.UserEntry.Action +import info.nightscout.androidaps.database.entities.UserEntry.Sources +import info.nightscout.androidaps.database.entities.ValueWithUnit import info.nightscout.androidaps.database.interfaces.end import info.nightscout.androidaps.database.transactions.CancelCurrentTemporaryTargetIfAnyTransaction import info.nightscout.androidaps.database.transactions.InsertTemporaryTargetAndCancelCurrentTransaction -import info.nightscout.androidaps.db.Source -import info.nightscout.androidaps.db.TDD +import info.nightscout.androidaps.extensions.total +import info.nightscout.androidaps.extensions.valueToUnits 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.aps.loop.LoopPlugin 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.wear.events.EventWearConfirmAction import info.nightscout.androidaps.plugins.general.wear.events.EventWearInitiateAction -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin import info.nightscout.androidaps.plugins.pump.insight.LocalInsightPlugin -import info.nightscout.androidaps.plugins.treatments.CarbsGenerator import info.nightscout.androidaps.queue.Callback import info.nightscout.androidaps.utils.* -import info.nightscout.androidaps.utils.extensions.valueToUnits import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.utils.sharedPreferences.SP @@ -69,20 +68,18 @@ class ActionStringHandler @Inject constructor( private val wearPlugin: WearPlugin, private val fabricPrivacy: FabricPrivacy, private val commandQueue: CommandQueueProvider, - private val activePlugin: ActivePluginProvider, - private val iobCobCalculatorPlugin: IobCobCalculatorPlugin, + private val activePlugin: ActivePlugin, + private val iobCobCalculator: IobCobCalculator, private val localInsightPlugin: LocalInsightPlugin, private val danaRPlugin: DanaRPlugin, private val danaRKoreanPlugin: DanaRKoreanPlugin, private val danaRv2Plugin: DanaRv2Plugin, private val danaRSPlugin: DanaRSPlugin, private val danaPump: DanaPump, - private val carbsGenerator: CarbsGenerator, private val dateUtil: DateUtil, private val config: Config, - private val databaseHelper: DatabaseHelperInterface, private val repository: AppRepository, - private val nsUpload: NSUpload + private val uel: UserEntryLogger ) { private val timeout = 65 * 1000 @@ -104,7 +101,7 @@ class ActionStringHandler @Inject constructor( .subscribe({ handleConfirmation(it.action) }, fabricPrivacy::logException) } - fun tearDown(){ + fun tearDown() { disposable.clear() } @@ -147,7 +144,7 @@ class ActionStringHandler @Inject constructor( rAction += "bolus $insulinAfterConstraints $carbsAfterConstraints" } else if ("temptarget" == act[0]) { ///////////////////////////////////////////////////////// TEMPTARGET val isMGDL = java.lang.Boolean.parseBoolean(act[1]) - if (profileFunction.getUnits() == Constants.MGDL != isMGDL) { + if (profileFunction.getUnits() == GlucoseUnit.MGDL != isMGDL) { sendError("Different units used on watch and phone!") return } @@ -208,24 +205,24 @@ class ActionStringHandler @Inject constructor( sendError("No profile found!") return } - val bgReading = iobCobCalculatorPlugin.actualBg() + val bgReading = iobCobCalculator.ads.actualBg() if (bgReading == null) { sendError("No recent BG to base calculation on!") return } - val cobInfo = iobCobCalculatorPlugin.getCobInfo(false, "Wizard wear") + val cobInfo = iobCobCalculator.getCobInfo(false, "Wizard wear") if (cobInfo.displayCob == null) { sendError("Unknown COB! BG reading missing or recent app restart?") return } val format = DecimalFormat("0.00") val formatInt = DecimalFormat("0") - val dbRecord = repository.getTemporaryTargetActiveAt(dateUtil._now()).blockingGet() + val dbRecord = repository.getTemporaryTargetActiveAt(dateUtil.now()).blockingGet() val tempTarget = if (dbRecord is ValueWrapper.Existing) dbRecord.value else null val bolusWizard = BolusWizard(injector).doCalc(profile, profileName, tempTarget, carbsAfterConstraints, if (cobInfo.displayCob != null) cobInfo.displayCob!! else 0.0, bgReading.valueToUnits(profileFunction.getUnits()), - 0.0, percentage.toDouble(), useBG, useCOB, useBolusIOB, useBasalIOB, false, useTT, useTrend, false) + 0.0, percentage, useBG, useCOB, useBolusIOB, useBasalIOB, false, useTT, useTrend, false) if (abs(bolusWizard.insulinAfterConstraints - bolusWizard.calculatedTotalInsulin) >= 0.01) { sendError("Insulin constraint violation!" + "\nCannot deliver " + format.format(bolusWizard.calculatedTotalInsulin) + "!") @@ -252,30 +249,30 @@ class ActionStringHandler @Inject constructor( } lastBolusWizard = bolusWizard } else if ("opencpp" == act[0]) { - val activeProfileSwitch = activePlugin.activeTreatments.getProfileSwitchFromHistory(System.currentTimeMillis()) - if (activeProfileSwitch == null) { - sendError("No active profile switch!") - return - } else { // read CPP values + val activeProfileSwitch = repository.getEffectiveProfileSwitchActiveAt(dateUtil.now()).blockingGet() + if (activeProfileSwitch is ValueWrapper.Existing) { // read CPP values rTitle = "opencpp" rMessage = "opencpp" - rAction = "opencpp" + " " + activeProfileSwitch.percentage + " " + activeProfileSwitch.timeshift - } - } else if ("cppset" == act[0]) { - val activeProfileSwitch = activePlugin.activeTreatments.getProfileSwitchFromHistory(System.currentTimeMillis()) - if (activeProfileSwitch == null) { + rAction = "opencpp" + " " + activeProfileSwitch.value.originalPercentage + " " + activeProfileSwitch.value.originalTimeshift + } else { sendError("No active profile switch!") return - } else { // read CPP values + } + } else if ("cppset" == act[0]) { + val activeProfileSwitch = repository.getEffectiveProfileSwitchActiveAt(dateUtil.now()).blockingGet() + if (activeProfileSwitch is ValueWrapper.Existing) { rMessage = "CPP:" + "\n\n" + "Timeshift: " + act[1] + "\n" + "Percentage: " + act[2] + "%" rAction = actionString + } else { // read CPP values + sendError("No active profile switch!") + return } } else if ("tddstats" == act[0]) { val activePump = activePlugin.activePump // check if DB up to date - val dummies: MutableList = LinkedList() + val dummies: MutableList = LinkedList() val historyList = getTDDList(dummies) if (isOldData(historyList)) { rTitle = "TDD" @@ -288,7 +285,7 @@ class ActionStringHandler @Inject constructor( rMessage += "trying to fetch data from pump." commandQueue.loadTDDs(object : Callback() { override fun run() { - val dummies1: MutableList = LinkedList() + val dummies1: MutableList = LinkedList() val historyList1 = getTDDList(dummies1) if (isOldData(historyList1)) { sendStatusMessage("TDD: Still old data! Cannot load from pump.\n" + generateTDDMessage(historyList1, dummies1)) @@ -341,7 +338,7 @@ class ActionStringHandler @Inject constructor( lastConfirmActionString = rAction } - private fun generateTDDMessage(historyList: MutableList, dummies: MutableList): String { + private fun generateTDDMessage(historyList: MutableList, dummies: MutableList): String { val profile = profileFunction.getProfile() ?: return "No profile loaded :(" if (historyList.isEmpty()) { return "No history data!" @@ -350,8 +347,8 @@ class ActionStringHandler @Inject constructor( var message = "" val refTDD = profile.baseBasalSum() * 2 val pump = activePlugin.activePump - if (df.format(Date(historyList[0].date)) == df.format(Date())) { - val tdd = historyList[0].getTotal() + if (df.format(Date(historyList[0].timestamp)) == df.format(Date())) { + val tdd = historyList[0].total historyList.removeAt(0) message += "Today: " + DecimalFormatter.to2Decimal(tdd) + "U " + (DecimalFormatter.to0Decimal(100 * tdd / refTDD) + "%") + "\n" message += "\n" @@ -365,7 +362,7 @@ class ActionStringHandler @Inject constructor( var weighted07 = 0.0 historyList.reverse() for ((i, record) in historyList.withIndex()) { - val tdd = record.getTotal() + val tdd = record.total if (i == 0) { weighted03 = tdd weighted05 = tdd @@ -384,40 +381,38 @@ class ActionStringHandler @Inject constructor( historyList.reverse() //add TDDs: for (record in historyList) { - val tdd = record.getTotal() - message += df.format(Date(record.date)) + " " + DecimalFormatter.to2Decimal(tdd) + "U " + (DecimalFormatter.to0Decimal(100 * tdd / refTDD) + "%") + (if (dummies.contains(record)) "x" else "") + "\n" + val tdd = record.total + message += df.format(Date(record.timestamp)) + " " + DecimalFormatter.to2Decimal(tdd) + "U " + (DecimalFormatter.to0Decimal(100 * tdd / refTDD) + "%") + (if (dummies.contains(record)) "x" else "") + "\n" } return message } - private fun isOldData(historyList: List): Boolean { + private fun isOldData(historyList: List): Boolean { val activePump = activePlugin.activePump val startsYesterday = activePump === danaRPlugin || activePump === danaRSPlugin || activePump === danaRv2Plugin || activePump === danaRKoreanPlugin || activePump === localInsightPlugin val df: DateFormat = SimpleDateFormat("dd.MM.", Locale.getDefault()) - return historyList.size < 3 || df.format(Date(historyList[0].date)) != df.format(Date(System.currentTimeMillis() - if (startsYesterday) 1000 * 60 * 60 * 24 else 0)) + return historyList.size < 3 || df.format(Date(historyList[0].timestamp)) != df.format(Date(System.currentTimeMillis() - if (startsYesterday) 1000 * 60 * 60 * 24 else 0)) } - private fun getTDDList(returnDummies: MutableList): MutableList { - var historyList = databaseHelper.getTDDs().toMutableList() + private fun getTDDList(returnDummies: MutableList): MutableList { + var historyList = repository.getLastTotalDailyDoses(10, false).blockingGet().toMutableList() + //var historyList = databaseHelper.getTDDs().toMutableList() historyList = historyList.subList(0, min(10, historyList.size)) //fill single gaps - only needed for Dana*R data - val dummies: MutableList = returnDummies + val dummies: MutableList = returnDummies val df: DateFormat = SimpleDateFormat("dd.MM.", Locale.getDefault()) for (i in 0 until historyList.size - 1) { val elem1 = historyList[i] val elem2 = historyList[i + 1] - if (df.format(Date(elem1.date)) != df.format(Date(elem2.date + 25 * 60 * 60 * 1000))) { - val dummy = TDD() - dummy.date = elem1.date - 24 * 60 * 60 * 1000 - dummy.basal = elem1.basal / 2 - dummy.bolus = elem1.bolus / 2 + if (df.format(Date(elem1.timestamp)) != df.format(Date(elem2.timestamp + 25 * 60 * 60 * 1000))) { + val dummy = TotalDailyDose(timestamp = elem1.timestamp - T.hours(24).msecs(), bolusAmount = elem1.bolusAmount / 2, basalAmount = elem1.basalAmount / 2) dummies.add(dummy) - elem1.basal /= 2.0 - elem1.bolus /= 2.0 + elem1.basalAmount /= 2.0 + elem1.bolusAmount /= 2.0 } } historyList.addAll(dummies) - historyList.sortWith { lhs, rhs -> (rhs.date - lhs.date).toInt() } + historyList.sortWith { lhs, rhs -> (rhs.timestamp - lhs.timestamp).toInt() } return historyList } @@ -457,15 +452,15 @@ class ActionStringHandler @Inject constructor( } val profile = profileFunction.getProfile() ?: return "No profile set :(" //Check for Temp-Target: - val tempTarget = repository.getTemporaryTargetActiveAt(dateUtil._now()).blockingGet() + val tempTarget = repository.getTemporaryTargetActiveAt(dateUtil.now()).blockingGet() if (tempTarget is ValueWrapper.Existing) { - ret += "Temp Target: " + Profile.toTargetRangeString(tempTarget.value.lowTarget, tempTarget.value.lowTarget, Constants.MGDL, profileFunction.getUnits()) + ret += "Temp Target: " + Profile.toTargetRangeString(tempTarget.value.lowTarget, tempTarget.value.lowTarget, GlucoseUnit.MGDL, profileFunction.getUnits()) ret += "\nuntil: " + dateUtil.timeString(tempTarget.value.end) ret += "\n\n" } ret += "DEFAULT RANGE: " - ret += Profile.fromMgdlToUnits(profile.targetLowMgdl, profileFunction.getUnits()).toString() + " - " + Profile.fromMgdlToUnits(profile.targetHighMgdl, profileFunction.getUnits()) - ret += " target: " + Profile.fromMgdlToUnits(profile.targetMgdl, profileFunction.getUnits()) + ret += Profile.fromMgdlToUnits(profile.getTargetLowMgdl(), profileFunction.getUnits()).toString() + " - " + Profile.fromMgdlToUnits(profile.getTargetHighMgdl(), profileFunction.getUnits()) + ret += " target: " + Profile.fromMgdlToUnits(profile.getTargetMgdl(), profileFunction.getUnits()) return ret } @@ -520,13 +515,13 @@ class ActionStringHandler @Inject constructor( generateTempTarget(duration, low, high) } else if ("wizard2" == act[0]) { if (lastBolusWizard != null) { //use last calculation as confirmed string matches - doBolus(lastBolusWizard!!.calculatedTotalInsulin, lastBolusWizard!!.carbs) + doBolus(lastBolusWizard!!.calculatedTotalInsulin, lastBolusWizard!!.carbs, null, 0) lastBolusWizard = null } } else if ("bolus" == act[0]) { val insulin = SafeParse.stringToDouble(act[1]) val carbs = SafeParse.stringToInt(act[2]) - doBolus(insulin, carbs) + doBolus(insulin, carbs, null, 0) } else if ("cppset" == act[0]) { val timeshift = SafeParse.stringToInt(act[1]) val percentage = SafeParse.stringToInt(act[2]) @@ -546,16 +541,6 @@ class ActionStringHandler @Inject constructor( lastBolusWizard = null } - private fun doECarbs(carbs: Int, time: Long, duration: Int) { - if (carbs > 0) { - if (duration == 0) { - carbsGenerator.createCarb(carbs, time, TherapyEvent.Type.CARBS_CORRECTION.text, "watch") - } else { - carbsGenerator.generateCarbs(carbs, time, duration, "watch eCarbs") - } - } - } - private fun setCPP(timeshift: Int, percentage: Int) { var msg = "" //check for validity @@ -579,11 +564,14 @@ class ActionStringHandler @Inject constructor( return } //send profile to pump - activePlugin.activeTreatments.doProfileSwitch(0, percentage, timeshift) + uel.log(Action.PROFILE_SWITCH, Sources.Wear, + ValueWithUnit.Percent(percentage), + ValueWithUnit.Hour(timeshift).takeIf { timeshift != 0 }) + profileFunction.createProfileSwitch(0, percentage, timeshift) } private fun generateTempTarget(duration: Int, low: Double, high: Double) { - if (duration != 0) + if (duration != 0) { disposable += repository.runTransactionForResult(InsertTemporaryTargetAndCancelCurrentTransaction( timestamp = System.currentTimeMillis(), duration = TimeUnit.MINUTES.toMillis(duration.toLong()), @@ -591,25 +579,34 @@ class ActionStringHandler @Inject constructor( lowTarget = Profile.toMgdl(low, profileFunction.getUnits()), highTarget = Profile.toMgdl(high, profileFunction.getUnits()) )).subscribe({ result -> - result.inserted.forEach { nsUpload.uploadTempTarget(it) } - result.updated.forEach { nsUpload.updateTempTarget(it) } + result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted temp target $it") } + result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated temp target $it") } }, { - aapsLogger.error("Error while saving temporary target", it) + aapsLogger.error(LTag.DATABASE, "Error while saving temporary target", it) }) - else + uel.log(Action.TT, Sources.Wear, + ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.WEAR), + ValueWithUnit.fromGlucoseUnit(low, profileFunction.getUnits().asText), + ValueWithUnit.fromGlucoseUnit(high, profileFunction.getUnits().asText).takeIf { low != high }, + ValueWithUnit.Minute(duration)) + } else { disposable += repository.runTransactionForResult(CancelCurrentTemporaryTargetIfAnyTransaction(System.currentTimeMillis())) .subscribe({ result -> - result.updated.forEach { nsUpload.updateTempTarget(it) } + result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated temp target $it") } }, { - aapsLogger.error("Error while saving temporary target", it) + aapsLogger.error(LTag.DATABASE, "Error while saving temporary target", it) }) + uel.log(Action.CANCEL_TT, Sources.Wear, + ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.WEAR)) + } } private fun doFillBolus(amount: Double) { val detailedBolusInfo = DetailedBolusInfo() detailedBolusInfo.insulin = amount - detailedBolusInfo.isValid = false - detailedBolusInfo.source = Source.USER + detailedBolusInfo.bolusType = DetailedBolusInfo.BolusType.PRIMING + uel.log(Action.PRIME_BOLUS, Sources.Wear, + ValueWithUnit.Insulin(amount).takeIf { amount != 0.0 }) commandQueue.bolus(detailedBolusInfo, object : Callback() { override fun run() { if (!result.success) { @@ -621,13 +618,32 @@ class ActionStringHandler @Inject constructor( }) } - private fun doBolus(amount: Double, carbs: Int) { + private fun doECarbs(carbs: Int, time: Long, duration: Int) { + uel.log(if (duration == 0) Action.CARBS else Action.EXTENDED_CARBS, Sources.Wear, + ValueWithUnit.Timestamp(time), + ValueWithUnit.Gram(carbs), + ValueWithUnit.Hour(duration).takeIf { duration != 0 }) + doBolus(0.0, carbs, time, duration) + } + + private fun doBolus(amount: Double, carbs: Int, carbsTime: Long?, carbsDuration: Int) { val detailedBolusInfo = DetailedBolusInfo() detailedBolusInfo.insulin = amount detailedBolusInfo.carbs = carbs.toDouble() - detailedBolusInfo.source = Source.USER - val storesCarbs = activePlugin.activePump.pumpDescription.storesCarbInfo - if (detailedBolusInfo.insulin > 0 || storesCarbs) { + detailedBolusInfo.bolusType = DetailedBolusInfo.BolusType.NORMAL + detailedBolusInfo.carbsTimestamp = carbsTime + detailedBolusInfo.carbsDuration = T.hours(carbsDuration.toLong()).msecs() + if (detailedBolusInfo.insulin > 0 || detailedBolusInfo.carbs > 0) { + val action = when { + amount.equals(0.0) -> Action.CARBS + carbs.equals(0) -> Action.BOLUS + carbsDuration > 0 -> Action.EXTENDED_CARBS + else -> Action.TREATMENT + } + uel.log(action, Sources.Wear, + ValueWithUnit.Insulin(amount).takeIf { amount != 0.0 }, + ValueWithUnit.Gram(carbs).takeIf { carbs != 0 }, + ValueWithUnit.Hour(carbsDuration).takeIf { carbsDuration != 0 }) commandQueue.bolus(detailedBolusInfo, object : Callback() { override fun run() { if (!result.success) { @@ -637,8 +653,6 @@ class ActionStringHandler @Inject constructor( } } }) - } else { - activePlugin.activeTreatments.addToHistoryTreatment(detailedBolusInfo, false) } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/WearPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/WearPlugin.kt index e4a486a89e..d08dba47d8 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/WearPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/WearPlugin.kt @@ -1,9 +1,9 @@ package info.nightscout.androidaps.plugins.general.wear +import android.content.Context import android.content.Intent import dagger.Lazy import dagger.android.HasAndroidInjector -import info.nightscout.androidaps.MainApp import info.nightscout.androidaps.R import info.nightscout.androidaps.events.* import info.nightscout.androidaps.interfaces.PluginBase @@ -16,7 +16,6 @@ import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.general.overview.events.EventDismissBolusProgressIfRunning import info.nightscout.androidaps.plugins.general.overview.events.EventOverviewBolusProgress import info.nightscout.androidaps.plugins.general.wear.wearintegration.WatchUpdaterService -import info.nightscout.androidaps.events.EventAutosensCalculationFinished import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers @@ -32,7 +31,7 @@ class WearPlugin @Inject constructor( resourceHelper: ResourceHelper, private val aapsSchedulers: AapsSchedulers, private val sp: SP, - private val mainApp: MainApp, + private val ctx: Context, private val fabricPrivacy: FabricPrivacy, private val loopPlugin: Lazy, private val rxBus: RxBusWrapper, @@ -97,10 +96,10 @@ class WearPlugin @Inject constructor( .observeOn(aapsSchedulers.io) .subscribe({ event: EventBolusRequested -> val status = String.format(resourceHelper.gs(R.string.bolusrequested), event.amount) - val intent = Intent(mainApp, WatchUpdaterService::class.java).setAction(WatchUpdaterService.ACTION_SEND_BOLUSPROGRESS) + val intent = Intent(ctx, WatchUpdaterService::class.java).setAction(WatchUpdaterService.ACTION_SEND_BOLUSPROGRESS) intent.putExtra("progresspercent", 0) intent.putExtra("progressstatus", status) - mainApp.startService(intent) + ctx.startService(intent) }, fabricPrivacy::logException)) disposable.add(rxBus .toObservable(EventDismissBolusProgressIfRunning::class.java) @@ -112,20 +111,20 @@ class WearPlugin @Inject constructor( } else { resourceHelper.gs(R.string.nosuccess) } - val intent = Intent(mainApp, WatchUpdaterService::class.java).setAction(WatchUpdaterService.ACTION_SEND_BOLUSPROGRESS) + val intent = Intent(ctx, WatchUpdaterService::class.java).setAction(WatchUpdaterService.ACTION_SEND_BOLUSPROGRESS) intent.putExtra("progresspercent", 100) intent.putExtra("progressstatus", status) - mainApp.startService(intent) + ctx.startService(intent) }, fabricPrivacy::logException)) disposable.add(rxBus .toObservable(EventOverviewBolusProgress::class.java) .observeOn(aapsSchedulers.io) .subscribe({ event: EventOverviewBolusProgress -> if (!event.isSMB() || sp.getBoolean("wear_notifySMB", true)) { - val intent = Intent(mainApp, WatchUpdaterService::class.java).setAction(WatchUpdaterService.ACTION_SEND_BOLUSPROGRESS) + val intent = Intent(ctx, WatchUpdaterService::class.java).setAction(WatchUpdaterService.ACTION_SEND_BOLUSPROGRESS) intent.putExtra("progresspercent", event.percent) intent.putExtra("progressstatus", event.status) - mainApp.startService(intent) + ctx.startService(intent) } }, fabricPrivacy::logException)) actionStringHandler.get().setup() @@ -142,47 +141,47 @@ class WearPlugin @Inject constructor( if (isEnabled(getType())) { // only start service when this plugin is enabled if (bgValue) { - mainApp.startService(Intent(mainApp, WatchUpdaterService::class.java)) + ctx.startService(Intent(ctx, WatchUpdaterService::class.java)) } if (basals) { - mainApp.startService(Intent(mainApp, WatchUpdaterService::class.java).setAction(WatchUpdaterService.ACTION_SEND_BASALS)) + ctx.startService(Intent(ctx, WatchUpdaterService::class.java).setAction(WatchUpdaterService.ACTION_SEND_BASALS)) } if (status) { - mainApp.startService(Intent(mainApp, WatchUpdaterService::class.java).setAction(WatchUpdaterService.ACTION_SEND_STATUS)) + ctx.startService(Intent(ctx, WatchUpdaterService::class.java).setAction(WatchUpdaterService.ACTION_SEND_STATUS)) } } } fun resendDataToWatch() { //Log.d(TAG, "WR: WearPlugin:resendDataToWatch"); - mainApp.startService(Intent(mainApp, WatchUpdaterService::class.java).setAction(WatchUpdaterService.ACTION_RESEND)) + ctx.startService(Intent(ctx, WatchUpdaterService::class.java).setAction(WatchUpdaterService.ACTION_RESEND)) } fun openSettings() { //Log.d(TAG, "WR: WearPlugin:openSettings"); - mainApp.startService(Intent(mainApp, WatchUpdaterService::class.java).setAction(WatchUpdaterService.ACTION_OPEN_SETTINGS)) + ctx.startService(Intent(ctx, WatchUpdaterService::class.java).setAction(WatchUpdaterService.ACTION_OPEN_SETTINGS)) } fun requestNotificationCancel(actionString: String?) { //Log.d(TAG, "WR: WearPlugin:requestNotificationCancel"); - val intent = Intent(mainApp, WatchUpdaterService::class.java) + val intent = Intent(ctx, WatchUpdaterService::class.java) .setAction(WatchUpdaterService.ACTION_CANCEL_NOTIFICATION) intent.putExtra("actionstring", actionString) - mainApp.startService(intent) + ctx.startService(intent) } fun requestActionConfirmation(title: String, message: String, actionString: String) { - val intent = Intent(mainApp, WatchUpdaterService::class.java).setAction(WatchUpdaterService.ACTION_SEND_ACTIONCONFIRMATIONREQUEST) + val intent = Intent(ctx, WatchUpdaterService::class.java).setAction(WatchUpdaterService.ACTION_SEND_ACTIONCONFIRMATIONREQUEST) intent.putExtra("title", title) intent.putExtra("message", message) intent.putExtra("actionstring", actionString) - mainApp.startService(intent) + ctx.startService(intent) } fun requestChangeConfirmation(title: String, message: String, actionString: String) { - val intent = Intent(mainApp, WatchUpdaterService::class.java).setAction(WatchUpdaterService.ACTION_SEND_CHANGECONFIRMATIONREQUEST) + val intent = Intent(ctx, WatchUpdaterService::class.java).setAction(WatchUpdaterService.ACTION_SEND_CHANGECONFIRMATIONREQUEST) intent.putExtra("title", title) intent.putExtra("message", message) intent.putExtra("actionstring", actionString) - mainApp.startService(intent) + ctx.startService(intent) } } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/WatchUpdaterService.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/WatchUpdaterService.java index db97d25b17..d18d9e96bb 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/WatchUpdaterService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/WatchUpdaterService.java @@ -24,22 +24,25 @@ import com.google.android.gms.wearable.WearableListenerService; import java.util.ArrayList; import java.util.List; import java.util.Set; +import java.util.stream.Collectors; import javax.inject.Inject; import dagger.android.AndroidInjection; -import dagger.android.HasAndroidInjector; -import info.nightscout.androidaps.Config; +import info.nightscout.androidaps.interfaces.Config; import info.nightscout.androidaps.Constants; import info.nightscout.androidaps.R; -import info.nightscout.androidaps.data.GlucoseValueDataPoint; import info.nightscout.androidaps.data.IobTotal; -import info.nightscout.androidaps.data.Profile; +import info.nightscout.androidaps.interfaces.GlucoseUnit; +import info.nightscout.androidaps.interfaces.Profile; import info.nightscout.androidaps.database.AppRepository; +import info.nightscout.androidaps.database.entities.Bolus; import info.nightscout.androidaps.database.entities.GlucoseValue; -import info.nightscout.androidaps.db.TemporaryBasal; -import info.nightscout.androidaps.db.Treatment; -import info.nightscout.androidaps.interfaces.ActivePluginProvider; +import info.nightscout.androidaps.database.entities.TemporaryBasal; +import info.nightscout.androidaps.extensions.GlucoseValueExtensionKt; +import info.nightscout.androidaps.extensions.TemporaryBasalExtensionKt; +import info.nightscout.androidaps.interfaces.ActivePlugin; +import info.nightscout.androidaps.interfaces.IobCobCalculator; import info.nightscout.androidaps.interfaces.PluginType; import info.nightscout.androidaps.interfaces.ProfileFunction; import info.nightscout.androidaps.logging.AAPSLogger; @@ -47,18 +50,16 @@ import info.nightscout.androidaps.logging.LTag; import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin; import info.nightscout.androidaps.plugins.bus.RxBusWrapper; import info.nightscout.androidaps.plugins.general.nsclient.data.NSDeviceStatus; +import info.nightscout.androidaps.plugins.general.overview.graphExtensions.GlucoseValueDataPoint; import info.nightscout.androidaps.plugins.general.wear.WearPlugin; import info.nightscout.androidaps.plugins.general.wear.events.EventWearConfirmAction; import info.nightscout.androidaps.plugins.general.wear.events.EventWearInitiateAction; import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus; import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatusProvider; -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin; -import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; import info.nightscout.androidaps.receivers.ReceiverStatusStore; import info.nightscout.androidaps.utils.DecimalFormatter; import info.nightscout.androidaps.utils.DefaultValueHelper; import info.nightscout.androidaps.utils.ToastUtils; -import info.nightscout.androidaps.utils.extensions.GlucoseValueUtilsKt; import info.nightscout.androidaps.utils.resources.ResourceHelper; import info.nightscout.androidaps.utils.sharedPreferences.SP; @@ -72,10 +73,9 @@ public class WatchUpdaterService extends WearableListenerService implements Goog @Inject public ProfileFunction profileFunction; @Inject public DefaultValueHelper defaultValueHelper; @Inject public NSDeviceStatus nsDeviceStatus; - @Inject public ActivePluginProvider activePlugin; + @Inject public ActivePlugin activePlugin; @Inject public LoopPlugin loopPlugin; - @Inject public IobCobCalculatorPlugin iobCobCalculatorPlugin; - @Inject public TreatmentsPlugin treatmentsPlugin; + @Inject public IobCobCalculator iobCobCalculator; @Inject public AppRepository repository; @Inject ReceiverStatusStore receiverStatusStore; @Inject Config config; @@ -280,7 +280,7 @@ public class WatchUpdaterService extends WearableListenerService implements Goog private void sendData() { - GlucoseValue lastBG = iobCobCalculatorPlugin.lastBg(); + GlucoseValue lastBG = iobCobCalculator.getAds().lastBg(); // Log.d(TAG, logPrefix + "LastBg=" + lastBG); if (lastBG != null) { GlucoseStatus glucoseStatus = glucoseStatusProvider.getGlucoseStatusData(); @@ -299,9 +299,9 @@ public class WatchUpdaterService extends WearableListenerService implements Goog private DataMap dataMapSingleBG(GlucoseValue lastBG, GlucoseStatus glucoseStatus) { - String units = profileFunction.getUnits(); + GlucoseUnit units = profileFunction.getUnits(); double convert2MGDL = 1.0; - if (units.equals(Constants.MMOL)) + if (units.equals(GlucoseUnit.MMOL)) convert2MGDL = Constants.MMOLL_TO_MGDL; double lowLine = defaultValueHelper.determineLowLine() * convert2MGDL; double highLine = defaultValueHelper.determineHighLine() * convert2MGDL; @@ -314,8 +314,8 @@ public class WatchUpdaterService extends WearableListenerService implements Goog } DataMap dataMap = new DataMap(); - dataMap.putString("sgvString", GlucoseValueUtilsKt.valueToUnitsString(lastBG, units)); - dataMap.putString("glucoseUnits", units); + dataMap.putString("sgvString", GlucoseValueExtensionKt.valueToUnitsString(lastBG, units)); + dataMap.putString("glucoseUnits", units.getAsText()); dataMap.putLong("timestamp", lastBG.getTimestamp()); if (glucoseStatus == null) { dataMap.putString("slopeArrow", ""); @@ -333,7 +333,7 @@ public class WatchUpdaterService extends WearableListenerService implements Goog return dataMap; } - private String deltastring(double deltaMGDL, double deltaMMOL, String units) { + private String deltastring(double deltaMGDL, double deltaMMOL, GlucoseUnit units) { String deltastring = ""; if (deltaMGDL >= 0) { deltastring += "+"; @@ -342,7 +342,7 @@ public class WatchUpdaterService extends WearableListenerService implements Goog } boolean detailed = sp.getBoolean(R.string.key_wear_detailed_delta, false); - if (units.equals(Constants.MGDL)) { + if (units.equals(GlucoseUnit.MGDL)) { if (detailed) { deltastring += DecimalFormatter.INSTANCE.to1Decimal(Math.abs(deltaMGDL)); } else { @@ -382,7 +382,7 @@ public class WatchUpdaterService extends WearableListenerService implements Goog googleApiConnect(); } long startTime = System.currentTimeMillis() - (long) (60000 * 60 * 5.5); - GlucoseValue last_bg = iobCobCalculatorPlugin.lastBg(); + GlucoseValue last_bg = iobCobCalculator.getAds().lastBg(); if (last_bg == null) return; @@ -437,8 +437,8 @@ public class WatchUpdaterService extends WearableListenerService implements Goog double beginBasalValue = profile.getBasal(beginBasalSegmentTime); double endBasalValue = beginBasalValue; - TemporaryBasal tb1 = treatmentsPlugin.getTempBasalFromHistory(runningTime); - TemporaryBasal tb2 = treatmentsPlugin.getTempBasalFromHistory(runningTime); //TODO for Adrian ... what's the meaning? + TemporaryBasal tb1 = iobCobCalculator.getTempBasalIncludingConvertedExtended(runningTime); + TemporaryBasal tb2 = iobCobCalculator.getTempBasalIncludingConvertedExtended(runningTime); //TODO for Adrian ... what's the meaning? double tb_before = beginBasalValue; double tb_amount = beginBasalValue; long tb_start = runningTime; @@ -447,7 +447,7 @@ public class WatchUpdaterService extends WearableListenerService implements Goog tb_before = beginBasalValue; Profile profileTB = profileFunction.getProfile(runningTime); if (profileTB != null) { - tb_amount = tb1.tempBasalConvertedToAbsolute(runningTime, profileTB); + tb_amount = TemporaryBasalExtensionKt.convertedToAbsolute(tb1, runningTime, profileTB); tb_start = runningTime; } } @@ -469,7 +469,7 @@ public class WatchUpdaterService extends WearableListenerService implements Goog } //temps - tb2 = treatmentsPlugin.getTempBasalFromHistory(runningTime); + tb2 = iobCobCalculator.getTempBasalIncludingConvertedExtended(runningTime); if (tb1 == null && tb2 == null) { //no temp stays no temp @@ -484,10 +484,10 @@ public class WatchUpdaterService extends WearableListenerService implements Goog tb1 = tb2; tb_start = runningTime; tb_before = endBasalValue; - tb_amount = tb1.tempBasalConvertedToAbsolute(runningTime, profileTB); + tb_amount = TemporaryBasalExtensionKt.convertedToAbsolute(tb1, runningTime, profileTB); } else if (tb1 != null && tb2 != null) { - double currentAmount = tb2.tempBasalConvertedToAbsolute(runningTime, profileTB); + double currentAmount = TemporaryBasalExtensionKt.convertedToAbsolute(tb2, runningTime, profileTB); if (currentAmount != tb_amount) { temps.add(tempDatamap(tb_start, tb_before, runningTime, currentAmount, tb_amount)); tb_start = runningTime; @@ -502,14 +502,14 @@ public class WatchUpdaterService extends WearableListenerService implements Goog basals.add(basalMap(beginBasalSegmentTime, runningTime, beginBasalValue)); } if (tb1 != null) { - tb2 = treatmentsPlugin.getTempBasalFromHistory(now); //use "now" to express current situation + tb2 = iobCobCalculator.getTempBasalIncludingConvertedExtended(now); //use "now" to express current situation if (tb2 == null) { //express the cancelled temp by painting it down one minute early temps.add(tempDatamap(tb_start, tb_before, now - 1 * 60 * 1000, endBasalValue, tb_amount)); } else { //express currently running temp by painting it a bit into the future Profile profileNow = profileFunction.getProfile(now); - double currentAmount = tb2.tempBasalConvertedToAbsolute(now, profileNow); + double currentAmount = TemporaryBasalExtensionKt.convertedToAbsolute(tb2, now, profileNow); if (currentAmount != tb_amount) { temps.add(tempDatamap(tb_start, tb_before, now, tb_amount, tb_amount)); temps.add(tempDatamap(now, tb_amount, runningTime + 5 * 60 * 1000, currentAmount, currentAmount)); @@ -518,29 +518,30 @@ public class WatchUpdaterService extends WearableListenerService implements Goog } } } else { - tb2 = treatmentsPlugin.getTempBasalFromHistory(now); //use "now" to express current situation + tb2 = iobCobCalculator.getTempBasalIncludingConvertedExtended(now); //use "now" to express current situation if (tb2 != null) { //onset at the end Profile profileTB = profileFunction.getProfile(runningTime); - double currentAmount = tb2.tempBasalConvertedToAbsolute(runningTime, profileTB); + double currentAmount = TemporaryBasalExtensionKt.convertedToAbsolute(tb2, runningTime, profileTB); temps.add(tempDatamap(now - 1 * 60 * 1000, endBasalValue, runningTime + 5 * 60 * 1000, currentAmount, currentAmount)); } } - List treatments = treatmentsPlugin.getTreatmentsFromHistory(); - for (Treatment treatment : treatments) { - if (treatment.date > startTimeWindow) { - boluses.add(treatmentMap(treatment.date, treatment.insulin, treatment.carbs, treatment.isSMB, treatment.isValid)); - } - - } + repository.getBolusesIncludingInvalidFromTime(startTimeWindow, true).blockingGet() + .stream() + .filter(bolus -> bolus.getType() != Bolus.Type.PRIMING) + .forEach(bolus -> boluses.add(treatmentMap(bolus.getTimestamp(), bolus.getAmount(), 0, bolus.getType() == Bolus.Type.SMB, bolus.isValid()))); + repository.getCarbsDataFromTimeExpanded(startTimeWindow, true).blockingGet() + .forEach(carb -> boluses.add(treatmentMap(carb.getTimestamp(), 0, carb.getAmount(), false, carb.isValid()))); final LoopPlugin.LastRun finalLastRun = loopPlugin.getLastRun(); if (sp.getBoolean("wear_predictions", true) && finalLastRun != null && finalLastRun.getRequest().getHasPredictions() && finalLastRun.getConstraintsProcessed() != null) { - List predArray = finalLastRun.getConstraintsProcessed().getPredictions(); + List predArray = + finalLastRun.getConstraintsProcessed().getPredictions() + .stream().map(bg -> new GlucoseValueDataPoint(bg, defaultValueHelper, profileFunction, resourceHelper)) + .collect(Collectors.toList()); if (!predArray.isEmpty()) { - final String units = profileFunction.getUnits(); for (GlucoseValueDataPoint bg : predArray) { if (bg.getData().getValue() < 40) continue; predictions.add(predictionMap(bg.getData().getTimestamp(), bg.getData().getValue(), bg.getPredictionColor())); @@ -685,18 +686,16 @@ public class WatchUpdaterService extends WearableListenerService implements Goog String iobSum, iobDetail, cobString, currentBasal, bgiString; iobSum = iobDetail = cobString = currentBasal = bgiString = ""; if (profile != null) { - treatmentsPlugin.updateTotalIOBTreatments(); - IobTotal bolusIob = treatmentsPlugin.getLastCalculationTreatments().round(); - treatmentsPlugin.updateTotalIOBTempBasals(); - IobTotal basalIob = treatmentsPlugin.getLastCalculationTempBasals().round(); + IobTotal bolusIob = iobCobCalculator.calculateIobFromBolus().round(); + IobTotal basalIob = iobCobCalculator.calculateIobFromTempBasalsIncludingConvertedExtended().round(); iobSum = DecimalFormatter.INSTANCE.to2Decimal(bolusIob.iob + basalIob.basaliob); iobDetail = "(" + DecimalFormatter.INSTANCE.to2Decimal(bolusIob.iob) + "|" + DecimalFormatter.INSTANCE.to2Decimal(basalIob.basaliob) + ")"; - cobString = iobCobCalculatorPlugin.getCobInfo(false, "WatcherUpdaterService").generateCOBString(); + cobString = iobCobCalculator.getCobInfo(false, "WatcherUpdaterService").generateCOBString(); currentBasal = generateBasalString(); //bgi - double bgi = -(bolusIob.activity + basalIob.activity) * 5 * Profile.fromMgdlToUnits(profile.getIsfMgdl(), profileFunction.getUnits()); + double bgi = -(bolusIob.activity + basalIob.activity) * 5 * Profile.Companion.fromMgdlToUnits(profile.getIsfMgdl(), profileFunction.getUnits()); bgiString = "" + ((bgi >= 0) ? "+" : "") + DecimalFormatter.INSTANCE.to1Decimal(bgi); status = generateStatusString(profile, currentBasal, iobSum, iobDetail, bgiString); @@ -798,15 +797,11 @@ public class WatchUpdaterService extends WearableListenerService implements Goog if (profile == null) return ""; - TemporaryBasal activeTemp = treatmentsPlugin.getTempBasalFromHistory(System.currentTimeMillis()); + TemporaryBasal activeTemp = iobCobCalculator.getTempBasalIncludingConvertedExtended(System.currentTimeMillis()); if (activeTemp != null) { - basalStringResult = activeTemp.toStringShort(); + basalStringResult = TemporaryBasalExtensionKt.toStringShort(activeTemp); } else { - if (sp.getBoolean(R.string.key_danar_visualizeextendedaspercentage, false)) { - basalStringResult = "100%"; - } else { - basalStringResult = DecimalFormatter.INSTANCE.to2Decimal(profile.getBasal()) + "U/h"; - } + basalStringResult = DecimalFormatter.INSTANCE.to2Decimal(profile.getBasal()) + "U/h"; } return basalStringResult; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/xdripStatusline/StatusLinePlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/xdripStatusline/StatusLinePlugin.kt index a705394f59..1bfb7f7bd3 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/xdripStatusline/StatusLinePlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/xdripStatusline/StatusLinePlugin.kt @@ -5,25 +5,19 @@ import android.content.Intent import android.os.Bundle import dagger.android.HasAndroidInjector import info.nightscout.androidaps.R -import info.nightscout.androidaps.data.Profile import info.nightscout.androidaps.events.* -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.extensions.toStringShort +import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin import info.nightscout.androidaps.plugins.bus.RxBusWrapper -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin -import info.nightscout.androidaps.events.EventAutosensCalculationFinished import info.nightscout.androidaps.utils.DecimalFormatter import info.nightscout.androidaps.utils.FabricPrivacy -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 import io.reactivex.disposables.CompositeDisposable +import io.reactivex.rxkotlin.plusAssign import javax.inject.Inject import javax.inject.Singleton @@ -36,9 +30,8 @@ class StatusLinePlugin @Inject constructor( private val aapsSchedulers: AapsSchedulers, private val context: Context, private val fabricPrivacy: FabricPrivacy, - private val activePlugin: ActivePluginProvider, private val loopPlugin: LoopPlugin, - private val iobCobCalculatorPlugin: IobCobCalculatorPlugin, + private val iobCobCalculator: IobCobCalculator, private val rxBus: RxBusWrapper, aapsLogger: AAPSLogger ) : PluginBase( @@ -127,15 +120,13 @@ class StatusLinePlugin @Inject constructor( lastLoopStatus = true } //Temp basal - val activeTemp = activePlugin.activeTreatments.getTempBasalFromHistory(System.currentTimeMillis()) + val activeTemp = iobCobCalculator.getTempBasalIncludingConvertedExtended(System.currentTimeMillis()) if (activeTemp != null) { status += activeTemp.toStringShort() + " " } //IOB - activePlugin.activeTreatments.updateTotalIOBTreatments() - val bolusIob = activePlugin.activeTreatments.lastCalculationTreatments.round() - activePlugin.activeTreatments.updateTotalIOBTempBasals() - val basalIob = activePlugin.activeTreatments.lastCalculationTempBasals.round() + val bolusIob = iobCobCalculator.calculateIobFromBolus().round() + val basalIob = iobCobCalculator.calculateIobFromTempBasalsIncludingConvertedExtended().round() status += DecimalFormatter.to2Decimal(bolusIob.iob + basalIob.basaliob) + "U" if (sp.getBoolean(R.string.key_xdripstatus_detailediob, true)) { status += ("(" @@ -143,11 +134,11 @@ class StatusLinePlugin @Inject constructor( + DecimalFormatter.to2Decimal(basalIob.basaliob) + ")") } if (sp.getBoolean(R.string.key_xdripstatus_showbgi, true)) { - val bgi = -(bolusIob.activity + basalIob.activity) * 5 * Profile.fromMgdlToUnits(profile.isfMgdl, profileFunction.getUnits()) + val bgi = -(bolusIob.activity + basalIob.activity) * 5 * Profile.fromMgdlToUnits(profile.getIsfMgdl(), profileFunction.getUnits()) status += " " + (if (bgi >= 0) "+" else "") + DecimalFormatter.to2Decimal(bgi) } // COB - status += " " + iobCobCalculatorPlugin.getCobInfo(false, "StatusLinePlugin").generateCOBString() + status += " " + iobCobCalculator.getCobInfo(false, "StatusLinePlugin").generateCOBString() return status } } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/insulin/ActivityGraph.kt b/app/src/main/java/info/nightscout/androidaps/plugins/insulin/ActivityGraph.kt index a6575c1886..93932d5633 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/insulin/ActivityGraph.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/insulin/ActivityGraph.kt @@ -6,8 +6,8 @@ import android.util.AttributeSet import com.jjoe64.graphview.GraphView import com.jjoe64.graphview.series.DataPoint import com.jjoe64.graphview.series.LineGraphSeries -import info.nightscout.androidaps.db.Treatment -import info.nightscout.androidaps.interfaces.InsulinInterface +import info.nightscout.androidaps.database.entities.Bolus +import info.nightscout.androidaps.interfaces.Insulin import info.nightscout.androidaps.utils.T import java.util.* import kotlin.math.floor @@ -18,19 +18,20 @@ class ActivityGraph : GraphView { constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) - fun show(insulin: InsulinInterface) { + fun show(insulin: Insulin) { removeAllSeries() mSecondScale = null val hours = floor(insulin.dia + 1).toLong() - val t = Treatment().also { - it.date = 0 - it.insulin = 1.0 - } + val bolus = Bolus( + timestamp = 0, + amount = 1.0, + type = Bolus.Type.NORMAL + ) val activityArray: MutableList = ArrayList() val iobArray: MutableList = ArrayList() var time: Long = 0 while (time <= T.hours(hours).msecs()) { - val iob = t.iobCalc(time, insulin.dia) + val iob = insulin.iobCalcForTreatment(bolus, time, insulin.dia) activityArray.add(DataPoint(T.msecs(time).mins().toDouble(), iob.activityContrib)) iobArray.add(DataPoint(T.msecs(time).mins().toDouble(), iob.iobContrib)) time += T.mins(5).msecs() diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/insulin/InsulinFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/insulin/InsulinFragment.kt index 6d5f2c6df8..640a0b80be 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/insulin/InsulinFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/insulin/InsulinFragment.kt @@ -7,13 +7,13 @@ import android.view.ViewGroup import dagger.android.support.DaggerFragment import info.nightscout.androidaps.R import info.nightscout.androidaps.databinding.InsulinFragmentBinding -import info.nightscout.androidaps.interfaces.ActivePluginProvider +import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.utils.resources.ResourceHelper import javax.inject.Inject class InsulinFragment : DaggerFragment() { - @Inject lateinit var activePlugin: ActivePluginProvider + @Inject lateinit var activePlugin: ActivePlugin @Inject lateinit var resourceHelper: ResourceHelper private var _binding: InsulinFragmentBinding? = null diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/insulin/InsulinLyumjevPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/insulin/InsulinLyumjevPlugin.kt index 717429eb44..9c40a7bcdc 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/insulin/InsulinLyumjevPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/insulin/InsulinLyumjevPlugin.kt @@ -2,7 +2,7 @@ package info.nightscout.androidaps.plugins.insulin import dagger.android.HasAndroidInjector import info.nightscout.androidaps.R -import info.nightscout.androidaps.interfaces.InsulinInterface +import info.nightscout.androidaps.interfaces.Insulin import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.plugins.bus.RxBusWrapper @@ -19,7 +19,7 @@ class InsulinLyumjevPlugin @Inject constructor( rxBus: RxBusWrapper, aapsLogger: AAPSLogger ) : InsulinOrefBasePlugin(injector, resourceHelper, profileFunction, rxBus, aapsLogger) { - override val id get(): InsulinInterface.InsulinType = InsulinInterface.InsulinType.OREF_LYUMJEV + override val id get(): Insulin.InsulinType = Insulin.InsulinType.OREF_LYUMJEV override val friendlyName get(): String = resourceHelper.gs(R.string.lyumjev) override fun configuration(): JSONObject = JSONObject() diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/insulin/InsulinOrefBasePlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/insulin/InsulinOrefBasePlugin.kt index 1ffb885866..708f1dde9f 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/insulin/InsulinOrefBasePlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/insulin/InsulinOrefBasePlugin.kt @@ -3,8 +3,9 @@ package info.nightscout.androidaps.plugins.insulin import dagger.android.HasAndroidInjector import info.nightscout.androidaps.R import info.nightscout.androidaps.data.Iob -import info.nightscout.androidaps.db.Treatment -import info.nightscout.androidaps.interfaces.InsulinInterface +import info.nightscout.androidaps.database.embedments.InsulinConfiguration +import info.nightscout.androidaps.database.entities.Bolus +import info.nightscout.androidaps.interfaces.Insulin import info.nightscout.androidaps.interfaces.PluginBase import info.nightscout.androidaps.interfaces.PluginDescription import info.nightscout.androidaps.interfaces.PluginType @@ -13,7 +14,10 @@ import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification import info.nightscout.androidaps.plugins.general.overview.notifications.Notification +import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.resources.ResourceHelper +import kotlin.math.exp +import kotlin.math.pow /** * Created by adrian on 13.08.2017. @@ -33,7 +37,7 @@ abstract class InsulinOrefBasePlugin( .shortName(R.string.insulin_shortname) .visibleByDefault(false), aapsLogger, resourceHelper, injector -), InsulinInterface { +), Insulin { private var lastWarned: Long = 0 override val dia @@ -64,11 +68,11 @@ abstract class InsulinOrefBasePlugin( return profile?.dia ?: MIN_DIA } - override fun iobCalcForTreatment(treatment: Treatment, time: Long, dia: Double): Iob { + override fun iobCalcForTreatment(bolus: Bolus, time: Long, dia: Double): Iob { val result = Iob() val peak = peak - if (treatment.insulin != 0.0) { - val bolusTime = treatment.date + if (bolus.amount != 0.0) { + val bolusTime = bolus.timestamp val t = (time - bolusTime) / 1000.0 / 60.0 val td = dia * 60 //getDIA() always >= MIN_DIA val tp = peak.toDouble() @@ -76,14 +80,17 @@ abstract class InsulinOrefBasePlugin( if (t < td) { val tau = tp * (1 - tp / td) / (1 - 2 * tp / td) val a = 2 * tau / td - val S = 1 / (1 - a + (1 + a) * Math.exp(-td / tau)) - result.activityContrib = treatment.insulin * (S / Math.pow(tau, 2.0)) * t * (1 - t / td) * Math.exp(-t / tau) - result.iobContrib = treatment.insulin * (1 - S * (1 - a) * ((Math.pow(t, 2.0) / (tau * td * (1 - a)) - t / tau - 1) * Math.exp(-t / tau) + 1)) + val S = 1 / (1 - a + (1 + a) * exp(-td / tau)) + result.activityContrib = bolus.amount * (S / tau.pow(2.0)) * t * (1 - t / td) * exp(-t / tau) + result.iobContrib = bolus.amount * (1 - S * (1 - a) * ((t.pow(2.0) / (tau * td * (1 - a)) - t / tau - 1) * Math.exp(-t / tau) + 1)) } } return result } + override val insulinConfiguration: InsulinConfiguration + get() = InsulinConfiguration(friendlyName, (dia * 1000.0 * 3600.0).toLong(), T.mins(peak.toLong()).msecs()) + override val comment get(): String { var comment = commentStandardText() diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/insulin/InsulinOrefFreePeakPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/insulin/InsulinOrefFreePeakPlugin.kt index 7200032b3c..d80ac43481 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/insulin/InsulinOrefFreePeakPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/insulin/InsulinOrefFreePeakPlugin.kt @@ -2,12 +2,12 @@ package info.nightscout.androidaps.plugins.insulin import dagger.android.HasAndroidInjector import info.nightscout.androidaps.R -import info.nightscout.androidaps.interfaces.InsulinInterface +import info.nightscout.androidaps.interfaces.Insulin import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.plugins.bus.RxBusWrapper -import info.nightscout.androidaps.utils.extensions.storeInt -import info.nightscout.androidaps.utils.extensions.putInt +import info.nightscout.androidaps.extensions.storeInt +import info.nightscout.androidaps.extensions.putInt import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.sharedPreferences.SP import org.json.JSONObject @@ -26,7 +26,7 @@ class InsulinOrefFreePeakPlugin @Inject constructor( rxBus: RxBusWrapper, aapsLogger: AAPSLogger ) : InsulinOrefBasePlugin(injector, resourceHelper, profileFunction, rxBus, aapsLogger) { - override val id get(): InsulinInterface.InsulinType = InsulinInterface.InsulinType.OREF_FREE_PEAK + override val id get(): Insulin.InsulinType = Insulin.InsulinType.OREF_FREE_PEAK override val friendlyName get(): String = resourceHelper.gs(R.string.free_peak_oref) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/insulin/InsulinOrefRapidActingPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/insulin/InsulinOrefRapidActingPlugin.kt index bcf526253c..c18313a405 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/insulin/InsulinOrefRapidActingPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/insulin/InsulinOrefRapidActingPlugin.kt @@ -2,7 +2,7 @@ package info.nightscout.androidaps.plugins.insulin import dagger.android.HasAndroidInjector import info.nightscout.androidaps.R -import info.nightscout.androidaps.interfaces.InsulinInterface +import info.nightscout.androidaps.interfaces.Insulin import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.plugins.bus.RxBusWrapper @@ -22,7 +22,7 @@ class InsulinOrefRapidActingPlugin @Inject constructor( rxBus: RxBusWrapper, aapsLogger: AAPSLogger ) : InsulinOrefBasePlugin(injector, resourceHelper, profileFunction, rxBus, aapsLogger) { - override val id get(): InsulinInterface.InsulinType = InsulinInterface.InsulinType.OREF_RAPID_ACTING + override val id get(): Insulin.InsulinType = Insulin.InsulinType.OREF_RAPID_ACTING override val friendlyName get(): String = resourceHelper.gs(R.string.rapid_acting_oref) override fun configuration(): JSONObject = JSONObject() diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/insulin/InsulinOrefUltraRapidActingPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/insulin/InsulinOrefUltraRapidActingPlugin.kt index 257ae10ffe..7a32408984 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/insulin/InsulinOrefUltraRapidActingPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/insulin/InsulinOrefUltraRapidActingPlugin.kt @@ -2,7 +2,7 @@ package info.nightscout.androidaps.plugins.insulin import dagger.android.HasAndroidInjector import info.nightscout.androidaps.R -import info.nightscout.androidaps.interfaces.InsulinInterface +import info.nightscout.androidaps.interfaces.Insulin import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.plugins.bus.RxBusWrapper @@ -22,7 +22,7 @@ class InsulinOrefUltraRapidActingPlugin @Inject constructor( rxBus: RxBusWrapper, aapsLogger: AAPSLogger ) : InsulinOrefBasePlugin(injector, resourceHelper, profileFunction, rxBus, aapsLogger) { - override val id get(): InsulinInterface.InsulinType = InsulinInterface.InsulinType.OREF_ULTRA_RAPID_ACTING + override val id get(): Insulin.InsulinType = Insulin.InsulinType.OREF_ULTRA_RAPID_ACTING override val friendlyName get(): String = resourceHelper.gs(R.string.ultrarapid_oref) override fun configuration(): JSONObject = JSONObject() diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/InMemoryGlucoseValue.kt b/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/InMemoryGlucoseValue.kt index 52cd403f54..fef18ff95c 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/InMemoryGlucoseValue.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/InMemoryGlucoseValue.kt @@ -1,9 +1,2 @@ package info.nightscout.androidaps.plugins.iob.iobCobCalculator -import info.nightscout.androidaps.database.entities.GlucoseValue - -class InMemoryGlucoseValue @JvmOverloads constructor(var timestamp: Long = 0L, var value: Double = 0.0, var interpolated: Boolean = false) { - - constructor(gv: GlucoseValue) : this(gv.timestamp, gv.value) - // var generated : value doesn't correspond to real value with timestamp close to real BG -} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/IobCobCalculatorPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/IobCobCalculatorPlugin.kt index 1009972939..6a0d5bb2de 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/IobCobCalculatorPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/IobCobCalculatorPlugin.kt @@ -7,22 +7,25 @@ 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.database.AppRepository -import info.nightscout.androidaps.database.entities.GlucoseValue -import info.nightscout.androidaps.db.TemporaryBasal +import info.nightscout.androidaps.database.ValueWrapper +import info.nightscout.androidaps.database.entities.Bolus +import info.nightscout.androidaps.database.entities.ExtendedBolus +import info.nightscout.androidaps.database.entities.TemporaryBasal +import info.nightscout.androidaps.database.interfaces.end import info.nightscout.androidaps.events.* +import info.nightscout.androidaps.extensions.convertedToAbsolute +import info.nightscout.androidaps.extensions.iobCalc +import info.nightscout.androidaps.extensions.toTemporaryBasal import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.iob.iobCobCalculator.data.AutosensData -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventNewHistoryBgData import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventNewHistoryData import info.nightscout.androidaps.plugins.sensitivity.SensitivityAAPSPlugin import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref1Plugin import info.nightscout.androidaps.plugins.sensitivity.SensitivityWeightedAveragePlugin -import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DecimalFormatter import info.nightscout.androidaps.utils.FabricPrivacy @@ -31,14 +34,13 @@ 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.JSONArray -import java.util.concurrent.TimeUnit import javax.inject.Inject import javax.inject.Singleton -import kotlin.math.abs import kotlin.math.floor import kotlin.math.max -import kotlin.math.roundToLong +import kotlin.math.min @Singleton open class IobCobCalculatorPlugin @Inject constructor( @@ -49,8 +51,7 @@ open class IobCobCalculatorPlugin @Inject constructor( private val sp: SP, resourceHelper: ResourceHelper, private val profileFunction: ProfileFunction, - private val activePlugin: ActivePluginProvider, - private val treatmentsPlugin: TreatmentsPlugin, + private val activePlugin: ActivePlugin, private val sensitivityOref1Plugin: SensitivityOref1Plugin, private val sensitivityAAPSPlugin: SensitivityAAPSPlugin, private val sensitivityWeightedAveragePlugin: SensitivityWeightedAveragePlugin, @@ -64,64 +65,33 @@ open class IobCobCalculatorPlugin @Inject constructor( .neverVisible(true) .alwaysEnabled(true), aapsLogger, resourceHelper, injector -), IobCobCalculatorInterface { +), IobCobCalculator { private val disposable = CompositeDisposable() - private var iobTable = LongSparseArray() // oldest at index 0 - private var absIobTable = LongSparseArray() // oldest at index 0, absolute insulin in the body - private var autosensDataTable = LongSparseArray() // oldest at index 0 - private var basalDataTable = LongSparseArray() // oldest at index 0 - @Volatile override var bgReadings: List = listOf() // newest at index 0 - @Volatile var bucketedData: MutableList? = null - // we need to make sure that bucketed_data will always have the same timestamp for correct use of cached values - // once referenceTime != null all bucketed data should be (x * 5min) from referenceTime - var referenceTime: Long = -1 - private var lastUsed5minCalculation: Boolean? = null // true if used 5min bucketed data - override val dataLock = Any() + private var iobTable = LongSparseArray() // oldest at index 0 + private var basalDataTable = LongSparseArray() // oldest at index 0 + + override var ads: AutosensDataStore = AutosensDataStore() + + private val dataLock = Any() var stopCalculationTrigger = false private var thread: Thread? = null override fun onStart() { super.onStart() // EventConfigBuilderChange - disposable.add(rxBus + disposable += rxBus .toObservable(EventConfigBuilderChange::class.java) .observeOn(aapsSchedulers.io) - .subscribe({ event -> - stopCalculation("onEventConfigBuilderChange") - synchronized(dataLock) { - aapsLogger.debug(LTag.AUTOSENS, "Invalidating cached data because of configuration change.") - resetData() - } - runCalculation("onEventConfigBuilderChange", System.currentTimeMillis(), bgDataReload = false, limitDataToOldestAvailable = true, cause = event) - }, fabricPrivacy::logException) - ) + .subscribe({ event -> resetDataAndRunCalculation("onEventConfigBuilderChange", event) }, fabricPrivacy::logException) // EventNewBasalProfile - disposable.add(rxBus + disposable += rxBus .toObservable(EventNewBasalProfile::class.java) .observeOn(aapsSchedulers.io) - .subscribe({ event -> - stopCalculation("onNewProfile") - synchronized(dataLock) { - aapsLogger.debug(LTag.AUTOSENS, "Invalidating cached data because of new profile.") - resetData() - } - runCalculation("onNewProfile", System.currentTimeMillis(), bgDataReload = false, limitDataToOldestAvailable = true, cause = event) - }, fabricPrivacy::logException) - ) - // EventNewBG .... cannot be used for invalidating because only event with last BG is fired - disposable.add(rxBus - .toObservable(EventNewBG::class.java) - .observeOn(aapsSchedulers.io) - .debounce(1L, TimeUnit.SECONDS) - .subscribe({ event -> - stopCalculation("onEventNewBG") - runCalculation("onEventNewBG", System.currentTimeMillis(), bgDataReload = true, limitDataToOldestAvailable = true, cause = event) - }, fabricPrivacy::logException) - ) + .subscribe({ event -> resetDataAndRunCalculation("onNewProfile", event) }, fabricPrivacy::logException) // EventPreferenceChange - disposable.add(rxBus + disposable += rxBus .toObservable(EventPreferenceChange::class.java) .observeOn(aapsSchedulers.io) .subscribe({ event -> @@ -133,33 +103,19 @@ open class IobCobCalculatorPlugin @Inject constructor( event.isChanged(resourceHelper, R.string.key_openapsama_autosens_max) || event.isChanged(resourceHelper, R.string.key_openapsama_autosens_min) || event.isChanged(resourceHelper, R.string.key_insulin_oref_peak)) { - stopCalculation("onEventPreferenceChange") - synchronized(dataLock) { - aapsLogger.debug(LTag.AUTOSENS, "Invalidating cached data because of preference change.") - resetData() - } - runCalculation("onEventPreferenceChange", System.currentTimeMillis(), bgDataReload = false, limitDataToOldestAvailable = true, cause = event) + resetDataAndRunCalculation("onEventPreferenceChange", event) } }, fabricPrivacy::logException) - ) // EventAppInitialized - disposable.add(rxBus + disposable += rxBus .toObservable(EventAppInitialized::class.java) .observeOn(aapsSchedulers.io) .subscribe({ event -> runCalculation("onEventAppInitialized", System.currentTimeMillis(), bgDataReload = true, limitDataToOldestAvailable = true, cause = event) }, fabricPrivacy::logException) - ) // EventNewHistoryData - disposable.add(rxBus + disposable += rxBus .toObservable(EventNewHistoryData::class.java) .observeOn(aapsSchedulers.io) - .subscribe({ event -> newHistoryData(event, false) }, fabricPrivacy::logException) - ) - // EventNewHistoryBgData - disposable.add(rxBus - .toObservable(EventNewHistoryBgData::class.java) - .observeOn(aapsSchedulers.io) - .subscribe({ event -> newHistoryData(EventNewHistoryData(event.timestamp), true) }, fabricPrivacy::logException) - ) + .subscribe({ event -> newHistoryData(event.oldDataTimestamp, event.reloadBgData, if (event.newestGlucoseValue != null) EventNewBG(event.newestGlucoseValue) else event) }, fabricPrivacy::logException) } override fun onStop() { @@ -167,219 +123,42 @@ open class IobCobCalculatorPlugin @Inject constructor( super.onStop() } - override fun getAutosensDataTable(): LongSparseArray { - return autosensDataTable + private fun resetDataAndRunCalculation(reason: String, event: Event?) { + stopCalculation(reason) + clearCache() + ads.reset() + runCalculation(reason, System.currentTimeMillis(), bgDataReload = false, limitDataToOldestAvailable = true, cause = event) } - fun adjustToReferenceTime(someTime: Long): Long { - if (referenceTime == -1L) { - referenceTime = someTime - return someTime - } - var diff = abs(someTime - referenceTime) - diff %= T.mins(5).msecs() - if (diff > T.mins(2).plus(T.secs(30)).msecs()) diff -= T.mins(5).msecs() - return someTime + diff - } - - fun loadBgData(to: Long) { - val profile = profileFunction.getProfile(to) - var dia = Constants.defaultDIA - if (profile != null) dia = profile.dia - val start = to - T.hours((24 + dia).toLong()).msecs() - if (DateUtil.isCloseToNow(to)) { - // if close to now expect there can be some readings with time in close future (caused by wrong time setting) - // so read all records - bgReadings = repository.compatGetBgReadingsDataFromTime(start, false).blockingGet() - aapsLogger.debug(LTag.AUTOSENS, "BG data loaded. Size: " + bgReadings.size + " Start date: " + dateUtil.dateAndTimeString(start)) - } else { - bgReadings = repository.compatGetBgReadingsDataFromTime(start, to, false).blockingGet() - aapsLogger.debug(LTag.AUTOSENS, "BG data loaded. Size: " + bgReadings.size + " Start date: " + dateUtil.dateAndTimeString(start) + " End date: " + dateUtil.dateAndTimeString(to)) - } - } - - val isAbout5minData: Boolean - get() { - synchronized(dataLock) { - if (bgReadings.size < 3) return true - - var totalDiff: Long = 0 - for (i in 1 until bgReadings.size) { - val bgTime = bgReadings[i].timestamp - val lastBgTime = bgReadings[i - 1].timestamp - var diff = lastBgTime - bgTime - diff %= T.mins(5).msecs() - if (diff > T.mins(2).plus(T.secs(30)).msecs()) diff -= T.mins(5).msecs() - totalDiff += diff - diff = abs(diff) - if (diff > T.secs(30).msecs()) { - aapsLogger.debug(LTag.AUTOSENS, "Interval detection: values: " + bgReadings.size + " diff: " + diff / 1000 + "[s] is5minData: " + false) - return false - } - } - val averageDiff = totalDiff / bgReadings.size / 1000 - val is5minData = averageDiff < 1 - aapsLogger.debug(LTag.AUTOSENS, "Interval detection: values: " + bgReadings.size + " averageDiff: " + averageDiff + "[s] is5minData: " + is5minData) - return is5minData - } - } - - private fun resetData() { + fun clearCache() { synchronized(dataLock) { + aapsLogger.debug(LTag.AUTOSENS, "Clearing cached data.") iobTable = LongSparseArray() - autosensDataTable = LongSparseArray() basalDataTable = LongSparseArray() - absIobTable = LongSparseArray() } } - fun createBucketedData() { - val fiveMinData = isAbout5minData - if (lastUsed5minCalculation != null && lastUsed5minCalculation != fiveMinData) { - // changing mode => clear cache - aapsLogger.debug("Invalidating cached data because of changed mode.") - resetData() - } - lastUsed5minCalculation = fiveMinData - if (isAbout5minData) createBucketedData5min() else createBucketedDataRecalculated() - } - - fun findNewer(time: Long): GlucoseValue? { - var lastFound = bgReadings[0] - if (lastFound.timestamp < time) return null - for (i in 1 until bgReadings.size) { - if (bgReadings[i].timestamp == time) return bgReadings[i] - if (bgReadings[i].timestamp > time) continue - lastFound = bgReadings[i - 1] - if (bgReadings[i].timestamp < time) break - } - return lastFound - } - - fun findOlder(time: Long): GlucoseValue? { - var lastFound = bgReadings[bgReadings.size - 1] - if (lastFound.timestamp > time) return null - for (i in bgReadings.size - 2 downTo 0) { - if (bgReadings[i].timestamp == time) return bgReadings[i] - if (bgReadings[i].timestamp < time) continue - lastFound = bgReadings[i + 1] - if (bgReadings[i].timestamp > time) break - } - return lastFound - } - - private fun createBucketedDataRecalculated() { - if (bgReadings.size < 3) { - bucketedData = null - return - } - bucketedData = ArrayList() - var currentTime = bgReadings[0].timestamp - bgReadings[0].timestamp % T.mins(5).msecs() - currentTime = adjustToReferenceTime(currentTime) - aapsLogger.debug("Adjusted time " + dateUtil.dateAndTimeAndSecondsString(currentTime)) - //log.debug("First reading: " + new Date(currentTime).toLocaleString()); - while (true) { - // test if current value is older than current time - val newer = findNewer(currentTime) - val older = findOlder(currentTime) - if (newer == null || older == null) break - if (older.timestamp == newer.timestamp) { // direct hit - bucketedData?.add(InMemoryGlucoseValue(newer)) - } else { - val bgDelta = newer.value - older.value - val timeDiffToNew = newer.timestamp - currentTime - val currentBg = newer.value - timeDiffToNew.toDouble() / (newer.timestamp - older.timestamp) * bgDelta - val newBgReading = InMemoryGlucoseValue(currentTime, currentBg.roundToLong().toDouble(), true) - bucketedData?.add(newBgReading) - //log.debug("BG: " + newBgReading.value + " (" + new Date(newBgReading.date).toLocaleString() + ") Prev: " + older.value + " (" + new Date(older.date).toLocaleString() + ") Newer: " + newer.value + " (" + new Date(newer.date).toLocaleString() + ")"); - } - currentTime -= T.mins(5).msecs() - } - } - - private fun createBucketedData5min() { - if (bgReadings.size < 3) { - bucketedData = null - return - } - val bData: MutableList = ArrayList() - bData.add(InMemoryGlucoseValue(bgReadings[0])) - aapsLogger.debug(LTag.AUTOSENS, "Adding. bgTime: " + DateUtil.toISOString(bgReadings[0].timestamp) + " lastBgTime: " + "none-first-value" + " " + bgReadings[0].toString()) - var j = 0 - for (i in 1 until bgReadings.size) { - val bgTime = bgReadings[i].timestamp - var lastBgTime = bgReadings[i - 1].timestamp - //log.error("Processing " + i + ": " + new Date(bgTime).toString() + " " + bgReadings.get(i).value + " Previous: " + new Date(lastBgTime).toString() + " " + bgReadings.get(i - 1).value); - check(!(bgReadings[i].value < 39 || bgReadings[i - 1].value < 39)) { "<39" } - var elapsedMinutes = (bgTime - lastBgTime) / (60 * 1000) - when { - abs(elapsedMinutes) > 8 -> { - // interpolate missing data points - var lastBg = bgReadings[i - 1].value - elapsedMinutes = abs(elapsedMinutes) - //console.error(elapsed_minutes); - var nextBgTime: Long - while (elapsedMinutes > 5) { - nextBgTime = lastBgTime - 5 * 60 * 1000 - j++ - val gapDelta = bgReadings[i].value - lastBg - //console.error(gapDelta, lastBg, elapsed_minutes); - val nextBg = lastBg + 5.0 / elapsedMinutes * gapDelta - val newBgReading = InMemoryGlucoseValue(nextBgTime, nextBg.roundToLong().toDouble(), true) - //console.error("Interpolated", bData[j]); - bData.add(newBgReading) - aapsLogger.debug(LTag.AUTOSENS, "Adding. bgTime: " + DateUtil.toISOString(bgTime) + " lastBgTime: " + DateUtil.toISOString(lastBgTime) + " " + newBgReading.toString()) - elapsedMinutes -= 5 - lastBg = nextBg - lastBgTime = nextBgTime - } - j++ - val newBgReading = InMemoryGlucoseValue(bgTime, bgReadings[i].value) - bData.add(newBgReading) - aapsLogger.debug(LTag.AUTOSENS, "Adding. bgTime: " + DateUtil.toISOString(bgTime) + " lastBgTime: " + DateUtil.toISOString(lastBgTime) + " " + newBgReading.toString()) - } - - abs(elapsedMinutes) > 2 -> { - j++ - val newBgReading = InMemoryGlucoseValue(bgTime, bgReadings[i].value) - bData.add(newBgReading) - aapsLogger.debug(LTag.AUTOSENS, "Adding. bgTime: " + DateUtil.toISOString(bgTime) + " lastBgTime: " + DateUtil.toISOString(lastBgTime) + " " + newBgReading.toString()) - } - - else -> { - bData[j].value = (bData[j].value + bgReadings[i].value) / 2 - //log.error("***** Average"); - } - } - } - - // Normalize bucketed data - val oldest = bData[bData.size - 1] - oldest.timestamp = adjustToReferenceTime(oldest.timestamp) - aapsLogger.debug("Adjusted time " + dateUtil.dateAndTimeAndSecondsString(oldest.timestamp)) - for (i in bData.size - 2 downTo 0) { - val current = bData[i] - val previous = bData[i + 1] - val mSecDiff = current.timestamp - previous.timestamp - val adjusted = (mSecDiff - T.mins(5).msecs()) / 1000 - aapsLogger.debug(LTag.AUTOSENS, "Adjusting bucketed data time. Current: " + dateUtil.dateAndTimeAndSecondsString(current.timestamp) + " to: " + dateUtil.dateAndTimeAndSecondsString(previous.timestamp + T.mins(5).msecs()) + " by " + adjusted + " sec") - if (abs(adjusted) > 90) { - // too big adjustment, fallback to non 5 min data - aapsLogger.debug(LTag.AUTOSENS, "Fallback to non 5 min data") - createBucketedDataRecalculated() - return - } - current.timestamp = previous.timestamp + T.mins(5).msecs() - } - aapsLogger.debug(LTag.AUTOSENS, "Bucketed data created. Size: " + bData.size) - bucketedData = bData + private fun oldestDataAvailable(): Long { + var oldestTime = System.currentTimeMillis() + val oldestTempBasal = repository.getOldestTemporaryBasalRecord() + if (oldestTempBasal != null) oldestTime = min(oldestTime, oldestTempBasal.timestamp) + val oldestExtendedBolus = repository.getOldestExtendedBolusRecord() + if (oldestExtendedBolus != null) oldestTime = min(oldestTime, oldestExtendedBolus.timestamp) + val oldestBolus = repository.getOldestBolusRecord() + if (oldestBolus != null) oldestTime = min(oldestTime, oldestBolus.timestamp) + val oldestCarbs = repository.getOldestCarbsRecord() + if (oldestCarbs != null) oldestTime = min(oldestTime, oldestCarbs.timestamp) + val oldestPs = repository.getOldestEffectiveProfileSwitchRecord() + if (oldestPs != null) oldestTime = min(oldestTime, oldestPs.timestamp) + oldestTime -= 15 * 60 * 1000L // allow 15 min before + return oldestTime } fun calculateDetectionStart(from: Long, limitDataToOldestAvailable: Boolean): Long { val profile = profileFunction.getProfile(from) var dia = Constants.defaultDIA if (profile != null) dia = profile.dia - val oldestDataAvailable = treatmentsPlugin.oldestDataAvailable() + val oldestDataAvailable = oldestDataAvailable() val getBGDataFrom: Long if (limitDataToOldestAvailable) { getBGDataFrom = max(oldestDataAvailable, (from - T.hours(1).msecs() * (24 + dia)).toLong()) @@ -388,77 +167,56 @@ open class IobCobCalculatorPlugin @Inject constructor( return getBGDataFrom } - override fun calculateFromTreatmentsAndTempsSynchronized(time: Long, profile: Profile?): IobTotal { - synchronized(dataLock) { return calculateFromTreatmentsAndTemps(time, profile) } - } - - private fun calculateFromTreatmentsAndTempsSynchronized(time: Long, lastAutosensResult: AutosensResult, exercise_mode: Boolean, half_basal_exercise_target: Int, isTempTarget: Boolean): IobTotal { - synchronized(dataLock) { return calculateFromTreatmentsAndTemps(time, lastAutosensResult, exercise_mode, half_basal_exercise_target, isTempTarget) } - } - - fun calculateFromTreatmentsAndTemps(fromTime: Long, profile: Profile?): IobTotal { + override fun calculateFromTreatmentsAndTemps(toTime: Long, profile: Profile): IobTotal { val now = System.currentTimeMillis() - val time = roundUpTime(fromTime) + val time = ads.roundUpTime(toTime) val cacheHit = iobTable[time] if (time < now && cacheHit != null) { //og.debug(">>> calculateFromTreatmentsAndTemps Cache hit " + new Date(time).toLocaleString()); return cacheHit } // else log.debug(">>> calculateFromTreatmentsAndTemps Cache miss " + new Date(time).toLocaleString()); - val bolusIob = treatmentsPlugin.getCalculationToTimeTreatments(time).round() - val basalIob = treatmentsPlugin.getCalculationToTimeTempBasals(time, true, now).round() + val bolusIob = calculateIobFromBolusToTime(time).round() + val basalIob = calculateIobToTimeFromTempBasalsIncludingConvertedExtended(time).round() // OpenAPSSMB only // Add expected zero temp basal for next 240 minutes val basalIobWithZeroTemp = basalIob.copy() - val t = TemporaryBasal(injector) - .date(now + 60 * 1000L) - .duration(240) - .absolute(0.0) - if (t.date < time) { - val calc = t.iobCalc(time, profile) + val t = TemporaryBasal( + timestamp = now + 60 * 1000L, + duration = 240, + rate = 0.0, + isAbsolute = true, + type = TemporaryBasal.Type.NORMAL) + if (t.timestamp < time) { + val calc = t.iobCalc(time, profile, activePlugin.activeInsulin) basalIobWithZeroTemp.plus(calc) } basalIob.iobWithZeroTemp = IobTotal.combine(bolusIob, basalIobWithZeroTemp).round() val iobTotal = IobTotal.combine(bolusIob, basalIob).round() if (time < System.currentTimeMillis()) { - iobTable.put(time, iobTotal) + synchronized(dataLock) { + iobTable.put(time, iobTotal) + } } return iobTotal } - fun calculateAbsInsulinFromTreatmentsAndTempsSynchronized(fromTime: Long): IobTotal { - synchronized(dataLock) { - val now = System.currentTimeMillis() - val time = roundUpTime(fromTime) - val cacheHit = absIobTable[time] - if (time < now && cacheHit != null) { - //log.debug(">>> calculateFromTreatmentsAndTemps Cache hit " + new Date(time).toLocaleString()); - return cacheHit - } // else log.debug(">>> calculateFromTreatmentsAndTemps Cache miss " + new Date(time).toLocaleString()); - val bolusIob = treatmentsPlugin.getCalculationToTimeTreatments(time).round() - val basalIob = treatmentsPlugin.getAbsoluteIOBTempBasals(time).round() - val iobTotal = IobTotal.combine(bolusIob, basalIob).round() - if (time < System.currentTimeMillis()) { - absIobTable.put(time, iobTotal) - } - return iobTotal - } - } - private fun calculateFromTreatmentsAndTemps(time: Long, lastAutosensResult: AutosensResult, exercise_mode: Boolean, half_basal_exercise_target: Int, isTempTarget: Boolean): IobTotal { - val now = DateUtil.now() - val bolusIob = treatmentsPlugin.getCalculationToTimeTreatments(time).round() - val basalIob = treatmentsPlugin.getCalculationToTimeTempBasals(time, now, lastAutosensResult, exercise_mode, half_basal_exercise_target, isTempTarget).round() + val now = dateUtil.now() + val bolusIob = calculateIobFromBolusToTime(time).round() + val basalIob = getCalculationToTimeTempBasals(time, lastAutosensResult, exercise_mode, half_basal_exercise_target, isTempTarget).round() // OpenAPSSMB only // Add expected zero temp basal for next 240 minutes val basalIobWithZeroTemp = basalIob.copy() - val t = TemporaryBasal(injector) - .date(now + 60 * 1000L) - .duration(240) - .absolute(0.0) - if (t.date < time) { - val profile = profileFunction.getProfile(t.date) + val t = TemporaryBasal( + timestamp = now + 60 * 1000L, + duration = 240, + rate = 0.0, + isAbsolute = true, + type = TemporaryBasal.Type.NORMAL) + if (t.timestamp < time) { + val profile = profileFunction.getProfile(t.timestamp) if (profile != null) { - val calc = t.iobCalc(time, profile, lastAutosensResult, exercise_mode, half_basal_exercise_target, isTempTarget) + val calc = t.iobCalc(time, profile, lastAutosensResult, exercise_mode, half_basal_exercise_target, isTempTarget, activePlugin.activeInsulin) basalIobWithZeroTemp.plus(calc) } } @@ -466,53 +224,32 @@ open class IobCobCalculatorPlugin @Inject constructor( return IobTotal.combine(bolusIob, basalIob).round() } - fun findPreviousTimeFromBucketedData(time: Long): Long? { - val bData = bucketedData ?: return null - for (index in bData.indices) { - if (bData[index].timestamp <= time) return bData[index].timestamp - } - return null - } - - fun getBasalData(profile: Profile, fromTime: Long): BasalData { - synchronized(dataLock) { - val now = System.currentTimeMillis() - val time = roundUpTime(fromTime) - var retVal = basalDataTable[time] - if (retVal == null) { - //log.debug(">>> getBasalData Cache miss " + new Date(time).toLocaleString()); - retVal = BasalData() - val tb = treatmentsPlugin.getTempBasalFromHistory(time) - retVal.basal = profile.getBasal(time) - if (tb != null) { - retVal.isTempBasalRunning = true - retVal.tempBasalAbsolute = tb.tempBasalConvertedToAbsolute(time, profile) - } else { - retVal.isTempBasalRunning = false - retVal.tempBasalAbsolute = retVal.basal - } - if (time < now) { + override fun getBasalData(profile: Profile, fromTime: Long): BasalData { + val now = System.currentTimeMillis() + val time = ads.roundUpTime(fromTime) + var retVal = basalDataTable[time] + if (retVal == null) { + //log.debug(">>> getBasalData Cache miss " + new Date(time).toLocaleString()); + retVal = BasalData() + val tb = getTempBasalIncludingConvertedExtended(time) + retVal.basal = profile.getBasal(time) + if (tb != null) { + retVal.isTempBasalRunning = true + retVal.tempBasalAbsolute = tb.convertedToAbsolute(time, profile) + } else { + retVal.isTempBasalRunning = false + retVal.tempBasalAbsolute = retVal.basal + } + if (time < now) { + synchronized(dataLock) { basalDataTable.append(time, retVal) } - } //else log.debug(">>> getBasalData Cache hit " + new Date(time).toLocaleString()); - return retVal - } - } - - override fun getAutosensData(fromTime: Long): AutosensData? { - var time = fromTime - synchronized(dataLock) { - val now = System.currentTimeMillis() - if (time > now) { - return null } - val previous = findPreviousTimeFromBucketedData(time) ?: return null - time = roundUpTime(previous) - return autosensDataTable[time] - } + } //else log.debug(">>> getBasalData Cache hit " + new Date(time).toLocaleString()); + return retVal } - fun getLastAutosensDataSynchronized(reason: String): AutosensData? { + override fun getLastAutosensDataWithWaitForCalculationFinish(reason: String): AutosensData? { if (thread?.isAlive == true) { aapsLogger.debug(LTag.AUTOSENS, "AUTOSENSDATA is waiting for calculation thread: $reason") try { @@ -521,146 +258,87 @@ open class IobCobCalculatorPlugin @Inject constructor( } aapsLogger.debug(LTag.AUTOSENS, "AUTOSENSDATA finished waiting for calculation thread: $reason") } - synchronized(dataLock) { return getLastAutosensData(reason) } + return ads.getLastAutosensData(reason, aapsLogger, dateUtil) } - override fun getCobInfo(_synchronized: Boolean, reason: String): CobInfo { - val autosensData = if (_synchronized) getLastAutosensDataSynchronized(reason) else getLastAutosensData(reason) + override fun getCobInfo(waitForCalculationFinish: Boolean, reason: String): CobInfo { + val autosensData = + if (waitForCalculationFinish) getLastAutosensDataWithWaitForCalculationFinish(reason) + else ads.getLastAutosensData(reason, aapsLogger, dateUtil) var displayCob: Double? = null var futureCarbs = 0.0 - val now = DateUtil.now() - val treatments = treatmentsPlugin.treatmentsFromHistory + val now = dateUtil.now() + val carbs = repository.getCarbsDataFromTimeExpanded(now, true).blockingGet() if (autosensData != null) { displayCob = autosensData.cob - for (treatment in treatments) { - if (!treatment.isValid) continue - if (roundUpTime(treatment.date) > roundUpTime(autosensData.time) && treatment.date <= now && treatment.carbs > 0) { - displayCob += treatment.carbs + carbs.forEach { carb -> + if (ads.roundUpTime(carb.timestamp) > ads.roundUpTime(autosensData.time) && carb.timestamp <= now) { + displayCob += carb.amount } } } - for (treatment in treatments) { - if (!treatment.isValid) continue - if (treatment.date > now && treatment.carbs > 0) { - futureCarbs += treatment.carbs - } - } + // Future carbs + carbs.forEach { carb -> if (carb.timestamp > now) futureCarbs += carb.amount } return CobInfo(displayCob, futureCarbs) } - fun slowAbsorptionPercentage(timeInMinutes: Int): Double { - var sum = 0.0 - var count = 0 - val valuesToProcess = timeInMinutes / 5 - synchronized(dataLock) { - var i = autosensDataTable.size() - 1 - while (i >= 0 && count < valuesToProcess) { - if (autosensDataTable.valueAt(i).failoverToMinAbsorbtionRate) sum++ - count++ - i-- - } - } - return sum / count - } - - override fun getLastAutosensData(reason: String): AutosensData? { - if (autosensDataTable.size() < 1) { - aapsLogger.debug(LTag.AUTOSENS, "AUTOSENSDATA null: autosensDataTable empty ($reason)") - return null - } - val data: AutosensData? = try { - autosensDataTable.valueAt(autosensDataTable.size() - 1) - } catch (e: Exception) { - // data can be processed on the background - // in this rare case better return null and do not block UI - // APS plugin should use getLastAutosensDataSynchronized where the blocking is not an issue - aapsLogger.error("AUTOSENSDATA null: Exception caught ($reason)") - return null - } - if (data == null) { - aapsLogger.error("AUTOSENSDATA null: data==null") - return null - } - return if (data.time < System.currentTimeMillis() - 11 * 60 * 1000) { - aapsLogger.debug(LTag.AUTOSENS, "AUTOSENSDATA null: data is old (" + reason + ") size()=" + autosensDataTable.size() + " lastData=" + dateUtil.dateAndTimeAndSecondsString(data.time)) - null + override fun getMealDataWithWaitingForCalculationFinish(): MealData { + val result = MealData() + val now = System.currentTimeMillis() + val maxAbsorptionHours: Double = if (sensitivityAAPSPlugin.isEnabled() || sensitivityWeightedAveragePlugin.isEnabled()) { + sp.getDouble(R.string.key_absorption_maxtime, Constants.DEFAULT_MAX_ABSORPTION_TIME) } else { - aapsLogger.debug(LTag.AUTOSENS, "AUTOSENSDATA ($reason) $data") - data + sp.getDouble(R.string.key_absorption_cutoff, Constants.DEFAULT_MAX_ABSORPTION_TIME) } - } - - override fun lastDataTime(): String { - return if (autosensDataTable.size() > 0) dateUtil.dateAndTimeAndSecondsString(autosensDataTable.valueAt(autosensDataTable.size() - 1).time) else "autosensDataTable empty" - } - - val mealData: MealData - get() { - val result = MealData() - val profile = profileFunction.getProfile() ?: return result - val now = System.currentTimeMillis() - val diaAgo = now - java.lang.Double.valueOf(profile.dia * T.hours(1).msecs()).toLong() - val maxAbsorptionHours: Double = if (sensitivityAAPSPlugin.isEnabled() || sensitivityWeightedAveragePlugin.isEnabled()) { - sp.getDouble(R.string.key_absorption_maxtime, Constants.DEFAULT_MAX_ABSORPTION_TIME) - } else { - sp.getDouble(R.string.key_absorption_cutoff, Constants.DEFAULT_MAX_ABSORPTION_TIME) - } - val absorptionTimeAgo = now - java.lang.Double.valueOf(maxAbsorptionHours * T.hours(1).msecs()).toLong() - val treatments = treatmentsPlugin.treatmentsFromHistory - for (treatment in treatments) { - if (!treatment.isValid) continue - val t = treatment.date - if (t in (diaAgo + 1)..now) { - if (treatment.insulin > 0 && treatment.mealBolus) { - result.boluses += treatment.insulin - } - } - if (t in (absorptionTimeAgo + 1)..now) { - if (treatment.carbs >= 1) { - result.carbs += treatment.carbs - if (t > result.lastCarbTime) result.lastCarbTime = t - } + val absorptionTimeAgo = now - (maxAbsorptionHours * T.hours(1).msecs()).toLong() + repository.getCarbsDataFromTimeToTimeExpanded(absorptionTimeAgo + 1, now, true) + .blockingGet() + .forEach { + if (it.amount > 0) { + result.carbs += it.amount + if (it.timestamp > result.lastCarbTime) result.lastCarbTime = it.timestamp } } - val autosensData = getLastAutosensDataSynchronized("getMealData()") - if (autosensData != null) { - result.mealCOB = autosensData.cob - result.slopeFromMinDeviation = autosensData.slopeFromMinDeviation - result.slopeFromMaxDeviation = autosensData.slopeFromMaxDeviation - result.usedMinCarbsImpact = autosensData.usedMinCarbsImpact - } - result.lastBolusTime = treatmentsPlugin.lastBolusTime - return result + val autosensData = getLastAutosensDataWithWaitForCalculationFinish("getMealData()") + if (autosensData != null) { + result.mealCOB = autosensData.cob + result.slopeFromMinDeviation = autosensData.slopeFromMinDeviation + result.slopeFromMaxDeviation = autosensData.slopeFromMaxDeviation + result.usedMinCarbsImpact = autosensData.usedMinCarbsImpact } + val lastBolus = repository.getLastBolusRecordWrapped().blockingGet() + result.lastBolusTime = if (lastBolus is ValueWrapper.Existing) lastBolus.value.timestamp else 0L + return result + } override fun calculateIobArrayInDia(profile: Profile): Array { // predict IOB out to DIA plus 30m var time = System.currentTimeMillis() - time = roundUpTime(time) + time = ads.roundUpTime(time) val len = ((profile.dia * 60 + 30) / 5).toInt() val array = Array(len) { IobTotal(0) } for ((pos, i) in (0 until len).withIndex()) { val t = time + i * 5 * 60000 - val iob = calculateFromTreatmentsAndTempsSynchronized(t, profile) + val iob = calculateFromTreatmentsAndTemps(t, profile) array[pos] = iob } return array } - fun calculateIobArrayForSMB(lastAutosensResult: AutosensResult, exercise_mode: Boolean, half_basal_exercise_target: Int, isTempTarget: Boolean): Array { + override fun calculateIobArrayForSMB(lastAutosensResult: AutosensResult, exercise_mode: Boolean, half_basal_exercise_target: Int, isTempTarget: Boolean): Array { // predict IOB out to DIA plus 30m - val now = DateUtil.now() + val now = dateUtil.now() val len = 4 * 60 / 5 val array = Array(len) { IobTotal(0) } for ((pos, i) in (0 until len).withIndex()) { val t = now + i * 5 * 60000 - val iob = calculateFromTreatmentsAndTempsSynchronized(t, lastAutosensResult, exercise_mode, half_basal_exercise_target, isTempTarget) + val iob = calculateFromTreatmentsAndTemps(t, lastAutosensResult, exercise_mode, half_basal_exercise_target, isTempTarget) array[pos] = iob } return array } - fun iobArrayToString(array: Array): String { + override fun iobArrayToString(array: Array): String { val sb = StringBuilder() sb.append("[") for (i in array) { @@ -671,37 +349,35 @@ open class IobCobCalculatorPlugin @Inject constructor( return sb.toString() } - fun detectSensitivityWithLock(fromTime: Long, toTime: Long): AutosensResult { - synchronized(dataLock) { return activePlugin.activeSensitivity.detectSensitivity(this, fromTime, toTime) } - } - fun stopCalculation(from: String) { if (thread?.state != Thread.State.TERMINATED) { stopCalculationTrigger = true aapsLogger.debug(LTag.AUTOSENS, "Stopping calculation thread: $from") - while (thread?.state != Thread.State.TERMINATED) { + while (thread != null && thread?.state != Thread.State.TERMINATED) { SystemClock.sleep(100) } aapsLogger.debug(LTag.AUTOSENS, "Calculation thread stopped: $from") } } - fun runCalculation(from: String, end: Long, bgDataReload: Boolean, limitDataToOldestAvailable: Boolean, cause: Event) { + fun runCalculation(from: String, end: Long, bgDataReload: Boolean, limitDataToOldestAvailable: Boolean, cause: Event?) { aapsLogger.debug(LTag.AUTOSENS, "Starting calculation thread: " + from + " to " + dateUtil.dateAndTimeAndSecondsString(end)) if (thread == null || thread?.state == Thread.State.TERMINATED) { - thread = if (sensitivityOref1Plugin.isEnabled()) IobCobOref1Thread(injector, this, treatmentsPlugin, from, end, bgDataReload, limitDataToOldestAvailable, cause) else IobCobThread(injector, this, treatmentsPlugin, from, end, bgDataReload, limitDataToOldestAvailable, cause) + thread = + if (sensitivityOref1Plugin.isEnabled()) IobCobOref1Thread(injector, this, from, end, bgDataReload, limitDataToOldestAvailable, cause) + else IobCobThread(injector, this, from, end, bgDataReload, limitDataToOldestAvailable, cause) thread?.start() } } // When historical data is changed (coming from NS etc) finished calculations after this date must be invalidated - private fun newHistoryData(ev: EventNewHistoryData, bgDataReload: Boolean) { + private fun newHistoryData(oldDataTimestamp: Long, bgDataReload: Boolean, event: Event) { //log.debug("Locking onNewHistoryData"); stopCalculation("onEventNewHistoryData") synchronized(dataLock) { // clear up 5 min back for proper COB calculation - val time = ev.time - 5 * 60 * 1000L + val time = oldDataTimestamp - 5 * 60 * 1000L aapsLogger.debug(LTag.AUTOSENS, "Invalidating cached data to: " + dateUtil.dateAndTimeAndSecondsString(time)) for (index in iobTable.size() - 1 downTo 0) { if (iobTable.keyAt(index) > time) { @@ -711,22 +387,6 @@ open class IobCobCalculatorPlugin @Inject constructor( break } } - for (index in absIobTable.size() - 1 downTo 0) { - if (absIobTable.keyAt(index) > time) { - aapsLogger.debug(LTag.AUTOSENS, "Removing from absIobTable: " + dateUtil.dateAndTimeAndSecondsString(absIobTable.keyAt(index))) - absIobTable.removeAt(index) - } else { - break - } - } - for (index in autosensDataTable.size() - 1 downTo 0) { - if (autosensDataTable.keyAt(index) > time) { - aapsLogger.debug(LTag.AUTOSENS, "Removing from autosensDataTable: " + dateUtil.dateAndTimeAndSecondsString(autosensDataTable.keyAt(index))) - autosensDataTable.removeAt(index) - } else { - break - } - } for (index in basalDataTable.size() - 1 downTo 0) { if (basalDataTable.keyAt(index) > time) { aapsLogger.debug(LTag.AUTOSENS, "Removing from basalDataTable: " + dateUtil.dateAndTimeAndSecondsString(basalDataTable.keyAt(index))) @@ -735,53 +395,22 @@ open class IobCobCalculatorPlugin @Inject constructor( break } } + ads.newHistoryData(time, aapsLogger, dateUtil) } - runCalculation("onEventNewHistoryData", System.currentTimeMillis(), bgDataReload, true, ev) + runCalculation("onEventNewHistoryData", System.currentTimeMillis(), bgDataReload, true, event) //log.debug("Releasing onNewHistoryData"); } - fun clearCache() { - synchronized(dataLock) { - aapsLogger.debug(LTag.AUTOSENS, "Clearing cached data.") - iobTable = LongSparseArray() - autosensDataTable = LongSparseArray() - basalDataTable = LongSparseArray() + override fun convertToJSONArray(iobArray: Array): JSONArray { + val array = JSONArray() + for (i in iobArray.indices) { + array.put(iobArray[i].determineBasalJson(dateUtil)) } - } - - /* - * Return last BgReading from database or null if db is empty - */ - fun lastBg(): GlucoseValue? { - val bgList = bgReadings - for (i in bgList.indices) if (bgList[i].value >= 39) return bgList[i] - return null - } - - /* - * Return bg reading if not old ( <9 min ) - * or null if older - */ - fun actualBg(): GlucoseValue? { - val lastBg = lastBg() ?: return null - return if (lastBg.timestamp > System.currentTimeMillis() - 9 * 60 * 1000) lastBg else null + return array } companion object { - // roundup to whole minute - fun roundUpTime(time: Long): Long { - return if (time % 60000 == 0L) time else (time / 60000 + 1) * 60000 - } - - fun convertToJSONArray(iobArray: Array): JSONArray { - val array = JSONArray() - for (i in iobArray.indices) { - array.put(iobArray[i].determineBasalJson()) - } - return array - } - // From https://gist.github.com/IceCreamYou/6ffa1b18c4c8f6aeaad2 // Returns the value at a given percentile in a sorted numeric array. // "Linear interpolation between closest ranks" method @@ -796,4 +425,190 @@ open class IobCobCalculatorPlugin @Inject constructor( return if (upper >= arr.size) arr[lower.toInt()] else arr[lower.toInt()] * (1 - weight) + arr[upper.toInt()] * weight } } + + /** + * Time range to the past for IOB calculation + * @return milliseconds + */ + fun range(): Long = ((/*overviewData.rangeToDisplay + */(profileFunction.getProfile()?.dia + ?: Constants.defaultDIA)) * 60 * 60 * 1000).toLong() + + override fun calculateIobFromBolus(): IobTotal = calculateIobFromBolusToTime(dateUtil.now()) + + /** + * Calculate IobTotal from boluses and extended to provided timestamp. + * NOTE: Only isValid == true boluses are included + * NOTE: if faking by TBR by extended boluses is enabled, extended boluses are not included + * and are calculated towards temporary basals + * + * @param toTime timestamp in milliseconds + * @return calculated iob + */ + private fun calculateIobFromBolusToTime(toTime: Long): IobTotal { + val total = IobTotal(toTime) + val profile = profileFunction.getProfile() ?: return total + val dia = profile.dia + val divisor = sp.getDouble(R.string.key_openapsama_bolussnooze_dia_divisor, 2.0) + + val boluses = repository.getBolusesDataFromTime(toTime - range(), true).blockingGet() + + boluses.forEach { t -> + if (t.isValid && t.timestamp < toTime) { + val tIOB = t.iobCalc(activePlugin, toTime, dia) + total.iob += tIOB.iobContrib + total.activity += tIOB.activityContrib + if (t.amount > 0 && t.timestamp > total.lastBolusTime) total.lastBolusTime = t.timestamp + if (t.type != Bolus.Type.SMB) { + // instead of dividing the DIA that only worked on the bilinear curves, + // multiply the time the treatment is seen active. + val timeSinceTreatment = toTime - t.timestamp + val snoozeTime = t.timestamp + (timeSinceTreatment * divisor).toLong() + val bIOB = t.iobCalc(activePlugin, snoozeTime, dia) + total.bolussnooze += bIOB.iobContrib + } + } + } + + total.plus(calculateIobToTimeFromExtendedBoluses(toTime)) + return total + } + + private fun calculateIobToTimeFromExtendedBoluses(toTime: Long): IobTotal { + val total = IobTotal(toTime) + val now = dateUtil.now() + val pumpInterface = activePlugin.activePump + if (!pumpInterface.isFakingTempsByExtendedBoluses) { + val extendedBoluses = repository.getExtendedBolusDataFromTimeToTime(toTime - range(), toTime, true).blockingGet() + for (pos in extendedBoluses.indices) { + val e = extendedBoluses[pos] + if (e.timestamp > toTime) continue + if (e.end > now) e.end = now + val profile = profileFunction.getProfile(e.timestamp) ?: return total + val calc = e.iobCalc(toTime, profile, activePlugin.activeInsulin) + total.plus(calc) + } + } + return total + } + + override fun getTempBasal(timestamp: Long): TemporaryBasal? { + val tb = repository.getTemporaryBasalActiveAt(timestamp).blockingGet() + if (tb is ValueWrapper.Existing) return tb.value + return null + } + + override fun getExtendedBolus(timestamp: Long): ExtendedBolus? { + val tb = repository.getExtendedBolusActiveAt(timestamp).blockingGet() + if (tb is ValueWrapper.Existing) return tb.value + return null + } + + override fun getTempBasalIncludingConvertedExtended(timestamp: Long): TemporaryBasal? { + + val tb = repository.getTemporaryBasalActiveAt(timestamp).blockingGet() + if (tb is ValueWrapper.Existing) return tb.value + val eb = repository.getExtendedBolusActiveAt(timestamp).blockingGet() + val profile = profileFunction.getProfile(timestamp) ?: return null + if (eb is ValueWrapper.Existing && activePlugin.activePump.isFakingTempsByExtendedBoluses) + return eb.value.toTemporaryBasal(profile) + return null + } + + override fun calculateAbsoluteIobFromBaseBasals(toTime: Long): IobTotal { + val total = IobTotal(toTime) + var i = toTime - range() + while (i < toTime) { + val profile = profileFunction.getProfile(i) + if (profile == null) { + i += T.mins(5).msecs() + continue + } + val running = profile.getBasal(i) + val bolus = Bolus( + timestamp = i, + amount = running * 5.0 / 60.0, + type = Bolus.Type.NORMAL, + isBasalInsulin = true + ) + val iob = bolus.iobCalc(activePlugin, toTime, profile.dia) + total.basaliob += iob.iobContrib + total.activity += iob.activityContrib + i += T.mins(5).msecs() + } + return total + } + + override fun calculateIobFromTempBasalsIncludingConvertedExtended(): IobTotal = + calculateIobToTimeFromTempBasalsIncludingConvertedExtended(dateUtil.now()) + + override fun calculateIobToTimeFromTempBasalsIncludingConvertedExtended(toTime: Long): IobTotal { + val total = IobTotal(toTime) + val now = dateUtil.now() + val pumpInterface = activePlugin.activePump + + val temporaryBasals = repository.getTemporaryBasalsDataFromTimeToTime(toTime - range(), toTime, true).blockingGet() + for (pos in temporaryBasals.indices) { + val t = temporaryBasals[pos] + if (t.timestamp > toTime) continue + val profile = profileFunction.getProfile(t.timestamp) ?: continue + if (t.end > now) t.end = now + val calc = t.iobCalc(toTime, profile, activePlugin.activeInsulin) + //log.debug("BasalIOB " + new Date(time) + " >>> " + calc.basalIob); + total.plus(calc) + } + if (pumpInterface.isFakingTempsByExtendedBoluses) { + val totalExt = IobTotal(toTime) + val extendedBoluses = repository.getExtendedBolusDataFromTimeToTime(toTime - range(), toTime, true).blockingGet() + for (pos in extendedBoluses.indices) { + val e = extendedBoluses[pos] + if (e.timestamp > toTime) continue + val profile = profileFunction.getProfile(e.timestamp) ?: continue + if (e.end > now) e.end = now + val calc = e.iobCalc(toTime, profile, activePlugin.activeInsulin) + totalExt.plus(calc) + } + // Convert to basal iob + totalExt.basaliob = totalExt.iob + totalExt.iob = 0.0 + totalExt.netbasalinsulin = totalExt.extendedBolusInsulin + totalExt.hightempinsulin = totalExt.extendedBolusInsulin + total.plus(totalExt) + } + return total + } + + open fun getCalculationToTimeTempBasals(toTime: Long, lastAutosensResult: AutosensResult, exercise_mode: Boolean, half_basal_exercise_target: Int, isTempTarget: Boolean): IobTotal { + val total = IobTotal(toTime) + val pumpInterface = activePlugin.activePump + val now = dateUtil.now() + val temporaryBasals = repository.getTemporaryBasalsDataFromTimeToTime(toTime - range(), toTime, true).blockingGet() + for (pos in temporaryBasals.indices) { + val t = temporaryBasals[pos] + if (t.timestamp > toTime) continue + val profile = profileFunction.getProfile(t.timestamp) ?: continue + if (t.end > now) t.end = now + val calc = t.iobCalc(toTime, profile, lastAutosensResult, exercise_mode, half_basal_exercise_target, isTempTarget, activePlugin.activeInsulin) + //log.debug("BasalIOB " + new Date(time) + " >>> " + calc.basalIob); + total.plus(calc) + } + if (pumpInterface.isFakingTempsByExtendedBoluses) { + val totalExt = IobTotal(toTime) + val extendedBoluses = repository.getExtendedBolusDataFromTimeToTime(toTime - range(), toTime, true).blockingGet() + for (pos in extendedBoluses.indices) { + val e = extendedBoluses[pos] + if (e.timestamp > toTime) continue + val profile = profileFunction.getProfile(e.timestamp) ?: continue + if (e.end > now) e.end = now + val calc = e.iobCalc(toTime, profile, lastAutosensResult, exercise_mode, half_basal_exercise_target, isTempTarget, activePlugin.activeInsulin) + totalExt.plus(calc) + } + // Convert to basal iob + totalExt.basaliob = totalExt.iob + totalExt.iob = 0.0 + totalExt.netbasalinsulin = totalExt.extendedBolusInsulin + totalExt.hightempinsulin = totalExt.extendedBolusInsulin + total.plus(totalExt) + } + return total + } } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/IobCobOref1Thread.kt b/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/IobCobOref1Thread.kt index 2bfc38d813..d577666d27 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/IobCobOref1Thread.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/IobCobOref1Thread.kt @@ -10,6 +10,8 @@ import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.ValueWrapper import info.nightscout.androidaps.events.Event import info.nightscout.androidaps.events.EventAutosensCalculationFinished +import info.nightscout.androidaps.extensions.target +import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag @@ -17,16 +19,16 @@ import info.nightscout.androidaps.plugins.aps.openAPSSMB.SMBDefaults import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification import info.nightscout.androidaps.plugins.general.overview.notifications.Notification -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin.Companion.roundUpTime import info.nightscout.androidaps.plugins.iob.iobCobCalculator.data.AutosensData -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventAutosensBgLoaded import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventIobCalculationProgress import info.nightscout.androidaps.plugins.sensitivity.SensitivityAAPSPlugin import info.nightscout.androidaps.plugins.sensitivity.SensitivityWeightedAveragePlugin -import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin -import info.nightscout.androidaps.utils.* +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.DecimalFormatter +import info.nightscout.androidaps.utils.FabricPrivacy +import info.nightscout.androidaps.utils.Profiler +import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.buildHelper.BuildHelper -import info.nightscout.androidaps.utils.extensions.target import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.sharedPreferences.SP import java.util.* @@ -39,12 +41,11 @@ import kotlin.math.roundToLong class IobCobOref1Thread internal constructor( private val injector: HasAndroidInjector, private val iobCobCalculatorPlugin: IobCobCalculatorPlugin, // cannot be injected : HistoryBrowser uses different instance - private val treatmentsPlugin: TreatmentsPlugin, // cannot be injected : HistoryBrowser uses different instance private val from: String, private val end: Long, private val bgDataReload: Boolean, private val limitDataToOldestAvailable: Boolean, - private val cause: Event + private val cause: Event? ) : Thread() { @Inject lateinit var aapsLogger: AAPSLogger @@ -55,6 +56,7 @@ class IobCobOref1Thread internal constructor( @Inject lateinit var context: Context @Inject lateinit var sensitivityAAPSPlugin: SensitivityAAPSPlugin @Inject lateinit var sensitivityWeightedAveragePlugin: SensitivityWeightedAveragePlugin + @Inject lateinit var activePlugin: ActivePlugin @Inject lateinit var buildHelper: BuildHelper @Inject lateinit var profiler: Profiler @Inject lateinit var fabricPrivacy: FabricPrivacy @@ -69,7 +71,7 @@ class IobCobOref1Thread internal constructor( } override fun run() { - val start = DateUtil.now() + val start = dateUtil.now() mWakeLock?.acquire(T.mins(10).msecs()) try { aapsLogger.debug(LTag.AUTOSENS, "AUTOSENSDATA thread started: $from") @@ -79,244 +81,244 @@ class IobCobOref1Thread internal constructor( } //log.debug("Locking calculateSensitivityData"); val oldestTimeWithData = iobCobCalculatorPlugin.calculateDetectionStart(end, limitDataToOldestAvailable) - synchronized(iobCobCalculatorPlugin.dataLock) { - if (bgDataReload) { - iobCobCalculatorPlugin.loadBgData(end) - iobCobCalculatorPlugin.createBucketedData() - rxBus.send(EventAutosensBgLoaded(cause)) - } - val bucketedData = iobCobCalculatorPlugin.bucketedData - val autosensDataTable = iobCobCalculatorPlugin.getAutosensDataTable() - if (bucketedData == null || bucketedData.size < 3) { - aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (No bucketed data available): $from") + if (bgDataReload) { + iobCobCalculatorPlugin.ads.loadBgData(end, repository, aapsLogger, dateUtil, rxBus) + iobCobCalculatorPlugin.clearCache() + } + // work on local copy and set back when finished + val ads = iobCobCalculatorPlugin.ads.clone() + val bucketedData = ads.bucketedData + val autosensDataTable = ads.autosensDataTable + if (bucketedData == null || bucketedData.size < 3) { + aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (No bucketed data available): $from") + return + } + val prevDataTime = ads.roundUpTime(bucketedData[bucketedData.size - 3].timestamp) + aapsLogger.debug(LTag.AUTOSENS, "Prev data time: " + dateUtil.dateAndTimeString(prevDataTime)) + var previous = autosensDataTable[prevDataTime] + // start from oldest to be able sub cob + for (i in bucketedData.size - 4 downTo 0) { + val progress = i.toString() + if (buildHelper.isDev()) " ($from)" else "" + rxBus.send(EventIobCalculationProgress(progress)) + if (iobCobCalculatorPlugin.stopCalculationTrigger) { + iobCobCalculatorPlugin.stopCalculationTrigger = false + aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (trigger): $from") return } - val prevDataTime = roundUpTime(bucketedData[bucketedData.size - 3].timestamp) - aapsLogger.debug(LTag.AUTOSENS, "Prev data time: " + dateUtil.dateAndTimeString(prevDataTime)) - var previous = autosensDataTable[prevDataTime] - // start from oldest to be able sub cob - for (i in bucketedData.size - 4 downTo 0) { - val progress = i.toString() + if (buildHelper.isDev()) " ($from)" else "" - rxBus.send(EventIobCalculationProgress(progress)) - if (iobCobCalculatorPlugin.stopCalculationTrigger) { - iobCobCalculatorPlugin.stopCalculationTrigger = false - aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (trigger): $from") - return - } - // check if data already exists - var bgTime = bucketedData[i].timestamp - bgTime = roundUpTime(bgTime) - if (bgTime > roundUpTime(DateUtil.now())) continue - var existing: AutosensData? - if (autosensDataTable[bgTime].also { existing = it } != null) { - previous = existing - continue - } - val profile = profileFunction.getProfile(bgTime) - if (profile == null) { - aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (no profile): $from") - return // profile not set yet - } - aapsLogger.debug(LTag.AUTOSENS, "Processing calculation thread: " + from + " (" + i + "/" + bucketedData.size + ")") - val sens = profile.getIsfMgdl(bgTime) - val autosensData = AutosensData(injector) - autosensData.time = bgTime - if (previous != null) autosensData.activeCarbsList = previous.cloneCarbsList() else autosensData.activeCarbsList = ArrayList() - - //console.error(bgTime , bucketed_data[i].glucose); - var avgDelta: Double - var delta: Double - val bg: Double = bucketedData[i].value - if (bg < 39 || bucketedData[i + 3].value < 39) { - aapsLogger.error("! value < 39") - continue - } - autosensData.bg = bg - delta = bg - bucketedData[i + 1].value - avgDelta = (bg - bucketedData[i + 3].value) / 3 - val iob = iobCobCalculatorPlugin.calculateFromTreatmentsAndTemps(bgTime, profile) - val bgi = -iob.activity * sens * 5 - val deviation = delta - bgi - val avgDeviation = ((avgDelta - bgi) * 1000).roundToLong() / 1000.0 - var slopeFromMaxDeviation = 0.0 - var slopeFromMinDeviation = 999.0 - - // https://github.com/openaps/oref0/blob/master/lib/determine-basal/cob-autosens.js#L169 - if (i < bucketedData.size - 16) { // we need 1h of data to calculate minDeviationSlope - @Suppress("UNUSED_VARIABLE") var maxDeviation = 0.0 - @Suppress("UNUSED_VARIABLE") var minDeviation = 999.0 - val hourAgo = bgTime + 10 * 1000 - 60 * 60 * 1000L - val hourAgoData = iobCobCalculatorPlugin.getAutosensData(hourAgo) - if (hourAgoData != null) { - val initialIndex = autosensDataTable.indexOfKey(hourAgoData.time) - aapsLogger.debug(LTag.AUTOSENS, ">>>>> bucketed_data.size()=" + bucketedData.size + " i=" + i + " hourAgoData=" + hourAgoData.toString()) - var past = 1 - try { - while (past < 12) { - val ad = autosensDataTable.valueAt(initialIndex + past) - aapsLogger.debug(LTag.AUTOSENS, ">>>>> past=" + past + " ad=" + ad?.toString()) - if (ad == null) { - aapsLogger.debug(LTag.AUTOSENS, autosensDataTable.toString()) - aapsLogger.debug(LTag.AUTOSENS, bucketedData.toString()) - aapsLogger.debug(LTag.AUTOSENS, iobCobCalculatorPlugin.bgReadings.toString()) - val notification = Notification(Notification.SEND_LOGFILES, resourceHelper.gs(R.string.sendlogfiles), Notification.LOW) - rxBus.send(EventNewNotification(notification)) - sp.putBoolean("log_AUTOSENS", true) - break - } - // let it here crash on NPE to get more data as i cannot reproduce this bug - val deviationSlope = (ad.avgDeviation - avgDeviation) / (ad.time - bgTime) * 1000 * 60 * 5 - if (ad.avgDeviation > maxDeviation) { - slopeFromMaxDeviation = min(0.0, deviationSlope) - maxDeviation = ad.avgDeviation - } - if (ad.avgDeviation < minDeviation) { - slopeFromMinDeviation = max(0.0, deviationSlope) - minDeviation = ad.avgDeviation - } - past++ - } - } catch (e: Exception) { - aapsLogger.error("Unhandled exception", e) - fabricPrivacy.logException(e) - aapsLogger.debug(autosensDataTable.toString()) - aapsLogger.debug(bucketedData.toString()) - aapsLogger.debug(iobCobCalculatorPlugin.bgReadings.toString()) - val notification = Notification(Notification.SEND_LOGFILES, resourceHelper.gs(R.string.sendlogfiles), Notification.LOW) - rxBus.send(EventNewNotification(notification)) - sp.putBoolean("log_AUTOSENS", true) - break - } - } else { - aapsLogger.debug(LTag.AUTOSENS, ">>>>> bucketed_data.size()=" + bucketedData.size + " i=" + i + " hourAgoData=" + "null") - } - } - val recentCarbTreatments = treatmentsPlugin.getCarbTreatments5MinBackFromHistory(bgTime) - for (recentCarbTreatment in recentCarbTreatments) { - autosensData.carbsFromBolus += recentCarbTreatment.carbs - val isAAPSOrWeighted = sensitivityAAPSPlugin.isEnabled() || sensitivityWeightedAveragePlugin.isEnabled() - autosensData.activeCarbsList.add(autosensData.CarbsInPast(recentCarbTreatment, isAAPSOrWeighted)) - autosensData.pastSensitivity += "[" + DecimalFormatter.to0Decimal(recentCarbTreatment.carbs) + "g]" - } - - // if we are absorbing carbs - if (previous != null && previous.cob > 0) { - // calculate sum of min carb impact from all active treatments - val totalMinCarbsImpact = sp.getDouble(R.string.key_openapsama_min_5m_carbimpact, SMBDefaults.min_5m_carbimpact) - - // figure out how many carbs that represents - // but always assume at least 3mg/dL/5m (default) absorption per active treatment - val ci = max(deviation, totalMinCarbsImpact) - if (ci != deviation) autosensData.failoverToMinAbsorbtionRate = true - autosensData.absorbed = ci * profile.getIc(bgTime) / sens - // and add that to the running total carbsAbsorbed - autosensData.cob = max(previous.cob - autosensData.absorbed, 0.0) - autosensData.mealCarbs = previous.mealCarbs - autosensData.substractAbosorbedCarbs() - autosensData.usedMinCarbsImpact = totalMinCarbsImpact - autosensData.absorbing = previous.absorbing - autosensData.mealStartCounter = previous.mealStartCounter - autosensData.type = previous.type - autosensData.uam = previous.uam - } - val isAAPSOrWeighted = sensitivityAAPSPlugin.isEnabled() || sensitivityWeightedAveragePlugin.isEnabled() - autosensData.removeOldCarbs(bgTime, isAAPSOrWeighted) - autosensData.cob += autosensData.carbsFromBolus - autosensData.mealCarbs += autosensData.carbsFromBolus - autosensData.deviation = deviation - autosensData.bgi = bgi - autosensData.delta = delta - autosensData.avgDelta = avgDelta - autosensData.avgDeviation = avgDeviation - autosensData.slopeFromMaxDeviation = slopeFromMaxDeviation - autosensData.slopeFromMinDeviation = slopeFromMinDeviation - - // If mealCOB is zero but all deviations since hitting COB=0 are positive, exclude from autosens - if (autosensData.cob > 0 || autosensData.absorbing || autosensData.mealCarbs > 0) { - autosensData.absorbing = deviation > 0 - // stop excluding positive deviations as soon as mealCOB=0 if meal has been absorbing for >5h - if (autosensData.mealStartCounter > 60 && autosensData.cob < 0.5) { - autosensData.absorbing = false - } - if (!autosensData.absorbing && autosensData.cob < 0.5) { - autosensData.mealCarbs = 0.0 - } - // check previous "type" value, and if it wasn't csf, set a mealAbsorption start flag - if (autosensData.type != "csf") { -// process.stderr.write("("); - autosensData.mealStartCounter = 0 - } - autosensData.mealStartCounter++ - autosensData.type = "csf" - } else { - // check previous "type" value, and if it was csf, set a mealAbsorption end flag - val currentBasal = profile.getBasal(bgTime) - // always exclude the first 45m after each carb entry - //if (iob.iob > currentBasal || uam ) { - if (iob.iob > 2 * currentBasal || autosensData.uam || autosensData.mealStartCounter < 9) { - autosensData.mealStartCounter++ - autosensData.uam = deviation > 0 - autosensData.type = "uam" - } else { - autosensData.type = "non-meal" - } - } - - // Exclude meal-related deviations (carb absorption) from autosens - when (autosensData.type) { - "non-meal" -> { - when { - abs(deviation) < Constants.DEVIATION_TO_BE_EQUAL -> { - autosensData.pastSensitivity += "=" - autosensData.validDeviation = true - } - - deviation > 0 -> { - autosensData.pastSensitivity += "+" - autosensData.validDeviation = true - } - - else -> { - autosensData.pastSensitivity += "-" - autosensData.validDeviation = true - } - } - } - - "uam" -> { - autosensData.pastSensitivity += "u" - } - - else -> { - autosensData.pastSensitivity += "x" - } - } - - // add an extra negative deviation if a high temp target is running and exercise mode is set - // TODO AS-FIX - @Suppress("SimplifyBooleanWithConstants") - if (false && sp.getBoolean(R.string.key_high_temptarget_raises_sensitivity, SMBDefaults.high_temptarget_raises_sensitivity)) { - val tempTarget = repository.getTemporaryTargetActiveAt(dateUtil._now()).blockingGet() - if (tempTarget is ValueWrapper.Existing && tempTarget.value.target() >= 100) { - autosensData.extraDeviation.add(-(tempTarget.value.target() - 100) / 20) - } - } - - // add one neutral deviation every 2 hours to help decay over long exclusion periods - val calendar = GregorianCalendar() - calendar.timeInMillis = bgTime - val min = calendar[Calendar.MINUTE] - val hours = calendar[Calendar.HOUR_OF_DAY] - if (min in 0..4 && hours % 2 == 0) autosensData.extraDeviation.add(0.0) - previous = autosensData - if (bgTime < DateUtil.now()) autosensDataTable.put(bgTime, autosensData) - aapsLogger.debug(LTag.AUTOSENS, "Running detectSensitivity from: " + dateUtil.dateAndTimeString(oldestTimeWithData) + " to: " + dateUtil.dateAndTimeString(bgTime) + " lastDataTime:" + iobCobCalculatorPlugin.lastDataTime()) - val sensitivity = iobCobCalculatorPlugin.detectSensitivityWithLock(oldestTimeWithData, bgTime) - aapsLogger.debug(LTag.AUTOSENS, "Sensitivity result: $sensitivity") - autosensData.autosensResult = sensitivity - aapsLogger.debug(LTag.AUTOSENS, autosensData.toString()) + // check if data already exists + var bgTime = bucketedData[i].timestamp + bgTime = ads.roundUpTime(bgTime) + if (bgTime > ads.roundUpTime(dateUtil.now())) continue + var existing: AutosensData? + if (autosensDataTable[bgTime].also { existing = it } != null) { + previous = existing + continue } + val profile = profileFunction.getProfile(bgTime) + if (profile == null) { + aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (no profile): $from") + continue // profile not set yet + } + aapsLogger.debug(LTag.AUTOSENS, "Processing calculation thread: " + from + " (" + i + "/" + bucketedData.size + ")") + val sens = profile.getIsfMgdl(bgTime) + val autosensData = AutosensData(injector) + autosensData.time = bgTime + if (previous != null) autosensData.activeCarbsList = previous.cloneCarbsList() else autosensData.activeCarbsList = ArrayList() + + //console.error(bgTime , bucketed_data[i].glucose); + var avgDelta: Double + var delta: Double + val bg: Double = bucketedData[i].value + if (bg < 39 || bucketedData[i + 3].value < 39) { + aapsLogger.error("! value < 39") + continue + } + autosensData.bg = bg + delta = bg - bucketedData[i + 1].value + avgDelta = (bg - bucketedData[i + 3].value) / 3 + val iob = iobCobCalculatorPlugin.calculateFromTreatmentsAndTemps(bgTime, profile) + val bgi = -iob.activity * sens * 5 + val deviation = delta - bgi + val avgDeviation = ((avgDelta - bgi) * 1000).roundToLong() / 1000.0 + var slopeFromMaxDeviation = 0.0 + var slopeFromMinDeviation = 999.0 + + // https://github.com/openaps/oref0/blob/master/lib/determine-basal/cob-autosens.js#L169 + if (i < bucketedData.size - 16) { // we need 1h of data to calculate minDeviationSlope + @Suppress("UNUSED_VARIABLE") var maxDeviation = 0.0 + @Suppress("UNUSED_VARIABLE") var minDeviation = 999.0 + val hourAgo = bgTime + 10 * 1000 - 60 * 60 * 1000L + val hourAgoData = ads.getAutosensDataAtTime(hourAgo) + if (hourAgoData != null) { + val initialIndex = autosensDataTable.indexOfKey(hourAgoData.time) + aapsLogger.debug(LTag.AUTOSENS, ">>>>> bucketed_data.size()=" + bucketedData.size + " i=" + i + " hourAgoData=" + hourAgoData.toString()) + var past = 1 + try { + while (past < 12) { + val ad = autosensDataTable.valueAt(initialIndex + past) + aapsLogger.debug(LTag.AUTOSENS, ">>>>> past=" + past + " ad=" + ad?.toString()) + if (ad == null) { + aapsLogger.debug(LTag.AUTOSENS, autosensDataTable.toString()) + aapsLogger.debug(LTag.AUTOSENS, bucketedData.toString()) + //aapsLogger.debug(LTag.AUTOSENS, iobCobCalculatorPlugin.getBgReadingsDataTable().toString()) + val notification = Notification(Notification.SEND_LOGFILES, resourceHelper.gs(R.string.sendlogfiles), Notification.LOW) + rxBus.send(EventNewNotification(notification)) + sp.putBoolean("log_AUTOSENS", true) + break + } + // let it here crash on NPE to get more data as i cannot reproduce this bug + val deviationSlope = (ad.avgDeviation - avgDeviation) / (ad.time - bgTime) * 1000 * 60 * 5 + if (ad.avgDeviation > maxDeviation) { + slopeFromMaxDeviation = min(0.0, deviationSlope) + maxDeviation = ad.avgDeviation + } + if (ad.avgDeviation < minDeviation) { + slopeFromMinDeviation = max(0.0, deviationSlope) + minDeviation = ad.avgDeviation + } + past++ + } + } catch (e: Exception) { + aapsLogger.error("Unhandled exception", e) + fabricPrivacy.logException(e) + aapsLogger.debug(autosensDataTable.toString()) + aapsLogger.debug(bucketedData.toString()) + //aapsLogger.debug(iobCobCalculatorPlugin.getBgReadingsDataTable().toString()) + val notification = Notification(Notification.SEND_LOGFILES, resourceHelper.gs(R.string.sendlogfiles), Notification.LOW) + rxBus.send(EventNewNotification(notification)) + sp.putBoolean("log_AUTOSENS", true) + break + } + } else { + aapsLogger.debug(LTag.AUTOSENS, ">>>>> bucketed_data.size()=" + bucketedData.size + " i=" + i + " hourAgoData=" + "null") + } + } + val recentCarbTreatments = repository.getCarbsDataFromTimeToTimeExpanded(bgTime - T.mins(5).msecs(), bgTime, true).blockingGet() + for (recentCarbTreatment in recentCarbTreatments) { + autosensData.carbsFromBolus += recentCarbTreatment.amount + val isAAPSOrWeighted = sensitivityAAPSPlugin.isEnabled() || sensitivityWeightedAveragePlugin.isEnabled() + autosensData.activeCarbsList.add(autosensData.CarbsInPast(recentCarbTreatment, isAAPSOrWeighted)) + autosensData.pastSensitivity += "[" + DecimalFormatter.to0Decimal(recentCarbTreatment.amount) + "g]" + } + + // if we are absorbing carbs + if (previous != null && previous.cob > 0) { + // calculate sum of min carb impact from all active treatments + val totalMinCarbsImpact = sp.getDouble(R.string.key_openapsama_min_5m_carbimpact, SMBDefaults.min_5m_carbimpact) + + // figure out how many carbs that represents + // but always assume at least 3mg/dL/5m (default) absorption per active treatment + val ci = max(deviation, totalMinCarbsImpact) + if (ci != deviation) autosensData.failoverToMinAbsorbtionRate = true + autosensData.absorbed = ci * profile.getIc(bgTime) / sens + // and add that to the running total carbsAbsorbed + autosensData.cob = max(previous.cob - autosensData.absorbed, 0.0) + autosensData.mealCarbs = previous.mealCarbs + autosensData.substractAbosorbedCarbs() + autosensData.usedMinCarbsImpact = totalMinCarbsImpact + autosensData.absorbing = previous.absorbing + autosensData.mealStartCounter = previous.mealStartCounter + autosensData.type = previous.type + autosensData.uam = previous.uam + } + val isAAPSOrWeighted = sensitivityAAPSPlugin.isEnabled() || sensitivityWeightedAveragePlugin.isEnabled() + autosensData.removeOldCarbs(bgTime, isAAPSOrWeighted) + autosensData.cob += autosensData.carbsFromBolus + autosensData.mealCarbs += autosensData.carbsFromBolus + autosensData.deviation = deviation + autosensData.bgi = bgi + autosensData.delta = delta + autosensData.avgDelta = avgDelta + autosensData.avgDeviation = avgDeviation + autosensData.slopeFromMaxDeviation = slopeFromMaxDeviation + autosensData.slopeFromMinDeviation = slopeFromMinDeviation + + // If mealCOB is zero but all deviations since hitting COB=0 are positive, exclude from autosens + if (autosensData.cob > 0 || autosensData.absorbing || autosensData.mealCarbs > 0) { + autosensData.absorbing = deviation > 0 + // stop excluding positive deviations as soon as mealCOB=0 if meal has been absorbing for >5h + if (autosensData.mealStartCounter > 60 && autosensData.cob < 0.5) { + autosensData.absorbing = false + } + if (!autosensData.absorbing && autosensData.cob < 0.5) { + autosensData.mealCarbs = 0.0 + } + // check previous "type" value, and if it wasn't csf, set a mealAbsorption start flag + if (autosensData.type != "csf") { +// process.stderr.write("("); + autosensData.mealStartCounter = 0 + } + autosensData.mealStartCounter++ + autosensData.type = "csf" + } else { + // check previous "type" value, and if it was csf, set a mealAbsorption end flag + val currentBasal = profile.getBasal(bgTime) + // always exclude the first 45m after each carb entry + //if (iob.iob > currentBasal || uam ) { + if (iob.iob > 2 * currentBasal || autosensData.uam || autosensData.mealStartCounter < 9) { + autosensData.mealStartCounter++ + autosensData.uam = deviation > 0 + autosensData.type = "uam" + } else { + autosensData.type = "non-meal" + } + } + + // Exclude meal-related deviations (carb absorption) from autosens + when (autosensData.type) { + "non-meal" -> { + when { + abs(deviation) < Constants.DEVIATION_TO_BE_EQUAL -> { + autosensData.pastSensitivity += "=" + autosensData.validDeviation = true + } + + deviation > 0 -> { + autosensData.pastSensitivity += "+" + autosensData.validDeviation = true + } + + else -> { + autosensData.pastSensitivity += "-" + autosensData.validDeviation = true + } + } + } + + "uam" -> { + autosensData.pastSensitivity += "u" + } + + else -> { + autosensData.pastSensitivity += "x" + } + } + + // add an extra negative deviation if a high temp target is running and exercise mode is set + // TODO AS-FIX + @Suppress("SimplifyBooleanWithConstants") + if (false && sp.getBoolean(R.string.key_high_temptarget_raises_sensitivity, SMBDefaults.high_temptarget_raises_sensitivity)) { + val tempTarget = repository.getTemporaryTargetActiveAt(dateUtil.now()).blockingGet() + if (tempTarget is ValueWrapper.Existing && tempTarget.value.target() >= 100) { + autosensData.extraDeviation.add(-(tempTarget.value.target() - 100) / 20) + } + } + + // add one neutral deviation every 2 hours to help decay over long exclusion periods + val calendar = GregorianCalendar() + calendar.timeInMillis = bgTime + val min = calendar[Calendar.MINUTE] + val hours = calendar[Calendar.HOUR_OF_DAY] + if (min in 0..4 && hours % 2 == 0) autosensData.extraDeviation.add(0.0) + previous = autosensData + if (bgTime < dateUtil.now()) autosensDataTable.put(bgTime, autosensData) + aapsLogger.debug(LTag.AUTOSENS, "Running detectSensitivity from: " + dateUtil.dateAndTimeString(oldestTimeWithData) + " to: " + dateUtil.dateAndTimeString(bgTime) + " lastDataTime:" + ads.lastDataTime(dateUtil)) + val sensitivity = activePlugin.activeSensitivity.detectSensitivity(ads, oldestTimeWithData, bgTime) + aapsLogger.debug(LTag.AUTOSENS, "Sensitivity result: $sensitivity") + autosensData.autosensResult = sensitivity + aapsLogger.debug(LTag.AUTOSENS, autosensData.toString()) } + iobCobCalculatorPlugin.ads = ads Thread { SystemClock.sleep(1000) rxBus.send(EventAutosensCalculationFinished(cause)) @@ -325,7 +327,6 @@ class IobCobOref1Thread internal constructor( mWakeLock?.release() rxBus.send(EventIobCalculationProgress("")) aapsLogger.debug(LTag.AUTOSENS, "AUTOSENSDATA thread ended: $from") - aapsLogger.debug(LTag.AUTOSENS, "Midnights: " + MidnightTime.log()) profiler.log(LTag.AUTOSENS, "IobCobOref1Thread", start) } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/IobCobThread.kt b/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/IobCobThread.kt index bba3777c40..e6fa48cca2 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/IobCobThread.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/IobCobThread.kt @@ -6,7 +6,10 @@ import android.os.SystemClock import dagger.android.HasAndroidInjector import info.nightscout.androidaps.Constants import info.nightscout.androidaps.R +import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.events.Event +import info.nightscout.androidaps.events.EventAutosensCalculationFinished +import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.PluginType import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.logging.AAPSLogger @@ -15,15 +18,15 @@ import info.nightscout.androidaps.plugins.aps.openAPSSMB.SMBDefaults import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification import info.nightscout.androidaps.plugins.general.overview.notifications.Notification -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin.Companion.roundUpTime import info.nightscout.androidaps.plugins.iob.iobCobCalculator.data.AutosensData -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventAutosensBgLoaded -import info.nightscout.androidaps.events.EventAutosensCalculationFinished import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventIobCalculationProgress import info.nightscout.androidaps.plugins.sensitivity.SensitivityAAPSPlugin import info.nightscout.androidaps.plugins.sensitivity.SensitivityWeightedAveragePlugin -import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin -import info.nightscout.androidaps.utils.* +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.DecimalFormatter +import info.nightscout.androidaps.utils.FabricPrivacy +import info.nightscout.androidaps.utils.Profiler +import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.buildHelper.BuildHelper import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.sharedPreferences.SP @@ -37,12 +40,11 @@ import kotlin.math.roundToLong class IobCobThread @Inject internal constructor( private val injector: HasAndroidInjector, private val iobCobCalculatorPlugin: IobCobCalculatorPlugin, // cannot be injected : HistoryBrowser uses different instance - private val treatmentsPlugin: TreatmentsPlugin, // cannot be injected : HistoryBrowser uses different instance private val from: String, private val end: Long, private val bgDataReload: Boolean, private val limitDataToOldestAvailable: Boolean, - private val cause: Event + private val cause: Event? ) : Thread() { @Inject lateinit var aapsLogger: AAPSLogger @@ -53,10 +55,12 @@ class IobCobThread @Inject internal constructor( @Inject lateinit var context: Context @Inject lateinit var sensitivityAAPSPlugin: SensitivityAAPSPlugin @Inject lateinit var sensitivityWeightedAveragePlugin: SensitivityWeightedAveragePlugin + @Inject lateinit var activePlugin: ActivePlugin @Inject lateinit var buildHelper: BuildHelper @Inject lateinit var profiler: Profiler @Inject lateinit var fabricPrivacy: FabricPrivacy @Inject lateinit var dateUtil: DateUtil + @Inject lateinit var repository: AppRepository private var mWakeLock: PowerManager.WakeLock? = null @@ -66,7 +70,7 @@ class IobCobThread @Inject internal constructor( } override fun run() { - val start = DateUtil.now() + val start = dateUtil.now() mWakeLock?.acquire(T.mins(10).msecs()) try { aapsLogger.debug(LTag.AUTOSENS, "AUTOSENSDATA thread started: $from") @@ -76,192 +80,192 @@ class IobCobThread @Inject internal constructor( } //log.debug("Locking calculateSensitivityData"); val oldestTimeWithData = iobCobCalculatorPlugin.calculateDetectionStart(end, limitDataToOldestAvailable) - synchronized(iobCobCalculatorPlugin.dataLock) { - if (bgDataReload) { - iobCobCalculatorPlugin.loadBgData(end) - iobCobCalculatorPlugin.createBucketedData() - rxBus.send(EventAutosensBgLoaded(cause)) - } - val bucketedData = iobCobCalculatorPlugin.bucketedData - val autosensDataTable = iobCobCalculatorPlugin.getAutosensDataTable() - if (bucketedData == null || bucketedData.size < 3) { - aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (No bucketed data available): $from") + if (bgDataReload) { + iobCobCalculatorPlugin.ads.loadBgData(end, repository, aapsLogger, dateUtil, rxBus) + iobCobCalculatorPlugin.clearCache() + } + // work on local copy and set back when finished + val ads = iobCobCalculatorPlugin.ads.clone() + val bucketedData = ads.bucketedData + val autosensDataTable = ads.autosensDataTable + if (bucketedData == null || bucketedData.size < 3) { + aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (No bucketed data available): $from") + return + } + val prevDataTime = ads.roundUpTime(bucketedData[bucketedData.size - 3].timestamp) + aapsLogger.debug(LTag.AUTOSENS, "Prev data time: " + dateUtil.dateAndTimeString(prevDataTime)) + var previous = autosensDataTable[prevDataTime] + // start from oldest to be able sub cob + for (i in bucketedData.size - 4 downTo 0) { + val progress = i.toString() + if (buildHelper.isDev()) " ($from)" else "" + rxBus.send(EventIobCalculationProgress(progress)) + if (iobCobCalculatorPlugin.stopCalculationTrigger) { + iobCobCalculatorPlugin.stopCalculationTrigger = false + aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (trigger): $from") return } - val prevDataTime = roundUpTime(bucketedData[bucketedData.size - 3].timestamp) - aapsLogger.debug(LTag.AUTOSENS, "Prev data time: " + dateUtil.dateAndTimeString(prevDataTime)) - var previous = autosensDataTable[prevDataTime] - // start from oldest to be able sub cob - for (i in bucketedData.size - 4 downTo 0) { - val progress = i.toString() + if (buildHelper.isDev()) " ($from)" else "" - rxBus.send(EventIobCalculationProgress(progress)) - if (iobCobCalculatorPlugin.stopCalculationTrigger) { - iobCobCalculatorPlugin.stopCalculationTrigger = false - aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (trigger): $from") - return - } - // check if data already exists - var bgTime = bucketedData[i].timestamp - bgTime = roundUpTime(bgTime) - if (bgTime > roundUpTime(DateUtil.now())) continue - var existing: AutosensData? - if (autosensDataTable[bgTime].also { existing = it } != null) { - previous = existing - continue - } - val profile = profileFunction.getProfile(bgTime) - if (profile == null) { - aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (no profile): $from") - return // profile not set yet - } - aapsLogger.debug(LTag.AUTOSENS, "Processing calculation thread: " + from + " (" + i + "/" + bucketedData.size + ")") - val sens = profile.getIsfMgdl(bgTime) - val autosensData = AutosensData(injector) - autosensData.time = bgTime - if (previous != null) autosensData.activeCarbsList = previous.cloneCarbsList() else autosensData.activeCarbsList = ArrayList() + // check if data already exists + var bgTime = bucketedData[i].timestamp + bgTime = ads.roundUpTime(bgTime) + if (bgTime > ads.roundUpTime(dateUtil.now())) continue + var existing: AutosensData? + if (autosensDataTable[bgTime].also { existing = it } != null) { + previous = existing + continue + } + val profile = profileFunction.getProfile(bgTime) + if (profile == null) { + aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (no profile): $from") + continue // profile not set yet + } + aapsLogger.debug(LTag.AUTOSENS, "Processing calculation thread: " + from + " (" + i + "/" + bucketedData.size + ")") + val sens = profile.getIsfMgdl(bgTime) + val autosensData = AutosensData(injector) + autosensData.time = bgTime + if (previous != null) autosensData.activeCarbsList = previous.cloneCarbsList() else autosensData.activeCarbsList = ArrayList() - //console.error(bgTime , bucketed_data[i].glucose); - var avgDelta: Double - var delta: Double - val bg: Double = bucketedData[i].value - if (bg < 39 || bucketedData[i + 3].value < 39) { - aapsLogger.error("! value < 39") - continue - } - autosensData.bg = bg - delta = bg - bucketedData[i + 1].value - avgDelta = (bg - bucketedData[i + 3].value) / 3 - val iob = iobCobCalculatorPlugin.calculateFromTreatmentsAndTemps(bgTime, profile) - val bgi = -iob.activity * sens * 5 - val deviation = delta - bgi - val avgDeviation = ((avgDelta - bgi) * 1000).roundToLong() / 1000.0 - var slopeFromMaxDeviation = 0.0 - var slopeFromMinDeviation = 999.0 + //console.error(bgTime , bucketed_data[i].glucose); + var avgDelta: Double + var delta: Double + val bg: Double = bucketedData[i].value + if (bg < 39 || bucketedData[i + 3].value < 39) { + aapsLogger.error("! value < 39") + continue + } + autosensData.bg = bg + delta = bg - bucketedData[i + 1].value + avgDelta = (bg - bucketedData[i + 3].value) / 3 + val iob = iobCobCalculatorPlugin.calculateFromTreatmentsAndTemps(bgTime, profile) + val bgi = -iob.activity * sens * 5 + val deviation = delta - bgi + val avgDeviation = ((avgDelta - bgi) * 1000).roundToLong() / 1000.0 + var slopeFromMaxDeviation = 0.0 + var slopeFromMinDeviation = 999.0 - // https://github.com/openaps/oref0/blob/master/lib/determine-basal/cob-autosens.js#L169 - if (i < bucketedData.size - 16) { // we need 1h of data to calculate minDeviationSlope - @Suppress("UNUSED_VARIABLE") var maxDeviation = 0.0 - @Suppress("UNUSED_VARIABLE") var minDeviation = 999.0 - val hourAgo = bgTime + 10 * 1000 - 60 * 60 * 1000L - val hourAgoData = iobCobCalculatorPlugin.getAutosensData(hourAgo) - if (hourAgoData != null) { - val initialIndex = autosensDataTable.indexOfKey(hourAgoData.time) - aapsLogger.debug(LTag.AUTOSENS, ">>>>> bucketed_data.size()=" + bucketedData.size + " i=" + i + " hourAgoData=" + hourAgoData.toString()) - var past = 1 - try { - while (past < 12) { - val ad = autosensDataTable.valueAt(initialIndex + past) - aapsLogger.debug(LTag.AUTOSENS, ">>>>> past=" + past + " ad=" + ad?.toString()) - if (ad == null) { - aapsLogger.debug(LTag.AUTOSENS, autosensDataTable.toString()) - aapsLogger.debug(LTag.AUTOSENS, bucketedData.toString()) - aapsLogger.debug(LTag.AUTOSENS, iobCobCalculatorPlugin.bgReadings.toString()) - val notification = Notification(Notification.SEND_LOGFILES, resourceHelper.gs(R.string.sendlogfiles), Notification.LOW) - rxBus.send(EventNewNotification(notification)) - sp.putBoolean("log_AUTOSENS", true) - break - } - // let it here crash on NPE to get more data as i cannot reproduce this bug - val deviationSlope = (ad.avgDeviation - avgDeviation) / (ad.time - bgTime) * 1000 * 60 * 5 - if (ad.avgDeviation > maxDeviation) { - slopeFromMaxDeviation = min(0.0, deviationSlope) - maxDeviation = ad.avgDeviation - } - if (ad.avgDeviation < minDeviation) { - slopeFromMinDeviation = max(0.0, deviationSlope) - minDeviation = ad.avgDeviation - } - past++ + // https://github.com/openaps/oref0/blob/master/lib/determine-basal/cob-autosens.js#L169 + if (i < bucketedData.size - 16) { // we need 1h of data to calculate minDeviationSlope + @Suppress("UNUSED_VARIABLE") var maxDeviation = 0.0 + @Suppress("UNUSED_VARIABLE") var minDeviation = 999.0 + val hourAgo = bgTime + 10 * 1000 - 60 * 60 * 1000L + val hourAgoData = ads.getAutosensDataAtTime(hourAgo) + if (hourAgoData != null) { + val initialIndex = autosensDataTable.indexOfKey(hourAgoData.time) + aapsLogger.debug(LTag.AUTOSENS, ">>>>> bucketed_data.size()=" + bucketedData.size + " i=" + i + " hourAgoData=" + hourAgoData.toString()) + var past = 1 + try { + while (past < 12) { + val ad = autosensDataTable.valueAt(initialIndex + past) + aapsLogger.debug(LTag.AUTOSENS, ">>>>> past=" + past + " ad=" + ad?.toString()) + if (ad == null) { + aapsLogger.debug(LTag.AUTOSENS, autosensDataTable.toString()) + aapsLogger.debug(LTag.AUTOSENS, bucketedData.toString()) + //aapsLogger.debug(LTag.AUTOSENS, iobCobCalculatorPlugin.getBgReadingsDataTable().toString()) + val notification = Notification(Notification.SEND_LOGFILES, resourceHelper.gs(R.string.sendlogfiles), Notification.LOW) + rxBus.send(EventNewNotification(notification)) + sp.putBoolean("log_AUTOSENS", true) + break } - } catch (e: Exception) { - aapsLogger.error("Unhandled exception", e) - fabricPrivacy.logException(e) - aapsLogger.debug(autosensDataTable.toString()) - aapsLogger.debug(bucketedData.toString()) - aapsLogger.debug(iobCobCalculatorPlugin.bgReadings.toString()) - val notification = Notification(Notification.SEND_LOGFILES, resourceHelper.gs(R.string.sendlogfiles), Notification.LOW) - rxBus.send(EventNewNotification(notification)) - sp.putBoolean("log_AUTOSENS", true) - break - } - } else { - aapsLogger.debug(LTag.AUTOSENS, ">>>>> bucketed_data.size()=" + bucketedData.size + " i=" + i + " hourAgoData=" + "null") - } - } - val recentCarbTreatments = treatmentsPlugin.getCarbTreatments5MinBackFromHistory(bgTime) - for (recentCarbTreatment in recentCarbTreatments) { - autosensData.carbsFromBolus += recentCarbTreatment.carbs - val isAAPSOrWeighted = sensitivityAAPSPlugin.isEnabled() || sensitivityWeightedAveragePlugin.isEnabled() - autosensData.activeCarbsList.add(autosensData.CarbsInPast(recentCarbTreatment, isAAPSOrWeighted)) - autosensData.pastSensitivity += "[" + DecimalFormatter.to0Decimal(recentCarbTreatment.carbs) + "g]" - } - - // if we are absorbing carbs - if (previous != null && previous.cob > 0) { - // calculate sum of min carb impact from all active treatments - var totalMinCarbsImpact = 0.0 - if (sensitivityAAPSPlugin.isEnabled(PluginType.SENSITIVITY) || sensitivityWeightedAveragePlugin.isEnabled(PluginType.SENSITIVITY)) { - //when the impact depends on a max time, sum them up as smaller carb sizes make them smaller - for (ii in autosensData.activeCarbsList.indices) { - val c = autosensData.activeCarbsList[ii] - totalMinCarbsImpact += c.min5minCarbImpact - } - } else { - //Oref sensitivity - totalMinCarbsImpact = sp.getDouble(R.string.key_openapsama_min_5m_carbimpact, SMBDefaults.min_5m_carbimpact) - } - - // figure out how many carbs that represents - // but always assume at least 3mg/dL/5m (default) absorption per active treatment - val ci = max(deviation, totalMinCarbsImpact) - if (ci != deviation) autosensData.failoverToMinAbsorbtionRate = true - autosensData.absorbed = ci * profile.getIc(bgTime) / sens - // and add that to the running total carbsAbsorbed - autosensData.cob = max(previous.cob - autosensData.absorbed, 0.0) - autosensData.substractAbosorbedCarbs() - autosensData.usedMinCarbsImpact = totalMinCarbsImpact - } - val isAAPSOrWeighted = sensitivityAAPSPlugin.isEnabled() || sensitivityWeightedAveragePlugin.isEnabled() - autosensData.removeOldCarbs(bgTime, isAAPSOrWeighted) - autosensData.cob += autosensData.carbsFromBolus - autosensData.deviation = deviation - autosensData.bgi = bgi - autosensData.delta = delta - autosensData.avgDelta = avgDelta - autosensData.avgDeviation = avgDeviation - autosensData.slopeFromMaxDeviation = slopeFromMaxDeviation - autosensData.slopeFromMinDeviation = slopeFromMinDeviation - - // calculate autosens only without COB - if (autosensData.cob <= 0) { - when { - abs(deviation) < Constants.DEVIATION_TO_BE_EQUAL -> { - autosensData.pastSensitivity += "=" - autosensData.validDeviation = true - } - - deviation > 0 -> { - autosensData.pastSensitivity += "+" - autosensData.validDeviation = true - } - - else -> { - autosensData.pastSensitivity += "-" - autosensData.validDeviation = true + // let it here crash on NPE to get more data as i cannot reproduce this bug + val deviationSlope = (ad.avgDeviation - avgDeviation) / (ad.time - bgTime) * 1000 * 60 * 5 + if (ad.avgDeviation > maxDeviation) { + slopeFromMaxDeviation = min(0.0, deviationSlope) + maxDeviation = ad.avgDeviation + } + if (ad.avgDeviation < minDeviation) { + slopeFromMinDeviation = max(0.0, deviationSlope) + minDeviation = ad.avgDeviation + } + past++ } + } catch (e: Exception) { + aapsLogger.error("Unhandled exception", e) + fabricPrivacy.logException(e) + aapsLogger.debug(autosensDataTable.toString()) + aapsLogger.debug(bucketedData.toString()) + //aapsLogger.debug(iobCobCalculatorPlugin.getBgReadingsDataTable().toString()) + val notification = Notification(Notification.SEND_LOGFILES, resourceHelper.gs(R.string.sendlogfiles), Notification.LOW) + rxBus.send(EventNewNotification(notification)) + sp.putBoolean("log_AUTOSENS", true) + break } } else { - autosensData.pastSensitivity += "C" + aapsLogger.debug(LTag.AUTOSENS, ">>>>> bucketed_data.size()=" + bucketedData.size + " i=" + i + " hourAgoData=" + "null") } - previous = autosensData - if (bgTime < DateUtil.now()) autosensDataTable.put(bgTime, autosensData) - aapsLogger.debug(LTag.AUTOSENS, "Running detectSensitivity from: " + dateUtil.dateAndTimeString(oldestTimeWithData) + " to: " + dateUtil.dateAndTimeString(bgTime) + " lastDataTime:" + iobCobCalculatorPlugin.lastDataTime()) - val sensitivity = iobCobCalculatorPlugin.detectSensitivityWithLock(oldestTimeWithData, bgTime) - aapsLogger.debug(LTag.AUTOSENS, "Sensitivity result: $sensitivity") - autosensData.autosensResult = sensitivity - aapsLogger.debug(LTag.AUTOSENS, autosensData.toString()) } + val recentCarbTreatments = repository.getCarbsDataFromTimeToTimeExpanded(bgTime - T.mins(5).msecs(), bgTime, true).blockingGet() + for (recentCarbTreatment in recentCarbTreatments) { + autosensData.carbsFromBolus += recentCarbTreatment.amount + val isAAPSOrWeighted = sensitivityAAPSPlugin.isEnabled() || sensitivityWeightedAveragePlugin.isEnabled() + autosensData.activeCarbsList.add(autosensData.CarbsInPast(recentCarbTreatment, isAAPSOrWeighted)) + autosensData.pastSensitivity += "[" + DecimalFormatter.to0Decimal(recentCarbTreatment.amount) + "g]" + } + + // if we are absorbing carbs + if (previous != null && previous.cob > 0) { + // calculate sum of min carb impact from all active treatments + var totalMinCarbsImpact = 0.0 + if (sensitivityAAPSPlugin.isEnabled(PluginType.SENSITIVITY) || sensitivityWeightedAveragePlugin.isEnabled(PluginType.SENSITIVITY)) { + //when the impact depends on a max time, sum them up as smaller carb sizes make them smaller + for (ii in autosensData.activeCarbsList.indices) { + val c = autosensData.activeCarbsList[ii] + totalMinCarbsImpact += c.min5minCarbImpact + } + } else { + //Oref sensitivity + totalMinCarbsImpact = sp.getDouble(R.string.key_openapsama_min_5m_carbimpact, SMBDefaults.min_5m_carbimpact) + } + + // figure out how many carbs that represents + // but always assume at least 3mg/dL/5m (default) absorption per active treatment + val ci = max(deviation, totalMinCarbsImpact) + if (ci != deviation) autosensData.failoverToMinAbsorbtionRate = true + autosensData.absorbed = ci * profile.getIc(bgTime) / sens + // and add that to the running total carbsAbsorbed + autosensData.cob = max(previous.cob - autosensData.absorbed, 0.0) + autosensData.substractAbosorbedCarbs() + autosensData.usedMinCarbsImpact = totalMinCarbsImpact + } + val isAAPSOrWeighted = sensitivityAAPSPlugin.isEnabled() || sensitivityWeightedAveragePlugin.isEnabled() + autosensData.removeOldCarbs(bgTime, isAAPSOrWeighted) + autosensData.cob += autosensData.carbsFromBolus + autosensData.deviation = deviation + autosensData.bgi = bgi + autosensData.delta = delta + autosensData.avgDelta = avgDelta + autosensData.avgDeviation = avgDeviation + autosensData.slopeFromMaxDeviation = slopeFromMaxDeviation + autosensData.slopeFromMinDeviation = slopeFromMinDeviation + + // calculate autosens only without COB + if (autosensData.cob <= 0) { + when { + abs(deviation) < Constants.DEVIATION_TO_BE_EQUAL -> { + autosensData.pastSensitivity += "=" + autosensData.validDeviation = true + } + + deviation > 0 -> { + autosensData.pastSensitivity += "+" + autosensData.validDeviation = true + } + + else -> { + autosensData.pastSensitivity += "-" + autosensData.validDeviation = true + } + } + } else { + autosensData.pastSensitivity += "C" + } + previous = autosensData + if (bgTime < dateUtil.now()) autosensDataTable.put(bgTime, autosensData) + aapsLogger.debug(LTag.AUTOSENS, "Running detectSensitivity from: " + dateUtil.dateAndTimeString(oldestTimeWithData) + " to: " + dateUtil.dateAndTimeString(bgTime) + " lastDataTime:" + ads.lastDataTime(dateUtil)) + val sensitivity = activePlugin.activeSensitivity.detectSensitivity(ads, oldestTimeWithData, bgTime) + aapsLogger.debug(LTag.AUTOSENS, "Sensitivity result: $sensitivity") + autosensData.autosensResult = sensitivity + aapsLogger.debug(LTag.AUTOSENS, autosensData.toString()) } + iobCobCalculatorPlugin.ads = ads Thread { SystemClock.sleep(1000) rxBus.send(EventAutosensCalculationFinished(cause)) @@ -270,7 +274,6 @@ class IobCobThread @Inject internal constructor( mWakeLock?.release() rxBus.send(EventIobCalculationProgress("")) aapsLogger.debug(LTag.AUTOSENS, "AUTOSENSDATA thread ended: $from") - aapsLogger.debug(LTag.AUTOSENS, "Midnights: " + MidnightTime.log()) profiler.log(LTag.AUTOSENS, "IobCobThread", start) } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/events/EventAutosensBgLoaded.kt b/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/events/EventAutosensBgLoaded.kt deleted file mode 100644 index b02e96b67f..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/events/EventAutosensBgLoaded.kt +++ /dev/null @@ -1,6 +0,0 @@ -package info.nightscout.androidaps.plugins.iob.iobCobCalculator.events - -import info.nightscout.androidaps.events.Event -import info.nightscout.androidaps.events.EventLoop - -class EventAutosensBgLoaded(var cause: Event) : EventLoop() diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/events/EventNewHistoryData.kt b/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/events/EventNewHistoryData.kt index c2372aff83..c842a1dcb7 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/events/EventNewHistoryData.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/events/EventNewHistoryData.kt @@ -1,5 +1,6 @@ package info.nightscout.androidaps.plugins.iob.iobCobCalculator.events +import info.nightscout.androidaps.database.entities.GlucoseValue import info.nightscout.androidaps.events.Event -class EventNewHistoryData(var time: Long) : Event() \ No newline at end of file +class EventNewHistoryData(val oldDataTimestamp: Long, val reloadBgData: Boolean, val newestGlucoseValue : GlucoseValue? = null) : Event() \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/profile/local/LocalProfileFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/profile/local/LocalProfileFragment.kt index ccc2c19596..31f01df6c1 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/profile/local/LocalProfileFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/profile/local/LocalProfileFragment.kt @@ -11,11 +11,15 @@ import android.widget.ArrayAdapter import dagger.android.support.DaggerFragment import info.nightscout.androidaps.Constants import info.nightscout.androidaps.R -import info.nightscout.androidaps.data.Profile -import info.nightscout.androidaps.database.entities.UserEntry.* +import info.nightscout.androidaps.data.ProfileSealed +import info.nightscout.androidaps.database.entities.UserEntry.Action +import info.nightscout.androidaps.database.entities.UserEntry.Sources +import info.nightscout.androidaps.database.entities.ValueWithUnit import info.nightscout.androidaps.databinding.LocalprofileFragmentBinding import info.nightscout.androidaps.dialogs.ProfileSwitchDialog -import info.nightscout.androidaps.interfaces.ActivePluginProvider +import info.nightscout.androidaps.interfaces.ActivePlugin +import info.nightscout.androidaps.interfaces.GlucoseUnit +import info.nightscout.androidaps.interfaces.Profile import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.plugins.bus.RxBusWrapper @@ -34,7 +38,7 @@ class LocalProfileFragment : DaggerFragment() { @Inject lateinit var aapsLogger: AAPSLogger @Inject lateinit var rxBus: RxBusWrapper @Inject lateinit var resourceHelper: ResourceHelper - @Inject lateinit var activePlugin: ActivePluginProvider + @Inject lateinit var activePlugin: ActivePlugin @Inject lateinit var fabricPrivacy: FabricPrivacy @Inject lateinit var localProfilePlugin: LocalProfilePlugin @Inject lateinit var hardLimits: HardLimits @@ -50,6 +54,9 @@ class LocalProfileFragment : DaggerFragment() { private val save = Runnable { doEdit() basalView?.updateLabel(resourceHelper.gs(R.string.basal_label) + ": " + sumLabel()) + localProfilePlugin.profile?.getSpecificProfile(spinner?.selectedItem.toString())?.let { + binding.basalGraph.show(ProfileSealed.Pure(it)) + } } private val textWatch = object : TextWatcher { @@ -64,7 +71,7 @@ class LocalProfileFragment : DaggerFragment() { private fun sumLabel(): String { val profile = localProfilePlugin.createProfileStore().getDefaultProfile() - val sum = profile?.baseBasalSum() ?: 0.0 + val sum = profile?.let { ProfileSealed.Pure(profile).baseBasalSum() } ?: 0.0 return " ∑" + DecimalFormatter.to2Decimal(sum) + resourceHelper.gs(R.string.insulin_unit_shortname) } @@ -120,13 +127,13 @@ class LocalProfileFragment : DaggerFragment() { binding.dia.setParams(currentProfile.dia, hardLimits.minDia(), hardLimits.maxDia(), 0.1, DecimalFormat("0.0"), false, binding.save, textWatch) binding.dia.tag = "LP_DIA" TimeListEdit(context, aapsLogger, dateUtil, view, R.id.ic, "IC", resourceHelper.gs(R.string.ic_label), currentProfile.ic, null, hardLimits.minIC(), hardLimits.maxIC(), 0.1, DecimalFormat("0.0"), save) - basalView = TimeListEdit(context, aapsLogger, dateUtil, view, R.id.basal, "BASAL", resourceHelper.gs(R.string.basal_label) + ": " + sumLabel(), currentProfile.basal, null, pumpDescription.basalMinimumRate, 10.0, 0.01, DecimalFormat("0.00"), save) + basalView = TimeListEdit(context, aapsLogger, dateUtil, view, R.id.basal_holder, "BASAL", resourceHelper.gs(R.string.basal_label) + ": " + sumLabel(), currentProfile.basal, null, pumpDescription.basalMinimumRate, 10.0, 0.01, DecimalFormat("0.00"), save) if (units == Constants.MGDL) { TimeListEdit(context, aapsLogger, dateUtil, view, R.id.isf, "ISF", resourceHelper.gs(R.string.isf_label), currentProfile.isf, null, HardLimits.MIN_ISF, HardLimits.MAX_ISF, 1.0, DecimalFormat("0"), save) TimeListEdit(context, aapsLogger, dateUtil, view, R.id.target, "TARGET", resourceHelper.gs(R.string.target_label), currentProfile.targetLow, currentProfile.targetHigh, HardLimits.VERY_HARD_LIMIT_TARGET_BG[0].toDouble(), HardLimits.VERY_HARD_LIMIT_TARGET_BG[1].toDouble(), 1.0, DecimalFormat("0"), save) } else { - TimeListEdit(context, aapsLogger, dateUtil, view, R.id.isf, "ISF", resourceHelper.gs(R.string.isf_label), currentProfile.isf, null, Profile.fromMgdlToUnits(HardLimits.MIN_ISF, Constants.MMOL), Profile.fromMgdlToUnits(HardLimits.MAX_ISF, Constants.MMOL), 0.1, DecimalFormat("0.0"), save) - TimeListEdit(context, aapsLogger, dateUtil, view, R.id.target, "TARGET", resourceHelper.gs(R.string.target_label), currentProfile.targetLow, currentProfile.targetHigh, Profile.fromMgdlToUnits(HardLimits.VERY_HARD_LIMIT_TARGET_BG[0].toDouble(), Constants.MMOL), Profile.fromMgdlToUnits(HardLimits.VERY_HARD_LIMIT_TARGET_BG[1].toDouble(), Constants.MMOL), 0.1, DecimalFormat("0.0"), save) + TimeListEdit(context, aapsLogger, dateUtil, view, R.id.isf, "ISF", resourceHelper.gs(R.string.isf_label), currentProfile.isf, null, Profile.fromMgdlToUnits(HardLimits.MIN_ISF, GlucoseUnit.MMOL), Profile.fromMgdlToUnits(HardLimits.MAX_ISF, GlucoseUnit.MMOL), 0.1, DecimalFormat("0.0"), save) + TimeListEdit(context, aapsLogger, dateUtil, view, R.id.target, "TARGET", resourceHelper.gs(R.string.target_label), currentProfile.targetLow, currentProfile.targetHigh, Profile.fromMgdlToUnits(HardLimits.VERY_HARD_LIMIT_TARGET_BG[0].toDouble(), GlucoseUnit.MMOL), Profile.fromMgdlToUnits(HardLimits.VERY_HARD_LIMIT_TARGET_BG[1].toDouble(), GlucoseUnit.MMOL), 0.1, DecimalFormat("0.0"), save) } // Spinner @@ -158,12 +165,15 @@ class LocalProfileFragment : DaggerFragment() { } } }) + localProfilePlugin.profile?.getSpecificProfile(spinner?.selectedItem.toString())?.let { + binding.basalGraph.show(ProfileSealed.Pure(it)) + } binding.profileAdd.setOnClickListener { if (localProfilePlugin.isEdited) { activity?.let { OKDialog.show(it, "", resourceHelper.gs(R.string.saveorresetchangesfirst)) } } else { - uel.log(Action.NEW_PROFILE) + uel.log(Action.NEW_PROFILE, Sources.LocalProfile) localProfilePlugin.addNewProfile() build() } @@ -173,7 +183,8 @@ class LocalProfileFragment : DaggerFragment() { if (localProfilePlugin.isEdited) { activity?.let { OKDialog.show(it, "", resourceHelper.gs(R.string.saveorresetchangesfirst)) } } else { - uel.log(Action.CLONE_PROFILE, localProfilePlugin.currentProfile()?.name ?: "") + uel.log(Action.CLONE_PROFILE, Sources.LocalProfile, ValueWithUnit.SimpleString(localProfilePlugin.currentProfile()?.name + ?: "")) localProfilePlugin.cloneProfile() build() } @@ -182,7 +193,8 @@ class LocalProfileFragment : DaggerFragment() { binding.profileRemove.setOnClickListener { activity?.let { activity -> OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.deletecurrentprofile), { - uel.log(Action.PROFILE_REMOVED, localProfilePlugin.currentProfile()?.name ?: "") + uel.log(Action.PROFILE_REMOVED, Sources.LocalProfile, ValueWithUnit.SimpleString(localProfilePlugin.currentProfile()?.name + ?: "")) localProfilePlugin.removeCurrentProfile() build() }, null) @@ -210,6 +222,8 @@ class LocalProfileFragment : DaggerFragment() { if (!localProfilePlugin.isValidEditState()) { return@setOnClickListener //Should not happen as saveButton should not be visible if not valid } + uel.log(Action.STORE_PROFILE, Sources.LocalProfile, ValueWithUnit.SimpleString(localProfilePlugin.currentProfile()?.name + ?: "")) localProfilePlugin.storeSettings(activity) build() } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/profile/local/LocalProfilePlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/profile/local/LocalProfilePlugin.kt index 8da1e6b188..42afbb4c5c 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/profile/local/LocalProfilePlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/profile/local/LocalProfilePlugin.kt @@ -1,26 +1,33 @@ package info.nightscout.androidaps.plugins.profile.local +import android.content.Context import androidx.fragment.app.FragmentActivity +import androidx.work.Worker +import androidx.work.WorkerParameters +import androidx.work.workDataOf import dagger.android.HasAndroidInjector import info.nightscout.androidaps.Constants import info.nightscout.androidaps.R -import info.nightscout.androidaps.data.Profile -import info.nightscout.androidaps.database.entities.UserEntry.* +import info.nightscout.androidaps.data.ProfileSealed +import info.nightscout.androidaps.data.PureProfile import info.nightscout.androidaps.events.EventProfileStoreChanged +import info.nightscout.androidaps.extensions.blockFromJsonArray 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.general.nsclient.NSUpload +import info.nightscout.androidaps.plugins.profile.local.events.EventLocalProfileChanged +import info.nightscout.androidaps.receivers.DataWorker import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DecimalFormatter +import info.nightscout.androidaps.utils.HardLimits import info.nightscout.androidaps.utils.alertDialogs.OKDialog 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 java.lang.Integer.min import java.util.* import javax.inject.Inject import javax.inject.Singleton @@ -34,8 +41,9 @@ class LocalProfilePlugin @Inject constructor( resourceHelper: ResourceHelper, private val sp: SP, private val profileFunction: ProfileFunction, - private val nsUpload: NSUpload, - private val uel: UserEntryLogger + private val activePlugin: ActivePlugin, + private val hardLimits: HardLimits, + private val dateUtil: DateUtil ) : PluginBase(PluginDescription() .mainType(PluginType.PROFILE) .fragmentClass(LocalProfileFragment::class.java.name) @@ -46,7 +54,7 @@ class LocalProfilePlugin @Inject constructor( .description(R.string.description_profile_local) .setDefault(), aapsLogger, resourceHelper, injector -), ProfileInterface { +), ProfileSource { private var rawProfile: ProfileStore? = null @@ -58,6 +66,7 @@ class LocalProfilePlugin @Inject constructor( } class SingleProfile { + internal var name: String? = null internal var mgdl: Boolean = false internal var dia: Double = Constants.defaultDIA @@ -92,8 +101,25 @@ class LocalProfilePlugin @Inject constructor( @Synchronized fun isValidEditState(): Boolean { - return createProfileStore().getDefaultProfile()?.isValid(resourceHelper.gs(R.string.localprofile), false) - ?: false + val pumpDescription = activePlugin.activePump.pumpDescription + with(profiles[currentProfileIndex]) { + if (dia < hardLimits.minDia() || dia > hardLimits.maxDia()) return false + if (name.isNullOrEmpty()) return false + if (blockFromJsonArray(ic, dateUtil)?.any { it.amount < hardLimits.minIC() || it.amount > hardLimits.maxIC() } != false) return false + if (blockFromJsonArray(isf, dateUtil)?.any { it.amount < HardLimits.MIN_ISF || it.amount > HardLimits.MAX_ISF } != false) return false + if (blockFromJsonArray(basal, dateUtil)?.any { it.amount < pumpDescription.basalMinimumRate || it.amount > 10.0 } != false) return false + val low = blockFromJsonArray(targetLow, dateUtil) + val high = blockFromJsonArray(targetHigh, dateUtil) + if (profileFunction.getUnits() == GlucoseUnit.MGDL) { + if (low?.any { it.amount < HardLimits.VERY_HARD_LIMIT_TARGET_BG[0].toDouble() || it.amount > HardLimits.VERY_HARD_LIMIT_TARGET_BG[1].toDouble() } != false) return false + if (high?.any { it.amount < HardLimits.VERY_HARD_LIMIT_TARGET_BG[0].toDouble() || it.amount > HardLimits.VERY_HARD_LIMIT_TARGET_BG[1].toDouble() } != false) return false + } else { + if (low?.any { it.amount < Profile.fromMgdlToUnits(HardLimits.VERY_HARD_LIMIT_TARGET_BG[0].toDouble(), GlucoseUnit.MMOL) || it.amount > Profile.fromMgdlToUnits(HardLimits.VERY_HARD_LIMIT_TARGET_BG[1].toDouble(), GlucoseUnit.MMOL) } != false) return false + if (high?.any { it.amount < Profile.fromMgdlToUnits(HardLimits.VERY_HARD_LIMIT_TARGET_BG[0].toDouble(), GlucoseUnit.MMOL) || it.amount > Profile.fromMgdlToUnits(HardLimits.VERY_HARD_LIMIT_TARGET_BG[1].toDouble(), GlucoseUnit.MMOL) } != false) return false + } + for (i in low.indices) if (low[i].amount > high[i].amount) return false + } + return true } @Synchronized @@ -116,7 +142,6 @@ class LocalProfilePlugin @Inject constructor( createAndStoreConvertedProfile() isEdited = false aapsLogger.debug(LTag.PROFILE, "Storing settings: " + rawProfile?.data.toString()) - uel.log(Action.STORE_PROFILE) rxBus.send(EventProfileStoreChanged()) var namesOK = true profiles.forEach { @@ -124,7 +149,7 @@ class LocalProfilePlugin @Inject constructor( if (name.contains(".")) namesOK = false } if (namesOK) - rawProfile?.let { nsUpload.uploadProfileStore(it.data) } + sp.putLong(R.string.key_local_profile_last_change, dateUtil.now()) else activity?.let { OKDialog.show(it, "", resourceHelper.gs(R.string.profilenamecontainsdot)) @@ -147,75 +172,80 @@ class LocalProfilePlugin @Inject constructor( p.dia = sp.getDouble(localProfileNumbered + "dia", Constants.defaultDIA) try { p.ic = JSONArray(sp.getString(localProfileNumbered + "ic", defaultArray)) - } catch (e1: JSONException) { - try { - p.ic = JSONArray(defaultArray) - } catch (ignored: JSONException) { - } - aapsLogger.error("Exception", e1) - } - - try { p.isf = JSONArray(sp.getString(localProfileNumbered + "isf", defaultArray)) - } catch (e1: JSONException) { - try { - p.isf = JSONArray(defaultArray) - } catch (ignored: JSONException) { - } - aapsLogger.error("Exception", e1) - } - - try { p.basal = JSONArray(sp.getString(localProfileNumbered + "basal", defaultArray)) - } catch (e1: JSONException) { - try { - p.basal = JSONArray(defaultArray) - } catch (ignored: JSONException) { - } - aapsLogger.error("Exception", e1) - } - - try { p.targetLow = JSONArray(sp.getString(localProfileNumbered + "targetlow", defaultArray)) - } catch (e1: JSONException) { - try { - p.targetLow = JSONArray(defaultArray) - } catch (ignored: JSONException) { - } - aapsLogger.error("Exception", e1) - } - - try { p.targetHigh = JSONArray(sp.getString(localProfileNumbered + "targethigh", defaultArray)) - } catch (e1: JSONException) { - try { - p.targetHigh = JSONArray(defaultArray) - } catch (ignored: JSONException) { - } - aapsLogger.error("Exception", e1) + profiles.add(p) + } catch (e: JSONException) { + aapsLogger.error("Exception", e) } - - profiles.add(p) } + // create at least one profile if doesn't exist + if (profiles.size < 1) profiles.add(defaultProfile()) isEdited = false numOfProfiles = profiles.size createAndStoreConvertedProfile() } - fun copyFrom(profile: Profile, newName: String): SingleProfile { + @Synchronized + fun loadFromStore(store: ProfileStore) { + try { + val newProfiles: ArrayList = ArrayList() + for (p in store.getProfileList()) { + store.getSpecificProfile(p.toString())?.let { + val sp = copyFrom(it, p.toString()) + sp.name = p.toString() + newProfiles.add(sp) + } + } + if (newProfiles.size > 0) { + profiles = newProfiles + numOfProfiles = profiles.size + currentProfileIndex = 0 + isEdited = false + createAndStoreConvertedProfile() + aapsLogger.debug(LTag.PROFILE, "Accepted ${profiles.size} profiles") + rxBus.send(EventLocalProfileChanged()) + } else + aapsLogger.debug(LTag.PROFILE, "ProfileStore not accepted") + } catch (e: Exception) { + aapsLogger.error("Error loading ProfileStore", e) + } + } + + private fun defaultProfile(): SingleProfile = + SingleProfile().also { p -> + p.name = Constants.LOCAL_PROFILE + p.mgdl = profileFunction.getUnits() == GlucoseUnit.MGDL + p.dia = Constants.defaultDIA + try { + p.ic = JSONArray(defaultArray) + p.isf = JSONArray(defaultArray) + p.basal = JSONArray(defaultArray) + p.targetLow = JSONArray(defaultArray) + p.targetHigh = JSONArray(defaultArray) + } catch (e: JSONException) { + aapsLogger.error("Exception", e) + } + } + + fun copyFrom(pureProfile: PureProfile, newName: String): SingleProfile { var verifiedName = newName if (rawProfile?.getSpecificProfile(newName) != null) { - verifiedName += " " + DateUtil.now().toString() + verifiedName += " " + dateUtil.now().toString() } + val profile = ProfileSealed.Pure(pureProfile) + val pureJson = pureProfile.jsonObject val sp = SingleProfile() sp.name = verifiedName - sp.mgdl = profile.units == Constants.MGDL - sp.dia = profile.dia - sp.ic = JSONArray(profile.data.getJSONArray("carbratio").toString()) - sp.isf = JSONArray(profile.data.getJSONArray("sens").toString()) - sp.basal = JSONArray(profile.data.getJSONArray("basal").toString()) - sp.targetLow = JSONArray(profile.data.getJSONArray("target_low").toString()) - sp.targetHigh = JSONArray(profile.data.getJSONArray("target_high").toString()) + sp.mgdl = profile.units == GlucoseUnit.MGDL + sp.dia = pureJson.getDouble("dia") + sp.ic = pureJson.getJSONArray("carbratio") + sp.isf = pureJson.getJSONArray("sens") + sp.basal = pureJson.getJSONArray("basal") + sp.targetLow = pureJson.getJSONArray("target_low") + sp.targetHigh = pureJson.getJSONArray("target_high") return sp } @@ -278,7 +308,7 @@ class LocalProfilePlugin @Inject constructor( } val p = SingleProfile() p.name = Constants.LOCAL_PROFILE + free - p.mgdl = profileFunction.getUnits() == Constants.MGDL + p.mgdl = profileFunction.getUnits() == GlucoseUnit.MGDL p.dia = Constants.defaultDIA p.ic = JSONArray(defaultArray) p.isf = JSONArray(defaultArray) @@ -342,21 +372,52 @@ class LocalProfilePlugin @Inject constructor( } } if (numOfProfiles > 0) json.put("defaultProfile", currentProfile()?.name) - json.put("startDate", DateUtil.toISOAsUTC(DateUtil.now())) + json.put("startDate", dateUtil.toISOAsUTC(dateUtil.now())) json.put("store", store) } catch (e: JSONException) { aapsLogger.error("Unhandled exception", e) } - return ProfileStore(injector, json) + return ProfileStore(injector, json, dateUtil) } - override fun getProfile(): ProfileStore? { - return rawProfile + override val profile: ProfileStore? + get() = rawProfile + + override val profileName: String + get() = rawProfile?.getDefaultProfile()?.let { + DecimalFormatter.to2Decimal(ProfileSealed.Pure(it).percentageBasalSum()) + "U " + } ?: "INVALID" + + // cannot be inner class because of needed injection + class NSProfileWorker( + context: Context, + params: WorkerParameters + ) : Worker(context, params) { + + @Inject lateinit var injector: HasAndroidInjector + @Inject lateinit var aapsLogger: AAPSLogger + @Inject lateinit var rxBus: RxBusWrapper + @Inject lateinit var dateUtil: DateUtil + @Inject lateinit var dataWorker: DataWorker + @Inject lateinit var sp: SP + @Inject lateinit var config: Config + @Inject lateinit var localProfilePlugin: LocalProfilePlugin + + init { + (context.applicationContext as HasAndroidInjector).androidInjector().inject(this) + } + + override fun doWork(): Result { + val profileJson = dataWorker.pickupJSONObject(inputData.getLong(DataWorker.STORE_KEY, -1)) + ?: return Result.failure(workDataOf("Error" to "missing input data")) + if (sp.getBoolean(R.string.key_ns_receive_profile_store, false) || config.NSCLIENT) { + localProfilePlugin.loadFromStore(ProfileStore(injector, profileJson, dateUtil)) + aapsLogger.debug(LTag.PROFILE, "Received profileStore: $profileJson") + return Result.success(workDataOf("Data" to profileJson.toString().substring(0..min(5000, profileJson.length())))) + } + return Result.success() + } } - override fun getProfileName(): String { - return DecimalFormatter.to2Decimal(rawProfile?.getDefaultProfile()?.percentageBasalSum() - ?: 0.0) + "U " - } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/profile/ns/NSProfileFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/profile/ns/NSProfileFragment.kt deleted file mode 100644 index 3b0b60768b..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/profile/ns/NSProfileFragment.kt +++ /dev/null @@ -1,161 +0,0 @@ -package info.nightscout.androidaps.plugins.profile.ns - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.AdapterView -import android.widget.ArrayAdapter -import dagger.android.support.DaggerFragment -import info.nightscout.androidaps.R -import info.nightscout.androidaps.database.entities.UserEntry.* -import info.nightscout.androidaps.databinding.NsprofileFragmentBinding -import info.nightscout.androidaps.interfaces.ProfileFunction -import info.nightscout.androidaps.logging.UserEntryLogger -import info.nightscout.androidaps.plugins.bus.RxBusWrapper -import info.nightscout.androidaps.plugins.profile.ns.events.EventNSProfileUpdateGUI -import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin -import info.nightscout.androidaps.utils.DateUtil -import info.nightscout.androidaps.utils.DecimalFormatter -import info.nightscout.androidaps.utils.FabricPrivacy -import info.nightscout.androidaps.utils.alertDialogs.OKDialog -import io.reactivex.rxkotlin.plusAssign -import info.nightscout.androidaps.utils.resources.ResourceHelper -import info.nightscout.androidaps.utils.rx.AapsSchedulers -import io.reactivex.disposables.CompositeDisposable -import javax.inject.Inject - -class NSProfileFragment : DaggerFragment() { - - @Inject lateinit var treatmentsPlugin: TreatmentsPlugin - @Inject lateinit var rxBus: RxBusWrapper - @Inject lateinit var resourceHelper: ResourceHelper - @Inject lateinit var fabricPrivacy: FabricPrivacy - @Inject lateinit var profileFunction: ProfileFunction - @Inject lateinit var nsProfilePlugin: NSProfilePlugin - @Inject lateinit var aapsSchedulers: AapsSchedulers - @Inject lateinit var uel: UserEntryLogger - - private var disposable: CompositeDisposable = CompositeDisposable() - - private var _binding: NsprofileFragmentBinding? = null - - // This property is only valid between onCreateView and - // onDestroyView. - private val binding get() = _binding!! - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle?): View { - _binding = NsprofileFragmentBinding.inflate(inflater, container, false) - return binding.root - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - binding.profileviewer.closeLayout.close.visibility = View.GONE // not needed for fragment - - binding.profileswitch.setOnClickListener { - val name = binding.spinner.selectedItem?.toString() ?: "" - nsProfilePlugin.profile?.let { store -> - store.getSpecificProfile(name)?.let { - activity?.let { activity -> - OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.nsprofile), - resourceHelper.gs(R.string.activate_profile) + ": " + name + " ?", Runnable { - uel.log(Action.PROFILE_SWITCH, ValueWithUnit(name, Units.None), ValueWithUnit(100.toInt(), Units.Percent)) - treatmentsPlugin.doProfileSwitch(store, name, 0, 100, 0, DateUtil.now()) - }) - } - } - } - } - - binding.spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { - override fun onNothingSelected(parent: AdapterView<*>?) { - if (_binding == null) return - binding.profileviewer.invalidprofile.visibility = View.VISIBLE - binding.profileviewer.noprofile.visibility = View.VISIBLE - binding.profileviewer.units.text = "" - binding.profileviewer.dia.text = "" - binding.profileviewer.activeprofile.text = "" - binding.profileviewer.ic.text = "" - binding.profileviewer.isf.text = "" - binding.profileviewer.basal.text = "" - binding.profileviewer.basaltotal.text = "" - binding.profileviewer.target.text = "" - binding.profileswitch.visibility = View.GONE - } - - override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { - if (_binding == null) return - val name = binding.spinner.getItemAtPosition(position).toString() - - binding.profileswitch.visibility = View.GONE - - nsProfilePlugin.profile?.let { store -> - store.getSpecificProfile(name)?.let { profile -> - if (_binding == null) return - binding.profileviewer.units.text = profile.units - binding.profileviewer.dia.text = resourceHelper.gs(R.string.format_hours, profile.dia) - binding.profileviewer.activeprofile.text = name - binding.profileviewer.ic.text = profile.icList - binding.profileviewer.isf.text = profile.isfList - binding.profileviewer.basal.text = profile.basalList - binding.profileviewer.basaltotal.text = String.format(resourceHelper.gs(R.string.profile_total), DecimalFormatter.to2Decimal(profile.baseBasalSum())) - binding.profileviewer.target.text = profile.targetList - binding.profileviewer.basalGraph.show(profile) - if (profile.isValid("NSProfileFragment")) { - binding.profileviewer.invalidprofile.visibility = View.GONE - binding.profileswitch.visibility = View.VISIBLE - } else { - binding.profileviewer.invalidprofile.visibility = View.VISIBLE - binding.profileswitch.visibility = View.GONE - } - } - } - } - } - } - - @Synchronized - override fun onResume() { - super.onResume() - disposable += rxBus - .toObservable(EventNSProfileUpdateGUI::class.java) - .observeOn(aapsSchedulers.main) - .subscribe({ updateGUI() }, fabricPrivacy::logException) - updateGUI() - } - - @Synchronized - override fun onPause() { - super.onPause() - disposable.clear() - } - - @Synchronized - override fun onDestroyView() { - super.onDestroyView() - _binding = null - } - - @Synchronized - fun updateGUI() { - if (_binding == null) return - binding.profileviewer.noprofile.visibility = View.VISIBLE - - nsProfilePlugin.profile?.let { profileStore -> - context?.let { context -> - val profileList = profileStore.getProfileList() - val adapter = ArrayAdapter(context, R.layout.spinner_centered, profileList) - binding.spinner.adapter = adapter - // set selected to actual profile - for (p in profileList.indices) { - if (profileList[p] == profileFunction.getProfileName()) - binding.spinner.setSelection(p) - } - binding.profileviewer.noprofile.visibility = View.GONE - } - } - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/profile/ns/NSProfilePlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/profile/ns/NSProfilePlugin.kt deleted file mode 100644 index e2c3c56608..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/profile/ns/NSProfilePlugin.kt +++ /dev/null @@ -1,110 +0,0 @@ -package info.nightscout.androidaps.plugins.profile.ns - -import android.content.Context -import androidx.work.Worker -import androidx.work.WorkerParameters -import dagger.android.HasAndroidInjector -import info.nightscout.androidaps.Config -import info.nightscout.androidaps.R -import info.nightscout.androidaps.events.EventProfileStoreChanged -import info.nightscout.androidaps.interfaces.PluginBase -import info.nightscout.androidaps.interfaces.PluginDescription -import info.nightscout.androidaps.interfaces.PluginType -import info.nightscout.androidaps.interfaces.ProfileInterface -import info.nightscout.androidaps.interfaces.ProfileStore -import info.nightscout.androidaps.logging.AAPSLogger -import info.nightscout.androidaps.logging.LTag -import info.nightscout.androidaps.plugins.bus.RxBusWrapper -import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart -import info.nightscout.androidaps.plugins.profile.ns.events.EventNSProfileUpdateGUI -import info.nightscout.androidaps.receivers.DataWorker -import info.nightscout.androidaps.utils.resources.ResourceHelper -import info.nightscout.androidaps.utils.sharedPreferences.SP -import org.json.JSONObject -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class NSProfilePlugin @Inject constructor( - injector: HasAndroidInjector, - aapsLogger: AAPSLogger, - private val rxBus: RxBusWrapper, - resourceHelper: ResourceHelper, - private val sp: SP, - config: Config -) : PluginBase(PluginDescription() - .mainType(PluginType.PROFILE) - .fragmentClass(NSProfileFragment::class.java.name) - .pluginIcon(R.drawable.ic_nightscout_profile) - .pluginName(R.string.nsprofile) - .shortName(R.string.profileviewer_shortname) - .alwaysEnabled(config.NSCLIENT) - .alwaysVisible(config.NSCLIENT) - .showInList(!config.NSCLIENT) - .description(R.string.description_profile_nightscout), - aapsLogger, resourceHelper, injector -), ProfileInterface { - - private var profile: ProfileStore? = null - - override fun onStart() { - super.onStart() - loadNSProfile() - } - - private fun storeNSProfile() { - sp.putString("profile", profile!!.data.toString()) - aapsLogger.debug(LTag.PROFILE, "Storing profile") - } - - private fun loadNSProfile() { - aapsLogger.debug(LTag.PROFILE, "Loading stored profile") - val profileString = sp.getStringOrNull("profile", null) - if (profileString != null) { - aapsLogger.debug(LTag.PROFILE, "Loaded profile: $profileString") - profile = ProfileStore(injector, JSONObject(profileString)) - } else { - aapsLogger.debug(LTag.PROFILE, "Stored profile not found") - // force restart of nsclient to fetch profile - rxBus.send(EventNSClientRestart()) - } - } - - override fun getProfile(): ProfileStore? { - return profile - } - - override fun getProfileName(): String { - return profile!!.getDefaultProfileName()!! - } - - // cannot be inner class because of needed injection - class NSProfileWorker( - context: Context, - params: WorkerParameters - ) : Worker(context, params) { - - @Inject lateinit var injector: HasAndroidInjector - @Inject lateinit var nsProfilePlugin: NSProfilePlugin - @Inject lateinit var aapsLogger: AAPSLogger - @Inject lateinit var rxBus: RxBusWrapper - @Inject lateinit var dataWorker: DataWorker - - init { - (context.applicationContext as HasAndroidInjector).androidInjector().inject(this) - } - - override fun doWork(): Result { - val profileString = dataWorker.pickupJSONObject(inputData.getLong(DataWorker.STORE_KEY, -1)) - ?: return Result.failure() - nsProfilePlugin.profile = ProfileStore(injector, profileString) - nsProfilePlugin.storeNSProfile() - if (nsProfilePlugin.isEnabled()) { - rxBus.send(EventProfileStoreChanged()) - rxBus.send(EventNSProfileUpdateGUI()) - } - aapsLogger.debug(LTag.PROFILE, "Received profileStore: ${nsProfilePlugin.profile}") - return Result.success() - } - } -} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/profile/ns/events/EventNSProfileUpdateGUI.kt b/app/src/main/java/info/nightscout/androidaps/plugins/profile/ns/events/EventNSProfileUpdateGUI.kt deleted file mode 100644 index 0d7c320876..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/profile/ns/events/EventNSProfileUpdateGUI.kt +++ /dev/null @@ -1,5 +0,0 @@ -package info.nightscout.androidaps.plugins.profile.ns.events - -import info.nightscout.androidaps.events.EventUpdateGui - -class EventNSProfileUpdateGUI : EventUpdateGui() diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/mdi/MDIPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/pump/mdi/MDIPlugin.kt index ea7b47f332..3612901830 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/mdi/MDIPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/mdi/MDIPlugin.kt @@ -3,16 +3,16 @@ package info.nightscout.androidaps.plugins.pump.mdi import dagger.android.HasAndroidInjector import info.nightscout.androidaps.R import info.nightscout.androidaps.data.DetailedBolusInfo -import info.nightscout.androidaps.data.Profile +import info.nightscout.androidaps.interfaces.Profile import info.nightscout.androidaps.data.PumpEnactResult import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.plugins.common.ManufacturerType import info.nightscout.androidaps.plugins.pump.common.defs.PumpType -import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.InstanceId.instanceId +import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.resources.ResourceHelper import org.json.JSONException import org.json.JSONObject @@ -25,14 +25,15 @@ class MDIPlugin @Inject constructor( aapsLogger: AAPSLogger, resourceHelper: ResourceHelper, commandQueue: CommandQueueProvider, - private val treatmentsPlugin: TreatmentsPlugin + private val dateUtil: DateUtil, + private val pumpSync: PumpSync ) : PumpPluginBase(PluginDescription() .mainType(PluginType.PUMP) .pluginIcon(R.drawable.ic_ict) .pluginName(R.string.mdi) .description(R.string.description_pump_mdi), injector, aapsLogger, resourceHelper, commandQueue -), PumpInterface { +), Pump { override val pumpDescription = PumpDescription() @@ -60,7 +61,7 @@ class MDIPlugin @Inject constructor( override fun waitForDisconnectionInSeconds(): Int = 0 override fun stopConnecting() {} override fun getPumpStatus(reason: String) {} - override fun setNewBasalProfile(profile: Profile): PumpEnactResult = PumpEnactResult(injector).success(true) + override fun setNewBasalProfile(profile: Profile): PumpEnactResult = PumpEnactResult(injector).success(true).enacted(true) override fun isThisProfileSet(profile: Profile): Boolean = false override fun lastDataTime(): Long = System.currentTimeMillis() override val baseBasalRate: Double = 0.0 @@ -73,12 +74,26 @@ class MDIPlugin @Inject constructor( result.bolusDelivered = detailedBolusInfo.insulin result.carbsDelivered = detailedBolusInfo.carbs result.comment = resourceHelper.gs(R.string.virtualpump_resultok) - treatmentsPlugin.addToHistoryTreatment(detailedBolusInfo, false) + if (detailedBolusInfo.insulin > 0) + pumpSync.syncBolusWithPumpId( + timestamp = detailedBolusInfo.timestamp, + amount = detailedBolusInfo.insulin, + type = detailedBolusInfo.bolusType, + pumpId = dateUtil.now(), + pumpType = PumpType.MDI, + pumpSerial = serialNumber()) + if (detailedBolusInfo.carbs > 0) + pumpSync.syncCarbsWithTimestamp( + timestamp = detailedBolusInfo.timestamp + T.mins(detailedBolusInfo.carbTime.toLong()).msecs(), + amount = detailedBolusInfo.carbs, + pumpId = null, + pumpType = PumpType.MDI, + pumpSerial = serialNumber()) return result } override fun stopBolusDelivering() {} - override fun setTempBasalAbsolute(absoluteRate: Double, durationInMinutes: Int, profile: Profile, enforceNew: Boolean): PumpEnactResult { + override fun setTempBasalAbsolute(absoluteRate: Double, durationInMinutes: Int, profile: Profile, enforceNew: Boolean, tbrType: PumpSync.TemporaryBasalType): PumpEnactResult { val result = PumpEnactResult(injector) result.success = false result.comment = resourceHelper.gs(R.string.pumperror) @@ -86,7 +101,7 @@ class MDIPlugin @Inject constructor( return result } - override fun setTempBasalPercent(percent: Int, durationInMinutes: Int, profile: Profile, enforceNew: Boolean): PumpEnactResult { + override fun setTempBasalPercent(percent: Int, durationInMinutes: Int, profile: Profile, enforceNew: Boolean, tbrType: PumpSync.TemporaryBasalType): PumpEnactResult { val result = PumpEnactResult(injector) result.success = false result.comment = resourceHelper.gs(R.string.pumperror) @@ -127,10 +142,10 @@ class MDIPlugin @Inject constructor( status.put("status", "normal") extended.put("Version", version) extended.put("ActiveProfile", profileName) - status.put("timestamp", DateUtil.toISOString(now)) + status.put("timestamp", dateUtil.toISOString(now)) pump.put("status", status) pump.put("extended", extended) - pump.put("clock", DateUtil.toISOString(now)) + pump.put("clock", dateUtil.toISOString(now)) } catch (e: JSONException) { aapsLogger.error("Exception: ", e) } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/virtual/VirtualPumpFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/pump/virtual/VirtualPumpFragment.kt index bdbc298ff5..f2eb4bb5e1 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/virtual/VirtualPumpFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/virtual/VirtualPumpFragment.kt @@ -10,24 +10,29 @@ import info.nightscout.androidaps.R import info.nightscout.androidaps.databinding.VirtualpumpFragmentBinding import info.nightscout.androidaps.events.EventExtendedBolusChange import info.nightscout.androidaps.events.EventTempBasalChange +import info.nightscout.androidaps.interfaces.IobCobCalculator +import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.pump.virtual.events.EventVirtualPumpUpdateGui -import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin +import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.T -import io.reactivex.rxkotlin.plusAssign +import info.nightscout.androidaps.extensions.toStringFull 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 javax.inject.Inject class VirtualPumpFragment : DaggerFragment() { @Inject lateinit var rxBus: RxBusWrapper @Inject lateinit var resourceHelper: ResourceHelper + @Inject lateinit var dateUtil: DateUtil @Inject lateinit var fabricPrivacy: FabricPrivacy @Inject lateinit var virtualPumpPlugin: VirtualPumpPlugin - @Inject lateinit var treatmentsPlugin: TreatmentsPlugin + @Inject lateinit var profileFunction: ProfileFunction + @Inject lateinit var iobCobCalculator: IobCobCalculator @Inject lateinit var aapsSchedulers: AapsSchedulers private val disposable = CompositeDisposable() @@ -88,10 +93,11 @@ class VirtualPumpFragment : DaggerFragment() { @Synchronized private fun updateGui() { if (_binding == null) return + val profile = profileFunction.getProfile() ?: return binding.basabasalrate.text = resourceHelper.gs(R.string.pump_basebasalrate, virtualPumpPlugin.baseBasalRate) - binding.tempbasal.text = treatmentsPlugin.getTempBasalFromHistory(System.currentTimeMillis())?.toStringFull() + binding.tempbasal.text = iobCobCalculator.getTempBasal(dateUtil.now())?.toStringFull(profile, dateUtil) ?: "" - binding.extendedbolus.text = treatmentsPlugin.getExtendedBolusFromHistory(System.currentTimeMillis())?.toString() + binding.extendedbolus.text = iobCobCalculator.getExtendedBolus(dateUtil.now())?.toStringFull(dateUtil) ?: "" binding.battery.text = resourceHelper.gs(R.string.format_percent, virtualPumpPlugin.batteryPercent) binding.reservoir.text = resourceHelper.gs(R.string.formatinsulinunits, virtualPumpPlugin.reservoirInUnits.toDouble()) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/virtual/VirtualPumpPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/pump/virtual/VirtualPumpPlugin.kt index 7d72e9deae..cd352a63c0 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/virtual/VirtualPumpPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/virtual/VirtualPumpPlugin.kt @@ -4,15 +4,12 @@ import android.os.SystemClock import androidx.preference.PreferenceFragmentCompat import androidx.preference.SwitchPreference import dagger.android.HasAndroidInjector -import info.nightscout.androidaps.Config import info.nightscout.androidaps.R import info.nightscout.androidaps.data.DetailedBolusInfo -import info.nightscout.androidaps.data.Profile import info.nightscout.androidaps.data.PumpEnactResult -import info.nightscout.androidaps.db.ExtendedBolus -import info.nightscout.androidaps.db.Source -import info.nightscout.androidaps.db.TemporaryBasal import info.nightscout.androidaps.events.EventPreferenceChange +import info.nightscout.androidaps.extensions.convertedToAbsolute +import info.nightscout.androidaps.extensions.plannedRemainingMinutes import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag @@ -23,10 +20,10 @@ import info.nightscout.androidaps.plugins.general.overview.events.EventOverviewB import info.nightscout.androidaps.plugins.general.overview.notifications.Notification import info.nightscout.androidaps.plugins.pump.common.defs.PumpType import info.nightscout.androidaps.plugins.pump.virtual.events.EventVirtualPumpUpdateGui -import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.InstanceId.instanceId +import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.TimeChangeType import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers @@ -49,8 +46,9 @@ open class VirtualPumpPlugin @Inject constructor( private val aapsSchedulers: AapsSchedulers, private val sp: SP, private val profileFunction: ProfileFunction, - private val treatmentsPlugin: TreatmentsPlugin, + private val iobCobCalculator: IobCobCalculator, commandQueue: CommandQueueProvider, + private val pumpSync: PumpSync, private val config: Config, private val dateUtil: DateUtil ) : PumpPluginBase(PluginDescription() @@ -63,7 +61,7 @@ open class VirtualPumpPlugin @Inject constructor( .description(R.string.description_pump_virtual) .setDefault(), injector, aapsLogger, resourceHelper, commandQueue -), PumpInterface { +), Pump { private val disposable = CompositeDisposable() var batteryPercent = 50 @@ -72,32 +70,30 @@ open class VirtualPumpPlugin @Inject constructor( var pumpType: PumpType? = null private set private var lastDataTime: Long = 0 - override val pumpDescription = PumpDescription() - - init { - pumpDescription.isBolusCapable = true - pumpDescription.bolusStep = 0.1 - pumpDescription.isExtendedBolusCapable = true - pumpDescription.extendedBolusStep = 0.05 - pumpDescription.extendedBolusDurationStep = 30.0 - pumpDescription.extendedBolusMaxDuration = 8 * 60.toDouble() - pumpDescription.isTempBasalCapable = true - pumpDescription.tempBasalStyle = PumpDescription.PERCENT or PumpDescription.ABSOLUTE - pumpDescription.maxTempPercent = 500 - pumpDescription.tempPercentStep = 10 - pumpDescription.tempDurationStep = 30 - pumpDescription.tempDurationStep15mAllowed = true - pumpDescription.tempDurationStep30mAllowed = true - pumpDescription.tempMaxDuration = 24 * 60 - pumpDescription.isSetBasalProfileCapable = true - pumpDescription.basalStep = 0.01 - pumpDescription.basalMinimumRate = 0.01 - pumpDescription.isRefillingCapable = true - pumpDescription.storesCarbInfo = false - pumpDescription.is30minBasalRatesCapable = true + override val pumpDescription = PumpDescription().also { + it.isBolusCapable = true + it.bolusStep = 0.1 + it.isExtendedBolusCapable = true + it.extendedBolusStep = 0.05 + it.extendedBolusDurationStep = 30.0 + it.extendedBolusMaxDuration = 8 * 60.toDouble() + it.isTempBasalCapable = true + it.tempBasalStyle = PumpDescription.PERCENT or PumpDescription.ABSOLUTE + it.maxTempPercent = 500 + it.tempPercentStep = 10 + it.tempDurationStep = 30 + it.tempDurationStep15mAllowed = true + it.tempDurationStep30mAllowed = true + it.tempMaxDuration = 24 * 60 + it.isSetBasalProfileCapable = true + it.basalStep = 0.01 + it.basalMinimumRate = 0.01 + it.isRefillingCapable = true + it.storesCarbInfo = false + it.is30minBasalRatesCapable = true } - fun getFakingStatus(): Boolean { + private fun getFakingStatus(): Boolean { return sp.getBoolean(R.string.key_fromNSAreCommingFakedExtendedBoluses, false) } @@ -141,7 +137,6 @@ open class VirtualPumpPlugin @Inject constructor( override fun isHandshakeInProgress(): Boolean = false override fun connect(reason: String) { - //if (!Config.NSCLIENT) NSUpload.uploadDeviceStatus() lastDataTime = System.currentTimeMillis() } @@ -154,16 +149,14 @@ open class VirtualPumpPlugin @Inject constructor( override fun setNewBasalProfile(profile: Profile): PumpEnactResult { lastDataTime = System.currentTimeMillis() - // Do nothing here. we are using ConfigBuilderPlugin.getPlugin().getActiveProfile().getProfile(); - val result = PumpEnactResult(injector) - result.success = true - val notification = Notification(Notification.PROFILE_SET_OK, resourceHelper.gs(R.string.profile_set_ok), Notification.INFO, 60) - rxBus.send(EventNewNotification(notification)) - return result + rxBus.send(EventNewNotification(Notification(Notification.PROFILE_SET_OK, resourceHelper.gs(R.string.profile_set_ok), Notification.INFO, 60))) + // Do nothing here. we are using database profile + return PumpEnactResult(injector).success(true).enacted(true) } override fun isThisProfileSet(profile: Profile): Boolean { - return true + val running = pumpSync.expectedPumpState().profile + return running?.isEqual(profile) ?: false } override fun lastDataTime(): Long { @@ -171,7 +164,7 @@ open class VirtualPumpPlugin @Inject constructor( } override val baseBasalRate: Double - get() = profileFunction.getProfile()?.basal ?: 0.0 + get() = profileFunction.getProfile()?.getBasal() ?: 0.0 override val reservoirLevel: Double get() = reservoirInUnits.toDouble() @@ -181,22 +174,21 @@ open class VirtualPumpPlugin @Inject constructor( override fun deliverTreatment(detailedBolusInfo: DetailedBolusInfo): PumpEnactResult { val result = PumpEnactResult(injector) - result.success = true - result.bolusDelivered = detailedBolusInfo.insulin - result.carbsDelivered = detailedBolusInfo.carbs - result.enacted = result.bolusDelivered > 0 || result.carbsDelivered > 0 - result.comment = resourceHelper.gs(R.string.virtualpump_resultok) + .success(true) + .bolusDelivered(detailedBolusInfo.insulin) + .carbsDelivered(detailedBolusInfo.carbs) + .enacted(detailedBolusInfo.insulin > 0 || detailedBolusInfo.carbs > 0) + .comment(resourceHelper.gs(R.string.virtualpump_resultok)) + val bolusingEvent = EventOverviewBolusProgress var delivering = 0.0 while (delivering < detailedBolusInfo.insulin) { SystemClock.sleep(200) - val bolusingEvent = EventOverviewBolusProgress bolusingEvent.status = resourceHelper.gs(R.string.bolusdelivering, delivering) bolusingEvent.percent = min((delivering / detailedBolusInfo.insulin * 100).toInt(), 100) rxBus.send(bolusingEvent) delivering += 0.1 } SystemClock.sleep(200) - val bolusingEvent = EventOverviewBolusProgress bolusingEvent.status = resourceHelper.gs(R.string.bolusdelivered, detailedBolusInfo.insulin) bolusingEvent.percent = 100 rxBus.send(bolusingEvent) @@ -204,17 +196,26 @@ open class VirtualPumpPlugin @Inject constructor( aapsLogger.debug(LTag.PUMP, "Delivering treatment insulin: " + detailedBolusInfo.insulin + "U carbs: " + detailedBolusInfo.carbs + "g " + result) rxBus.send(EventVirtualPumpUpdateGui()) lastDataTime = System.currentTimeMillis() - treatmentsPlugin.addToHistoryTreatment(detailedBolusInfo, false) + if (detailedBolusInfo.insulin > 0) + pumpSync.syncBolusWithPumpId( + timestamp = detailedBolusInfo.timestamp, + amount = detailedBolusInfo.insulin, + type = detailedBolusInfo.bolusType, + pumpId = dateUtil.now(), + pumpType = pumpType ?: PumpType.GENERIC_AAPS, + pumpSerial = serialNumber()) + if (detailedBolusInfo.carbs > 0) + pumpSync.syncCarbsWithTimestamp( + timestamp = detailedBolusInfo.timestamp + T.mins(detailedBolusInfo.carbTime.toLong()).msecs(), + amount = detailedBolusInfo.carbs, + pumpId = null, + pumpType = pumpType ?: PumpType.GENERIC_AAPS, + pumpSerial = serialNumber()) return result } override fun stopBolusDelivering() {} - override fun setTempBasalAbsolute(absoluteRate: Double, durationInMinutes: Int, profile: Profile, enforceNew: Boolean): PumpEnactResult { - val tempBasal = TemporaryBasal(injector) - .date(System.currentTimeMillis()) - .absolute(absoluteRate) - .duration(durationInMinutes) - .source(Source.USER) + override fun setTempBasalAbsolute(absoluteRate: Double, durationInMinutes: Int, profile: Profile, enforceNew: Boolean, tbrType: PumpSync.TemporaryBasalType): PumpEnactResult { val result = PumpEnactResult(injector) result.success = true result.enacted = true @@ -222,19 +223,23 @@ open class VirtualPumpPlugin @Inject constructor( result.absolute = absoluteRate result.duration = durationInMinutes result.comment = resourceHelper.gs(R.string.virtualpump_resultok) - treatmentsPlugin.addToHistoryTempBasal(tempBasal) + pumpSync.syncTemporaryBasalWithPumpId( + timestamp = dateUtil.now(), + rate = absoluteRate, + duration = T.mins(durationInMinutes.toLong()).msecs(), + isAbsolute = true, + type = tbrType, + pumpId = dateUtil.now(), + pumpType = pumpType ?: PumpType.GENERIC_AAPS, + pumpSerial = serialNumber() + ) aapsLogger.debug(LTag.PUMP, "Setting temp basal absolute: $result") rxBus.send(EventVirtualPumpUpdateGui()) lastDataTime = System.currentTimeMillis() return result } - override fun setTempBasalPercent(percent: Int, durationInMinutes: Int, profile: Profile, enforceNew: Boolean): PumpEnactResult { - val tempBasal = TemporaryBasal(injector) - .date(System.currentTimeMillis()) - .percent(percent) - .duration(durationInMinutes) - .source(Source.USER) + override fun setTempBasalPercent(percent: Int, durationInMinutes: Int, profile: Profile, enforceNew: Boolean, tbrType: PumpSync.TemporaryBasalType): PumpEnactResult { val result = PumpEnactResult(injector) result.success = true result.enacted = true @@ -243,7 +248,16 @@ open class VirtualPumpPlugin @Inject constructor( result.isTempCancel = false result.duration = durationInMinutes result.comment = resourceHelper.gs(R.string.virtualpump_resultok) - treatmentsPlugin.addToHistoryTempBasal(tempBasal) + pumpSync.syncTemporaryBasalWithPumpId( + timestamp = dateUtil.now(), + rate = percent.toDouble(), + duration = T.mins(durationInMinutes.toLong()).msecs(), + isAbsolute = false, + type = tbrType, + pumpId = dateUtil.now(), + pumpType = pumpType ?: PumpType.GENERIC_AAPS, + pumpSerial = serialNumber() + ) aapsLogger.debug(LTag.PUMP, "Settings temp basal percent: $result") rxBus.send(EventVirtualPumpUpdateGui()) lastDataTime = System.currentTimeMillis() @@ -253,18 +267,21 @@ open class VirtualPumpPlugin @Inject constructor( override fun setExtendedBolus(insulin: Double, durationInMinutes: Int): PumpEnactResult { val result = cancelExtendedBolus() if (!result.success) return result - val extendedBolus = ExtendedBolus(injector) - .date(System.currentTimeMillis()) - .insulin(insulin) - .durationInMinutes(durationInMinutes) - .source(Source.USER) result.success = true result.enacted = true result.bolusDelivered = insulin result.isTempCancel = false result.duration = durationInMinutes result.comment = resourceHelper.gs(R.string.virtualpump_resultok) - treatmentsPlugin.addToHistoryExtendedBolus(extendedBolus) + pumpSync.syncExtendedBolusWithPumpId( + timestamp = dateUtil.now(), + amount = insulin, + duration = T.mins(durationInMinutes.toLong()).msecs(), + isEmulatingTB = false, + pumpId = dateUtil.now(), + pumpType = pumpType ?: PumpType.GENERIC_AAPS, + pumpSerial = serialNumber() + ) aapsLogger.debug(LTag.PUMP, "Setting extended bolus: $result") rxBus.send(EventVirtualPumpUpdateGui()) lastDataTime = System.currentTimeMillis() @@ -276,11 +293,14 @@ open class VirtualPumpPlugin @Inject constructor( result.success = true result.isTempCancel = true result.comment = resourceHelper.gs(R.string.virtualpump_resultok) - if (treatmentsPlugin.isTempBasalInProgress) { + if (pumpSync.expectedPumpState().temporaryBasal != null) { result.enacted = true - val tempStop = TemporaryBasal(injector).date(System.currentTimeMillis()).source(Source.USER) - treatmentsPlugin.addToHistoryTempBasal(tempStop) - //tempBasal = null; + pumpSync.syncStopTemporaryBasalWithPumpId( + timestamp = dateUtil.now(), + endPumpId = dateUtil.now(), + pumpType = pumpType ?: PumpType.GENERIC_AAPS, + pumpSerial = serialNumber() + ) aapsLogger.debug(LTag.PUMP, "Canceling temp basal: $result") rxBus.send(EventVirtualPumpUpdateGui()) } @@ -290,10 +310,13 @@ open class VirtualPumpPlugin @Inject constructor( override fun cancelExtendedBolus(): PumpEnactResult { val result = PumpEnactResult(injector) - if (treatmentsPlugin.isInHistoryExtendedBolusInProgress) { - val exStop = ExtendedBolus(injector, System.currentTimeMillis()) - exStop.source = Source.USER - treatmentsPlugin.addToHistoryExtendedBolus(exStop) + if (pumpSync.expectedPumpState().extendedBolus != null) { + pumpSync.syncStopExtendedBolusWithPumpId( + timestamp = dateUtil.now(), + endPumpId = dateUtil.now(), + pumpType = pumpType ?: PumpType.GENERIC_AAPS, + pumpSerial = serialNumber() + ) } result.success = true result.enacted = true @@ -322,24 +345,24 @@ open class VirtualPumpPlugin @Inject constructor( extended.put("ActiveProfile", profileName) } catch (ignored: Exception) { } - val tb = treatmentsPlugin.getTempBasalFromHistory(now) + val tb = iobCobCalculator.getTempBasal(now) if (tb != null) { - extended.put("TempBasalAbsoluteRate", tb.tempBasalConvertedToAbsolute(now, profile)) - extended.put("TempBasalStart", dateUtil.dateAndTimeString(tb.date)) + extended.put("TempBasalAbsoluteRate", tb.convertedToAbsolute(now, profile)) + extended.put("TempBasalStart", dateUtil.dateAndTimeString(tb.timestamp)) extended.put("TempBasalRemaining", tb.plannedRemainingMinutes) } - val eb = treatmentsPlugin.getExtendedBolusFromHistory(now) + val eb = iobCobCalculator.getExtendedBolus(now) if (eb != null) { - extended.put("ExtendedBolusAbsoluteRate", eb.absoluteRate()) - extended.put("ExtendedBolusStart", dateUtil.dateAndTimeString(eb.date)) + extended.put("ExtendedBolusAbsoluteRate", eb.rate) + extended.put("ExtendedBolusStart", dateUtil.dateAndTimeString(eb.timestamp)) extended.put("ExtendedBolusRemaining", eb.plannedRemainingMinutes) } - status.put("timestamp", DateUtil.toISOString(now)) + status.put("timestamp", dateUtil.toISOString(now)) pump.put("battery", battery) pump.put("status", status) pump.put("extended", extended) pump.put("reservoir", reservoirInUnits) - pump.put("clock", DateUtil.toISOString(now)) + pump.put("clock", dateUtil.toISOString(now)) } catch (e: JSONException) { aapsLogger.error("Unhandled exception", e) } @@ -347,7 +370,7 @@ open class VirtualPumpPlugin @Inject constructor( } override fun manufacturer(): ManufacturerType { - return pumpDescription.pumpType.manufacturer + return pumpDescription.pumpType.manufacturer ?: ManufacturerType.AndroidAPS } override fun model(): PumpType { @@ -367,12 +390,12 @@ open class VirtualPumpPlugin @Inject constructor( } fun refreshConfiguration() { - val pumpType = sp.getString(R.string.key_virtualpump_type, PumpType.GenericAAPS.description) + val pumpType = sp.getString(R.string.key_virtualpump_type, PumpType.GENERIC_AAPS.description) val pumpTypeNew = PumpType.getByDescription(pumpType) aapsLogger.debug(LTag.PUMP, "Pump in configuration: $pumpType, PumpType object: $pumpTypeNew") if (this.pumpType == pumpTypeNew) return aapsLogger.debug(LTag.PUMP, "New pump configuration found ($pumpTypeNew), changing from previous (${this.pumpType})") - pumpDescription.setPumpDescription(pumpTypeNew) + pumpDescription.fillFor(pumpTypeNew) this.pumpType = pumpTypeNew } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/sensitivity/AbstractSensitivityPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sensitivity/AbstractSensitivityPlugin.kt index 9e826156ee..66b070075d 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/sensitivity/AbstractSensitivityPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sensitivity/AbstractSensitivityPlugin.kt @@ -2,12 +2,12 @@ package info.nightscout.androidaps.plugins.sensitivity import dagger.android.HasAndroidInjector import info.nightscout.androidaps.R -import info.nightscout.androidaps.interfaces.IobCobCalculatorInterface import info.nightscout.androidaps.interfaces.PluginBase import info.nightscout.androidaps.interfaces.PluginDescription -import info.nightscout.androidaps.interfaces.SensitivityInterface +import info.nightscout.androidaps.interfaces.Sensitivity import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensDataStore import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult import info.nightscout.androidaps.utils.Round import info.nightscout.androidaps.utils.SafeParse @@ -22,9 +22,9 @@ abstract class AbstractSensitivityPlugin( aapsLogger: AAPSLogger, resourceHelper: ResourceHelper, val sp: SP -) : PluginBase(pluginDescription, aapsLogger, resourceHelper, injector), SensitivityInterface { +) : PluginBase(pluginDescription, aapsLogger, resourceHelper, injector), Sensitivity { - abstract override fun detectSensitivity(plugin: IobCobCalculatorInterface, fromTime: Long, toTime: Long): AutosensResult + abstract override fun detectSensitivity(ads: AutosensDataStore, fromTime: Long, toTime: Long): AutosensResult fun fillResult(ratio: Double, carbsAbsorbed: Double, pastSensitivity: String, ratioLimit: String, sensResult: String, deviationsArraySize: Int): AutosensResult { @@ -46,11 +46,11 @@ abstract class AbstractSensitivityPlugin( //If not-excluded data <= MIN_HOURS -> don't do Autosens //If not-excluded data >= MIN_HOURS_FULL_AUTOSENS -> full Autosens //Between MIN_HOURS and MIN_HOURS_FULL_AUTOSENS: gradually increase autosens - val autosensContrib = (min(max(SensitivityInterface.MIN_HOURS, deviationsArraySize / 12.0), - SensitivityInterface.MIN_HOURS_FULL_AUTOSENS) - SensitivityInterface.MIN_HOURS) / (SensitivityInterface.MIN_HOURS_FULL_AUTOSENS - SensitivityInterface.MIN_HOURS) + val autosensContrib = (min(max(Sensitivity.MIN_HOURS, deviationsArraySize / 12.0), + Sensitivity.MIN_HOURS_FULL_AUTOSENS) - Sensitivity.MIN_HOURS) / (Sensitivity.MIN_HOURS_FULL_AUTOSENS - Sensitivity.MIN_HOURS) ratio = autosensContrib * (ratio - 1) + 1 if (autosensContrib != 1.0) { - ratioLimit += "(" + deviationsArraySize + " of " + SensitivityInterface.MIN_HOURS_FULL_AUTOSENS * 12 + " values) " + ratioLimit += "(" + deviationsArraySize + " of " + Sensitivity.MIN_HOURS_FULL_AUTOSENS * 12 + " values) " } if (ratio != rawRatio) { ratioLimit += "Ratio limited from $rawRatio to $ratio" diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/sensitivity/SensitivityAAPSPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sensitivity/SensitivityAAPSPlugin.kt index 127045ebd0..d29659738c 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/sensitivity/SensitivityAAPSPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sensitivity/SensitivityAAPSPlugin.kt @@ -3,22 +3,21 @@ package info.nightscout.androidaps.plugins.sensitivity import dagger.android.HasAndroidInjector import info.nightscout.androidaps.Constants import info.nightscout.androidaps.R -import info.nightscout.androidaps.data.Profile import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.entities.TherapyEvent -import info.nightscout.androidaps.db.ProfileSwitch -import info.nightscout.androidaps.interfaces.DatabaseHelperInterface -import info.nightscout.androidaps.interfaces.IobCobCalculatorInterface +import info.nightscout.androidaps.extensions.isEPSEvent5minBack +import info.nightscout.androidaps.extensions.isTherapyEventEvent5minBack import info.nightscout.androidaps.interfaces.PluginDescription import info.nightscout.androidaps.interfaces.PluginType +import info.nightscout.androidaps.interfaces.Profile import info.nightscout.androidaps.interfaces.ProfileFunction -import info.nightscout.androidaps.interfaces.SensitivityInterface.SensitivityType +import info.nightscout.androidaps.interfaces.Sensitivity.SensitivityType import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensDataStore import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin.Companion.percentile +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin import info.nightscout.androidaps.utils.DateUtil -import info.nightscout.androidaps.utils.extensions.isEvent5minBack import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.sharedPreferences.SP import org.json.JSONException @@ -30,13 +29,12 @@ import kotlin.math.roundToInt @Singleton open class SensitivityAAPSPlugin @Inject constructor( - injector: HasAndroidInjector?, - aapsLogger: AAPSLogger?, - resourceHelper: ResourceHelper?, - sp: SP?, + injector: HasAndroidInjector, + aapsLogger: AAPSLogger, + resourceHelper: ResourceHelper, + sp: SP, private val profileFunction: ProfileFunction, private val dateUtil: DateUtil, - private val databaseHelper: DatabaseHelperInterface, private val repository: AppRepository ) : AbstractSensitivityPlugin(PluginDescription() .mainType(PluginType.SENSITIVITY) @@ -45,11 +43,10 @@ open class SensitivityAAPSPlugin @Inject constructor( .shortName(R.string.sensitivity_shortname) .preferencesId(R.xml.pref_absorption_aaps) .description(R.string.description_sensitivity_aaps), - injector!!, aapsLogger!!, resourceHelper!!, sp!! + injector, aapsLogger, resourceHelper, sp ) { - override fun detectSensitivity(plugin: IobCobCalculatorInterface, fromTime: Long, toTime: Long): AutosensResult { - val autosensDataTable = plugin.getAutosensDataTable() + override fun detectSensitivity(ads: AutosensDataStore, fromTime: Long, toTime: Long): AutosensResult { val age = sp.getString(R.string.key_age, "") var defaultHours = 24 if (age == resourceHelper.gs(R.string.key_adult)) defaultHours = 24 @@ -61,22 +58,22 @@ open class SensitivityAAPSPlugin @Inject constructor( aapsLogger.error("No profile") return AutosensResult() } - if (autosensDataTable.size() < 4) { - aapsLogger.debug(LTag.AUTOSENS, "No autosens data available. lastDataTime=" + plugin.lastDataTime()) + if (ads.autosensDataTable.size() < 4) { + aapsLogger.debug(LTag.AUTOSENS, "No autosens data available. lastDataTime=" + ads.lastDataTime(dateUtil)) return AutosensResult() } - val current = plugin.getAutosensData(toTime) // this is running inside lock already + val current = ads.getAutosensDataAtTime(toTime) // this is running inside lock already if (current == null) { - aapsLogger.debug(LTag.AUTOSENS, "No autosens data available. toTime: " + dateUtil.dateAndTimeString(toTime) + " lastDataTime: " + plugin.lastDataTime()) + aapsLogger.debug(LTag.AUTOSENS, "No autosens data available. toTime: " + dateUtil.dateAndTimeString(toTime) + " lastDataTime: " + ads.lastDataTime(dateUtil)) return AutosensResult() } val siteChanges = repository.getTherapyEventDataFromTime(fromTime, TherapyEvent.Type.CANNULA_CHANGE, true).blockingGet() - val profileSwitches = databaseHelper.getProfileSwitchEventsFromTime(fromTime, true) + val profileSwitches = repository.getEffectiveProfileSwitchDataFromTime(fromTime, true).blockingGet() val deviationsArray: MutableList = ArrayList() var pastSensitivity = "" var index = 0 - while (index < autosensDataTable.size()) { - val autosensData = autosensDataTable.valueAt(index) + while (index < ads.autosensDataTable.size()) { + val autosensData = ads.autosensDataTable.valueAt(index) if (autosensData.time < fromTime) { index++ continue @@ -87,13 +84,13 @@ open class SensitivityAAPSPlugin @Inject constructor( } // reset deviations after site change - if (isEvent5minBack(siteChanges, autosensData.time)) { + if (siteChanges.isTherapyEventEvent5minBack(autosensData.time)) { deviationsArray.clear() pastSensitivity += "(SITECHANGE)" } // reset deviations after profile switch - if (ProfileSwitch(injector).isEvent5minBack(profileSwitches, autosensData.time, true)) { + if (profileSwitches.isEPSEvent5minBack(autosensData.time)) { deviationsArray.clear() pastSensitivity += "(PROFILESWITCH)" } @@ -111,14 +108,14 @@ open class SensitivityAAPSPlugin @Inject constructor( index++ } val deviations = Array(deviationsArray.size) { i -> deviationsArray[i] } - val sens = profile.isfMgdl + val sens = profile.getIsfMgdl() val ratioLimit = "" val sensResult: String aapsLogger.debug(LTag.AUTOSENS, "Records: $index $pastSensitivity") Arrays.sort(deviations) - val percentile = percentile(deviations, 0.50) + val percentile = IobCobCalculatorPlugin.percentile(deviations, 0.50) val basalOff = percentile * (60.0 / 5.0) / sens - val ratio = 1 + basalOff / profile.maxDailyBasal + val ratio = 1 + basalOff / profile.getMaxDailyBasal() sensResult = when { percentile < 0 -> "Excess insulin sensitivity detected" percentile > 0 -> "Excess insulin resistance detected" diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/sensitivity/SensitivityOref1Plugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sensitivity/SensitivityOref1Plugin.kt index 9e5860de44..06a704137c 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/sensitivity/SensitivityOref1Plugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sensitivity/SensitivityOref1Plugin.kt @@ -3,23 +3,22 @@ package info.nightscout.androidaps.plugins.sensitivity import dagger.android.HasAndroidInjector import info.nightscout.androidaps.Constants import info.nightscout.androidaps.R -import info.nightscout.androidaps.data.Profile import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.entities.TherapyEvent -import info.nightscout.androidaps.db.ProfileSwitch -import info.nightscout.androidaps.interfaces.DatabaseHelperInterface -import info.nightscout.androidaps.interfaces.IobCobCalculatorInterface +import info.nightscout.androidaps.extensions.isEPSEvent5minBack +import info.nightscout.androidaps.extensions.isTherapyEventEvent5minBack import info.nightscout.androidaps.interfaces.PluginDescription import info.nightscout.androidaps.interfaces.PluginType +import info.nightscout.androidaps.interfaces.Profile import info.nightscout.androidaps.interfaces.ProfileFunction -import info.nightscout.androidaps.interfaces.SensitivityInterface.SensitivityType +import info.nightscout.androidaps.interfaces.Sensitivity.SensitivityType import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.plugins.aps.openAPSSMB.SMBDefaults +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensDataStore import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin.Companion.percentile +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin import info.nightscout.androidaps.utils.DateUtil -import info.nightscout.androidaps.utils.extensions.isEvent5minBack import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.sharedPreferences.SP import org.json.JSONException @@ -31,13 +30,12 @@ import kotlin.math.roundToInt @Singleton open class SensitivityOref1Plugin @Inject constructor( - injector: HasAndroidInjector?, - aapsLogger: AAPSLogger?, - resourceHelper: ResourceHelper?, - sp: SP?, + injector: HasAndroidInjector, + aapsLogger: AAPSLogger, + resourceHelper: ResourceHelper, + sp: SP, private val profileFunction: ProfileFunction, private val dateUtil: DateUtil, - private val databaseHelper: DatabaseHelperInterface, private val repository: AppRepository ) : AbstractSensitivityPlugin(PluginDescription() .mainType(PluginType.SENSITIVITY) @@ -48,31 +46,30 @@ open class SensitivityOref1Plugin @Inject constructor( .preferencesId(R.xml.pref_absorption_oref1) .description(R.string.description_sensitivity_oref1) .setDefault(), - injector!!, aapsLogger!!, resourceHelper!!, sp!! + injector, aapsLogger, resourceHelper, sp ) { - override fun detectSensitivity(plugin: IobCobCalculatorInterface, fromTime: Long, toTime: Long): AutosensResult { + override fun detectSensitivity(ads: AutosensDataStore, fromTime: Long, toTime: Long): AutosensResult { // todo this method is called from the IobCobCalculatorPlugin, which leads to a circular // dependency, this should be avoided - val autosensDataTable = plugin.getAutosensDataTable() val profile = profileFunction.getProfile() if (profile == null) { aapsLogger.error("No profile") return AutosensResult() } - if (autosensDataTable.size() < 4) { - aapsLogger.debug(LTag.AUTOSENS, "No autosens data available. lastDataTime=" + plugin.lastDataTime()) + if (ads.autosensDataTable.size() < 4) { + aapsLogger.debug(LTag.AUTOSENS, "No autosens data available. lastDataTime=" + ads.lastDataTime(dateUtil)) return AutosensResult() } // the current - val current = plugin.getAutosensData(toTime) // this is running inside lock already + val current = ads.getAutosensDataAtTime(toTime) // this is running inside lock already if (current == null) { - aapsLogger.debug(LTag.AUTOSENS, "No autosens data available. toTime: " + dateUtil.dateAndTimeString(toTime) + " lastDataTime: " + plugin.lastDataTime()) + aapsLogger.debug(LTag.AUTOSENS, "No autosens data available. toTime: " + dateUtil.dateAndTimeString(toTime) + " lastDataTime: " + ads.lastDataTime(dateUtil)) return AutosensResult() } val siteChanges = repository.getTherapyEventDataFromTime(fromTime, TherapyEvent.Type.CANNULA_CHANGE, true).blockingGet() - val profileSwitches = databaseHelper.getProfileSwitchEventsFromTime(fromTime, true) + val profileSwitches = repository.getEffectiveProfileSwitchDataFromTime(fromTime, true).blockingGet() //[0] = 8 hour //[1] = 24 hour @@ -85,8 +82,8 @@ open class SensitivityOref1Plugin @Inject constructor( val ratioLimitArray = mutableListOf("", "") val hoursDetection = listOf(8.0, 24.0) var index = 0 - while (index < autosensDataTable.size()) { - val autosensData = autosensDataTable.valueAt(index) + while (index < ads.autosensDataTable.size()) { + val autosensData = ads.autosensDataTable.valueAt(index) if (autosensData.time < fromTime) { index++ continue @@ -103,13 +100,14 @@ open class SensitivityOref1Plugin @Inject constructor( var pastSensitivity = pastSensitivityArray[hourSegment] // reset deviations after site change - if (isEvent5minBack(siteChanges, autosensData.time)) { + if (siteChanges.isTherapyEventEvent5minBack(autosensData.time)) { deviationsArray.clear() pastSensitivity += "(SITECHANGE)" + pastSensitivity += "(SITECHANGE)" } // reset deviations after profile switch - if (ProfileSwitch(injector).isEvent5minBack(profileSwitches, autosensData.time, true)) { + if (profileSwitches.isEPSEvent5minBack(autosensData.time)) { deviationsArray.clear() pastSensitivity += "(PROFILESWITCH)" } @@ -159,11 +157,11 @@ open class SensitivityOref1Plugin @Inject constructor( if (hourUsed == 1) sensResult = "(24 hours) " val ratioLimit = "" val deviations: Array = Array(deviationsArray.size) { i -> deviationsArray[i] } - val sens = profile.isfMgdl + val sens = profile.getIsfMgdl() aapsLogger.debug(LTag.AUTOSENS, "Records: $index $pastSensitivity") Arrays.sort(deviations) - val pSensitive = percentile(deviations, 0.50) - val pResistant = percentile(deviations, 0.50) + val pSensitive = IobCobCalculatorPlugin.percentile(deviations, 0.50) + val pResistant = IobCobCalculatorPlugin.percentile(deviations, 0.50) var basalOff = 0.0 when { pSensitive < 0 -> { // sensitive @@ -179,7 +177,7 @@ open class SensitivityOref1Plugin @Inject constructor( else -> sensResult += "Sensitivity normal" } aapsLogger.debug(LTag.AUTOSENS, sensResult) - val ratio = 1 + basalOff / profile.maxDailyBasal + val ratio = 1 + basalOff / profile.getMaxDailyBasal() //Update the data back to the parent sensResultArray[hourUsed] = sensResult diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/sensitivity/SensitivityWeightedAveragePlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/sensitivity/SensitivityWeightedAveragePlugin.kt index f875f9f44c..755dece81f 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/sensitivity/SensitivityWeightedAveragePlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/sensitivity/SensitivityWeightedAveragePlugin.kt @@ -4,21 +4,20 @@ import androidx.collection.LongSparseArray import dagger.android.HasAndroidInjector import info.nightscout.androidaps.Constants import info.nightscout.androidaps.R -import info.nightscout.androidaps.data.Profile import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.entities.TherapyEvent -import info.nightscout.androidaps.db.ProfileSwitch -import info.nightscout.androidaps.interfaces.DatabaseHelperInterface -import info.nightscout.androidaps.interfaces.IobCobCalculatorInterface +import info.nightscout.androidaps.extensions.isEPSEvent5minBack +import info.nightscout.androidaps.extensions.isTherapyEventEvent5minBack import info.nightscout.androidaps.interfaces.PluginDescription import info.nightscout.androidaps.interfaces.PluginType +import info.nightscout.androidaps.interfaces.Profile import info.nightscout.androidaps.interfaces.ProfileFunction -import info.nightscout.androidaps.interfaces.SensitivityInterface.SensitivityType +import info.nightscout.androidaps.interfaces.Sensitivity.SensitivityType import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensDataStore import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult import info.nightscout.androidaps.utils.DateUtil -import info.nightscout.androidaps.utils.extensions.isEvent5minBack import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.sharedPreferences.SP import org.json.JSONException @@ -35,7 +34,6 @@ open class SensitivityWeightedAveragePlugin @Inject constructor( sp: SP, private val profileFunction: ProfileFunction, private val dateUtil: DateUtil, - private val databaseHelper: DatabaseHelperInterface, private val repository: AppRepository ) : AbstractSensitivityPlugin(PluginDescription() .mainType(PluginType.SENSITIVITY) @@ -47,21 +45,20 @@ open class SensitivityWeightedAveragePlugin @Inject constructor( injector, aapsLogger, resourceHelper, sp ) { - override fun detectSensitivity(plugin: IobCobCalculatorInterface, fromTime: Long, toTime: Long): AutosensResult { - val autosensDataTable = plugin.getAutosensDataTable() + override fun detectSensitivity(ads: AutosensDataStore, fromTime: Long, toTime: Long): AutosensResult { val age = sp.getString(R.string.key_age, "") var defaultHours = 24 if (age == resourceHelper.gs(R.string.key_adult)) defaultHours = 24 if (age == resourceHelper.gs(R.string.key_teenage)) defaultHours = 4 if (age == resourceHelper.gs(R.string.key_child)) defaultHours = 4 val hoursForDetection = sp.getInt(R.string.key_openapsama_autosens_period, defaultHours) - if (autosensDataTable.size() < 4) { - aapsLogger.debug(LTag.AUTOSENS, "No autosens data available. lastDataTime=" + plugin.lastDataTime()) + if (ads.autosensDataTable.size() < 4) { + aapsLogger.debug(LTag.AUTOSENS, "No autosens data available. lastDataTime=" + ads.lastDataTime(dateUtil)) return AutosensResult() } - val current = plugin.getAutosensData(toTime) // this is running inside lock already + val current = ads.getAutosensDataAtTime(toTime) // this is running inside lock already if (current == null) { - aapsLogger.debug(LTag.AUTOSENS, "No autosens data available. toTime: " + dateUtil.dateAndTimeString(toTime) + " lastDataTime: " + plugin.lastDataTime()) + aapsLogger.debug(LTag.AUTOSENS, "No autosens data available. toTime: " + dateUtil.dateAndTimeString(toTime) + " lastDataTime: " + ads.lastDataTime(dateUtil)) return AutosensResult() } val profile = profileFunction.getProfile() @@ -70,12 +67,12 @@ open class SensitivityWeightedAveragePlugin @Inject constructor( return AutosensResult() } val siteChanges = repository.getTherapyEventDataFromTime(fromTime, TherapyEvent.Type.CANNULA_CHANGE, true).blockingGet() - val profileSwitches = databaseHelper.getProfileSwitchEventsFromTime(fromTime, true) + val profileSwitches = repository.getEffectiveProfileSwitchDataFromTime(fromTime, true).blockingGet() var pastSensitivity = "" var index = 0 val data = LongSparseArray() - while (index < autosensDataTable.size()) { - val autosensData = autosensDataTable.valueAt(index) + while (index < ads.autosensDataTable.size()) { + val autosensData = ads.autosensDataTable.valueAt(index) if (autosensData.time < fromTime) { index++ continue @@ -90,13 +87,13 @@ open class SensitivityWeightedAveragePlugin @Inject constructor( } // reset deviations after site change - if (isEvent5minBack(siteChanges, autosensData.time)) { + if (siteChanges.isTherapyEventEvent5minBack(autosensData.time)) { data.clear() pastSensitivity += "(SITECHANGE)" } // reset deviations after profile switch - if (ProfileSwitch(injector).isEvent5minBack(profileSwitches, autosensData.time, true)) { + if (profileSwitches.isEPSEvent5minBack(autosensData.time)) { data.clear() pastSensitivity += "(PROFILESWITCH)" } @@ -134,13 +131,13 @@ open class SensitivityWeightedAveragePlugin @Inject constructor( if (weights == 0.0) { return AutosensResult() } - val sens = profile.isfMgdl + val sens = profile.getIsfMgdl() val ratioLimit = "" val sensResult: String aapsLogger.debug(LTag.AUTOSENS, "Records: $index $pastSensitivity") val average = weightedSum / weights val basalOff = average * (60 / 5.0) / sens - val ratio = 1 + basalOff / profile.maxDailyBasal + val ratio = 1 + basalOff / profile.getMaxDailyBasal() sensResult = when { average < 0 -> "Excess insulin sensitivity detected" average > 0 -> "Excess insulin resistance detected" diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/source/BGSourceFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/source/BGSourceFragment.kt index a889ea38ba..8d37940f34 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/source/BGSourceFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/source/BGSourceFragment.kt @@ -11,12 +11,18 @@ import dagger.android.support.DaggerFragment import info.nightscout.androidaps.R import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.entities.GlucoseValue -import info.nightscout.androidaps.database.entities.UserEntry.* +import info.nightscout.androidaps.database.entities.UserEntry.Action +import info.nightscout.androidaps.database.entities.UserEntry.Sources +import info.nightscout.androidaps.database.entities.ValueWithUnit import info.nightscout.androidaps.database.transactions.InvalidateGlucoseValueTransaction import info.nightscout.androidaps.databinding.BgsourceFragmentBinding import info.nightscout.androidaps.databinding.BgsourceItemBinding import info.nightscout.androidaps.events.EventNewBG -import info.nightscout.androidaps.interfaces.DatabaseHelperInterface +import info.nightscout.androidaps.extensions.directionToIcon +import info.nightscout.androidaps.extensions.toVisibility +import info.nightscout.androidaps.extensions.valueToUnitsString +import info.nightscout.androidaps.interfaces.ActivePlugin +import info.nightscout.androidaps.interfaces.PluginBase import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.plugins.bus.RxBusWrapper @@ -24,9 +30,6 @@ import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.alertDialogs.OKDialog -import info.nightscout.androidaps.utils.extensions.directionToIcon -import info.nightscout.androidaps.utils.extensions.toVisibility -import info.nightscout.androidaps.utils.extensions.valueToUnitsString import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers import io.reactivex.disposables.CompositeDisposable @@ -41,10 +44,10 @@ class BGSourceFragment : DaggerFragment() { @Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var profileFunction: ProfileFunction @Inject lateinit var dateUtil: DateUtil - @Inject lateinit var databaseHelper: DatabaseHelperInterface @Inject lateinit var repository: AppRepository @Inject lateinit var aapsSchedulers: AapsSchedulers @Inject lateinit var uel: UserEntryLogger + @Inject lateinit var activePlugin: ActivePlugin private val disposable = CompositeDisposable() private val millsToThePast = T.hours(12).msecs() @@ -129,7 +132,19 @@ class BGSourceFragment : DaggerFragment() { activity?.let { activity -> val text = dateUtil.dateAndTimeString(glucoseValue.timestamp) + "\n" + glucoseValue.valueToUnitsString(profileFunction.getUnits()) OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.removerecord), text, Runnable { - uel.log(Action.BG_REMOVED, ValueWithUnit(glucoseValue.timestamp, Units.Timestamp)) + val source = when((activePlugin.activeBgSource as PluginBase).pluginDescription.pluginName) { + R.string.dexcom_app_patched -> Sources.Dexcom + R.string.eversense -> Sources.Eversense + R.string.Glimp -> Sources.Glimp + R.string.MM640g -> Sources.MM640g + R.string.nsclientbg -> Sources.NSClientSource + R.string.poctech -> Sources.PocTech + R.string.tomato -> Sources.Tomato + R.string.xdrip -> Sources.Xdrip + else -> Sources.Unknown + } + uel.log(Action.BG_REMOVED, source, + ValueWithUnit.Timestamp(glucoseValue.timestamp)) disposable += repository.runTransaction(InvalidateGlucoseValueTransaction(glucoseValue.id)).subscribe() }) } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/source/DexcomPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/source/DexcomPlugin.kt index 71a2a0ce65..d1f02d3871 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/source/DexcomPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/source/DexcomPlugin.kt @@ -6,29 +6,31 @@ import android.content.pm.PackageManager import androidx.core.content.ContextCompat import androidx.work.Worker import androidx.work.WorkerParameters +import androidx.work.workDataOf import dagger.android.HasAndroidInjector -import info.nightscout.androidaps.Config +import info.nightscout.androidaps.interfaces.Config import info.nightscout.androidaps.R import info.nightscout.androidaps.activities.RequestDexcomPermissionActivity import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.entities.GlucoseValue import info.nightscout.androidaps.database.entities.TherapyEvent +import info.nightscout.androidaps.database.entities.UserEntry.Action +import info.nightscout.androidaps.database.entities.UserEntry.Sources +import info.nightscout.androidaps.database.entities.ValueWithUnit import info.nightscout.androidaps.database.transactions.CgmSourceTransaction -import info.nightscout.androidaps.interfaces.BgSourceInterface +import info.nightscout.androidaps.interfaces.BgSource import info.nightscout.androidaps.interfaces.PluginBase import info.nightscout.androidaps.interfaces.PluginDescription import info.nightscout.androidaps.interfaces.PluginType import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag -import info.nightscout.androidaps.plugins.general.nsclient.NSUpload +import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.receivers.DataWorker import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.XDripBroadcast import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.sharedPreferences.SP -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.rxkotlin.plusAssign import javax.inject.Inject import javax.inject.Singleton @@ -37,6 +39,7 @@ class DexcomPlugin @Inject constructor( injector: HasAndroidInjector, resourceHelper: ResourceHelper, aapsLogger: AAPSLogger, + private val sp: SP, private val dexcomMediator: DexcomMediator, config: Config ) : PluginBase(PluginDescription() @@ -48,9 +51,7 @@ class DexcomPlugin @Inject constructor( .preferencesId(R.xml.pref_bgsourcedexcom) .description(R.string.description_source_dexcom), aapsLogger, resourceHelper, injector -), BgSourceInterface { - - private val disposable = CompositeDisposable() +), BgSource { init { if (!config.NSCLIENT) { @@ -62,16 +63,17 @@ class DexcomPlugin @Inject constructor( return true } + override fun shouldUploadToNs(glucoseValue: GlucoseValue): Boolean = + (glucoseValue.sourceSensor == GlucoseValue.SourceSensor.DEXCOM_G6_NATIVE || + glucoseValue.sourceSensor == GlucoseValue.SourceSensor.DEXCOM_G5_NATIVE || + glucoseValue.sourceSensor == GlucoseValue.SourceSensor.DEXCOM_NATIVE_UNKNOWN) + && sp.getBoolean(R.string.key_dexcomg5_nsupload, false) + override fun onStart() { super.onStart() dexcomMediator.requestPermissionIfNeeded() } - override fun onStop() { - disposable.clear() - super.onStop() - } - // cannot be inner class because of needed injection class DexcomWorker( context: Context, @@ -81,11 +83,12 @@ class DexcomPlugin @Inject constructor( @Inject lateinit var aapsLogger: AAPSLogger @Inject lateinit var injector: HasAndroidInjector @Inject lateinit var dexcomPlugin: DexcomPlugin - @Inject lateinit var nsUpload: NSUpload @Inject lateinit var sp: SP + @Inject lateinit var dateUtil: DateUtil @Inject lateinit var dataWorker: DataWorker @Inject lateinit var broadcastToXDrip: XDripBroadcast @Inject lateinit var repository: AppRepository + @Inject lateinit var uel: UserEntryLogger init { (context.applicationContext as HasAndroidInjector).androidInjector().inject(this) @@ -94,9 +97,9 @@ class DexcomPlugin @Inject constructor( override fun doWork(): Result { var ret = Result.success() - if (!dexcomPlugin.isEnabled(PluginType.BGSOURCE)) return Result.failure() + if (!dexcomPlugin.isEnabled(PluginType.BGSOURCE)) return Result.success() val bundle = dataWorker.pickupBundle(inputData.getLong(DataWorker.STORE_KEY, -1)) - ?: return Result.failure() + ?: return Result.failure(workDataOf("Error" to "missing input data")) try { val sourceSensor = when (bundle.getString("sensorType") ?: "") { "G6" -> GlucoseValue.SourceSensor.DEXCOM_G6_NATIVE @@ -104,7 +107,7 @@ class DexcomPlugin @Inject constructor( else -> GlucoseValue.SourceSensor.DEXCOM_NATIVE_UNKNOWN } val glucoseValuesBundle = bundle.getBundle("glucoseValues") - ?: return Result.failure() + ?: return Result.failure(workDataOf("Error" to "missing glucoseValues")) val glucoseValues = mutableListOf() for (i in 0 until glucoseValuesBundle.size()) { val glucoseValueBundle = glucoseValuesBundle.getBundle(i.toString())!! @@ -122,7 +125,7 @@ class DexcomPlugin @Inject constructor( for (i in 0 until meters.size()) { meters.getBundle(i.toString())?.let { val timestamp = it.getLong("timestamp") * 1000 - val now = DateUtil.now() + val now = dateUtil.now() if (timestamp > now - T.months(1).msecs() && timestamp < now) { calibrations.add(CgmSourceTransaction.Calibration( timestamp = it.getLong("timestamp") * 1000, @@ -138,40 +141,39 @@ class DexcomPlugin @Inject constructor( } else { null } - dexcomPlugin.disposable += repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, calibrations, sensorStartTime)).subscribe({ result -> - result.inserted.forEach { - broadcastToXDrip(it) - if (sp.getBoolean(R.string.key_dexcomg5_nsupload, false)) { - nsUpload.uploadBg(it, sourceSensor.text) - } - aapsLogger.debug(LTag.BGSOURCE, "Inserted bg $it") + repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, calibrations, sensorStartTime)) + .doOnError { + aapsLogger.error(LTag.DATABASE, "Error while saving values from Dexcom App", it) + ret = Result.failure(workDataOf("Error" to it.toString())) } - result.updated.forEach { - broadcastToXDrip(it) - if (sp.getBoolean(R.string.key_dexcomg5_nsupload, false)) { - nsUpload.updateBg(it, sourceSensor.text) + .blockingGet() + .also { result -> + result.inserted.forEach { + broadcastToXDrip(it) + aapsLogger.debug(LTag.DATABASE, "Inserted bg $it") } - aapsLogger.debug(LTag.BGSOURCE, "Updated bg $it") - } - result.sensorInsertionsInserted.forEach { - if (sp.getBoolean(R.string.key_dexcomg5_nsupload, false)) { - nsUpload.uploadEvent(it) + result.updated.forEach { + broadcastToXDrip(it) + aapsLogger.debug(LTag.DATABASE, "Updated bg $it") } - aapsLogger.debug(LTag.BGSOURCE, "Inserted sensor insertion $it") - } - result.calibrationsInserted.forEach { - if (sp.getBoolean(R.string.key_dexcomg5_nsupload, false)) { - nsUpload.uploadEvent(it) + result.sensorInsertionsInserted.forEach { + uel.log(Action.CAREPORTAL, + Sources.Dexcom, + ValueWithUnit.Timestamp(it.timestamp), + ValueWithUnit.TherapyEventType(it.type)) + aapsLogger.debug(LTag.DATABASE, "Inserted sensor insertion $it") + } + result.calibrationsInserted.forEach { + uel.log(Action.CAREPORTAL, + Sources.Dexcom, + ValueWithUnit.Timestamp(it.timestamp), + ValueWithUnit.TherapyEventType(it.type)) + aapsLogger.debug(LTag.DATABASE, "Inserted calibration $it") } - aapsLogger.debug(LTag.BGSOURCE, "Inserted calibration $it") } - }, { - aapsLogger.error("Error while saving values from Dexcom App", it) - ret = Result.failure() - }) } catch (e: Exception) { aapsLogger.error("Error while processing intent from Dexcom App", e) - ret = Result.failure() + ret = Result.failure(workDataOf("Error" to e.toString())) } return ret } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/source/EversensePlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/source/EversensePlugin.kt index f585fbfff5..434e5eb873 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/source/EversensePlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/source/EversensePlugin.kt @@ -3,20 +3,20 @@ package info.nightscout.androidaps.plugins.source import android.content.Context import androidx.work.Worker import androidx.work.WorkerParameters +import androidx.work.workDataOf import dagger.android.HasAndroidInjector import info.nightscout.androidaps.R import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.entities.GlucoseValue import info.nightscout.androidaps.database.entities.TherapyEvent import info.nightscout.androidaps.database.transactions.CgmSourceTransaction -import info.nightscout.androidaps.database.transactions.InsertTherapyEventIfNewTransaction -import info.nightscout.androidaps.interfaces.BgSourceInterface +import info.nightscout.androidaps.database.transactions.InsertIfNewByTimestampTherapyEventTransaction +import info.nightscout.androidaps.interfaces.BgSource import info.nightscout.androidaps.interfaces.PluginBase import info.nightscout.androidaps.interfaces.PluginDescription import info.nightscout.androidaps.interfaces.PluginType import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag -import info.nightscout.androidaps.plugins.general.nsclient.NSUpload import info.nightscout.androidaps.receivers.DataWorker import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.XDripBroadcast @@ -30,7 +30,8 @@ import javax.inject.Singleton class EversensePlugin @Inject constructor( injector: HasAndroidInjector, resourceHelper: ResourceHelper, - aapsLogger: AAPSLogger + aapsLogger: AAPSLogger, + private val sp: SP ) : PluginBase(PluginDescription() .mainType(PluginType.BGSOURCE) .fragmentClass(BGSourceFragment::class.java.name) @@ -40,10 +41,13 @@ class EversensePlugin @Inject constructor( .preferencesId(R.xml.pref_bgsource) .description(R.string.description_source_eversense), aapsLogger, resourceHelper, injector -), BgSourceInterface { +), BgSource { override var sensorBatteryLevel = -1 + override fun shouldUploadToNs(glucoseValue: GlucoseValue): Boolean = + glucoseValue.sourceSensor == GlucoseValue.SourceSensor.EVERSENSE && sp.getBoolean(R.string.key_dexcomg5_nsupload, false) + // cannot be inner class because of needed injection class EversenseWorker( context: Context, @@ -53,8 +57,6 @@ class EversensePlugin @Inject constructor( @Inject lateinit var injector: HasAndroidInjector @Inject lateinit var eversensePlugin: EversensePlugin @Inject lateinit var aapsLogger: AAPSLogger - @Inject lateinit var sp: SP - @Inject lateinit var nsUpload: NSUpload @Inject lateinit var dateUtil: DateUtil @Inject lateinit var dataWorker: DataWorker @Inject lateinit var repository: AppRepository @@ -67,9 +69,9 @@ class EversensePlugin @Inject constructor( override fun doWork(): Result { var ret = Result.success() - if (!eversensePlugin.isEnabled(PluginType.BGSOURCE)) return Result.failure() + if (!eversensePlugin.isEnabled(PluginType.BGSOURCE)) return Result.success() val bundle = dataWorker.pickupBundle(inputData.getLong(DataWorker.STORE_KEY, -1)) - ?: return Result.failure() + ?: return Result.failure(workDataOf("Error" to "missing input data")) if (bundle.containsKey("currentCalibrationPhase")) aapsLogger.debug(LTag.BGSOURCE, "currentCalibrationPhase: " + bundle.getString("currentCalibrationPhase")) if (bundle.containsKey("placementModeInProgress")) aapsLogger.debug(LTag.BGSOURCE, "placementModeInProgress: " + bundle.getBoolean("placementModeInProgress")) if (bundle.containsKey("glucoseLevel")) aapsLogger.debug(LTag.BGSOURCE, "glucoseLevel: " + bundle.getInt("glucoseLevel")) @@ -109,16 +111,14 @@ class EversensePlugin @Inject constructor( ) repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, emptyList(), null)) .doOnError { - aapsLogger.error("Error while saving values from Eversense App", it) - ret = Result.failure() + aapsLogger.error(LTag.DATABASE, "Error while saving values from Eversense App", it) + ret = Result.failure(workDataOf("Error" to it.toString())) } .blockingGet() .also { savedValues -> savedValues.inserted.forEach { broadcastToXDrip(it) - if (sp.getBoolean(R.string.key_dexcomg5_nsupload, false)) - nsUpload.uploadBg(it, GlucoseValue.SourceSensor.EVERSENSE.text) - aapsLogger.debug(LTag.BGSOURCE, "Inserted bg $it") + aapsLogger.debug(LTag.DATABASE, "Inserted bg $it") } } } @@ -132,7 +132,7 @@ class EversensePlugin @Inject constructor( aapsLogger.debug(LTag.BGSOURCE, "calibrationTimestamps" + Arrays.toString(calibrationTimestamps)) aapsLogger.debug(LTag.BGSOURCE, "calibrationRecordNumbers" + Arrays.toString(calibrationRecordNumbers)) for (i in calibrationGlucoseLevels.indices) { - repository.runTransactionForResult(InsertTherapyEventIfNewTransaction( + repository.runTransactionForResult(InsertIfNewByTimestampTherapyEventTransaction( timestamp = calibrationTimestamps[i], type = TherapyEvent.Type.FINGER_STICK_BG_VALUE, glucose = calibrationGlucoseLevels[i].toDouble(), @@ -141,15 +141,12 @@ class EversensePlugin @Inject constructor( enteredBy = "AndroidAPS-Eversense" )) .doOnError { - aapsLogger.error(LTag.BGSOURCE, "Error while saving therapy event", it) - ret = Result.failure() + aapsLogger.error(LTag.DATABASE, "Error while saving therapy event", it) + ret = Result.failure(workDataOf("Error" to it.toString())) } .blockingGet() .also { result -> - result.inserted.forEach { - nsUpload.uploadEvent(it) - aapsLogger.debug(LTag.BGSOURCE, "Inserted bg $it") - } + result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted therapy event $it") } } } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/source/GlimpPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/source/GlimpPlugin.kt index c61e2a4065..36724eb4b5 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/source/GlimpPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/source/GlimpPlugin.kt @@ -3,18 +3,18 @@ package info.nightscout.androidaps.plugins.source import android.content.Context import androidx.work.Worker import androidx.work.WorkerParameters +import androidx.work.workDataOf import dagger.android.HasAndroidInjector import info.nightscout.androidaps.R import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.entities.GlucoseValue import info.nightscout.androidaps.database.transactions.CgmSourceTransaction -import info.nightscout.androidaps.interfaces.BgSourceInterface +import info.nightscout.androidaps.interfaces.BgSource import info.nightscout.androidaps.interfaces.PluginBase import info.nightscout.androidaps.interfaces.PluginDescription import info.nightscout.androidaps.interfaces.PluginType import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag -import info.nightscout.androidaps.plugins.general.nsclient.NSUpload import info.nightscout.androidaps.utils.XDripBroadcast import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.sharedPreferences.SP @@ -25,7 +25,8 @@ import javax.inject.Singleton class GlimpPlugin @Inject constructor( injector: HasAndroidInjector, resourceHelper: ResourceHelper, - aapsLogger: AAPSLogger + aapsLogger: AAPSLogger, + private val sp: SP ) : PluginBase(PluginDescription() .mainType(PluginType.BGSOURCE) .fragmentClass(BGSourceFragment::class.java.name) @@ -34,7 +35,7 @@ class GlimpPlugin @Inject constructor( .preferencesId(R.xml.pref_bgsource) .description(R.string.description_source_glimp), aapsLogger, resourceHelper, injector -), BgSourceInterface { +), BgSource { // cannot be inner class because of needed injection class GlimpWorker( @@ -47,8 +48,6 @@ class GlimpPlugin @Inject constructor( @Inject lateinit var aapsLogger: AAPSLogger @Inject lateinit var repository: AppRepository @Inject lateinit var broadcastToXDrip: XDripBroadcast - @Inject lateinit var sp: SP - @Inject lateinit var nsUpload: NSUpload init { (context.applicationContext as HasAndroidInjector).androidInjector().inject(this) @@ -57,7 +56,7 @@ class GlimpPlugin @Inject constructor( override fun doWork(): Result { var ret = Result.success() - if (!glimpPlugin.isEnabled(PluginType.BGSOURCE)) return Result.failure() + if (!glimpPlugin.isEnabled(PluginType.BGSOURCE)) return Result.success() aapsLogger.debug(LTag.BGSOURCE, "Received Glimp Data: $inputData}") val glucoseValues = mutableListOf() glucoseValues += CgmSourceTransaction.TransactionGlucoseValue( @@ -70,19 +69,21 @@ class GlimpPlugin @Inject constructor( ) repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, emptyList(), null)) .doOnError { - aapsLogger.error("Error while saving values from Glimp App", it) - ret = Result.failure() + aapsLogger.error(LTag.DATABASE, "Error while saving values from Glimp App", it) + ret = Result.failure(workDataOf("Error" to it.toString())) } .blockingGet() .also { savedValues -> savedValues.inserted.forEach { broadcastToXDrip(it) - if (sp.getBoolean(R.string.key_dexcomg5_nsupload, false)) - nsUpload.uploadBg(it, GlucoseValue.SourceSensor.GLIMP.text) - aapsLogger.debug(LTag.BGSOURCE, "Inserted bg $it") + aapsLogger.debug(LTag.DATABASE, "Inserted bg $it") } } return ret } } + + override fun shouldUploadToNs(glucoseValue: GlucoseValue): Boolean = + glucoseValue.sourceSensor == GlucoseValue.SourceSensor.GLIMP && sp.getBoolean(R.string.key_dexcomg5_nsupload, false) + } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/source/MM640gPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/source/MM640gPlugin.kt index 1bb431e59b..728e9c12c9 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/source/MM640gPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/source/MM640gPlugin.kt @@ -3,18 +3,18 @@ package info.nightscout.androidaps.plugins.source import android.content.Context import androidx.work.Worker import androidx.work.WorkerParameters +import androidx.work.workDataOf import dagger.android.HasAndroidInjector import info.nightscout.androidaps.R import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.entities.GlucoseValue import info.nightscout.androidaps.database.transactions.CgmSourceTransaction -import info.nightscout.androidaps.interfaces.BgSourceInterface +import info.nightscout.androidaps.interfaces.BgSource import info.nightscout.androidaps.interfaces.PluginBase import info.nightscout.androidaps.interfaces.PluginDescription import info.nightscout.androidaps.interfaces.PluginType import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag -import info.nightscout.androidaps.plugins.general.nsclient.NSUpload import info.nightscout.androidaps.receivers.DataWorker import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.XDripBroadcast @@ -29,7 +29,8 @@ import javax.inject.Singleton class MM640gPlugin @Inject constructor( injector: HasAndroidInjector, resourceHelper: ResourceHelper, - aapsLogger: AAPSLogger + aapsLogger: AAPSLogger, + private val sp: SP ) : PluginBase(PluginDescription() .mainType(PluginType.BGSOURCE) .fragmentClass(BGSourceFragment::class.java.name) @@ -37,7 +38,7 @@ class MM640gPlugin @Inject constructor( .pluginName(R.string.MM640g) .description(R.string.description_source_mm640g), aapsLogger, resourceHelper, injector -), BgSourceInterface { +), BgSource { // cannot be inner class because of needed injection class MM640gWorker( @@ -48,8 +49,6 @@ class MM640gPlugin @Inject constructor( @Inject lateinit var mM640gPlugin: MM640gPlugin @Inject lateinit var injector: HasAndroidInjector @Inject lateinit var aapsLogger: AAPSLogger - @Inject lateinit var sp: SP - @Inject lateinit var nsUpload: NSUpload @Inject lateinit var dateUtil: DateUtil @Inject lateinit var dataWorker: DataWorker @Inject lateinit var repository: AppRepository @@ -62,8 +61,8 @@ class MM640gPlugin @Inject constructor( override fun doWork(): Result { var ret = Result.success() - if (!mM640gPlugin.isEnabled(PluginType.BGSOURCE)) return Result.failure() - val collection = inputData.getString("collection") ?: return Result.failure() + if (!mM640gPlugin.isEnabled(PluginType.BGSOURCE)) return Result.success() + val collection = inputData.getString("collection") ?: return Result.failure(workDataOf("Error" to "missing collection")) if (collection == "entries") { val data = inputData.getString("data") aapsLogger.debug(LTag.BGSOURCE, "Received MM640g Data: $data") @@ -88,25 +87,27 @@ class MM640gPlugin @Inject constructor( } repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, emptyList(), null)) .doOnError { - aapsLogger.error("Error while saving values from Eversense App", it) - ret = Result.failure() + aapsLogger.error(LTag.DATABASE, "Error while saving values from Eversense App", it) + ret = Result.failure(workDataOf("Error" to it.toString())) } .blockingGet() .also { savedValues -> - savedValues.all().forEach { - broadcastToXDrip(it) - if (sp.getBoolean(R.string.key_dexcomg5_nsupload, false)) - nsUpload.uploadBg(it, GlucoseValue.SourceSensor.MM_600_SERIES.text) - aapsLogger.debug(LTag.BGSOURCE, "Inserted bg $it") + savedValues.all().forEach { + broadcastToXDrip(it) + aapsLogger.debug(LTag.DATABASE, "Inserted bg $it") + } } - } } catch (e: JSONException) { aapsLogger.error("Exception: ", e) - ret = Result.failure() + ret = Result.failure(workDataOf("Error" to e.toString())) } } } return ret } } + + override fun shouldUploadToNs(glucoseValue: GlucoseValue): Boolean = + glucoseValue.sourceSensor == GlucoseValue.SourceSensor.MM_600_SERIES && sp.getBoolean(R.string.key_dexcomg5_nsupload, false) + } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/source/NSClientSourcePlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/source/NSClientSourcePlugin.kt index b885e3b545..52874bb335 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/source/NSClientSourcePlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/source/NSClientSourcePlugin.kt @@ -3,13 +3,14 @@ package info.nightscout.androidaps.plugins.source import android.content.Context import androidx.work.Worker import androidx.work.WorkerParameters +import androidx.work.workDataOf import dagger.android.HasAndroidInjector -import info.nightscout.androidaps.Config import info.nightscout.androidaps.R import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.entities.GlucoseValue import info.nightscout.androidaps.database.transactions.CgmSourceTransaction -import info.nightscout.androidaps.interfaces.BgSourceInterface +import info.nightscout.androidaps.interfaces.BgSource +import info.nightscout.androidaps.interfaces.Config import info.nightscout.androidaps.interfaces.PluginBase import info.nightscout.androidaps.interfaces.PluginDescription import info.nightscout.androidaps.interfaces.PluginType @@ -17,7 +18,6 @@ import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.general.nsclient.NSClientPlugin -import info.nightscout.androidaps.plugins.general.nsclient.NSUpload import info.nightscout.androidaps.plugins.general.nsclient.data.NSSgv import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification import info.nightscout.androidaps.plugins.general.overview.notifications.Notification @@ -44,7 +44,7 @@ class NSClientSourcePlugin @Inject constructor( .pluginName(R.string.nsclientbg) .description(R.string.description_source_ns_client), aapsLogger, resourceHelper, injector -), BgSourceInterface { +), BgSource { private var lastBGTimeStamp: Long = 0 private var isAdvancedFilteringEnabled = false @@ -61,6 +61,8 @@ class NSClientSourcePlugin @Inject constructor( return isAdvancedFilteringEnabled } + override fun shouldUploadToNs(glucoseValue: GlucoseValue): Boolean = false + private fun detectSource(glucoseValue: GlucoseValue) { if (glucoseValue.timestamp > lastBGTimeStamp) { isAdvancedFilteringEnabled = arrayOf( @@ -85,7 +87,6 @@ class NSClientSourcePlugin @Inject constructor( @Inject lateinit var aapsLogger: AAPSLogger @Inject lateinit var sp: SP @Inject lateinit var rxBus: RxBusWrapper - @Inject lateinit var nsUpload: NSUpload @Inject lateinit var dateUtil: DateUtil @Inject lateinit var dataWorker: DataWorker @Inject lateinit var repository: AppRepository @@ -103,7 +104,7 @@ class NSClientSourcePlugin @Inject constructor( timestamp = sgv.mills ?: return null, value = sgv.mgdl?.toDouble() ?: return null, noise = null, - raw = sgv.filtered?.toDouble() ?: sgv.mgdl?.toDouble(), + raw = sgv.filtered?.toDouble() ?: sgv.mgdl?.toDouble(), trendArrow = GlucoseValue.TrendArrow.fromString(sgv.direction), nightscoutId = sgv.id, sourceSensor = GlucoseValue.SourceSensor.fromString(sgv.device) @@ -114,10 +115,10 @@ class NSClientSourcePlugin @Inject constructor( override fun doWork(): Result { var ret = Result.success() - if (!nsClientSourcePlugin.isEnabled() && !sp.getBoolean(R.string.key_ns_autobackfill, true) && !dexcomPlugin.isEnabled()) return Result.failure() + if (!nsClientSourcePlugin.isEnabled() && !sp.getBoolean(R.string.key_ns_receive_cgm, true) && !dexcomPlugin.isEnabled()) return Result.success() val sgvs = dataWorker.pickupJSONArray(inputData.getLong(DataWorker.STORE_KEY, -1)) - ?: return Result.failure() + ?: return Result.failure(workDataOf("Error" to "missing input data")) try { var latestDateInReceivedData: Long = 0 @@ -126,12 +127,12 @@ class NSClientSourcePlugin @Inject constructor( val glucoseValues = mutableListOf() for (i in 0 until sgvs.length()) { val sgv = toGv(sgvs.getJSONObject(i)) ?: continue - if (sgv.timestamp < dateUtil._now() && sgv.timestamp > latestDateInReceivedData) latestDateInReceivedData = sgv.timestamp + if (sgv.timestamp < dateUtil.now() && sgv.timestamp > latestDateInReceivedData) latestDateInReceivedData = sgv.timestamp glucoseValues += sgv } // Was that sgv more less 5 mins ago ? - if (T.msecs(dateUtil._now() - latestDateInReceivedData).mins() < 5L) { + if (T.msecs(dateUtil.now() - latestDateInReceivedData).mins() < 5L) { rxBus.send(EventDismissNotification(Notification.NS_ALARM)) rxBus.send(EventDismissNotification(Notification.NS_URGENT_ALARM)) } @@ -140,25 +141,25 @@ class NSClientSourcePlugin @Inject constructor( repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, emptyList(), null, !nsClientSourcePlugin.isEnabled())) .doOnError { - aapsLogger.error("Error while saving values from NSClient App", it) - ret = Result.failure() + aapsLogger.error(LTag.DATABASE, "Error while saving values from NSClient App", it) + ret = Result.failure(workDataOf("Error" to it.toString())) } .blockingGet() .also { result -> result.updated.forEach { broadcastToXDrip(it) nsClientSourcePlugin.detectSource(it) - aapsLogger.debug(LTag.BGSOURCE, "Updated bg $it") + aapsLogger.debug(LTag.DATABASE, "Updated bg $it") } result.inserted.forEach { broadcastToXDrip(it) nsClientSourcePlugin.detectSource(it) - aapsLogger.debug(LTag.BGSOURCE, "Inserted bg $it") + aapsLogger.debug(LTag.DATABASE, "Inserted bg $it") } } } catch (e: Exception) { aapsLogger.error("Unhandled exception", e) - ret = Result.failure() + ret = Result.failure(workDataOf("Error" to e.toString())) } // Objectives 0 sp.putBoolean(R.string.key_ObjectivesbgIsAvailableInNS, true) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/source/PoctechPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/source/PoctechPlugin.kt index 09c5204309..17aefac5b5 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/source/PoctechPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/source/PoctechPlugin.kt @@ -3,19 +3,19 @@ package info.nightscout.androidaps.plugins.source import android.content.Context import androidx.work.Worker import androidx.work.WorkerParameters +import androidx.work.workDataOf import dagger.android.HasAndroidInjector import info.nightscout.androidaps.Constants import info.nightscout.androidaps.R import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.entities.GlucoseValue import info.nightscout.androidaps.database.transactions.CgmSourceTransaction -import info.nightscout.androidaps.interfaces.BgSourceInterface +import info.nightscout.androidaps.interfaces.BgSource import info.nightscout.androidaps.interfaces.PluginBase import info.nightscout.androidaps.interfaces.PluginDescription import info.nightscout.androidaps.interfaces.PluginType import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag -import info.nightscout.androidaps.plugins.general.nsclient.NSUpload import info.nightscout.androidaps.utils.JsonHelper.safeGetString import info.nightscout.androidaps.utils.XDripBroadcast import info.nightscout.androidaps.utils.resources.ResourceHelper @@ -29,7 +29,8 @@ import javax.inject.Singleton class PoctechPlugin @Inject constructor( injector: HasAndroidInjector, resourceHelper: ResourceHelper, - aapsLogger: AAPSLogger + aapsLogger: AAPSLogger, + private val sp: SP ) : PluginBase(PluginDescription() .mainType(PluginType.BGSOURCE) .fragmentClass(BGSourceFragment::class.java.name) @@ -38,7 +39,7 @@ class PoctechPlugin @Inject constructor( .preferencesId(R.xml.pref_bgsource) .description(R.string.description_source_poctech), aapsLogger, resourceHelper, injector -), BgSourceInterface { +), BgSource { // cannot be inner class because of needed injection class PoctechWorker( @@ -49,8 +50,6 @@ class PoctechPlugin @Inject constructor( @Inject lateinit var injector: HasAndroidInjector @Inject lateinit var poctechPlugin: PoctechPlugin @Inject lateinit var aapsLogger: AAPSLogger - @Inject lateinit var sp: SP - @Inject lateinit var nsUpload: NSUpload @Inject lateinit var repository: AppRepository @Inject lateinit var broadcastToXDrip: XDripBroadcast @@ -61,7 +60,7 @@ class PoctechPlugin @Inject constructor( override fun doWork(): Result { var ret = Result.success() - if (!poctechPlugin.isEnabled(PluginType.BGSOURCE)) return Result.failure() + if (!poctechPlugin.isEnabled(PluginType.BGSOURCE)) return Result.success() aapsLogger.debug(LTag.BGSOURCE, "Received Poctech Data $inputData") try { val glucoseValues = mutableListOf() @@ -81,23 +80,25 @@ class PoctechPlugin @Inject constructor( } repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, emptyList(), null)) .doOnError { - aapsLogger.error("Error while saving values from Poctech App", it) - ret = Result.failure() + aapsLogger.error(LTag.DATABASE, "Error while saving values from Poctech App", it) + ret = Result.failure(workDataOf("Error" to it.toString())) } .blockingGet() .also { savedValues -> savedValues.inserted.forEach { broadcastToXDrip(it) - if (sp.getBoolean(R.string.key_dexcomg5_nsupload, false)) - nsUpload.uploadBg(it, GlucoseValue.SourceSensor.POCTECH_NATIVE.text) - aapsLogger.debug(LTag.BGSOURCE, "Inserted bg $it") + aapsLogger.debug(LTag.DATABASE, "Inserted bg $it") } } } catch (e: JSONException) { aapsLogger.error("Exception: ", e) - ret = Result.failure() + ret = Result.failure(workDataOf("Error" to e.toString())) } return ret } } + + override fun shouldUploadToNs(glucoseValue: GlucoseValue): Boolean = + glucoseValue.sourceSensor == GlucoseValue.SourceSensor.POCTECH_NATIVE && sp.getBoolean(R.string.key_dexcomg5_nsupload, false) + } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/source/RandomBgPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/source/RandomBgPlugin.kt index aa776557ae..06c7822036 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/source/RandomBgPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/source/RandomBgPlugin.kt @@ -7,19 +7,17 @@ import info.nightscout.androidaps.R import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.entities.GlucoseValue import info.nightscout.androidaps.database.transactions.CgmSourceTransaction -import info.nightscout.androidaps.interfaces.BgSourceInterface +import info.nightscout.androidaps.interfaces.BgSource import info.nightscout.androidaps.interfaces.PluginBase import info.nightscout.androidaps.interfaces.PluginDescription import info.nightscout.androidaps.interfaces.PluginType import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag -import info.nightscout.androidaps.plugins.general.nsclient.NSUpload import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.XDripBroadcast import info.nightscout.androidaps.utils.buildHelper.BuildHelper -import info.nightscout.androidaps.utils.extensions.isRunningTest import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.sharedPreferences.SP import io.reactivex.disposables.CompositeDisposable @@ -38,7 +36,6 @@ class RandomBgPlugin @Inject constructor( private val virtualPumpPlugin: VirtualPumpPlugin, private val buildHelper: BuildHelper, private val sp: SP, - private val nsUpload: NSUpload, private val dateUtil: DateUtil, private val repository: AppRepository, private val xDripBroadcast: XDripBroadcast @@ -51,7 +48,7 @@ class RandomBgPlugin @Inject constructor( .preferencesId(R.xml.pref_bgsource) .description(R.string.description_source_randombg), aapsLogger, resourceHelper, injector -), BgSourceInterface { +), BgSource { private val loopHandler: Handler = Handler(HandlerThread(RandomBgPlugin::class.java.simpleName + "Handler").also { it.start() }.looper) private lateinit var refreshLoop: Runnable @@ -59,12 +56,15 @@ class RandomBgPlugin @Inject constructor( companion object { const val interval = 5L // minutes + const val min = 70 // mgdl + const val max = 190 // mgdl + const val period = 90.0 // minutes } init { refreshLoop = Runnable { - handleNewData() loopHandler.postDelayed(refreshLoop, T.mins(interval).msecs()) + handleNewData() } } @@ -74,6 +74,9 @@ class RandomBgPlugin @Inject constructor( return true } + override fun shouldUploadToNs(glucoseValue: GlucoseValue): Boolean = + glucoseValue.sourceSensor == GlucoseValue.SourceSensor.RANDOM && sp.getBoolean(R.string.key_dexcomg5_nsupload, false) + override fun onStart() { super.onStart() loopHandler.postDelayed(refreshLoop, T.mins(interval).msecs()) @@ -86,37 +89,33 @@ class RandomBgPlugin @Inject constructor( } override fun specialEnableCondition(): Boolean { - return isRunningTest() || virtualPumpPlugin.isEnabled(PluginType.PUMP) && buildHelper.isEngineeringMode() +// return isRunningTest() || virtualPumpPlugin.isEnabled(PluginType.PUMP) && buildHelper.isEngineeringMode() + return true } private fun handleNewData() { if (!isEnabled(PluginType.BGSOURCE)) return - val min = 70 - val max = 190 val cal = GregorianCalendar() val currentMinute = cal.get(Calendar.MINUTE) + (cal.get(Calendar.HOUR_OF_DAY) % 2) * 60 - val bgMgdl = min + ((max - min) + (max - min) * sin(currentMinute / 120.0 * 2 * PI)) / 2 + val bgMgdl = min + ((max - min) + (max - min) * sin(currentMinute / period * 2 * PI)) / 2 val glucoseValues = mutableListOf() glucoseValues += CgmSourceTransaction.TransactionGlucoseValue( - timestamp = dateUtil._now(), + timestamp = dateUtil.now(), value = bgMgdl, raw = 0.0, noise = null, trendArrow = GlucoseValue.TrendArrow.NONE, sourceSensor = GlucoseValue.SourceSensor.RANDOM ) - disposable += repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, emptyList(), null)).subscribe({ savedValues -> - savedValues.inserted.forEach { - xDripBroadcast(it) - if (sp.getBoolean(R.string.key_dexcomg5_nsupload, false)) - nsUpload.uploadBg(it, GlucoseValue.SourceSensor.RANDOM.text) - aapsLogger.debug(LTag.BGSOURCE, "Inserted bg $it") - } - }, { - aapsLogger.error(LTag.BGSOURCE, "Error while saving values from Random plugin", it) - }) - aapsLogger.debug(LTag.BGSOURCE, "Generated BG: $bgMgdl ${Date()}") + disposable += repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, emptyList(), null)) + .subscribe({ savedValues -> + savedValues.inserted.forEach { + xDripBroadcast(it) + aapsLogger.debug(LTag.DATABASE, "Inserted bg $it") + } + }, { aapsLogger.error(LTag.DATABASE, "Error while saving values from Random plugin", it) } + ) } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/source/TomatoPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/source/TomatoPlugin.kt index db1710d6e2..cf6fce3da5 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/source/TomatoPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/source/TomatoPlugin.kt @@ -3,18 +3,18 @@ package info.nightscout.androidaps.plugins.source import android.content.Context import androidx.work.Worker import androidx.work.WorkerParameters +import androidx.work.workDataOf import dagger.android.HasAndroidInjector import info.nightscout.androidaps.R import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.entities.GlucoseValue import info.nightscout.androidaps.database.transactions.CgmSourceTransaction -import info.nightscout.androidaps.interfaces.BgSourceInterface +import info.nightscout.androidaps.interfaces.BgSource import info.nightscout.androidaps.interfaces.PluginBase import info.nightscout.androidaps.interfaces.PluginDescription import info.nightscout.androidaps.interfaces.PluginType import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag -import info.nightscout.androidaps.plugins.general.nsclient.NSUpload import info.nightscout.androidaps.utils.XDripBroadcast import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.sharedPreferences.SP @@ -25,7 +25,8 @@ import javax.inject.Singleton class TomatoPlugin @Inject constructor( injector: HasAndroidInjector, resourceHelper: ResourceHelper, - aapsLogger: AAPSLogger + aapsLogger: AAPSLogger, + private val sp: SP ) : PluginBase(PluginDescription() .mainType(PluginType.BGSOURCE) .fragmentClass(BGSourceFragment::class.java.name) @@ -35,7 +36,7 @@ class TomatoPlugin @Inject constructor( .shortName(R.string.tomato_short) .description(R.string.description_source_tomato), aapsLogger, resourceHelper, injector -), BgSourceInterface { +), BgSource { // cannot be inner class because of needed injection class TomatoWorker( @@ -47,7 +48,6 @@ class TomatoPlugin @Inject constructor( @Inject lateinit var tomatoPlugin: TomatoPlugin @Inject lateinit var aapsLogger: AAPSLogger @Inject lateinit var sp: SP - @Inject lateinit var nsUpload: NSUpload @Inject lateinit var repository: AppRepository @Inject lateinit var broadcastToXDrip: XDripBroadcast @@ -59,7 +59,7 @@ class TomatoPlugin @Inject constructor( override fun doWork(): Result { var ret = Result.success() - if (!tomatoPlugin.isEnabled(PluginType.BGSOURCE)) return Result.failure() + if (!tomatoPlugin.isEnabled(PluginType.BGSOURCE)) return Result.success() val glucoseValues = mutableListOf() glucoseValues += CgmSourceTransaction.TransactionGlucoseValue( timestamp = inputData.getLong("com.fanqies.tomatofn.Extras.Time", 0), @@ -71,19 +71,21 @@ class TomatoPlugin @Inject constructor( ) repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, emptyList(), null)) .doOnError { - aapsLogger.error("Error while saving values from Tomato App", it) - ret = Result.failure() + aapsLogger.error(LTag.DATABASE, "Error while saving values from Tomato App", it) + ret = Result.failure(workDataOf("Error" to it.toString())) } .blockingGet() .also { savedValues -> savedValues.inserted.forEach { broadcastToXDrip(it) - if (sp.getBoolean(R.string.key_dexcomg5_nsupload, false)) - nsUpload.uploadBg(it, GlucoseValue.SourceSensor.LIBRE_1_TOMATO.text) - aapsLogger.debug(LTag.BGSOURCE, "Inserted bg $it") + aapsLogger.debug(LTag.DATABASE, "Inserted bg $it") } } return ret } } + + override fun shouldUploadToNs(glucoseValue: GlucoseValue): Boolean = + glucoseValue.sourceSensor == GlucoseValue.SourceSensor.LIBRE_1_TOMATO && sp.getBoolean(R.string.key_dexcomg5_nsupload, false) + } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/source/XdripPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/source/XdripPlugin.kt index 3862e9f6d5..622d69c284 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/source/XdripPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/source/XdripPlugin.kt @@ -3,12 +3,13 @@ package info.nightscout.androidaps.plugins.source import android.content.Context import androidx.work.Worker import androidx.work.WorkerParameters +import androidx.work.workDataOf import dagger.android.HasAndroidInjector import info.nightscout.androidaps.R import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.entities.GlucoseValue import info.nightscout.androidaps.database.transactions.CgmSourceTransaction -import info.nightscout.androidaps.interfaces.BgSourceInterface +import info.nightscout.androidaps.interfaces.BgSource import info.nightscout.androidaps.interfaces.PluginBase import info.nightscout.androidaps.interfaces.PluginDescription import info.nightscout.androidaps.interfaces.PluginType @@ -32,11 +33,13 @@ class XdripPlugin @Inject constructor( .pluginName(R.string.xdrip) .description(R.string.description_source_xdrip), aapsLogger, resourceHelper, injector -), BgSourceInterface { +), BgSource { private var advancedFiltering = false override var sensorBatteryLevel = -1 + override fun shouldUploadToNs(glucoseValue: GlucoseValue): Boolean = false + override fun advancedFilteringSupported(): Boolean { return advancedFiltering } @@ -70,9 +73,9 @@ class XdripPlugin @Inject constructor( override fun doWork(): Result { var ret = Result.success() - if (!xdripPlugin.isEnabled(PluginType.BGSOURCE)) return Result.failure() + if (!xdripPlugin.isEnabled(PluginType.BGSOURCE)) return Result.success() val bundle = dataWorker.pickupBundle(inputData.getLong(DataWorker.STORE_KEY, -1)) - ?: return Result.failure() + ?: return Result.failure(workDataOf("Error" to "missing input data")) aapsLogger.debug(LTag.BGSOURCE, "Received xDrip data: $bundle") val glucoseValues = mutableListOf() @@ -87,14 +90,14 @@ class XdripPlugin @Inject constructor( ) repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, emptyList(), null)) .doOnError { - aapsLogger.error(LTag.BGSOURCE, "Error while saving values from Eversense App", it) - ret = Result.failure() + aapsLogger.error(LTag.DATABASE, "Error while saving values from Xdrip", it) + ret = Result.failure(workDataOf("Error" to it.toString())) } .blockingGet() .also { savedValues -> savedValues.all().forEach { xdripPlugin.detectSource(it) - aapsLogger.debug(LTag.BGSOURCE, "Inserted bg $it") + aapsLogger.debug(LTag.DATABASE, "Inserted bg $it") } } xdripPlugin.sensorBatteryLevel = bundle.getInt(Intents.EXTRA_SENSOR_BATTERY, -1) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/CarbsGenerator.kt b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/CarbsGenerator.kt deleted file mode 100644 index 7f9dbc94c3..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/CarbsGenerator.kt +++ /dev/null @@ -1,60 +0,0 @@ -package info.nightscout.androidaps.plugins.treatments - -import android.content.Context -import info.nightscout.androidaps.R -import info.nightscout.androidaps.activities.ErrorHelperActivity -import info.nightscout.androidaps.data.DetailedBolusInfo -import info.nightscout.androidaps.database.entities.TherapyEvent -import info.nightscout.androidaps.db.Source -import info.nightscout.androidaps.interfaces.ActivePluginProvider -import info.nightscout.androidaps.interfaces.CommandQueueProvider -import info.nightscout.androidaps.queue.Callback -import info.nightscout.androidaps.utils.DateUtil -import info.nightscout.androidaps.utils.T -import info.nightscout.androidaps.utils.resources.ResourceHelper -import javax.inject.Inject -import javax.inject.Singleton -import kotlin.math.roundToInt - -@Singleton -class CarbsGenerator @Inject constructor( - private val resourceHelper: ResourceHelper, - private val activePlugin: ActivePluginProvider, - private val commandQueue: CommandQueueProvider, - private val context: Context -) { - - fun generateCarbs(amount: Int, startTime: Long, duration: Int, notes: String) { - var remainingCarbs = amount.toLong() - val ticks = duration * 4 //duration guaranteed to be integer greater zero - for (i in 0 until ticks) { - val carbTime = startTime + i * 15 * 60 * 1000 - val smallCarbAmount = (1.0 * remainingCarbs / (ticks - i)).roundToInt() //on last iteration (ticks-i) is 1 -> smallCarbAmount == remainingCarbs - remainingCarbs -= smallCarbAmount.toLong() - if (smallCarbAmount > 0) createCarb(smallCarbAmount, carbTime, TherapyEvent.Type.MEAL_BOLUS.text, notes) - } - } - - fun createCarb(carbs: Int, time: Long, eventType: String, notes: String) { - val carbInfo = DetailedBolusInfo() - carbInfo.date = time - carbInfo.eventType = eventType - carbInfo.carbs = carbs.toDouble() - carbInfo.context = context - carbInfo.source = Source.USER - carbInfo.notes = notes - if (activePlugin.activePump.pumpDescription.storesCarbInfo && carbInfo.date <= DateUtil.now() && carbInfo.date > DateUtil.now() - T.mins(2).msecs()) { - commandQueue.bolus(carbInfo, object : Callback() { - override fun run() { - if (!result.success) { - ErrorHelperActivity.runAlarm(context, result.comment, resourceHelper.gs(R.string.treatmentdeliveryerror), R.raw.boluserror) - } - } - }) - } else { - // Don't send to pump if it is in the future or more than 5 minutes in the past - // as pumps might return those as as "now" when reading the history. - activePlugin.activeTreatments.addToHistoryTreatment(carbInfo, false) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentService.java b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentService.java index a4f85e3882..a8293002f8 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentService.java @@ -17,28 +17,19 @@ import com.j256.ormlite.table.TableUtils; import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; -import org.json.JSONException; -import org.json.JSONObject; import java.sql.SQLException; -import java.util.ArrayList; import java.util.List; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; import javax.inject.Inject; import dagger.android.HasAndroidInjector; import info.nightscout.androidaps.db.DatabaseHelper; -import info.nightscout.androidaps.db.ICallback; import info.nightscout.androidaps.db.Source; import info.nightscout.androidaps.db.Treatment; -import info.nightscout.androidaps.events.Event; -import info.nightscout.androidaps.events.EventNsTreatment; -import info.nightscout.androidaps.events.EventReloadTreatmentData; -import info.nightscout.androidaps.events.EventTreatmentChange; import info.nightscout.androidaps.interfaces.DatabaseHelperInterface; import info.nightscout.androidaps.interfaces.TreatmentServiceInterface; import info.nightscout.androidaps.interfaces.UpdateReturn; @@ -46,14 +37,9 @@ import info.nightscout.androidaps.logging.AAPSLogger; import info.nightscout.androidaps.logging.LTag; import info.nightscout.androidaps.plugins.bus.RxBusWrapper; import info.nightscout.androidaps.plugins.general.openhumans.OpenHumansUploader; -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventNewHistoryData; import info.nightscout.androidaps.plugins.pump.medtronic.MedtronicPumpPlugin; -import info.nightscout.androidaps.plugins.pump.medtronic.data.MedtronicHistoryData; -import info.nightscout.androidaps.utils.DateUtil; import info.nightscout.androidaps.utils.FabricPrivacy; -import info.nightscout.androidaps.utils.JsonHelper; import info.nightscout.androidaps.utils.rx.AapsSchedulers; -import io.reactivex.disposables.CompositeDisposable; /** @@ -70,29 +56,10 @@ public class TreatmentService extends OrmLiteBaseService impleme @Inject OpenHumansUploader openHumansUploader; @Inject AapsSchedulers aapsSchedulers; - private final CompositeDisposable disposable = new CompositeDisposable(); - - private static final ScheduledExecutorService treatmentEventWorker = Executors.newSingleThreadScheduledExecutor(); - private static ScheduledFuture scheduledTreatmentEventPost = null; - public TreatmentService(HasAndroidInjector injector) { injector.androidInjector().inject(this); onCreate(); dbInitialize(); - disposable.add(rxBus - .toObservable(EventNsTreatment.class) - .observeOn(aapsSchedulers.getIo()) - .subscribe(event -> { - int mode = event.getMode(); - JSONObject payload = event.getPayload(); - - if (mode == EventNsTreatment.Companion.getADD() || mode == EventNsTreatment.Companion.getUPDATE()) { - this.createTreatmentFromJsonIfNotExists(payload); - } else { // EventNsTreatment.REMOVE - this.deleteNS(payload); - } - }, fabricPrivacy::logException) - ); } /** @@ -138,25 +105,10 @@ public class TreatmentService extends OrmLiteBaseService impleme return wrapped.queryForAll(); } - public void delete(Treatment data) throws SQLException { - wrapped.delete(data); - openHumansUploader.enqueueTreatment(data, true); - } - - public void create(Treatment data) throws SQLException { - wrapped.create(data); - openHumansUploader.enqueueTreatment(data); - } - public Treatment queryForId(long id) throws SQLException { return wrapped.queryForId(id); } - public void update(Treatment data) throws SQLException { - wrapped.update(data); - openHumansUploader.enqueueTreatment(data); - } - public QueryBuilder queryBuilder() { return wrapped.queryBuilder(); } @@ -215,94 +167,6 @@ public class TreatmentService extends OrmLiteBaseService impleme } } - public void resetTreatments() { - try { - TableUtils.dropTable(this.getConnectionSource(), Treatment.class, true); - TableUtils.createTableIfNotExists(this.getConnectionSource(), Treatment.class); - DatabaseHelper.updateEarliestDataChange(0); - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - } - scheduleTreatmentChange(null, true); - } - - - /** - * A place to centrally register events to be posted, if any data changed. - * This should be implemented in an abstract service-class. - *

- * We do need to make sure, that ICallback is extended to be able to handle multiple - * events, or handle a list of events. - *

- * on some methods the earliestDataChange event is handled separatly, in that it is checked if it is - * set to null by another event already (eg. scheduleExtendedBolusChange). - * - * @param event - * @param eventWorker - * @param callback - */ - private void scheduleEvent(final Event event, ScheduledExecutorService eventWorker, - final ICallback callback) { - - class PostRunnable implements Runnable { - public void run() { - aapsLogger.debug(LTag.DATATREATMENTS, "Firing EventReloadTreatmentData"); - rxBus.send(event); - if (DatabaseHelper.earliestDataChange != null) { - aapsLogger.debug(LTag.DATATREATMENTS, "Firing EventNewHistoryData"); - rxBus.send(new EventNewHistoryData(DatabaseHelper.earliestDataChange)); - } - DatabaseHelper.earliestDataChange = null; - callback.setPost(null); - } - } - // prepare task for execution in 1 sec - // cancel waiting task to prevent sending multiple posts - ScheduledFuture scheduledFuture = callback.getPost(); - if (scheduledFuture != null) - scheduledFuture.cancel(false); - Runnable task = new PostRunnable(); - final int sec = 1; - callback.setPost(eventWorker.schedule(task, sec, TimeUnit.SECONDS)); - } - - /** - * Schedule a foodChange Event. - */ - public void scheduleTreatmentChange(@Nullable final Treatment treatment, boolean runImmediately) { - if (runImmediately) { - aapsLogger.debug(LTag.DATATREATMENTS, "Firing EventReloadTreatmentData"); - rxBus.send(new EventReloadTreatmentData(new EventTreatmentChange(treatment))); - if (DatabaseHelper.earliestDataChange != null) { - aapsLogger.debug(LTag.DATATREATMENTS, "Firing EventNewHistoryData"); - rxBus.send(new EventNewHistoryData(DatabaseHelper.earliestDataChange)); - } - DatabaseHelper.earliestDataChange = null; - } else { - this.scheduleEvent(new EventReloadTreatmentData(new EventTreatmentChange(treatment)), treatmentEventWorker, new ICallback() { - @Override - public void setPost(ScheduledFuture post) { - scheduledTreatmentEventPost = post; - } - - @Override - public ScheduledFuture getPost() { - return scheduledTreatmentEventPost; - } - }); - } - } - - public List getTreatmentData() { - try { - return this.getDao().queryForAll(); - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - } - - return new ArrayList<>(); - } - public long count() { try { return this.getDao().countOf(); @@ -326,28 +190,9 @@ public class TreatmentService extends OrmLiteBaseService impleme "unit": "ml" } */ - public void createTreatmentFromJsonIfNotExists(JSONObject json) { - try { - Treatment treatment = Treatment.createFromJson(json); - if (treatment != null) { - - if (MedtronicHistoryData.doubleBolusDebug) - aapsLogger.debug(LTag.DATATREATMENTS, "DoubleBolusDebug: createTreatmentFromJsonIfNotExists:: medtronicPump={}", medtronicPumpPlugin.isEnabled()); - - if (!medtronicPumpPlugin.isEnabled()) - createOrUpdate(treatment); - else - createOrUpdateMedtronic(treatment, true); - } else - aapsLogger.error("Date is null: " + treatment.toString()); - } catch (JSONException e) { - aapsLogger.error("Unhandled exception", e); - } - } - - // return true if new record is created public UpdateReturn createOrUpdate(Treatment treatment) { + /* if (treatment != null && treatment.source == Source.NONE) { aapsLogger.error("Coder error: source is not set for treatment: " + treatment, new Exception()); //FabricPrivacy.logException(new Exception("Coder error: source is not set for treatment: " + treatment)); @@ -470,12 +315,13 @@ public class TreatmentService extends OrmLiteBaseService impleme } catch (SQLException e) { aapsLogger.error("Unhandled exception", e); } + */ return new UpdateReturn(false, false); } @NotNull public UpdateReturn createOrUpdateMedtronic(@NotNull Treatment treatment, boolean fromNightScout) { - +/* if (MedtronicHistoryData.doubleBolusDebug) aapsLogger.debug(LTag.DATATREATMENTS, "DoubleBolusDebug: createOrUpdateMedtronic:: originalTreatment={}, fromNightScout={}", treatment, fromNightScout); @@ -524,6 +370,8 @@ public class TreatmentService extends OrmLiteBaseService impleme } catch (SQLException e) { aapsLogger.error("Unhandled SQL exception: {}", e.getMessage(), e); } + + */ return new UpdateReturn(false, false); } @@ -654,107 +502,6 @@ public class TreatmentService extends OrmLiteBaseService impleme } } - /** - * Returns the newest record with insulin > 0 - */ - @Nullable - public Treatment getLastBolus(boolean excludeSMB) { - try { - QueryBuilder queryBuilder = getDao().queryBuilder(); - Where where = queryBuilder.where(); - where.gt("insulin", 0); - where.and().le("date", DateUtil.now()); - where.and().eq("isValid", true); - if (excludeSMB) where.and().eq("isSMB", false); - queryBuilder.orderBy("date", false); - queryBuilder.limit(1L); - - List result = getDao().query(queryBuilder.prepare()); - if (result.isEmpty()) - return null; - return result.get(0); - } catch (SQLException e) { - throw new RuntimeException(e); - } - } - - /** - * Returns the newest record with carbs > 0 - */ - @Nullable - public Treatment getLastCarb() { - try { - QueryBuilder queryBuilder = getDao().queryBuilder(); - Where where = queryBuilder.where(); - where.gt("carbs", 0); - where.and().le("date", DateUtil.now()); - where.and().eq("isValid", true); - queryBuilder.orderBy("date", false); - queryBuilder.limit(1L); - - List result = getDao().query(queryBuilder.prepare()); - if (result.isEmpty()) - return null; - return result.get(0); - } catch (SQLException e) { - throw new RuntimeException(e); - } - } - - public void deleteNS(JSONObject json) { - String _id = JsonHelper.safeGetString(json, "_id"); - if (_id != null && !_id.isEmpty()) - this.deleteByNSId(_id); - } - - /** - * deletes an entry by its NS Id. - *

- * Basically a convenience method for findByNSId and delete. - * - * @param _id - */ - private void deleteByNSId(String _id) { - Treatment stored = findByNSId(_id); - if (stored != null) { - aapsLogger.debug(LTag.DATATREATMENTS, "Removing Treatment record from database: " + stored.toString()); - try { - getDao().delete(stored); - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - } - DatabaseHelper.updateEarliestDataChange(stored.date); - this.scheduleTreatmentChange(stored, false); - } - } - - /** - * deletes the treatment and sends the treatmentChange Event - *

- * should be moved ot a Service - * - * @param treatment - */ - public void delete(Treatment treatment) { - try { - getDao().delete(treatment); - DatabaseHelper.updateEarliestDataChange(treatment.date); - this.scheduleTreatmentChange(treatment, true); - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - } - } - - public void update(Treatment treatment) { - try { - getDao().update(treatment); - DatabaseHelper.updateEarliestDataChange(treatment.date); - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - } - scheduleTreatmentChange(treatment, true); - } - /** * finds treatment by its NS Id. * @@ -784,40 +531,6 @@ public class TreatmentService extends OrmLiteBaseService impleme return null; } - public List getTreatmentDataFromTime(long mills, boolean ascending) { - try { - TreatmentDaoWrapper daoTreatments = getDao(); - List treatments; - QueryBuilder queryBuilder = daoTreatments.queryBuilder(); - queryBuilder.orderBy("date", ascending); - Where where = queryBuilder.where(); - where.ge("date", mills); - PreparedQuery preparedQuery = queryBuilder.prepare(); - treatments = daoTreatments.query(preparedQuery); - return treatments; - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - } - return new ArrayList<>(); - } - - public List getTreatmentDataFromTime(long from, long to, boolean ascending) { - try { - TreatmentDaoWrapper daoTreatments = getDao(); - List treatments; - QueryBuilder queryBuilder = daoTreatments.queryBuilder(); - queryBuilder.orderBy("date", ascending); - Where where = queryBuilder.where(); - where.between("date", from, to); - PreparedQuery preparedQuery = queryBuilder.prepare(); - treatments = daoTreatments.query(preparedQuery); - return treatments; - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - } - return new ArrayList<>(); - } - @Nullable @Override public IBinder onBind(Intent intent) { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentsFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentsFragment.kt index 6115ce9185..9de459f622 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentsFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentsFragment.kt @@ -10,13 +10,14 @@ import dagger.android.support.DaggerFragment import info.nightscout.androidaps.R import info.nightscout.androidaps.databinding.TreatmentsFragmentBinding import info.nightscout.androidaps.events.EventExtendedBolusChange -import info.nightscout.androidaps.interfaces.ActivePluginProvider -import info.nightscout.androidaps.interfaces.ConfigInterface +import info.nightscout.androidaps.interfaces.ActivePlugin +import info.nightscout.androidaps.interfaces.IobCobCalculator import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.treatments.fragments.* +import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.buildHelper.BuildHelper -import info.nightscout.androidaps.utils.extensions.toVisibility +import info.nightscout.androidaps.extensions.toVisibility import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers import io.reactivex.disposables.CompositeDisposable @@ -28,10 +29,11 @@ class TreatmentsFragment : DaggerFragment() { @Inject lateinit var rxBus: RxBusWrapper @Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var fabricPrivacy: FabricPrivacy - @Inject lateinit var activePlugin: ActivePluginProvider - @Inject lateinit var treatmentsPlugin: TreatmentsPlugin + @Inject lateinit var activePlugin: ActivePlugin + @Inject lateinit var iobCobCalculator: IobCobCalculator @Inject lateinit var aapsSchedulers: AapsSchedulers @Inject lateinit var buildHelper: BuildHelper + @Inject lateinit var dateUtil: DateUtil private val disposable = CompositeDisposable() @@ -51,7 +53,7 @@ class TreatmentsFragment : DaggerFragment() { binding.extendedBoluses.visibility = (buildHelper.isEngineeringMode() && !activePlugin.activePump.isFakingTempsByExtendedBoluses).toVisibility() binding.treatments.setOnClickListener { - setFragment(TreatmentsBolusFragment()) + setFragment(TreatmentsBolusCarbsFragment()) setBackgroundColorOnSelected(it) } binding.extendedBoluses.setOnClickListener { @@ -78,7 +80,7 @@ class TreatmentsFragment : DaggerFragment() { setFragment(TreatmentsUserEntryFragment()) setBackgroundColorOnSelected(it) } - setFragment(TreatmentsBolusFragment()) + setFragment(TreatmentsBolusCarbsFragment()) setBackgroundColorOnSelected(binding.treatments) } @@ -125,6 +127,6 @@ class TreatmentsFragment : DaggerFragment() { private fun updateGui() { if (_binding == null) return - binding.extendedBoluses.visibility = (activePlugin.activePump.pumpDescription.isExtendedBolusCapable || treatmentsPlugin.extendedBolusesFromHistory.size() > 0).toVisibility() + binding.extendedBoluses.visibility = (activePlugin.activePump.pumpDescription.isExtendedBolusCapable || iobCobCalculator.getExtendedBolus(dateUtil.now()) != null).toVisibility() } } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentsPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentsPlugin.java index c91aac3c9c..d994c0475f 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentsPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentsPlugin.java @@ -1,15 +1,9 @@ package info.nightscout.androidaps.plugins.treatments; import android.content.Context; -import android.os.Bundle; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.google.firebase.analytics.FirebaseAnalytics; - -import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; import javax.inject.Inject; import javax.inject.Singleton; @@ -17,47 +11,23 @@ import javax.inject.Singleton; import dagger.android.HasAndroidInjector; import info.nightscout.androidaps.Constants; import info.nightscout.androidaps.R; -import info.nightscout.androidaps.activities.ErrorHelperActivity; import info.nightscout.androidaps.data.DetailedBolusInfo; -import info.nightscout.androidaps.data.Intervals; -import info.nightscout.androidaps.data.Iob; -import info.nightscout.androidaps.data.IobTotal; -import info.nightscout.androidaps.data.NonOverlappingIntervals; -import info.nightscout.androidaps.data.Profile; -import info.nightscout.androidaps.data.ProfileIntervals; import info.nightscout.androidaps.database.AppRepository; import info.nightscout.androidaps.db.ExtendedBolus; -import info.nightscout.androidaps.db.ProfileSwitch; -import info.nightscout.androidaps.db.Source; import info.nightscout.androidaps.db.TemporaryBasal; import info.nightscout.androidaps.db.Treatment; -import info.nightscout.androidaps.events.EventReloadProfileSwitchData; -import info.nightscout.androidaps.events.EventReloadTempBasalData; -import info.nightscout.androidaps.events.EventReloadTreatmentData; -import info.nightscout.androidaps.interfaces.ActivePluginProvider; +import info.nightscout.androidaps.interfaces.ActivePlugin; import info.nightscout.androidaps.interfaces.DatabaseHelperInterface; 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.ProfileStore; -import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.interfaces.TreatmentServiceInterface; import info.nightscout.androidaps.interfaces.TreatmentsInterface; -import info.nightscout.androidaps.interfaces.UpdateReturn; -import info.nightscout.androidaps.interfaces.UploadQueueInterface; import info.nightscout.androidaps.logging.AAPSLogger; -import info.nightscout.androidaps.logging.LTag; import info.nightscout.androidaps.plugins.bus.RxBusWrapper; -import info.nightscout.androidaps.plugins.general.nsclient.NSUpload; -import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification; -import info.nightscout.androidaps.plugins.general.overview.notifications.Notification; -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult; -import info.nightscout.androidaps.plugins.pump.medtronic.MedtronicPumpPlugin; -import info.nightscout.androidaps.plugins.pump.medtronic.data.MedtronicHistoryData; import info.nightscout.androidaps.utils.DateUtil; import info.nightscout.androidaps.utils.FabricPrivacy; -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; @@ -66,15 +36,10 @@ import io.reactivex.disposables.CompositeDisposable; @Singleton public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface { - private final Context context; - private final AapsSchedulers aapsSchedulers; private final SP sp; private final RxBusWrapper rxBus; - private final ResourceHelper resourceHelper; private final ProfileFunction profileFunction; - private final ActivePluginProvider activePlugin; - private final NSUpload nsUpload; - private final UploadQueueInterface uploadQueue; + private final ActivePlugin activePlugin; private final FabricPrivacy fabricPrivacy; private final DateUtil dateUtil; private final DatabaseHelperInterface databaseHelper; @@ -84,14 +49,6 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface protected TreatmentServiceInterface service; - private IobTotal lastTreatmentCalculation; - private IobTotal lastTempBasalsCalculation; - - private final ArrayList treatments = new ArrayList<>(); - private final Intervals tempBasals = new NonOverlappingIntervals<>(); - private final Intervals extendedBoluses = new NonOverlappingIntervals<>(); - private final ProfileIntervals profiles = new ProfileIntervals<>(); - @Inject public TreatmentsPlugin( HasAndroidInjector injector, @@ -102,11 +59,9 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface Context context, SP sp, ProfileFunction profileFunction, - ActivePluginProvider activePlugin, - NSUpload nsUpload, + ActivePlugin activePlugin, FabricPrivacy fabricPrivacy, DateUtil dateUtil, - UploadQueueInterface uploadQueue, DatabaseHelperInterface databaseHelper, AppRepository repository ) { @@ -121,17 +76,12 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface .setDefault(), aapsLogger, resourceHelper, injector ); - this.resourceHelper = resourceHelper; - this.context = context; this.rxBus = rxBus; - this.aapsSchedulers = aapsSchedulers; this.sp = sp; this.profileFunction = profileFunction; this.activePlugin = activePlugin; this.fabricPrivacy = fabricPrivacy; this.dateUtil = dateUtil; - this.nsUpload = nsUpload; - this.uploadQueue = uploadQueue; this.databaseHelper = databaseHelper; this.repository = repository; } @@ -139,36 +89,13 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface @Override protected void onStart() { this.service = new TreatmentService(getInjector()); - initializeData(range()); super.onStart(); - disposable.add(rxBus - .toObservable(EventReloadTreatmentData.class) - .observeOn(aapsSchedulers.getIo()) - .subscribe(event -> { - getAapsLogger().debug(LTag.DATATREATMENTS, "EventReloadTreatmentData"); - initializeTreatmentData(range()); - initializeExtendedBolusData(range()); - updateTotalIOBTreatments(); - rxBus.send(event.getNext()); - }, - fabricPrivacy::logException - )); - disposable.add(rxBus - .toObservable(EventReloadProfileSwitchData.class) - .observeOn(aapsSchedulers.getIo()) - .subscribe(event -> initializeProfileSwitchData(range()), - fabricPrivacy::logException - )); - disposable.add(rxBus - .toObservable(EventReloadTempBasalData.class) - .observeOn(aapsSchedulers.getIo()) - .subscribe(event -> { - getAapsLogger().debug(LTag.DATATREATMENTS, "EventReloadTempBasalData"); - initializeTempBasalData(range()); - updateTotalIOBTempBasals(); - }, - fabricPrivacy::logException - )); +// disposable.add(rxBus +// .toObservable(EventReloadProfileSwitchData.class) +// .observeOn(aapsSchedulers.getIo()) +// .subscribe(event -> initializeProfileSwitchData(range()), +// fabricPrivacy::logException +// )); } @Override @@ -189,116 +116,22 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface return (long) (60 * 60 * 1000L * (24 + dia)); } - public void initializeData(long range) { - initializeTempBasalData(range); - initializeTreatmentData(range); - initializeExtendedBolusData(range); - initializeProfileSwitchData(range); - } - - private void initializeTreatmentData(long range) { - getAapsLogger().debug(LTag.DATATREATMENTS, "initializeTreatmentData"); - synchronized (treatments) { - treatments.clear(); - treatments.addAll(getService().getTreatmentDataFromTime(DateUtil.now() - range, false)); - } - } - - private void initializeTempBasalData(long range) { - getAapsLogger().debug(LTag.DATATREATMENTS, "initializeTempBasalData"); - synchronized (tempBasals) { - tempBasals.reset().add(databaseHelper.getTemporaryBasalsDataFromTime(DateUtil.now() - range, false)); - } - - } - - private void initializeExtendedBolusData(long range) { - getAapsLogger().debug(LTag.DATATREATMENTS, "initializeExtendedBolusData"); - synchronized (extendedBoluses) { - extendedBoluses.reset().add(databaseHelper.getExtendedBolusDataFromTime(DateUtil.now() - range, false)); - } - - } - - private void initializeProfileSwitchData(long range) { - getAapsLogger().debug(LTag.DATATREATMENTS, "initializeProfileSwitchData"); - synchronized (profiles) { - profiles.reset().add(databaseHelper.getProfileSwitchData(DateUtil.now() - range, false)); - } - } - - @Override - public IobTotal getLastCalculationTreatments() { - return lastTreatmentCalculation; - } - - @Override - public IobTotal getCalculationToTimeTreatments(long time) { - IobTotal total = new IobTotal(time); - - Profile profile = profileFunction.getProfile(); - if (profile == null) - return total; - - PumpInterface pumpInterface = activePlugin.getActivePump(); - - double dia = profile.getDia(); - - synchronized (treatments) { - for (int pos = 0; pos < treatments.size(); pos++) { - Treatment t = treatments.get(pos); - if (!t.isValid) continue; - if (t.date > time) continue; - Iob tIOB = t.iobCalc(time, dia); - total.iob += tIOB.getIobContrib(); - total.activity += tIOB.getActivityContrib(); - if (t.insulin > 0 && t.date > total.lastBolusTime) - total.lastBolusTime = t.date; - if (!t.isSMB) { - // instead of dividing the DIA that only worked on the bilinear curves, - // multiply the time the treatment is seen active. - long timeSinceTreatment = time - t.date; - long snoozeTime = t.date + (long) (timeSinceTreatment * sp.getDouble(R.string.key_openapsama_bolussnooze_dia_divisor, 2.0)); - Iob bIOB = t.iobCalc(snoozeTime, dia); - total.bolussnooze += bIOB.getIobContrib(); - } - } - } - - if (!pumpInterface.isFakingTempsByExtendedBoluses()) - synchronized (extendedBoluses) { - for (int pos = 0; pos < extendedBoluses.size(); pos++) { - ExtendedBolus e = extendedBoluses.get(pos); - if (e.date > time) continue; - IobTotal calc = e.iobCalc(time, profile); - total.plus(calc); - } - } - return total; - } - - @Override - public void updateTotalIOBTreatments() { - lastTreatmentCalculation = getCalculationToTimeTreatments(System.currentTimeMillis()); - } - - @Override - public List getTreatmentsFromHistory() { - synchronized (treatments) { - return new ArrayList<>(treatments); - } - } - - /** * Returns all Treatments after specified timestamp. Also returns invalid entries (required to - * map "Fill Canula" entries to history (and not to add double bolus for it) + * map "Fill Cannula" entries to history (and not to add double bolus for it) * * @param fromTimestamp * @return */ + @Deprecated @Override public List getTreatmentsFromHistoryAfterTimestamp(long fromTimestamp) { + return repository.getBolusesIncludingInvalidFromTimeToTime(fromTimestamp, dateUtil.now(), true) + .blockingGet() + .stream() + .map(bolus -> new Treatment(getInjector(), bolus)) + .collect(Collectors.toList()); +/* List in5minback = new ArrayList<>(); long time = System.currentTimeMillis(); @@ -306,29 +139,15 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface // getAapsLogger().debug(MedtronicHistoryData.doubleBolusDebug, LTag.DATATREATMENTS, "DoubleBolusDebug: AllTreatmentsInDb: " + new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create().toJson(treatments)); for (Treatment t : treatments) { - if (t.date <= time && t.date >= fromTimestamp) + if (t.date >= fromTimestamp && t.date <= time) in5minback.add(t); } // getAapsLogger().debug(MedtronicHistoryData.doubleBolusDebug, LTag.DATATREATMENTS, "DoubleBolusDebug: FilteredTreatments: AfterTime={}, Items={} " + fromTimestamp + " " + new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create().toJson(in5minback)); return in5minback; } +*/ } - - - @Override - public List getCarbTreatments5MinBackFromHistory(long time) { - List in5minback = new ArrayList<>(); - synchronized (treatments) { - for (Treatment t : treatments) { - if (!t.isValid) - continue; - if (t.date <= time && t.date > time - 5 * 60 * 1000 && t.carbs > 0) - in5minback.add(t); - } - return in5minback; - } - } - +/* @Override public long getLastBolusTime() { Treatment last = getService().getLastBolus(false); @@ -364,205 +183,14 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface } - @Override - public boolean isInHistoryRealTempBasalInProgress() { - return getRealTempBasalFromHistory(System.currentTimeMillis()) != null; - } - - @Override - public TemporaryBasal getRealTempBasalFromHistory(long time) { - synchronized (tempBasals) { - return tempBasals.getValueByInterval(time); - } - } - - @Override - public boolean isTempBasalInProgress() { - return getTempBasalFromHistory(System.currentTimeMillis()) != null; - } - - @Override public void removeTempBasal(TemporaryBasal tempBasal) { - String tempBasalId = tempBasal._id; - if (NSUpload.isIdValid(tempBasalId)) { - nsUpload.removeCareportalEntryFromNS(tempBasalId); - } else { - uploadQueue.removeByMongoId("dbAdd", tempBasalId); - } - databaseHelper.delete(tempBasal); - } - - @Override - public boolean isInHistoryExtendedBolusInProgress() { - return getExtendedBolusFromHistory(System.currentTimeMillis()) != null; //TODO: crosscheck here - } - - @Override - public IobTotal getLastCalculationTempBasals() { - return lastTempBasalsCalculation; - } - - @Override - public IobTotal getCalculationToTimeTempBasals(long time) { - return getCalculationToTimeTempBasals(time, false, 0); - } - - public IobTotal getCalculationToTimeTempBasals(long time, boolean truncate, long truncateTime) { - IobTotal total = new IobTotal(time); - - PumpInterface pumpInterface = activePlugin.getActivePump(); - - synchronized (tempBasals) { - for (int pos = 0; pos < tempBasals.size(); pos++) { - TemporaryBasal t = tempBasals.get(pos); - if (t.date > time) continue; - IobTotal calc; - Profile profile = profileFunction.getProfile(t.date); - if (profile == null) continue; - if (truncate && t.end() > truncateTime) { - TemporaryBasal dummyTemp = new TemporaryBasal(getInjector()); - dummyTemp.copyFrom(t); - dummyTemp.cutEndTo(truncateTime); - calc = dummyTemp.iobCalc(time, profile); - } else { - calc = t.iobCalc(time, profile); - } - //log.debug("BasalIOB " + new Date(time) + " >>> " + calc.basaliob); - total.plus(calc); - } - } - if (pumpInterface.isFakingTempsByExtendedBoluses()) { - IobTotal totalExt = new IobTotal(time); - synchronized (extendedBoluses) { - for (int pos = 0; pos < extendedBoluses.size(); pos++) { - ExtendedBolus e = extendedBoluses.get(pos); - if (e.date > time) continue; - IobTotal calc; - Profile profile = profileFunction.getProfile(e.date); - if (profile == null) continue; - if (truncate && e.end() > truncateTime) { - ExtendedBolus dummyExt = new ExtendedBolus(getInjector()); - dummyExt.copyFrom(e); - dummyExt.cutEndTo(truncateTime); - calc = dummyExt.iobCalc(time, profile); - } else { - calc = e.iobCalc(time, profile); - } - totalExt.plus(calc); - } - } - // Convert to basal iob - totalExt.basaliob = totalExt.iob; - totalExt.iob = 0d; - totalExt.netbasalinsulin = totalExt.extendedBolusInsulin; - totalExt.hightempinsulin = totalExt.extendedBolusInsulin; - total.plus(totalExt); - } - return total; - } - - public IobTotal getAbsoluteIOBTempBasals(long time) { - IobTotal total = new IobTotal(time); - - for (long i = time - range(); i < time; i += T.mins(5).msecs()) { - Profile profile = profileFunction.getProfile(i); - if (profile == null) continue; - double basal = profile.getBasal(i); - TemporaryBasal runningTBR = getTempBasalFromHistory(i); - double running = basal; - if (runningTBR != null) { - running = runningTBR.tempBasalConvertedToAbsolute(i, profile); - } - Treatment treatment = new Treatment(getInjector()); - treatment.date = i; - treatment.insulin = running * 5.0 / 60.0; // 5 min chunk - Iob iob = treatment.iobCalc(time, profile.getDia()); - total.basaliob += iob.getIobContrib(); - total.activity += iob.getActivityContrib(); - } - return total; - } - - public IobTotal getCalculationToTimeTempBasals(long time, long truncateTime, AutosensResult lastAutosensResult, boolean exercise_mode, int half_basal_exercise_target, boolean isTempTarget) { - IobTotal total = new IobTotal(time); - - PumpInterface pumpInterface = activePlugin.getActivePump(); - - synchronized (tempBasals) { - for (int pos = 0; pos < tempBasals.size(); pos++) { - TemporaryBasal t = tempBasals.get(pos); - if (t.date > time) continue; - IobTotal calc; - Profile profile = profileFunction.getProfile(t.date); - if (profile == null) continue; - if (t.end() > truncateTime) { - TemporaryBasal dummyTemp = new TemporaryBasal(getInjector()); - dummyTemp.copyFrom(t); - dummyTemp.cutEndTo(truncateTime); - calc = dummyTemp.iobCalc(time, profile, lastAutosensResult, exercise_mode, half_basal_exercise_target, isTempTarget); - } else { - calc = t.iobCalc(time, profile, lastAutosensResult, exercise_mode, half_basal_exercise_target, isTempTarget); - } - //log.debug("BasalIOB " + new Date(time) + " >>> " + calc.basaliob); - total.plus(calc); - } - } - if (pumpInterface.isFakingTempsByExtendedBoluses()) { - IobTotal totalExt = new IobTotal(time); - synchronized (extendedBoluses) { - for (int pos = 0; pos < extendedBoluses.size(); pos++) { - ExtendedBolus e = extendedBoluses.get(pos); - if (e.date > time) continue; - IobTotal calc; - Profile profile = profileFunction.getProfile(e.date); - if (profile == null) continue; - if (e.end() > truncateTime) { - ExtendedBolus dummyExt = new ExtendedBolus(getInjector()); - dummyExt.copyFrom(e); - dummyExt.cutEndTo(truncateTime); - calc = dummyExt.iobCalc(time, profile, lastAutosensResult, exercise_mode, half_basal_exercise_target, isTempTarget); - } else { - calc = e.iobCalc(time, profile, lastAutosensResult, exercise_mode, half_basal_exercise_target, isTempTarget); - } - totalExt.plus(calc); - } - } - // Convert to basal iob - totalExt.basaliob = totalExt.iob; - totalExt.iob = 0d; - totalExt.netbasalinsulin = totalExt.extendedBolusInsulin; - totalExt.hightempinsulin = totalExt.extendedBolusInsulin; - total.plus(totalExt); - } - return total; - } - - @Override - public void updateTotalIOBTempBasals() { - lastTempBasalsCalculation = getCalculationToTimeTempBasals(DateUtil.now()); - } - - @Nullable - @Override - public TemporaryBasal getTempBasalFromHistory(long time) { - TemporaryBasal tb = getRealTempBasalFromHistory(time); - if (tb != null) - return tb; - ExtendedBolus eb = getExtendedBolusFromHistory(time); - if (eb != null && activePlugin.getActivePump().isFakingTempsByExtendedBoluses()) - return new TemporaryBasal(eb); - return null; - } - - @Override - public ExtendedBolus getExtendedBolusFromHistory(long time) { - synchronized (extendedBoluses) { - return extendedBoluses.getValueByInterval(time); - } - } + */ + @Deprecated @Override public boolean addToHistoryExtendedBolus(ExtendedBolus extendedBolus) { + throw new IllegalStateException("Migrate to new DB"); //log.debug("Adding new ExtentedBolus record" + extendedBolus.log()); + /* boolean newRecordCreated = databaseHelper.createOrUpdate(extendedBolus); if (newRecordCreated) { if (extendedBolus.durationInMinutes == 0) { @@ -576,26 +204,14 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface nsUpload.uploadExtendedBolus(extendedBolus); } return newRecordCreated; + */ } - @Override - @NonNull - public Intervals getExtendedBolusesFromHistory() { - synchronized (extendedBoluses) { - return new NonOverlappingIntervals<>(extendedBoluses); - } - } - - @Override - @NonNull - public NonOverlappingIntervals getTemporaryBasalsFromHistory() { - synchronized (tempBasals) { - return new NonOverlappingIntervals<>(tempBasals); - } - } - + @Deprecated @Override public boolean addToHistoryTempBasal(TemporaryBasal tempBasal) { + throw new IllegalStateException("Migrate to new DB"); +/* //log.debug("Adding new TemporaryBasal record" + tempBasal.toString()); boolean newRecordCreated = databaseHelper.createOrUpdate(tempBasal); if (newRecordCreated) { @@ -607,32 +223,41 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface nsUpload.uploadTempBasalStartPercent(tempBasal, profileFunction.getProfile(tempBasal.date)); } return newRecordCreated; + */ } + @Deprecated public TreatmentUpdateReturn createOrUpdateMedtronic(Treatment treatment, boolean fromNightScout) { + throw new IllegalStateException("Migrate to new DB"); +/* UpdateReturn resultRecord = getService().createOrUpdateMedtronic(treatment, fromNightScout); return new TreatmentUpdateReturn(resultRecord.getSuccess(), resultRecord.getNewRecord()); + */ } // return true if new record is created + @Deprecated @Override public boolean addToHistoryTreatment(DetailedBolusInfo detailedBolusInfo, boolean allowUpdate) { + throw new IllegalStateException("Migrate to new DB"); +/* boolean medtronicPump = activePlugin.getActivePump() instanceof MedtronicPumpPlugin; getAapsLogger().debug(MedtronicHistoryData.doubleBolusDebug, LTag.DATATREATMENTS, "DoubleBolusDebug: addToHistoryTreatment::isMedtronicPump={} " + medtronicPump); Treatment treatment = new Treatment(); - treatment.date = detailedBolusInfo.date; - treatment.source = detailedBolusInfo.source; - treatment.pumpId = detailedBolusInfo.pumpId; + treatment.date = detailedBolusInfo.timestamp; + treatment.source = (detailedBolusInfo.getPumpType() == PumpType.USER) ? Source.USER : Source.PUMP; + treatment.pumpId = detailedBolusInfo.getBolusPumpId() != null ? detailedBolusInfo.getBolusPumpId() : 0; treatment.insulin = detailedBolusInfo.insulin; - treatment.isValid = detailedBolusInfo.isValid; - treatment.isSMB = detailedBolusInfo.isSMB; + treatment.isValid = detailedBolusInfo.getBolusType() != DetailedBolusInfo.BolusType.PRIMING; + treatment.isSMB = detailedBolusInfo.getBolusType() == DetailedBolusInfo.BolusType.SMB; if (detailedBolusInfo.carbTime == 0) treatment.carbs = detailedBolusInfo.carbs; treatment.mealBolus = treatment.carbs > 0; - treatment.boluscalc = detailedBolusInfo.boluscalc != null ? detailedBolusInfo.boluscalc.toString() : null; + // treatment.boluscalc = detailedBolusInfo.boluscalc != null ? detailedBolusInfo.boluscalc.toString() : null; + treatment.boluscalc = null; UpdateReturn creatOrUpdateResult; getAapsLogger().debug(medtronicPump && MedtronicHistoryData.doubleBolusDebug, LTag.DATATREATMENTS, "DoubleBolusDebug: addToHistoryTreatment::treatment={} " + treatment); @@ -647,9 +272,9 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface if (detailedBolusInfo.carbTime != 0) { Treatment carbsTreatment = new Treatment(); - carbsTreatment.source = detailedBolusInfo.source; - carbsTreatment.pumpId = detailedBolusInfo.pumpId; // but this should never happen - carbsTreatment.date = detailedBolusInfo.date + detailedBolusInfo.carbTime * 60 * 1000L + 1000L; // add 1 sec to make them different records + carbsTreatment.source = (detailedBolusInfo.getPumpType() == PumpType.USER) ? Source.USER : Source.PUMP; + carbsTreatment.pumpId = detailedBolusInfo.getCarbsPumpId() != null ? detailedBolusInfo.getCarbsPumpId() : 0; // but this should never happen + carbsTreatment.date = detailedBolusInfo.timestamp + detailedBolusInfo.carbTime * 60 * 1000L + 1000L; // add 1 sec to make them different records carbsTreatment.carbs = detailedBolusInfo.carbs; getAapsLogger().debug(medtronicPump && MedtronicHistoryData.doubleBolusDebug, LTag.DATATREATMENTS, "DoubleBolusDebug: carbTime!=0, creating second treatment. CarbsTreatment={}" + carbsTreatment); @@ -660,7 +285,7 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface getService().createOrUpdateMedtronic(carbsTreatment, false); //log.debug("Adding new Treatment record" + carbsTreatment); } - if (newRecordCreated && detailedBolusInfo.isValid) + if (newRecordCreated && detailedBolusInfo.getBolusType() != DetailedBolusInfo.BolusType.PRIMING) nsUpload.uploadTreatmentRecord(detailedBolusInfo); if (!allowUpdate && !creatOrUpdateResult.getSuccess()) { @@ -677,76 +302,6 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface } return newRecordCreated; + */ } - - @Override - public long oldestDataAvailable() { - long oldestTime = System.currentTimeMillis(); - synchronized (tempBasals) { - if (tempBasals.size() > 0) - oldestTime = Math.min(oldestTime, tempBasals.get(0).date); - } - synchronized (extendedBoluses) { - if (extendedBoluses.size() > 0) - oldestTime = Math.min(oldestTime, extendedBoluses.get(0).date); - } - synchronized (treatments) { - if (treatments.size() > 0) - oldestTime = Math.min(oldestTime, treatments.get(treatments.size() - 1).date); - } - oldestTime -= 15 * 60 * 1000L; // allow 15 min before - return oldestTime; - } - - @Override - @Nullable - public ProfileSwitch getProfileSwitchFromHistory(long time) { - synchronized (profiles) { - return (ProfileSwitch) profiles.getValueToTime(time); - } - } - - @Override - public ProfileIntervals getProfileSwitchesFromHistory() { - synchronized (profiles) { - return new ProfileIntervals<>(profiles); - } - } - - @Override - public void addToHistoryProfileSwitch(ProfileSwitch profileSwitch) { - //log.debug("Adding new TemporaryBasal record" + profileSwitch.log()); - rxBus.send(new EventDismissNotification(Notification.PROFILE_SWITCH_MISSING)); - databaseHelper.createOrUpdate(profileSwitch); - nsUpload.uploadProfileSwitch(profileSwitch, profileSwitch.date); - } - - @Override - public void doProfileSwitch(@NonNull final ProfileStore profileStore, @NonNull final String profileName, final int duration, final int percentage, final int timeShift, final long date) { - ProfileSwitch profileSwitch = profileFunction.prepareProfileSwitch(profileStore, profileName, duration, percentage, timeShift, date); - addToHistoryProfileSwitch(profileSwitch); - if (percentage == 90 && duration == 10) - sp.putBoolean(R.string.key_objectiveuseprofileswitch, true); - } - - @Override - public void doProfileSwitch(final int duration, final int percentage, final int timeShift) { - ProfileSwitch profileSwitch = getProfileSwitchFromHistory(System.currentTimeMillis()); - if (profileSwitch != null) { - profileSwitch = new ProfileSwitch(getInjector()); - profileSwitch.date = System.currentTimeMillis(); - profileSwitch.source = Source.USER; - profileSwitch.profileName = profileFunction.getProfileName(System.currentTimeMillis(), false, false); - profileSwitch.profileJson = profileFunction.getProfile().getData().toString(); - profileSwitch.profilePlugin = activePlugin.getActiveProfileInterface().getClass().getName(); - profileSwitch.durationInMinutes = duration; - profileSwitch.isCPP = percentage != 100 || timeShift != 0; - profileSwitch.timeshift = timeShift; - profileSwitch.percentage = percentage; - addToHistoryProfileSwitch(profileSwitch); - } else { - getAapsLogger().error(LTag.PROFILE, "No profile switch exists"); - } - } - } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsBolusCarbsFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsBolusCarbsFragment.kt new file mode 100644 index 0000000000..2ba8978ea8 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsBolusCarbsFragment.kt @@ -0,0 +1,377 @@ +package info.nightscout.androidaps.plugins.treatments.fragments + +import android.graphics.Paint +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import dagger.android.support.DaggerFragment +import info.nightscout.androidaps.R +import info.nightscout.androidaps.database.AppRepository +import info.nightscout.androidaps.database.entities.Bolus +import info.nightscout.androidaps.database.entities.BolusCalculatorResult +import info.nightscout.androidaps.database.entities.Carbs +import info.nightscout.androidaps.database.entities.UserEntry.Action +import info.nightscout.androidaps.database.entities.UserEntry.Sources +import info.nightscout.androidaps.database.entities.ValueWithUnit +import info.nightscout.androidaps.database.transactions.InvalidateBolusCalculatorResultTransaction +import info.nightscout.androidaps.database.transactions.InvalidateBolusTransaction +import info.nightscout.androidaps.database.transactions.InvalidateCarbsTransaction +import info.nightscout.androidaps.databinding.TreatmentsBolusCarbsFragmentBinding +import info.nightscout.androidaps.databinding.TreatmentsBolusCarbsItemBinding +import info.nightscout.androidaps.dialogs.WizardInfoDialog +import info.nightscout.androidaps.events.EventAutosensCalculationFinished +import info.nightscout.androidaps.events.EventTreatmentChange +import info.nightscout.androidaps.interfaces.ActivePlugin +import info.nightscout.androidaps.interfaces.ProfileFunction +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.nsclient.events.EventNSClientRestart +import info.nightscout.androidaps.plugins.treatments.events.EventTreatmentUpdateGui +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.FabricPrivacy +import info.nightscout.androidaps.utils.T +import info.nightscout.androidaps.utils.alertDialogs.OKDialog +import info.nightscout.androidaps.utils.buildHelper.BuildHelper +import info.nightscout.androidaps.extensions.iobCalc +import info.nightscout.androidaps.extensions.toVisibility +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventNewHistoryData +import info.nightscout.androidaps.utils.resources.ResourceHelper +import info.nightscout.androidaps.utils.rx.AapsSchedulers +import info.nightscout.androidaps.utils.sharedPreferences.SP +import io.reactivex.Completable +import io.reactivex.disposables.CompositeDisposable +import io.reactivex.rxkotlin.plusAssign +import io.reactivex.rxkotlin.subscribeBy +import java.util.concurrent.TimeUnit +import javax.inject.Inject + +class TreatmentsBolusCarbsFragment : DaggerFragment() { + + @Inject lateinit var rxBus: RxBusWrapper + @Inject lateinit var sp: SP + @Inject lateinit var aapsLogger: AAPSLogger + @Inject lateinit var resourceHelper: ResourceHelper + @Inject lateinit var fabricPrivacy: FabricPrivacy + @Inject lateinit var profileFunction: ProfileFunction + @Inject lateinit var dateUtil: DateUtil + @Inject lateinit var buildHelper: BuildHelper + @Inject lateinit var aapsSchedulers: AapsSchedulers + @Inject lateinit var uel: UserEntryLogger + @Inject lateinit var repository: AppRepository + @Inject lateinit var activePlugin: ActivePlugin + + class MealLink( + val bolus: Bolus? = null, + val carbs: Carbs? = null, + val bolusCalculatorResult: BolusCalculatorResult? = null + ) + + private val disposable = CompositeDisposable() + + private val millsToThePast = T.days(30).msecs() + + private var _binding: TreatmentsBolusCarbsFragmentBinding? = null + + // This property is only valid between onCreateView and + // onDestroyView. + private val binding get() = _binding!! + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = + TreatmentsBolusCarbsFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding.recyclerview.setHasFixedSize(true) + binding.recyclerview.layoutManager = LinearLayoutManager(view.context) + + binding.refreshFromNightscout.setOnClickListener { + activity?.let { activity -> + OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.refresheventsfromnightscout) + "?") { + uel.log(Action.TREATMENTS_NS_REFRESH, Sources.Treatments) + disposable += + Completable.fromAction { + repository.deleteAllBolusCalculatorResults() + repository.deleteAllBoluses() + repository.deleteAllCarbs() + } + .subscribeOn(aapsSchedulers.io) + .observeOn(aapsSchedulers.main) + .subscribeBy( + onError = { aapsLogger.error("Error removing entries", it) }, + onComplete = { + rxBus.send(EventTreatmentChange()) + rxBus.send(EventNewHistoryData(0, false)) + } + ) + rxBus.send(EventNSClientRestart()) + } + } + } + binding.deleteFutureTreatments.setOnClickListener { + activity?.let { activity -> + OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.overview_treatment_label), resourceHelper.gs(R.string.deletefuturetreatments) + "?", Runnable { + uel.log(Action.DELETE_FUTURE_TREATMENTS, Sources.Treatments) + repository + .getBolusesDataFromTime(dateUtil.now(), false) + .observeOn(aapsSchedulers.main) + .subscribe { list -> + list.forEach { bolus -> + disposable += repository.runTransactionForResult(InvalidateBolusTransaction(bolus.id)) + .subscribe( + { result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated bolus $it") } }, + { aapsLogger.error(LTag.DATABASE, "Error while invalidating bolus", it) } + ) + } + } + repository + .getCarbsDataFromTime(dateUtil.now(), false) + .observeOn(aapsSchedulers.main) + .subscribe { list -> + list.forEach { carb -> + disposable += repository.runTransactionForResult(InvalidateCarbsTransaction(carb.id)) + .subscribe( + { result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated carbs $it") } }, + { aapsLogger.error(LTag.DATABASE, "Error while invalidating carbs", it) } + ) + } + } + repository + .getBolusCalculatorResultsDataFromTime(dateUtil.now(), false) + .observeOn(aapsSchedulers.main) + .subscribe { list -> + list.forEach { bolusCalc -> + disposable += repository.runTransactionForResult(InvalidateBolusCalculatorResultTransaction(bolusCalc.id)) + .subscribe( + { result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated bolusCalculatorResult $it") } }, + { aapsLogger.error(LTag.DATABASE, "Error while invalidating bolusCalculatorResult", it) } + ) + } + } + binding.deleteFutureTreatments.visibility = View.GONE + }) + } + } + val nsUploadOnly = !sp.getBoolean(R.string.key_ns_receive_insulin, false) || !sp.getBoolean(R.string.key_ns_receive_carbs, false) || !buildHelper.isEngineeringMode() + if (nsUploadOnly) binding.refreshFromNightscout.visibility = View.GONE + binding.showInvalidated.setOnCheckedChangeListener { _, _ -> + rxBus.send(EventTreatmentUpdateGui()) + } + } + + private fun bolusMealLinksWithInvalid(now: Long) = repository + .getBolusesIncludingInvalidFromTime(now - millsToThePast, false) + .map { bolus -> bolus.map { MealLink(bolus = it) } } + + private fun carbsMealLinksWithInvalid(now: Long) = repository + .getCarbsIncludingInvalidFromTime(now - millsToThePast, false) + .map { carb -> carb.map { MealLink(carbs = it) } } + + private fun calcResultMealLinksWithInvalid(now: Long) = repository + .getBolusCalculatorResultsIncludingInvalidFromTime(now - millsToThePast, false) + .map { calc -> calc.map { MealLink(bolusCalculatorResult = it) } } + + private fun bolusMealLinks(now: Long) = repository + .getBolusesDataFromTime(now - millsToThePast, false) + .map { bolus -> bolus.map { MealLink(bolus = it) } } + + private fun carbsMealLinks(now: Long) = repository + .getCarbsDataFromTime(now - millsToThePast, false) + .map { carb -> carb.map { MealLink(carbs = it) } } + + private fun calcResultMealLinks(now: Long) = repository + .getBolusCalculatorResultsDataFromTime(now - millsToThePast, false) + .map { calc -> calc.map { MealLink(bolusCalculatorResult = it) } } + + fun swapAdapter() { + val now = System.currentTimeMillis() + + if (binding.showInvalidated.isChecked) + disposable += carbsMealLinksWithInvalid(now) + .zipWith(bolusMealLinksWithInvalid(now)) { first, second -> first + second } + .zipWith(calcResultMealLinksWithInvalid(now)) { first, second -> first + second } + .map { ml -> + ml.sortedByDescending { + it.carbs?.timestamp ?: it.bolus?.timestamp + ?: it.bolusCalculatorResult?.timestamp + } + } + .observeOn(aapsSchedulers.main) + .subscribe { list -> + binding.recyclerview.swapAdapter(RecyclerViewAdapter(list), true) + binding.deleteFutureTreatments.visibility = list.isNotEmpty().toVisibility() + } + else + disposable += carbsMealLinks(now) + .zipWith(bolusMealLinks(now)) { first, second -> first + second } + .zipWith(calcResultMealLinks(now)) { first, second -> first + second } + .map { ml -> + ml.sortedByDescending { + it.carbs?.timestamp ?: it.bolus?.timestamp + ?: it.bolusCalculatorResult?.timestamp + } + } + .observeOn(aapsSchedulers.main) + .subscribe { list -> + binding.recyclerview.swapAdapter(RecyclerViewAdapter(list), true) + binding.deleteFutureTreatments.visibility = list.isNotEmpty().toVisibility() + } + + } + + @Synchronized + override fun onResume() { + super.onResume() + swapAdapter() + disposable += rxBus + .toObservable(EventTreatmentChange::class.java) + .observeOn(aapsSchedulers.main) + .debounce(1L, TimeUnit.SECONDS) + .subscribe({ swapAdapter() }, fabricPrivacy::logException) + disposable += rxBus + .toObservable(EventTreatmentUpdateGui::class.java) // TODO join with above + .observeOn(aapsSchedulers.io) + .debounce(1L, TimeUnit.SECONDS) + .subscribe({ swapAdapter() }, fabricPrivacy::logException) + disposable += rxBus + .toObservable(EventAutosensCalculationFinished::class.java) + .observeOn(aapsSchedulers.main) + .debounce(1L, TimeUnit.SECONDS) + .subscribe({ swapAdapter() }, fabricPrivacy::logException) + } + + @Synchronized + override fun onPause() { + super.onPause() + disposable.clear() + } + + @Synchronized + override fun onDestroyView() { + super.onDestroyView() + binding.recyclerview.adapter = null // avoid leaks + _binding = null + } + + inner class RecyclerViewAdapter internal constructor(var mealLinks: List) : RecyclerView.Adapter() { + + override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): MealLinkLoadedViewHolder = + MealLinkLoadedViewHolder(LayoutInflater.from(viewGroup.context).inflate(R.layout.treatments_bolus_carbs_item, viewGroup, false)) + + override fun onBindViewHolder(holder: MealLinkLoadedViewHolder, position: Int) { + val profile = profileFunction.getProfile() ?: return + val ml = mealLinks[position] + + // Metadata + holder.binding.metadataLayout.visibility = (ml.bolusCalculatorResult != null && (ml.bolusCalculatorResult.isValid || binding.showInvalidated.isChecked)).toVisibility() + ml.bolusCalculatorResult?.let { bolusCalculatorResult -> + holder.binding.date.text = dateUtil.dateAndTimeString(bolusCalculatorResult.timestamp) + } + + // Bolus + holder.binding.bolusLayout.visibility = (ml.bolus != null && (ml.bolus.isValid || binding.showInvalidated.isChecked)).toVisibility() + ml.bolus?.let { bolus -> + holder.binding.bolusDate.text = dateUtil.timeString(bolus.timestamp) + holder.binding.insulin.text = resourceHelper.gs(R.string.formatinsulinunits, bolus.amount) + holder.binding.bolusNs.visibility = (bolus.interfaceIDs.nightscoutId != null).toVisibility() + holder.binding.bolusPump.visibility = (bolus.interfaceIDs.pumpId != null).toVisibility() + holder.binding.bolusInvalid.visibility = bolus.isValid.not().toVisibility() + val iob = bolus.iobCalc(activePlugin, System.currentTimeMillis(), profile.dia) + holder.binding.iob.text = resourceHelper.gs(R.string.formatinsulinunits, iob.iobContrib) + holder.binding.iobLabel.visibility = (iob.iobContrib != 0.0).toVisibility() + holder.binding.iob.visibility = (iob.iobContrib != 0.0).toVisibility() + if (bolus.timestamp > dateUtil.now()) holder.binding.date.setTextColor(resourceHelper.gc(R.color.colorScheduled)) else holder.binding.date.setTextColor(holder.binding.carbs.currentTextColor) + holder.binding.mealOrCorrection.text = + when (ml.bolus.type) { + Bolus.Type.SMB -> "SMB" + Bolus.Type.NORMAL -> resourceHelper.gs(R.string.mealbolus) + Bolus.Type.PRIMING -> resourceHelper.gs(R.string.prime) + } + } + // Carbs + holder.binding.carbsLayout.visibility = (ml.carbs != null && (ml.carbs.isValid || binding.showInvalidated.isChecked)).toVisibility() + ml.carbs?.let { carbs -> + holder.binding.carbsDate.text = dateUtil.timeString(carbs.timestamp) + holder.binding.carbs.text = resourceHelper.gs(R.string.format_carbs, carbs.amount.toInt()) + holder.binding.carbsDuration.text = if (carbs.duration > 0) resourceHelper.gs(R.string.format_mins, T.msecs(carbs.duration).mins().toInt()) else "" + holder.binding.carbsNs.visibility = (carbs.interfaceIDs.nightscoutId != null).toVisibility() + holder.binding.carbsPump.visibility = (carbs.interfaceIDs.pumpId != null).toVisibility() + holder.binding.carbsInvalid.visibility = carbs.isValid.not().toVisibility() + } + + holder.binding.bolusRemove.visibility = (ml.bolus?.isValid == true).toVisibility() + holder.binding.carbsRemove.visibility = (ml.carbs?.isValid == true).toVisibility() + holder.binding.bolusRemove.tag = ml + holder.binding.carbsRemove.tag = ml + holder.binding.calculation.tag = ml + } + + override fun getItemCount(): Int { + return mealLinks.size + } + + inner class MealLinkLoadedViewHolder internal constructor(view: View) : RecyclerView.ViewHolder(view) { + + val binding = TreatmentsBolusCarbsItemBinding.bind(view) + + init { + binding.calculation.setOnClickListener { + val mealLinkLoaded = it.tag as MealLink + mealLinkLoaded.bolusCalculatorResult?.let { bolusCalculatorResult -> + WizardInfoDialog().also { wizardDialog -> + wizardDialog.setData(bolusCalculatorResult) + wizardDialog.show(childFragmentManager, "WizardInfoDialog") + } + } + } + binding.calculation.paintFlags = binding.calculation.paintFlags or Paint.UNDERLINE_TEXT_FLAG + binding.bolusRemove.setOnClickListener { ml -> + val bolus = (ml.tag as MealLink?)?.bolus ?: return@setOnClickListener + activity?.let { activity -> + val text = resourceHelper.gs(R.string.configbuilder_insulin) + ": " + + resourceHelper.gs(R.string.formatinsulinunits, bolus.amount) + "\n" + + resourceHelper.gs(R.string.date) + ": " + dateUtil.dateAndTimeString(bolus.timestamp) + OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.removerecord), text, Runnable { + uel.log( + Action.BOLUS_REMOVED, Sources.Treatments, + ValueWithUnit.Timestamp(bolus.timestamp), + ValueWithUnit.Insulin(bolus.amount) + //ValueWithUnit.Gram(mealLinkLoaded.carbs.toInt()) + ) + disposable += repository.runTransactionForResult(InvalidateBolusTransaction(bolus.id)) + .subscribe( + { result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated bolus $it") } }, + { aapsLogger.error(LTag.DATABASE, "Error while invalidating bolus", it) } + ) + }) + } + } + binding.bolusRemove.paintFlags = binding.bolusRemove.paintFlags or Paint.UNDERLINE_TEXT_FLAG + binding.carbsRemove.setOnClickListener { ml -> + val carb = (ml.tag as MealLink?)?.carbs ?: return@setOnClickListener + activity?.let { activity -> + val text = resourceHelper.gs(R.string.carbs) + ": " + + resourceHelper.gs(R.string.carbs) + ": " + resourceHelper.gs(R.string.format_carbs, carb.amount.toInt()) + "\n" + + resourceHelper.gs(R.string.date) + ": " + dateUtil.dateAndTimeString(carb.timestamp) + OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.removerecord), text, Runnable { + uel.log( + Action.CARBS_REMOVED, Sources.Treatments, + ValueWithUnit.Timestamp(carb.timestamp), + ValueWithUnit.Gram(carb.amount.toInt())) + disposable += repository.runTransactionForResult(InvalidateCarbsTransaction(carb.id)) + .subscribe( + { result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated carbs $it") } }, + { aapsLogger.error(LTag.DATABASE, "Error while invalidating carbs", it) } + ) + }) + } + } + binding.carbsRemove.paintFlags = binding.carbsRemove.paintFlags or Paint.UNDERLINE_TEXT_FLAG + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsBolusFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsBolusFragment.kt deleted file mode 100644 index 5b53f9a757..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsBolusFragment.kt +++ /dev/null @@ -1,210 +0,0 @@ -package info.nightscout.androidaps.plugins.treatments.fragments - -import android.graphics.Paint -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView -import dagger.android.support.DaggerFragment -import info.nightscout.androidaps.R -import info.nightscout.androidaps.database.entities.UserEntry.* -import info.nightscout.androidaps.databinding.TreatmentsBolusFragmentBinding -import info.nightscout.androidaps.databinding.TreatmentsBolusItemBinding -import info.nightscout.androidaps.db.Source -import info.nightscout.androidaps.db.Treatment -import info.nightscout.androidaps.dialogs.WizardInfoDialog -import info.nightscout.androidaps.events.EventTreatmentChange -import info.nightscout.androidaps.interfaces.ProfileFunction -import info.nightscout.androidaps.logging.UserEntryLogger -import info.nightscout.androidaps.plugins.bus.RxBusWrapper -import info.nightscout.androidaps.plugins.general.nsclient.NSUpload -import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart -import info.nightscout.androidaps.events.EventAutosensCalculationFinished -import info.nightscout.androidaps.interfaces.UploadQueueInterface -import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin -import info.nightscout.androidaps.plugins.treatments.fragments.TreatmentsBolusFragment.RecyclerViewAdapter.TreatmentsViewHolder -import info.nightscout.androidaps.utils.DateUtil -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.resources.ResourceHelper -import info.nightscout.androidaps.utils.rx.AapsSchedulers -import info.nightscout.androidaps.utils.sharedPreferences.SP -import io.reactivex.disposables.CompositeDisposable -import javax.inject.Inject - -class TreatmentsBolusFragment : DaggerFragment() { - - private val disposable = CompositeDisposable() - - @Inject lateinit var rxBus: RxBusWrapper - @Inject lateinit var sp: SP - @Inject lateinit var resourceHelper: ResourceHelper - @Inject lateinit var fabricPrivacy: FabricPrivacy - @Inject lateinit var treatmentsPlugin: TreatmentsPlugin - @Inject lateinit var profileFunction: ProfileFunction - @Inject lateinit var nsUpload: NSUpload - @Inject lateinit var uploadQueue: UploadQueueInterface - @Inject lateinit var dateUtil: DateUtil - @Inject lateinit var buildHelper: BuildHelper - @Inject lateinit var aapsSchedulers: AapsSchedulers - @Inject lateinit var uel: UserEntryLogger - - private var _binding: TreatmentsBolusFragmentBinding? = null - - // This property is only valid between onCreateView and - // onDestroyView. - private val binding get() = _binding!! - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = - TreatmentsBolusFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - binding.recyclerview.setHasFixedSize(true) - binding.recyclerview.layoutManager = LinearLayoutManager(view.context) - binding.recyclerview.adapter = RecyclerViewAdapter(treatmentsPlugin.treatmentsFromHistory) - binding.refreshFromNightscout.setOnClickListener { - activity?.let { activity -> - OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.refresheventsfromnightscout) + "?") { - uel.log(Action.TREATMENTS_NS_REFRESH) - treatmentsPlugin.service.resetTreatments() - rxBus.send(EventNSClientRestart()) - } - } - } - binding.deleteFutureTreatments.setOnClickListener { - activity?.let { activity -> - OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.overview_treatment_label), resourceHelper.gs(R.string.deletefuturetreatments) + "?", Runnable { - uel.log(Action.DELETE_FUTURE_TREATMENTS) - val futureTreatments = treatmentsPlugin.service.getTreatmentDataFromTime(DateUtil.now() + 1000, true) - for (treatment in futureTreatments) { - if (NSUpload.isIdValid(treatment._id)) - nsUpload.removeCareportalEntryFromNS(treatment._id) - else - uploadQueue.removeByMongoId("dbAdd", treatment._id) - treatmentsPlugin.service.delete(treatment) - } - updateGui() - }) - } - } - val nsUploadOnly = sp.getBoolean(R.string.key_ns_upload_only, true) || !buildHelper.isEngineeringMode() - if (nsUploadOnly) binding.refreshFromNightscout.visibility = View.GONE - } - - @Synchronized - override fun onResume() { - super.onResume() - disposable.add(rxBus - .toObservable(EventTreatmentChange::class.java) - .observeOn(aapsSchedulers.main) - .subscribe({ updateGui() }, fabricPrivacy::logException) - ) - disposable.add(rxBus - .toObservable(EventAutosensCalculationFinished::class.java) - .observeOn(aapsSchedulers.main) - .subscribe({ updateGui() }, fabricPrivacy::logException) - ) - updateGui() - } - - @Synchronized - override fun onPause() { - super.onPause() - disposable.clear() - } - - @Synchronized - override fun onDestroyView() { - super.onDestroyView() - _binding = null - } - - inner class RecyclerViewAdapter internal constructor(var treatments: List) : RecyclerView.Adapter() { - - override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): TreatmentsViewHolder { - val v = LayoutInflater.from(viewGroup.context).inflate(R.layout.treatments_bolus_item, viewGroup, false) - return TreatmentsViewHolder(v) - } - - override fun onBindViewHolder(holder: TreatmentsViewHolder, position: Int) { - val profile = profileFunction.getProfile() ?: return - val t = treatments[position] - holder.binding.date.text = dateUtil.dateAndTimeString(t.date) - holder.binding.insulin.text = resourceHelper.gs(R.string.formatinsulinunits, t.insulin) - holder.binding.carbs.text = resourceHelper.gs(R.string.format_carbs, t.carbs.toInt()) - val iob = t.iobCalc(System.currentTimeMillis(), profile.dia) - holder.binding.iob.text = resourceHelper.gs(R.string.formatinsulinunits, iob.iobContrib) - holder.binding.mealOrCorrection.text = if (t.isSMB) "SMB" else if (t.mealBolus) resourceHelper.gs(R.string.mealbolus) else resourceHelper.gs(R.string.correctionbous) - holder.binding.pump.visibility = if (t.source == Source.PUMP) View.VISIBLE else View.GONE - holder.binding.ns.visibility = if (NSUpload.isIdValid(t._id)) View.VISIBLE else View.GONE - holder.binding.invalid.visibility = if (t.isValid) View.GONE else View.VISIBLE - if (iob.iobContrib != 0.0) holder.binding.iob.setTextColor(resourceHelper.gc(R.color.colorActive)) else holder.binding.iob.setTextColor(holder.binding.carbs.currentTextColor) - if (t.date > DateUtil.now()) holder.binding.date.setTextColor(resourceHelper.gc(R.color.colorScheduled)) else holder.binding.date.setTextColor(holder.binding.carbs.currentTextColor) - holder.binding.remove.tag = t - holder.binding.calculation.tag = t - holder.binding.calculation.visibility = if (t.getBoluscalc() == null) View.INVISIBLE else View.VISIBLE - } - - override fun getItemCount(): Int { - return treatments.size - } - - inner class TreatmentsViewHolder internal constructor(view: View) : RecyclerView.ViewHolder(view) { - - val binding = TreatmentsBolusItemBinding.bind(view) - - init { - binding.calculation.setOnClickListener { - val treatment = it.tag as Treatment - if (treatment.getBoluscalc() != null) { - val wizardDialog = WizardInfoDialog() - wizardDialog.setData(treatment.getBoluscalc()!!) - wizardDialog.show(childFragmentManager, "WizardInfoDialog") - } - } - binding.calculation.paintFlags = binding.calculation.paintFlags or Paint.UNDERLINE_TEXT_FLAG - binding.remove.setOnClickListener { - val treatment = it.tag as Treatment? ?: return@setOnClickListener - activity?.let { activity -> - val text = resourceHelper.gs(R.string.configbuilder_insulin) + ": " + - resourceHelper.gs(R.string.formatinsulinunits, treatment.insulin) + "\n" + - resourceHelper.gs(R.string.carbs) + ": " + resourceHelper.gs(R.string.format_carbs, treatment.carbs.toInt()) + "\n" + - resourceHelper.gs(R.string.date) + ": " + dateUtil.dateAndTimeString(treatment.date) - OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.removerecord), text, Runnable { - uel.log(Action.TREATMENT_REMOVED, ValueWithUnit(treatment.date, Units.Timestamp), ValueWithUnit(treatment.insulin, Units.U, treatment.insulin != 0.0), ValueWithUnit(treatment.carbs.toInt(), Units.G, treatment.carbs != 0.0)) - if (treatment.source == Source.PUMP) { - treatment.isValid = false - treatmentsPlugin.service.update(treatment) - } else { - if (NSUpload.isIdValid(treatment._id)) - nsUpload.removeCareportalEntryFromNS(treatment._id) - else - uploadQueue.removeByMongoId("dbAdd", treatment._id) - treatmentsPlugin.service.delete(treatment) - } - updateGui() - }) - } - } - binding.remove.paintFlags = binding.remove.paintFlags or Paint.UNDERLINE_TEXT_FLAG - } - } - } - - private fun updateGui() { - if (_binding == null) return - binding.recyclerview.swapAdapter(RecyclerViewAdapter(treatmentsPlugin.treatmentsFromHistory), false) - if (treatmentsPlugin.lastCalculationTreatments != null) { - binding.iobTotal.text = resourceHelper.gs(R.string.formatinsulinunits, treatmentsPlugin.lastCalculationTreatments.iob) - binding.iobActivityTotal.text = resourceHelper.gs(R.string.formatinsulinunits, treatmentsPlugin.lastCalculationTreatments.activity) - } - if (treatmentsPlugin.service.getTreatmentDataFromTime(DateUtil.now() + 1000, true).isNotEmpty()) - binding.deleteFutureTreatments.visibility = View.VISIBLE - else - binding.deleteFutureTreatments.visibility = View.GONE - } -} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsCareportalFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsCareportalFragment.kt index 666c38eb9c..ce75d4dc3c 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsCareportalFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsCareportalFragment.kt @@ -10,19 +10,19 @@ import androidx.recyclerview.widget.RecyclerView import dagger.android.support.DaggerFragment import info.nightscout.androidaps.R import info.nightscout.androidaps.database.AppRepository +import info.nightscout.androidaps.database.entities.ValueWithUnit import info.nightscout.androidaps.database.entities.TherapyEvent +import info.nightscout.androidaps.database.entities.UserEntry.Action +import info.nightscout.androidaps.database.entities.UserEntry.Sources import info.nightscout.androidaps.database.transactions.InvalidateAAPSStartedTherapyEventTransaction import info.nightscout.androidaps.database.transactions.InvalidateTherapyEventTransaction -import info.nightscout.androidaps.database.entities.UserEntry.* import info.nightscout.androidaps.databinding.TreatmentsCareportalFragmentBinding import info.nightscout.androidaps.databinding.TreatmentsCareportalItemBinding import info.nightscout.androidaps.events.EventTherapyEventChange -import info.nightscout.androidaps.interfaces.UploadQueueInterface 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.nsclient.NSUpload import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart import info.nightscout.androidaps.plugins.treatments.events.EventTreatmentUpdateGui import info.nightscout.androidaps.plugins.treatments.fragments.TreatmentsCareportalFragment.RecyclerViewAdapter.TherapyEventsViewHolder @@ -32,7 +32,7 @@ import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.Translator import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.buildHelper.BuildHelper -import info.nightscout.androidaps.utils.extensions.toVisibility +import info.nightscout.androidaps.extensions.toVisibility import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.utils.sharedPreferences.SP @@ -51,8 +51,6 @@ class TreatmentsCareportalFragment : DaggerFragment() { @Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var fabricPrivacy: FabricPrivacy @Inject lateinit var translator: Translator - @Inject lateinit var nsUpload: NSUpload - @Inject lateinit var uploadQueue: UploadQueueInterface @Inject lateinit var dateUtil: DateUtil @Inject lateinit var buildHelper: BuildHelper @Inject lateinit var aapsSchedulers: AapsSchedulers @@ -79,7 +77,7 @@ class TreatmentsCareportalFragment : DaggerFragment() { binding.refreshFromNightscout.setOnClickListener { activity?.let { activity -> OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.careportal), resourceHelper.gs(R.string.refresheventsfromnightscout) + " ?", Runnable { - uel.log(Action.CAREPORTAL_NS_REFRESH) + uel.log(Action.CAREPORTAL_NS_REFRESH, Sources.Treatments) disposable += Completable.fromAction { repository.deleteAllTherapyEventsEntries() } .subscribeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.main) @@ -94,24 +92,17 @@ class TreatmentsCareportalFragment : DaggerFragment() { binding.removeAndroidapsStartedEvents.setOnClickListener { activity?.let { activity -> OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.careportal), resourceHelper.gs(R.string.careportal_removestartedevents), Runnable { - uel.log(Action.RESTART_EVENTS_REMOVED) - // val events = databaseHelper.getCareportalEvents(false) - repository.runTransactionForResult(InvalidateAAPSStartedTherapyEventTransaction()) - .subscribe({ result -> - result.invalidated.forEach { event -> - if (NSUpload.isIdValid(event.interfaceIDs.nightscoutId)) - nsUpload.removeCareportalEntryFromNS(event.interfaceIDs.nightscoutId) - else - uploadQueue.removeByMongoId("dbAdd", event.timestamp.toString()) - } - }, { - aapsLogger.error(LTag.BGSOURCE, "Error while invalidating therapy event", it) - }) + uel.log(Action.RESTART_EVENTS_REMOVED, Sources.Treatments) + repository.runTransactionForResult(InvalidateAAPSStartedTherapyEventTransaction(resourceHelper.gs(R.string.androidaps_start))) + .subscribe( + { result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated therapy event $it") } }, + { aapsLogger.error(LTag.DATABASE, "Error while invalidating therapy event", it) } + ) }, null) } } - val nsUploadOnly = sp.getBoolean(R.string.key_ns_upload_only, true) || !buildHelper.isEngineeringMode() + val nsUploadOnly = !sp.getBoolean(R.string.key_ns_receive_therapy_events, false) || !buildHelper.isEngineeringMode() if (nsUploadOnly) binding.refreshFromNightscout.visibility = View.GONE binding.showInvalidated.setOnCheckedChangeListener { _, _ -> rxBus.send(EventTreatmentUpdateGui()) @@ -120,28 +111,28 @@ class TreatmentsCareportalFragment : DaggerFragment() { fun swapAdapter() { val now = System.currentTimeMillis() - if (binding.showInvalidated.isChecked) - repository - .getTherapyEventDataIncludingInvalidFromTime(now - millsToThePast, false) - .observeOn(aapsSchedulers.main) - .subscribe { list -> binding.recyclerview.swapAdapter(RecyclerViewAdapter(list), true) } - else - repository - .getTherapyEventDataFromTime(now - millsToThePast, false) - .observeOn(aapsSchedulers.main) - .subscribe { list -> binding.recyclerview.swapAdapter(RecyclerViewAdapter(list), true) } + disposable += + if (binding.showInvalidated.isChecked) + repository + .getTherapyEventDataIncludingInvalidFromTime(now - millsToThePast, false) + .observeOn(aapsSchedulers.main) + .subscribe { list -> binding.recyclerview.swapAdapter(RecyclerViewAdapter(list), true) } + else + repository + .getTherapyEventDataFromTime(now - millsToThePast, false) + .observeOn(aapsSchedulers.main) + .subscribe { list -> binding.recyclerview.swapAdapter(RecyclerViewAdapter(list), true) } } @Synchronized override fun onResume() { super.onResume() swapAdapter() - disposable.add(rxBus + disposable += rxBus .toObservable(EventTherapyEventChange::class.java) .observeOn(aapsSchedulers.main) .debounce(1L, TimeUnit.SECONDS) .subscribe({ swapAdapter() }, fabricPrivacy::logException) - ) disposable += rxBus .toObservable(EventTreatmentUpdateGui::class.java) // TODO join with above .observeOn(aapsSchedulers.io) @@ -174,9 +165,9 @@ class TreatmentsCareportalFragment : DaggerFragment() { holder.binding.ns.visibility = (therapyEvent.interfaceIDs.nightscoutId != null).toVisibility() holder.binding.invalid.visibility = therapyEvent.isValid.not().toVisibility() holder.binding.date.text = dateUtil.dateAndTimeString(therapyEvent.timestamp) - holder.binding.duration.text = if (therapyEvent.duration == 0L) "" else DateUtil.niceTimeScalar(therapyEvent.duration, resourceHelper) + holder.binding.duration.text = if (therapyEvent.duration == 0L) "" else dateUtil.niceTimeScalar(therapyEvent.duration, resourceHelper) holder.binding.note.text = therapyEvent.note - holder.binding.type.text = translator.translate(therapyEvent.type.text) + holder.binding.type.text = translator.translate(therapyEvent.type) holder.binding.remove.tag = therapyEvent } @@ -192,19 +183,19 @@ class TreatmentsCareportalFragment : DaggerFragment() { binding.remove.setOnClickListener { v: View -> val therapyEvent = v.tag as TherapyEvent activity?.let { activity -> - val text = resourceHelper.gs(R.string.eventtype) + ": " + translator.translate(therapyEvent.type.text) + "\n" + - resourceHelper.gs(R.string.notes_label) + ": " + (therapyEvent.note ?: "") + "\n" + + val text = resourceHelper.gs(R.string.eventtype) + ": " + translator.translate(therapyEvent.type) + "\n" + + resourceHelper.gs(R.string.notes_label) + ": " + (therapyEvent.note + ?: "") + "\n" + resourceHelper.gs(R.string.date) + ": " + dateUtil.dateAndTimeString(therapyEvent.timestamp) OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.removerecord), text, Runnable { - uel.log(Action.CAREPORTAL_REMOVED, therapyEvent.note , ValueWithUnit(therapyEvent.timestamp, Units.Timestamp), ValueWithUnit(therapyEvent.type.text, Units.TherapyEvent)) + uel.log(Action.CAREPORTAL_REMOVED, Sources.Treatments, therapyEvent.note , + ValueWithUnit.Timestamp(therapyEvent.timestamp), + ValueWithUnit.TherapyEventType(therapyEvent.type)) disposable += repository.runTransactionForResult(InvalidateTherapyEventTransaction(therapyEvent.id)) - .subscribe({ - val id = therapyEvent.interfaceIDs.nightscoutId - if (NSUpload.isIdValid(id)) nsUpload.removeCareportalEntryFromNS(id) - else uploadQueue.removeByMongoId("dbAdd", therapyEvent.timestamp.toString()) - }, { - aapsLogger.error(LTag.BGSOURCE, "Error while invalidating therapy event", it) - }) + .subscribe( + { result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated therapy event $it") } }, + { aapsLogger.error(LTag.DATABASE, "Error while invalidating therapy event", it) } + ) }, null) } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsExtendedBolusesFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsExtendedBolusesFragment.kt index bbecd9b5bd..5e52467f5e 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsExtendedBolusesFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsExtendedBolusesFragment.kt @@ -11,45 +11,52 @@ import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import dagger.android.support.DaggerFragment import info.nightscout.androidaps.R -import info.nightscout.androidaps.data.Intervals -import info.nightscout.androidaps.database.entities.UserEntry.* +import info.nightscout.androidaps.database.AppRepository +import info.nightscout.androidaps.database.entities.ExtendedBolus +import info.nightscout.androidaps.database.entities.UserEntry.Action +import info.nightscout.androidaps.database.entities.UserEntry.Sources +import info.nightscout.androidaps.database.interfaces.end +import info.nightscout.androidaps.database.transactions.InvalidateExtendedBolusTransaction import info.nightscout.androidaps.databinding.TreatmentsExtendedbolusFragmentBinding import info.nightscout.androidaps.databinding.TreatmentsExtendedbolusItemBinding -import info.nightscout.androidaps.db.ExtendedBolus -import info.nightscout.androidaps.db.Source -import info.nightscout.androidaps.events.EventAutosensCalculationFinished import info.nightscout.androidaps.events.EventExtendedBolusChange -import info.nightscout.androidaps.interfaces.ActivePluginProvider -import info.nightscout.androidaps.interfaces.DatabaseHelperInterface +import info.nightscout.androidaps.extensions.iobCalc +import info.nightscout.androidaps.extensions.isInProgress +import info.nightscout.androidaps.extensions.toVisibility +import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.ProfileFunction -import info.nightscout.androidaps.interfaces.UploadQueueInterface +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.nsclient.NSUpload import info.nightscout.androidaps.plugins.treatments.fragments.TreatmentsExtendedBolusesFragment.RecyclerViewAdapter.ExtendedBolusesViewHolder import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.FabricPrivacy +import info.nightscout.androidaps.utils.T 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 +import io.reactivex.rxkotlin.plusAssign +import java.util.concurrent.TimeUnit import javax.inject.Inject class TreatmentsExtendedBolusesFragment : DaggerFragment() { private val disposable = CompositeDisposable() - @Inject lateinit var activePlugin: ActivePluginProvider + private val millsToThePast = T.days(30).msecs() + + @Inject lateinit var activePlugin: ActivePlugin @Inject lateinit var rxBus: RxBusWrapper @Inject lateinit var resourceHelper: ResourceHelper + @Inject lateinit var aapsLogger: AAPSLogger @Inject lateinit var fabricPrivacy: FabricPrivacy - @Inject lateinit var nsUpload: NSUpload - @Inject lateinit var uploadQueue: UploadQueueInterface @Inject lateinit var profileFunction: ProfileFunction @Inject lateinit var dateUtil: DateUtil @Inject lateinit var aapsSchedulers: AapsSchedulers - @Inject lateinit var databaseHelper: DatabaseHelperInterface @Inject lateinit var uel: UserEntryLogger + @Inject lateinit var repository: AppRepository private var _binding: TreatmentsExtendedbolusFragmentBinding? = null @@ -58,18 +65,54 @@ class TreatmentsExtendedBolusesFragment : DaggerFragment() { private val binding get() = _binding!! override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle?): View { - _binding = TreatmentsExtendedbolusFragmentBinding.inflate(inflater, container, false) - return binding.root - } + savedInstanceState: Bundle?): View = + TreatmentsExtendedbolusFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root override fun onViewCreated(view: View, savedInstanceState: Bundle?) { binding.recyclerview.setHasFixedSize(true) binding.recyclerview.layoutManager = LinearLayoutManager(view.context) - binding.recyclerview.adapter = RecyclerViewAdapter(activePlugin.activeTreatments.extendedBolusesFromHistory) } - private inner class RecyclerViewAdapter(private var extendedBolusList: Intervals) : RecyclerView.Adapter() { + fun swapAdapter() { + val now = System.currentTimeMillis() + if (binding.showInvalidated.isChecked) + repository + .getExtendedBolusDataIncludingInvalidFromTime(now - millsToThePast, false) + .observeOn(aapsSchedulers.main) + .subscribe { list -> binding.recyclerview.swapAdapter(RecyclerViewAdapter(list), true) } + else + repository + .getExtendedBolusDataFromTime(now - millsToThePast, false) + .observeOn(aapsSchedulers.main) + .subscribe { list -> binding.recyclerview.swapAdapter(RecyclerViewAdapter(list), true) } + } + + @Synchronized + override fun onResume() { + super.onResume() + swapAdapter() + + disposable += rxBus + .toObservable(EventExtendedBolusChange::class.java) + .observeOn(aapsSchedulers.io) + .debounce(1L, TimeUnit.SECONDS) + .subscribe({ swapAdapter() }, fabricPrivacy::logException) + } + + @Synchronized + override fun onPause() { + super.onPause() + disposable.clear() + } + + @Synchronized + override fun onDestroyView() { + super.onDestroyView() + binding.recyclerview.adapter = null // avoid leaks + _binding = null + } + + private inner class RecyclerViewAdapter(private var extendedBolusList: List) : RecyclerView.Adapter() { override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ExtendedBolusesViewHolder { val v = LayoutInflater.from(viewGroup.context).inflate(R.layout.treatments_extendedbolus_item, viewGroup, false) @@ -77,38 +120,29 @@ class TreatmentsExtendedBolusesFragment : DaggerFragment() { } override fun onBindViewHolder(holder: ExtendedBolusesViewHolder, position: Int) { - val extendedBolus = extendedBolusList.getReversed(position) - holder.binding.ph.visibility = if (extendedBolus.source == Source.PUMP) View.VISIBLE else View.GONE - holder.binding.ns.visibility = if (NSUpload.isIdValid(extendedBolus._id)) View.VISIBLE else View.GONE - if (extendedBolus.isEndingEvent) { - holder.binding.date.text = dateUtil.dateAndTimeString(extendedBolus.date) - holder.binding.duration.text = resourceHelper.gs(R.string.cancel) - holder.binding.insulin.text = "" - holder.binding.realDuration.text = "" - holder.binding.iob.text = "" - holder.binding.insulinSoFar.text = "" - holder.binding.ratio.text = "" + val extendedBolus = extendedBolusList[position] + holder.binding.ns.visibility = (extendedBolus.interfaceIDs.nightscoutId != null).toVisibility() + holder.binding.ph.visibility = (extendedBolus.interfaceIDs.pumpId != null).toVisibility() + holder.binding.invalid.visibility = extendedBolus.isValid.not().toVisibility() + @SuppressLint("SetTextI18n") + if (extendedBolus.isInProgress(dateUtil)) { + holder.binding.date.text = dateUtil.dateAndTimeString(extendedBolus.timestamp) + holder.binding.date.setTextColor(resourceHelper.gc(R.color.colorActive)) } else { - @SuppressLint("SetTextI18n") - if (extendedBolus.isInProgress) holder.binding.date.text = dateUtil.dateAndTimeString(extendedBolus.date) - else holder.binding.date.text = dateUtil.dateAndTimeString(extendedBolus.date) + " - " + dateUtil.timeString(extendedBolus.end()) - val profile = profileFunction.getProfile(extendedBolus.date) - holder.binding.duration.text = resourceHelper.gs(R.string.format_mins, extendedBolus.durationInMinutes) - holder.binding.insulin.text = resourceHelper.gs(R.string.formatinsulinunits, extendedBolus.insulin) - holder.binding.realDuration.text = resourceHelper.gs(R.string.format_mins, extendedBolus.realDuration) - val iob = extendedBolus.iobCalc(System.currentTimeMillis(), profile) - holder.binding.iob.text = resourceHelper.gs(R.string.formatinsulinunits, iob.iob) - holder.binding.insulinSoFar.text = resourceHelper.gs(R.string.formatinsulinunits, extendedBolus.insulinSoFar()) - holder.binding.ratio.text = resourceHelper.gs(R.string.pump_basebasalrate, extendedBolus.absoluteRate()) - if (extendedBolus.isInProgress) holder.binding.date.setTextColor(resourceHelper.gc(R.color.colorActive)) else holder.binding.date.setTextColor(holder.binding.insulin.currentTextColor) - if (iob.iob != 0.0) holder.binding.iob.setTextColor(resourceHelper.gc(R.color.colorActive)) else holder.binding.iob.setTextColor(holder.binding.insulin.currentTextColor) + holder.binding.date.text = dateUtil.dateAndTimeString(extendedBolus.timestamp) + " - " + dateUtil.timeString(extendedBolus.end) + holder.binding.date.setTextColor(holder.binding.insulin.currentTextColor) } + val profile = profileFunction.getProfile(extendedBolus.timestamp) ?: return + holder.binding.duration.text = resourceHelper.gs(R.string.format_mins, T.msecs(extendedBolus.duration).mins()) + holder.binding.insulin.text = resourceHelper.gs(R.string.formatinsulinunits, extendedBolus.amount) + val iob = extendedBolus.iobCalc(System.currentTimeMillis(), profile, activePlugin.activeInsulin) + holder.binding.iob.text = resourceHelper.gs(R.string.formatinsulinunits, iob.iob) + holder.binding.ratio.text = resourceHelper.gs(R.string.pump_basebasalrate, extendedBolus.rate) + if (iob.iob != 0.0) holder.binding.iob.setTextColor(resourceHelper.gc(R.color.colorActive)) else holder.binding.iob.setTextColor(holder.binding.insulin.currentTextColor) holder.binding.remove.tag = extendedBolus } - override fun getItemCount(): Int { - return extendedBolusList.size() - } + override fun getItemCount(): Int = extendedBolusList.size inner class ExtendedBolusesViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { @@ -117,17 +151,17 @@ class TreatmentsExtendedBolusesFragment : DaggerFragment() { init { binding.remove.setOnClickListener { v: View -> val extendedBolus = v.tag as ExtendedBolus - context?.let { - OKDialog.showConfirmation(it, resourceHelper.gs(R.string.removerecord), + context?.let { context -> + OKDialog.showConfirmation(context, resourceHelper.gs(R.string.removerecord), """ ${resourceHelper.gs(R.string.extended_bolus)} - ${resourceHelper.gs(R.string.date)}: ${dateUtil.dateAndTimeString(extendedBolus.date)} + ${resourceHelper.gs(R.string.date)}: ${dateUtil.dateAndTimeString(extendedBolus.timestamp)} """.trimIndent(), { _: DialogInterface, _: Int -> - uel.log(Action.EXTENDED_BOLUS_REMOVED) - val id = extendedBolus._id - if (NSUpload.isIdValid(id)) nsUpload.removeCareportalEntryFromNS(id) - else uploadQueue.removeByMongoId("dbAdd", id) - databaseHelper.delete(extendedBolus) + uel.log(Action.EXTENDED_BOLUS_REMOVED, Sources.Treatments) + disposable += repository.runTransactionForResult(InvalidateExtendedBolusTransaction(extendedBolus.id)) + .subscribe( + { aapsLogger.debug(LTag.DATABASE, "Removed extended bolus $extendedBolus") }, + { aapsLogger.error(LTag.DATABASE, "Error while invalidating extended bolus", it) }) }, null) } } @@ -136,35 +170,4 @@ class TreatmentsExtendedBolusesFragment : DaggerFragment() { } } - - @Synchronized override fun onResume() { - super.onResume() - disposable.add(rxBus - .toObservable(EventExtendedBolusChange::class.java) - .observeOn(aapsSchedulers.main) - .subscribe({ updateGui() }, fabricPrivacy::logException) - ) - disposable.add(rxBus - .toObservable(EventAutosensCalculationFinished::class.java) - .observeOn(aapsSchedulers.main) - .subscribe({ updateGui() }, fabricPrivacy::logException) - ) - updateGui() - } - - @Synchronized override fun onPause() { - super.onPause() - disposable.clear() - } - - @Synchronized - override fun onDestroyView() { - super.onDestroyView() - _binding = null - } - - private fun updateGui() { - if (_binding == null) return - binding.recyclerview.swapAdapter(RecyclerViewAdapter(activePlugin.activeTreatments.extendedBolusesFromHistory), false) - } } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsProfileSwitchFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsProfileSwitchFragment.kt index c7b667b2fd..796c811f0f 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsProfileSwitchFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsProfileSwitchFragment.kt @@ -9,53 +9,62 @@ import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import dagger.android.support.DaggerFragment import info.nightscout.androidaps.R -import info.nightscout.androidaps.database.entities.UserEntry.* +import info.nightscout.androidaps.data.ProfileSealed +import info.nightscout.androidaps.database.AppRepository +import info.nightscout.androidaps.database.entities.UserEntry.Action +import info.nightscout.androidaps.database.entities.UserEntry.Sources +import info.nightscout.androidaps.database.entities.ValueWithUnit +import info.nightscout.androidaps.database.transactions.InvalidateProfileSwitchTransaction import info.nightscout.androidaps.databinding.TreatmentsProfileswitchFragmentBinding import info.nightscout.androidaps.databinding.TreatmentsProfileswitchItemBinding -import info.nightscout.androidaps.db.ProfileSwitch -import info.nightscout.androidaps.db.Source import info.nightscout.androidaps.dialogs.ProfileViewerDialog -import info.nightscout.androidaps.events.EventProfileNeedsUpdate -import info.nightscout.androidaps.interfaces.DatabaseHelperInterface -import info.nightscout.androidaps.interfaces.UploadQueueInterface +import info.nightscout.androidaps.events.EventProfileSwitchChanged +import info.nightscout.androidaps.extensions.getCustomizedName +import info.nightscout.androidaps.extensions.toVisibility +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.nsclient.NSUpload import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventNewHistoryData import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin import info.nightscout.androidaps.plugins.profile.local.events.EventLocalProfileChanged +import info.nightscout.androidaps.plugins.treatments.events.EventTreatmentUpdateGui import info.nightscout.androidaps.plugins.treatments.fragments.TreatmentsProfileSwitchFragment.RecyclerProfileViewAdapter.ProfileSwitchViewHolder import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.buildHelper.BuildHelper -import info.nightscout.androidaps.utils.extensions.toVisibility import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.utils.sharedPreferences.SP +import io.reactivex.Completable import io.reactivex.disposables.CompositeDisposable +import io.reactivex.rxkotlin.plusAssign +import io.reactivex.rxkotlin.subscribeBy import javax.inject.Inject class TreatmentsProfileSwitchFragment : DaggerFragment() { - private val disposable = CompositeDisposable() - @Inject lateinit var rxBus: RxBusWrapper @Inject lateinit var sp: SP + @Inject lateinit var aapsLogger: AAPSLogger @Inject lateinit var localProfilePlugin: LocalProfilePlugin @Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var fabricPrivacy: FabricPrivacy - @Inject lateinit var nsUpload: NSUpload - @Inject lateinit var uploadQueue: UploadQueueInterface @Inject lateinit var dateUtil: DateUtil @Inject lateinit var buildHelper: BuildHelper @Inject lateinit var aapsSchedulers: AapsSchedulers - @Inject lateinit var databaseHelper: DatabaseHelperInterface + @Inject lateinit var repository: AppRepository @Inject lateinit var uel: UserEntryLogger private var _binding: TreatmentsProfileswitchFragmentBinding? = null + private val disposable = CompositeDisposable() + + private val millsToThePast = T.days(30).msecs() + // This property is only valid between onCreateView and // onDestroyView. private val binding get() = _binding!! @@ -67,29 +76,82 @@ class TreatmentsProfileSwitchFragment : DaggerFragment() { super.onViewCreated(view, savedInstanceState) binding.recyclerview.setHasFixedSize(true) binding.recyclerview.layoutManager = LinearLayoutManager(view.context) - binding.recyclerview.adapter = RecyclerProfileViewAdapter(databaseHelper.getProfileSwitchData(DateUtil.now() - T.days(30).msecs(), false)) binding.refreshFromNightscout.setOnClickListener { activity?.let { activity -> - uel.log(Action.PROFILE_SWITCH_NS_REFRESH) OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.refresheventsfromnightscout) + "?") { - databaseHelper.resetProfileSwitch() + uel.log(Action.TREATMENTS_NS_REFRESH, Sources.Treatments) + disposable += + Completable.fromAction { + repository.deleteAllEffectiveProfileSwitches() + repository.deleteAllProfileSwitches() + } + .subscribeOn(aapsSchedulers.io) + .observeOn(aapsSchedulers.main) + .subscribeBy( + onError = { aapsLogger.error("Error removing entries", it) }, + onComplete = { + rxBus.send(EventProfileSwitchChanged()) + rxBus.send(EventNewHistoryData(0, false)) + } + ) rxBus.send(EventNSClientRestart()) } } } - if (sp.getBoolean(R.string.key_ns_upload_only, true) || !buildHelper.isEngineeringMode()) binding.refreshFromNightscout.visibility = View.GONE + if (!sp.getBoolean(R.string.key_ns_receive_profile_switch, false) || !buildHelper.isEngineeringMode()) binding.refreshFromNightscout.visibility = View.GONE + binding.showInvalidated.setOnCheckedChangeListener { _, _ -> + rxBus.send(EventTreatmentUpdateGui()) + } + } + + private fun profileSwitchWithInvalid(now: Long) = repository + .getProfileSwitchDataIncludingInvalidFromTime(now - millsToThePast, false) + .map { bolus -> bolus.map { ProfileSealed.PS(it) } } + + private fun effectiveProfileSwitchWithInvalid(now: Long) = repository + .getEffectiveProfileSwitchDataIncludingInvalidFromTime(now - millsToThePast, false) + .map { carb -> carb.map { ProfileSealed.EPS(it) } } + + private fun profileSwitches(now: Long) = repository + .getProfileSwitchDataFromTime(now - millsToThePast, false) + .map { bolus -> bolus.map { ProfileSealed.PS(it) } } + + private fun effectiveProfileSwitches(now: Long) = repository + .getEffectiveProfileSwitchDataFromTime(now - millsToThePast, false) + .map { carb -> carb.map { ProfileSealed.EPS(it) } } + + fun swapAdapter() { + val now = System.currentTimeMillis() + + if (binding.showInvalidated.isChecked) + disposable += profileSwitchWithInvalid(now) + .zipWith(effectiveProfileSwitchWithInvalid(now)) { first, second -> first + second } + .map { ml -> ml.sortedByDescending { it.timestamp } } + .observeOn(aapsSchedulers.main) + .subscribe { list -> + binding.recyclerview.swapAdapter(RecyclerProfileViewAdapter(list), true) + } + else + disposable += profileSwitches(now) + .zipWith(effectiveProfileSwitches(now)) { first, second -> first + second } + .map { ml -> ml.sortedByDescending { it.timestamp } } + .observeOn(aapsSchedulers.main) + .subscribe { list -> + binding.recyclerview.swapAdapter(RecyclerProfileViewAdapter(list), true) + } + } @Synchronized override fun onResume() { super.onResume() + swapAdapter() disposable.add(rxBus - .toObservable(EventProfileNeedsUpdate::class.java) + .toObservable(EventProfileSwitchChanged::class.java) .observeOn(aapsSchedulers.main) - .subscribe({ updateGUI() }, fabricPrivacy::logException) + .subscribe({ swapAdapter() }, fabricPrivacy::logException) ) - updateGUI() } @Synchronized @@ -101,36 +163,34 @@ class TreatmentsProfileSwitchFragment : DaggerFragment() { @Synchronized override fun onDestroyView() { super.onDestroyView() + binding.recyclerview.adapter = null // avoid leaks _binding = null } - fun updateGUI() { - if (_binding == null) return - binding.recyclerview.swapAdapter(RecyclerProfileViewAdapter(databaseHelper.getProfileSwitchData(DateUtil.now() - T.days(30).msecs(), false)), false) - } - - inner class RecyclerProfileViewAdapter(private var profileSwitchList: List) : RecyclerView.Adapter() { + inner class RecyclerProfileViewAdapter(private var profileSwitchList: List) : RecyclerView.Adapter() { override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ProfileSwitchViewHolder = ProfileSwitchViewHolder(LayoutInflater.from(viewGroup.context).inflate(R.layout.treatments_profileswitch_item, viewGroup, false)) override fun onBindViewHolder(holder: ProfileSwitchViewHolder, position: Int) { val profileSwitch = profileSwitchList[position] - holder.binding.ph.visibility = (profileSwitch.source == Source.PUMP).toVisibility() - holder.binding.ns.visibility = NSUpload.isIdValid(profileSwitch._id).toVisibility() - holder.binding.date.text = dateUtil.dateAndTimeString(profileSwitch.date) - if (!profileSwitch.isEndingEvent) { - holder.binding.duration.text = resourceHelper.gs(R.string.format_mins, profileSwitch.durationInMinutes) - } else { - holder.binding.duration.text = "" - } - holder.binding.name.text = profileSwitch.customizedName - if (profileSwitch.isInProgress) holder.binding.date.setTextColor(resourceHelper.gc(R.color.colorActive)) else holder.binding.date.setTextColor(holder.binding.duration.currentTextColor) + holder.binding.ph.visibility = (profileSwitch is ProfileSealed.EPS).toVisibility() + holder.binding.ns.visibility = (profileSwitch.interfaceIDs_backing?.nightscoutId != null).toVisibility() + holder.binding.date.text = dateUtil.dateAndTimeString(profileSwitch.timestamp) + holder.binding.duration.text = resourceHelper.gs(R.string.format_mins, T.msecs(profileSwitch.duration ?: 0L).mins()) + holder.binding.name.text = if (profileSwitch is ProfileSealed.PS) profileSwitch.value.getCustomizedName() else if (profileSwitch is ProfileSealed.EPS) profileSwitch.value.originalCustomizedName else "" + if (profileSwitch.isInProgress(dateUtil)) holder.binding.date.setTextColor(resourceHelper.gc(R.color.colorActive)) + else holder.binding.date.setTextColor(holder.binding.duration.currentTextColor) holder.binding.remove.tag = profileSwitch holder.binding.clone.tag = profileSwitch holder.binding.name.tag = profileSwitch holder.binding.date.tag = profileSwitch - holder.binding.invalid.visibility = if (profileSwitch.isValid) View.GONE else View.VISIBLE + holder.binding.invalid.visibility = profileSwitch.isValid.not().toVisibility() + holder.binding.duration.visibility = (profileSwitch.duration != 0L && profileSwitch.duration != null).toVisibility() + holder.binding.remove.visibility = (profileSwitch is ProfileSealed.PS).toVisibility() + holder.binding.clone.visibility = (profileSwitch is ProfileSealed.PS).toVisibility() + holder.binding.spacer.visibility = (profileSwitch is ProfileSealed.PS).toVisibility() + holder.binding.root.setBackgroundColor(resourceHelper.gc(if (profileSwitch is ProfileSealed.PS) R.color.defaultbackground else R.color.list_delimiter)) } override fun getItemCount(): Int { @@ -142,34 +202,34 @@ class TreatmentsProfileSwitchFragment : DaggerFragment() { val binding = TreatmentsProfileswitchItemBinding.bind(itemView) init { - binding.remove.setOnClickListener { - val profileSwitch = it.tag as ProfileSwitch + binding.remove.setOnClickListener { view -> + val profileSwitch = view.tag as ProfileSealed.PS activity?.let { activity -> OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.removerecord), resourceHelper.gs(R.string.careportal_profileswitch) + ": " + profileSwitch.profileName + - "\n" + resourceHelper.gs(R.string.date) + ": " + dateUtil.dateAndTimeString(profileSwitch.date), Runnable { - uel.log(Action.PROFILE_SWITCH_REMOVED, profileSwitch.profileName, ValueWithUnit(profileSwitch.date, Units.Timestamp)) - val id = profileSwitch._id - if (NSUpload.isIdValid(id)) nsUpload.removeCareportalEntryFromNS(id) - else uploadQueue.removeByMongoId("dbAdd", id) - databaseHelper.delete(profileSwitch) + "\n" + resourceHelper.gs(R.string.date) + ": " + dateUtil.dateAndTimeString(profileSwitch.timestamp), Runnable { + uel.log(Action.PROFILE_SWITCH_REMOVED, Sources.Treatments, profileSwitch.profileName, + ValueWithUnit.Timestamp(profileSwitch.timestamp)) + disposable += repository.runTransactionForResult(InvalidateProfileSwitchTransaction(profileSwitch.id)) + .subscribe( + { result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated ProfileSwitch $it") } }, + { aapsLogger.error(LTag.DATABASE, "Error while invalidating ProfileSwitch", it) } + ) }) } } binding.clone.setOnClickListener { activity?.let { activity -> - val profileSwitch = it.tag as ProfileSwitch - OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.careportal_profileswitch), resourceHelper.gs(R.string.copytolocalprofile) + "\n" + profileSwitch.customizedName + "\n" + dateUtil.dateAndTimeString(profileSwitch.date), Runnable { - profileSwitch.profileObject?.let { - uel.log(Action.PROFILE_SWITCH_CLONED, ValueWithUnit(profileSwitch.date, Units.Timestamp), ValueWithUnit(profileSwitch.profileName, Units.None)) - val nonCustomized = it.convertToNonCustomizedProfile() - if (nonCustomized.isValid(resourceHelper.gs(R.string.careportal_profileswitch, false))) { - localProfilePlugin.addProfile(localProfilePlugin.copyFrom(nonCustomized, profileSwitch.customizedName + " " + dateUtil.dateAndTimeString(profileSwitch.date).replace(".", "_"))) - rxBus.send(EventLocalProfileChanged()) - } else { - OKDialog.show(activity, resourceHelper.gs(R.string.careportal_profileswitch), resourceHelper.gs(R.string.copytolocalprofile_invalid)) - } - } + val profileSwitch = (it.tag as ProfileSealed.PS).value + val profileSealed = it.tag as ProfileSealed + OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.careportal_profileswitch), resourceHelper.gs(R.string.copytolocalprofile) + "\n" + profileSwitch.getCustomizedName() + "\n" + dateUtil.dateAndTimeString(profileSwitch.timestamp), Runnable { + uel.log(Action.PROFILE_SWITCH_CLONED, Sources.Treatments, + profileSwitch.getCustomizedName() + " " + dateUtil.dateAndTimeString(profileSwitch.timestamp).replace(".", "_"), + ValueWithUnit.Timestamp(profileSwitch.timestamp), + ValueWithUnit.SimpleString(profileSwitch.profileName)) + val nonCustomized = profileSealed.convertToNonCustomizedProfile(dateUtil) + localProfilePlugin.addProfile(localProfilePlugin.copyFrom(nonCustomized, profileSwitch.getCustomizedName() + " " + dateUtil.dateAndTimeString(profileSwitch.timestamp).replace(".", "_"))) + rxBus.send(EventLocalProfileChanged()) }) } } @@ -178,7 +238,7 @@ class TreatmentsProfileSwitchFragment : DaggerFragment() { binding.name.setOnClickListener { ProfileViewerDialog().also { pvd -> pvd.arguments = Bundle().also { args -> - args.putLong("time", (it.tag as ProfileSwitch).date) + args.putLong("time", (it.tag as ProfileSealed).timestamp) args.putInt("mode", ProfileViewerDialog.Mode.DB_PROFILE.ordinal) } pvd.show(childFragmentManager, "ProfileViewDialog") @@ -187,7 +247,7 @@ class TreatmentsProfileSwitchFragment : DaggerFragment() { binding.date.setOnClickListener { ProfileViewerDialog().also { pvd -> pvd.arguments = Bundle().also { args -> - args.putLong("time", (it.tag as ProfileSwitch).date) + args.putLong("time", (it.tag as ProfileSealed).timestamp) args.putInt("mode", ProfileViewerDialog.Mode.DB_PROFILE.ordinal) } pvd.show(childFragmentManager, "ProfileViewDialog") diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsTempTargetFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsTempTargetFragment.kt index 74fa9c95db..d603263fc1 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsTempTargetFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsTempTargetFragment.kt @@ -13,20 +13,20 @@ import dagger.android.support.DaggerFragment import info.nightscout.androidaps.R import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.ValueWrapper +import info.nightscout.androidaps.database.entities.ValueWithUnit import info.nightscout.androidaps.database.entities.TemporaryTarget -import info.nightscout.androidaps.database.entities.UserEntry.* +import info.nightscout.androidaps.database.entities.UserEntry.Action +import info.nightscout.androidaps.database.entities.UserEntry.Sources import info.nightscout.androidaps.database.interfaces.end import info.nightscout.androidaps.database.transactions.InvalidateTemporaryTargetTransaction import info.nightscout.androidaps.databinding.TreatmentsTemptargetFragmentBinding import info.nightscout.androidaps.databinding.TreatmentsTemptargetItemBinding import info.nightscout.androidaps.events.EventTempTargetChange import info.nightscout.androidaps.interfaces.ProfileFunction -import info.nightscout.androidaps.interfaces.UploadQueueInterface 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.nsclient.NSUpload import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart import info.nightscout.androidaps.plugins.treatments.events.EventTreatmentUpdateGui import info.nightscout.androidaps.plugins.treatments.fragments.TreatmentsTempTargetFragment.RecyclerViewAdapter.TempTargetsViewHolder @@ -36,10 +36,10 @@ import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.Translator import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.buildHelper.BuildHelper -import info.nightscout.androidaps.utils.extensions.friendlyDescription -import info.nightscout.androidaps.utils.extensions.highValueToUnitsToString -import info.nightscout.androidaps.utils.extensions.lowValueToUnitsToString -import info.nightscout.androidaps.utils.extensions.toVisibility +import info.nightscout.androidaps.extensions.friendlyDescription +import info.nightscout.androidaps.extensions.highValueToUnitsToString +import info.nightscout.androidaps.extensions.lowValueToUnitsToString +import info.nightscout.androidaps.extensions.toVisibility import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.utils.sharedPreferences.SP @@ -57,8 +57,6 @@ class TreatmentsTempTargetFragment : DaggerFragment() { @Inject lateinit var aapsLogger: AAPSLogger @Inject lateinit var profileFunction: ProfileFunction @Inject lateinit var resourceHelper: ResourceHelper - @Inject lateinit var nsUpload: NSUpload - @Inject lateinit var uploadQueue: UploadQueueInterface @Inject lateinit var fabricPrivacy: FabricPrivacy @Inject lateinit var translator: Translator @Inject lateinit var dateUtil: DateUtil @@ -86,7 +84,7 @@ class TreatmentsTempTargetFragment : DaggerFragment() { binding.refreshFromNightscout.setOnClickListener { context?.let { context -> OKDialog.showConfirmation(context, resourceHelper.gs(R.string.refresheventsfromnightscout) + " ?", { - uel.log(Action.TT_NS_REFRESH) + uel.log(Action.TT_NS_REFRESH, Sources.Treatments) disposable += Completable.fromAction { repository.deleteAllTempTargetEntries() } .subscribeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.main) @@ -99,7 +97,7 @@ class TreatmentsTempTargetFragment : DaggerFragment() { }) } } - val nsUploadOnly = sp.getBoolean(R.string.key_ns_upload_only, true) || !buildHelper.isEngineeringMode() + val nsUploadOnly = !sp.getBoolean(R.string.key_ns_receive_temp_target, false) || !buildHelper.isEngineeringMode() if (nsUploadOnly) binding.refreshFromNightscout.visibility = View.INVISIBLE binding.showInvalidated.setOnCheckedChangeListener { _, _ -> rxBus.send(EventTreatmentUpdateGui()) @@ -151,9 +149,9 @@ class TreatmentsTempTargetFragment : DaggerFragment() { _binding = null } - inner class RecyclerViewAdapter internal constructor(private var tempTargetList: List) : RecyclerView.Adapter() { + private inner class RecyclerViewAdapter(private var tempTargetList: List) : RecyclerView.Adapter() { - private val dbRecord = repository.getTemporaryTargetActiveAt(dateUtil._now()).blockingGet() + private val dbRecord = repository.getTemporaryTargetActiveAt(dateUtil.now()).blockingGet() private val currentlyActiveTarget = if (dbRecord is ValueWrapper.Existing) dbRecord.value else null override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): TempTargetsViewHolder = @@ -170,12 +168,12 @@ class TreatmentsTempTargetFragment : DaggerFragment() { holder.binding.duration.text = resourceHelper.gs(R.string.format_mins, T.msecs(tempTarget.duration).mins()) holder.binding.low.text = tempTarget.lowValueToUnitsToString(units) holder.binding.high.text = tempTarget.highValueToUnitsToString(units) - holder.binding.reason.text = translator.translate(tempTarget.reason.text) + holder.binding.reason.text = translator.translate(tempTarget.reason) holder.binding.date.setTextColor( when { - tempTarget.id == currentlyActiveTarget?.id -> resourceHelper.gc(R.color.colorActive) - tempTarget.timestamp > DateUtil.now() -> resourceHelper.gc(R.color.colorScheduled) - else -> holder.binding.reasonColon.currentTextColor + tempTarget.id == currentlyActiveTarget?.id -> resourceHelper.gc(R.color.colorActive) + tempTarget.timestamp > dateUtil.now() -> resourceHelper.gc(R.color.colorScheduled) + else -> holder.binding.reasonColon.currentTextColor }) holder.binding.remove.tag = tempTarget } @@ -196,15 +194,16 @@ class TreatmentsTempTargetFragment : DaggerFragment() { ${dateUtil.dateAndTimeString(tempTarget.timestamp)} """.trimIndent(), { _: DialogInterface?, _: Int -> - uel.log(Action.TT_REMOVED, ValueWithUnit(tempTarget.timestamp, Units.Timestamp), ValueWithUnit(tempTarget.reason.text, Units.TherapyEvent), ValueWithUnit(tempTarget.lowTarget, Units.Mg_Dl), ValueWithUnit(tempTarget.highTarget, Units.Mg_Dl, tempTarget.lowTarget != tempTarget.highTarget), ValueWithUnit(tempTarget.duration.toInt(), Units.M)) + uel.log(Action.TT_REMOVED, Sources.Treatments, + ValueWithUnit.Timestamp(tempTarget.timestamp), + ValueWithUnit.TherapyEventTTReason(tempTarget.reason), + ValueWithUnit.Mgdl(tempTarget.lowTarget), + ValueWithUnit.Mgdl(tempTarget.highTarget).takeIf { tempTarget.lowTarget != tempTarget.highTarget }, + ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(tempTarget.duration).toInt())) disposable += repository.runTransactionForResult(InvalidateTemporaryTargetTransaction(tempTarget.id)) - .subscribe({ - val id = tempTarget.interfaceIDs.nightscoutId - if (NSUpload.isIdValid(id)) nsUpload.removeCareportalEntryFromNS(id) - else uploadQueue.removeByMongoId("dbAdd", tempTarget.timestamp.toString()) - }, { - aapsLogger.error(LTag.BGSOURCE, "Error while invalidating temporary target", it) - }) + .subscribe( + { aapsLogger.debug(LTag.DATABASE, "Removed temp target $tempTarget") }, + { aapsLogger.error(LTag.DATABASE, "Error while invalidating temporary target", it) }) }, null) } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsTemporaryBasalsFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsTemporaryBasalsFragment.kt index 365c5ae4c2..d0ee9dcd5b 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsTemporaryBasalsFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsTemporaryBasalsFragment.kt @@ -10,44 +10,60 @@ import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import dagger.android.support.DaggerFragment import info.nightscout.androidaps.R -import info.nightscout.androidaps.data.Intervals import info.nightscout.androidaps.data.IobTotal +import info.nightscout.androidaps.database.AppRepository +import info.nightscout.androidaps.database.entities.TemporaryBasal import info.nightscout.androidaps.database.entities.UserEntry.* +import info.nightscout.androidaps.database.entities.UserEntry.Action +import info.nightscout.androidaps.database.entities.UserEntry.Sources +import info.nightscout.androidaps.database.entities.ValueWithUnit +import info.nightscout.androidaps.database.interfaces.end +import info.nightscout.androidaps.database.transactions.InvalidateTemporaryBasalTransaction import info.nightscout.androidaps.databinding.TreatmentsTempbasalsFragmentBinding import info.nightscout.androidaps.databinding.TreatmentsTempbasalsItemBinding -import info.nightscout.androidaps.db.Source -import info.nightscout.androidaps.db.TemporaryBasal +import info.nightscout.androidaps.events.EventAutosensCalculationFinished import info.nightscout.androidaps.events.EventTempBasalChange -import info.nightscout.androidaps.interfaces.ActivePluginProvider +import info.nightscout.androidaps.extensions.iobCalc +import info.nightscout.androidaps.extensions.toStringFull +import info.nightscout.androidaps.extensions.toTemporaryBasal +import info.nightscout.androidaps.extensions.toVisibility +import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.ProfileFunction +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.nsclient.NSUpload -import info.nightscout.androidaps.events.EventAutosensCalculationFinished import info.nightscout.androidaps.plugins.treatments.fragments.TreatmentsTemporaryBasalsFragment.RecyclerViewAdapter.TempBasalsViewHolder import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.FabricPrivacy +import info.nightscout.androidaps.utils.T 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 +import io.reactivex.rxkotlin.plusAssign import javax.inject.Inject +import kotlin.math.abs class TreatmentsTemporaryBasalsFragment : DaggerFragment() { private val disposable = CompositeDisposable() + @Inject lateinit var aapsLogger: AAPSLogger @Inject lateinit var rxBus: RxBusWrapper @Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var fabricPrivacy: FabricPrivacy - @Inject lateinit var activePlugin: ActivePluginProvider + @Inject lateinit var activePlugin: ActivePlugin @Inject lateinit var profileFunction: ProfileFunction @Inject lateinit var dateUtil: DateUtil @Inject lateinit var aapsSchedulers: AapsSchedulers @Inject lateinit var uel: UserEntryLogger + @Inject lateinit var repository: AppRepository private var _binding: TreatmentsTempbasalsFragmentBinding? = null + private val millsToThePast = T.days(30).msecs() + // This property is only valid between onCreateView and // onDestroyView. private val binding get() = _binding!! @@ -59,23 +75,67 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() { super.onViewCreated(view, savedInstanceState) binding.recyclerview.setHasFixedSize(true) binding.recyclerview.layoutManager = LinearLayoutManager(view.context) - binding.recyclerview.adapter = RecyclerViewAdapter(activePlugin.activeTreatments.temporaryBasalsFromHistory) + } + + private fun tempBasalsWithInvalid(now: Long) = repository + .getTemporaryBasalsDataIncludingInvalidFromTime(now - millsToThePast, false) + + private fun tempBasals(now: Long) = repository + .getTemporaryBasalsDataFromTime(now - millsToThePast, false) + + private fun extendedBolusesWithInvalid(now: Long) = repository + .getExtendedBolusDataIncludingInvalidFromTime(now - millsToThePast, false) + .map { eb -> eb.map { profileFunction.getProfile(it.timestamp)?.let { profile -> it.toTemporaryBasal(profile) } } } + + private fun extendedBoluses(now: Long) = repository + .getExtendedBolusDataFromTime(now - millsToThePast, false) + .map { eb -> eb.map { profileFunction.getProfile(it.timestamp)?.let { profile -> it.toTemporaryBasal(profile) } } } + + fun swapAdapter() { + val now = System.currentTimeMillis() + disposable += + if (activePlugin.activePump.isFakingTempsByExtendedBoluses) { + if (binding.showInvalidated.isChecked) + tempBasalsWithInvalid(now) + .zipWith(extendedBolusesWithInvalid(now)) { first, second -> first + second } + .map { list -> list.filterNotNull() } + .map { list -> list.sortedByDescending { it.timestamp } } + .observeOn(aapsSchedulers.main) + .subscribe { list -> binding.recyclerview.swapAdapter(RecyclerViewAdapter(list), true) } + else + tempBasals(now) + .zipWith(extendedBoluses(now)) { first, second -> first + second } + .map { list -> list.filterNotNull() } + .map { list -> list.sortedByDescending { it.timestamp } } + .observeOn(aapsSchedulers.main) + .subscribe { list -> binding.recyclerview.swapAdapter(RecyclerViewAdapter(list), true) } + } else { + if (binding.showInvalidated.isChecked) + tempBasalsWithInvalid(now) + .observeOn(aapsSchedulers.main) + .subscribe { list -> binding.recyclerview.swapAdapter(RecyclerViewAdapter(list), true) } + else + tempBasals(now) + .observeOn(aapsSchedulers.main) + .subscribe { list -> binding.recyclerview.swapAdapter(RecyclerViewAdapter(list), true) } + } + } @Synchronized override fun onResume() { super.onResume() - disposable.add(rxBus + swapAdapter() + + disposable += rxBus .toObservable(EventTempBasalChange::class.java) .observeOn(aapsSchedulers.main) - .subscribe({ updateGui() }, fabricPrivacy::logException) - ) - disposable.add(rxBus + .subscribe({ swapAdapter() }, fabricPrivacy::logException) + + disposable += rxBus .toObservable(EventAutosensCalculationFinished::class.java) .observeOn(aapsSchedulers.main) - .subscribe({ updateGui() }, fabricPrivacy::logException) - ) - updateGui() + .subscribe({ swapAdapter() }, fabricPrivacy::logException) } @Synchronized @@ -87,69 +147,46 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() { @Synchronized override fun onDestroyView() { super.onDestroyView() + binding.recyclerview.adapter = null // avoid leaks _binding = null } - inner class RecyclerViewAdapter internal constructor(private var tempBasalList: Intervals) : RecyclerView.Adapter() { + inner class RecyclerViewAdapter internal constructor(private var tempBasalList: List) : RecyclerView.Adapter() { override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): TempBasalsViewHolder = TempBasalsViewHolder(LayoutInflater.from(viewGroup.context).inflate(R.layout.treatments_tempbasals_item, viewGroup, false)) override fun onBindViewHolder(holder: TempBasalsViewHolder, position: Int) { - val tempBasal = tempBasalList.getReversed(position) - holder.binding.ph.visibility = if (tempBasal.source == Source.PUMP) View.VISIBLE else View.GONE - holder.binding.ns.visibility = if (NSUpload.isIdValid(tempBasal._id)) View.VISIBLE else View.GONE - if (tempBasal.isEndingEvent) { - holder.binding.date.text = dateUtil.dateAndTimeString(tempBasal.date) - holder.binding.duration.text = resourceHelper.gs(R.string.cancel) - holder.binding.absolute.text = "" - holder.binding.percent.text = "" - holder.binding.realDuration.text = "" - holder.binding.iob.text = "" - holder.binding.netInsulin.text = "" - holder.binding.netRatio.text = "" - holder.binding.extendedFlag.visibility = View.GONE - holder.binding.iob.setTextColor(holder.binding.netRatio.currentTextColor) + val tempBasal = tempBasalList[position] + holder.binding.ns.visibility = (tempBasal.interfaceIDs.nightscoutId != null).toVisibility() + holder.binding.invalid.visibility = tempBasal.isValid.not().toVisibility() + holder.binding.ph.visibility = (tempBasal.interfaceIDs.pumpId != null).toVisibility() + if (tempBasal.isInProgress) { + holder.binding.date.text = dateUtil.dateAndTimeString(tempBasal.timestamp) + holder.binding.date.setTextColor(resourceHelper.gc(R.color.colorActive)) } else { - if (tempBasal.isInProgress) { - holder.binding.date.text = dateUtil.dateAndTimeString(tempBasal.date) - holder.binding.date.setTextColor(resourceHelper.gc(R.color.colorActive)) - } else { - holder.binding.date.text = dateUtil.dateAndTimeRangeString(tempBasal.date, tempBasal.end()) - holder.binding.date.setTextColor(holder.binding.netRatio.currentTextColor) - } - holder.binding.duration.text = resourceHelper.gs(R.string.format_mins, tempBasal.durationInMinutes) - if (tempBasal.isAbsolute) { - val profile = profileFunction.getProfile(tempBasal.date) - if (profile != null) { - holder.binding.absolute.text = resourceHelper.gs(R.string.pump_basebasalrate, tempBasal.tempBasalConvertedToAbsolute(tempBasal.date, profile)) - holder.binding.percent.text = "" - } else { - holder.binding.absolute.text = resourceHelper.gs(R.string.noprofile) - holder.binding.percent.text = "" - } - } else { - holder.binding.absolute.text = "" - holder.binding.percent.text = resourceHelper.gs(R.string.format_percent, tempBasal.percentRate) - } - holder.binding.realDuration.text = resourceHelper.gs(R.string.format_mins, tempBasal.realDuration) - val now = DateUtil.now() - var iob = IobTotal(now) - val profile = profileFunction.getProfile(now) - if (profile != null) iob = tempBasal.iobCalc(now, profile) - holder.binding.iob.text = resourceHelper.gs(R.string.formatinsulinunits, iob.basaliob) - holder.binding.netInsulin.text = resourceHelper.gs(R.string.formatinsulinunits, iob.netInsulin) - holder.binding.netRatio.text = resourceHelper.gs(R.string.pump_basebasalrate, iob.netRatio) - holder.binding.extendedFlag.visibility = View.GONE - if (iob.basaliob != 0.0) holder.binding.iob.setTextColor(resourceHelper.gc(R.color.colorActive)) else holder.binding.iob.setTextColor(holder.binding.netRatio.currentTextColor) + holder.binding.date.text = dateUtil.dateAndTimeRangeString(tempBasal.timestamp, tempBasal.end) + holder.binding.date.setTextColor(holder.binding.duration.currentTextColor) } + holder.binding.duration.text = resourceHelper.gs(R.string.format_mins, T.msecs(tempBasal.duration).mins()) + if (tempBasal.isAbsolute) holder.binding.rate.text = resourceHelper.gs(R.string.pump_basebasalrate, tempBasal.rate) + else holder.binding.rate.text = resourceHelper.gs(R.string.format_percent, tempBasal.rate.toInt()) + val now = dateUtil.now() + var iob = IobTotal(now) + val profile = profileFunction.getProfile(now) + if (profile != null) iob = tempBasal.iobCalc(now, profile, activePlugin.activeInsulin) + holder.binding.iob.text = resourceHelper.gs(R.string.formatinsulinunits, iob.basaliob) + holder.binding.extendedFlag.visibility = (tempBasal.type == TemporaryBasal.Type.FAKE_EXTENDED).toVisibility() + holder.binding.suspendFlag.visibility = (tempBasal.type == TemporaryBasal.Type.PUMP_SUSPEND).toVisibility() + holder.binding.emulatedSuspendFlag.visibility = (tempBasal.type == TemporaryBasal.Type.EMULATED_PUMP_SUSPEND).toVisibility() + holder.binding.superBolusFlag.visibility = (tempBasal.type == TemporaryBasal.Type.SUPERBOLUS).toVisibility() + if (abs(iob.basaliob) > 0.01) holder.binding.iob.setTextColor(resourceHelper.gc(R.color.colorActive)) else holder.binding.iob.setTextColor(holder.binding.duration.currentTextColor) holder.binding.remove.tag = tempBasal } - override fun getItemCount(): Int { - return tempBasalList.size() - } + override fun getItemCount(): Int = tempBasalList.size + @Deprecated("remove remove functionality after finish") inner class TempBasalsViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { val binding = TreatmentsTempbasalsItemBinding.bind(itemView) @@ -157,15 +194,21 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() { init { binding.remove.setOnClickListener { v: View -> val tempBasal = v.tag as TemporaryBasal + val profile = profileFunction.getProfile(dateUtil.now()) + ?: return@setOnClickListener context?.let { OKDialog.showConfirmation(it, resourceHelper.gs(R.string.removerecord), """ - ${resourceHelper.gs(R.string.tempbasal_label)}: ${tempBasal.toStringFull()} - ${resourceHelper.gs(R.string.date)}: ${dateUtil.dateAndTimeString(tempBasal.date)} + ${resourceHelper.gs(R.string.tempbasal_label)}: ${tempBasal.toStringFull(profile, dateUtil)} + ${resourceHelper.gs(R.string.date)}: ${dateUtil.dateAndTimeString(tempBasal.timestamp)} """.trimIndent(), { _: DialogInterface?, _: Int -> - uel.log(Action.TT_REMOVED, ValueWithUnit(tempBasal.date, Units.Timestamp)) - activePlugin.activeTreatments.removeTempBasal(tempBasal) + uel.log(Action.TEMP_BASAL_REMOVED, Sources.Treatments, + ValueWithUnit.Timestamp(tempBasal.timestamp)) + disposable += repository.runTransactionForResult(InvalidateTemporaryBasalTransaction(tempBasal.id)) + .subscribe( + { aapsLogger.debug(LTag.DATABASE, "Removed temporary basal $tempBasal") }, + { aapsLogger.error(LTag.DATABASE, "Error while invalidating temporary basal", it) }) }, null) } } @@ -174,11 +217,4 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() { } } - - private fun updateGui() { - if (_binding == null) return - binding.recyclerview.swapAdapter(RecyclerViewAdapter(activePlugin.activeTreatments.temporaryBasalsFromHistory), false) - val tempBasalsCalculation = activePlugin.activeTreatments.lastCalculationTempBasals - if (tempBasalsCalculation != null) binding.totalTempIob.text = resourceHelper.gs(R.string.formatinsulinunits, tempBasalsCalculation.basaliob) - } } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsUserEntryFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsUserEntryFragment.kt index 5a7c6432f1..9bbc46d63b 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsUserEntryFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsUserEntryFragment.kt @@ -7,27 +7,29 @@ import android.view.ViewGroup import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import dagger.android.support.DaggerFragment -import info.nightscout.androidaps.Constants import info.nightscout.androidaps.R import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.entities.UserEntry -import info.nightscout.androidaps.database.entities.UserEntry.* +import info.nightscout.androidaps.database.entities.UserEntry.Action +import info.nightscout.androidaps.database.entities.UserEntry.Sources import info.nightscout.androidaps.databinding.TreatmentsUserEntryFragmentBinding import info.nightscout.androidaps.databinding.TreatmentsUserEntryItemBinding import info.nightscout.androidaps.events.EventPreferenceChange -import info.nightscout.androidaps.interfaces.ImportExportPrefsInterface +import info.nightscout.androidaps.interfaces.ImportExportPrefs import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.plugins.bus.RxBusWrapper +import info.nightscout.androidaps.plugins.treatments.events.EventTreatmentUpdateGui import info.nightscout.androidaps.utils.DateUtil -import info.nightscout.androidaps.utils.DecimalFormatter import info.nightscout.androidaps.utils.FabricPrivacy +import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.Translator +import info.nightscout.androidaps.utils.userEntry.UserEntryPresentationHelper import info.nightscout.androidaps.utils.alertDialogs.OKDialog -import info.nightscout.androidaps.utils.extensions.* import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers import io.reactivex.disposables.CompositeDisposable +import java.util.concurrent.TimeUnit import javax.inject.Inject class TreatmentsUserEntryFragment : DaggerFragment() { @@ -40,11 +42,15 @@ class TreatmentsUserEntryFragment : DaggerFragment() { @Inject lateinit var fabricPrivacy: FabricPrivacy @Inject lateinit var rxBus: RxBusWrapper @Inject lateinit var translator: Translator - @Inject lateinit var importExportPrefs: ImportExportPrefsInterface + @Inject lateinit var importExportPrefs: ImportExportPrefs @Inject lateinit var uel: UserEntryLogger + @Inject lateinit var userEntryPresentationHelper: UserEntryPresentationHelper private val disposable = CompositeDisposable() + private val millsToThePastFiltered = T.days(30).msecs() + private val millsToThePastUnFiltered = T.days(3).msecs() + private var _binding: TreatmentsUserEntryFragmentBinding? = null // This property is only valid between onCreateView and @@ -61,20 +67,30 @@ class TreatmentsUserEntryFragment : DaggerFragment() { binding.ueExportToXml.setOnClickListener { activity?.let { activity -> OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.ue_export_to_csv) + "?") { - uel.log(Action.EXPORT_CSV) + uel.log(Action.EXPORT_CSV, Sources.Treatments) importExportPrefs.exportUserEntriesCsv(activity, repository.getAllUserEntries()) } } } - + binding.showLoop.setOnCheckedChangeListener { _, _ -> + rxBus.send(EventTreatmentUpdateGui()) + } } fun swapAdapter() { - disposable.add( repository - .getAllUserEntries() - .observeOn(aapsSchedulers.main) - .subscribe { list -> binding.recyclerview.swapAdapter(UserEntryAdapter(list), true) } - ) + val now = System.currentTimeMillis() + if (binding.showLoop.isChecked) + disposable.add( repository + .getUserEntryDataFromTime(now - millsToThePastUnFiltered) + .observeOn(aapsSchedulers.main) + .subscribe { list -> binding.recyclerview.swapAdapter(UserEntryAdapter(list), true) } + ) + else + disposable.add( repository + .getUserEntryFilteredDataFromTime(now - millsToThePastFiltered) + .observeOn(aapsSchedulers.main) + .subscribe { list -> binding.recyclerview.swapAdapter(UserEntryAdapter(list), true) } + ) } @Synchronized @@ -86,6 +102,11 @@ class TreatmentsUserEntryFragment : DaggerFragment() { .toObservable(EventPreferenceChange::class.java) .observeOn(aapsSchedulers.io) .subscribe({ swapAdapter() }, fabricPrivacy::logException)) + disposable.add(rxBus + .toObservable(EventTreatmentUpdateGui::class.java) + .observeOn(aapsSchedulers.io) + .debounce(1L, TimeUnit.SECONDS) + .subscribe({ swapAdapter() }, fabricPrivacy::logException)) } @Synchronized @@ -111,44 +132,13 @@ class TreatmentsUserEntryFragment : DaggerFragment() { override fun onBindViewHolder(holder: UserEntryViewHolder, position: Int) { val current = entries[position] holder.binding.date.text = dateUtil.dateAndTimeAndSecondsString(current.timestamp) - holder.binding.action.text = translator.translate(current.action.name) - holder.binding.action.setTextColor(resourceHelper.gc(current.action.colorGroup.colorId())) - if (current.s != "") { - holder.binding.s.text = current.s - holder.binding.s.visibility = View.VISIBLE - } else - holder.binding.s.visibility = View.GONE - var valuesWithUnitString = "" - var rStringParam = 0 - val separator = " " - for(v in current.values) { - if (rStringParam >0) - rStringParam-- - else - when (v.unit) { - Units.Timestamp -> valuesWithUnitString += dateUtil.dateAndTimeAndSecondsString(v.lValue) + separator - Units.TherapyEvent -> valuesWithUnitString += translator.translate(v.sValue) + separator - Units.R_String -> { - rStringParam = v.lValue.toInt() - when (rStringParam) { // - 0 -> valuesWithUnitString += resourceHelper.gs(v.iValue) + separator - 1 -> valuesWithUnitString += resourceHelper.gs(v.iValue, current.values[current.values.indexOf(v)+1].value()) + separator - 2 -> valuesWithUnitString += resourceHelper.gs(v.iValue, current.values[current.values.indexOf(v)+1].value(), current.values[current.values.indexOf(v)+2].value()) + separator - 3 -> valuesWithUnitString += resourceHelper.gs(v.iValue, current.values[current.values.indexOf(v)+1].value(), current.values[current.values.indexOf(v)+2].value(), current.values[current.values.indexOf(v)+3].value()) + separator - 4 -> rStringParam = 0 - } - } - Units.Mg_Dl -> valuesWithUnitString += if (profileFunction.getUnits()==Constants.MGDL) DecimalFormatter.to0Decimal(v.dValue) + translator.translate(Units.Mg_Dl.name) + separator else DecimalFormatter.to1Decimal(v.dValue/Constants.MMOLL_TO_MGDL) + translator.translate(Units.Mmol_L.name) + separator - Units.Mmol_L -> valuesWithUnitString += if (profileFunction.getUnits()==Constants.MGDL) DecimalFormatter.to0Decimal(v.dValue*Constants.MMOLL_TO_MGDL) + translator.translate(Units.Mg_Dl.name) + separator else DecimalFormatter.to1Decimal(v.dValue) + translator.translate(Units.Mmol_L.name) + separator - Units.U_H, Units.U - -> valuesWithUnitString += DecimalFormatter.to2Decimal(v.dValue) + translator.translate(v.unit.name) + separator - Units.G, Units.M, Units.H, Units.Percent - -> valuesWithUnitString += v.iValue.toString() + translator.translate(v.unit.name) + separator - else -> valuesWithUnitString += if (v.iValue != 0 || v.sValue != "") { v.value().toString() + separator } else "" - } - } - holder.binding.values.text = valuesWithUnitString.trim() - holder.binding.values.visibility = if (current.values.size > 0) View.VISIBLE else View.GONE + holder.binding.action.text = userEntryPresentationHelper.actionToColoredString(current.action) + holder.binding.s.text = current.note + holder.binding.s.visibility = if (current.note != "") View.VISIBLE else View.GONE + holder.binding.iconSource.setImageResource(userEntryPresentationHelper.iconId(current.source)) + holder.binding.iconSource.visibility = View.VISIBLE + holder.binding.values.text = userEntryPresentationHelper.listToPresentationString(current.values) + holder.binding.values.visibility = if (holder.binding.values.text != "") View.VISIBLE else View.GONE } inner class UserEntryViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { @@ -157,6 +147,6 @@ class TreatmentsUserEntryFragment : DaggerFragment() { } override fun getItemCount(): Int = entries.size - } + } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/queue/CommandQueue.kt b/app/src/main/java/info/nightscout/androidaps/queue/CommandQueue.kt index aeb00da0fa..448e917ae9 100644 --- a/app/src/main/java/info/nightscout/androidaps/queue/CommandQueue.kt +++ b/app/src/main/java/info/nightscout/androidaps/queue/CommandQueue.kt @@ -5,22 +5,24 @@ import android.content.Intent import android.os.SystemClock import android.text.Spanned import androidx.appcompat.app.AppCompatActivity -import dagger.Lazy import dagger.android.HasAndroidInjector import info.nightscout.androidaps.R import info.nightscout.androidaps.activities.BolusProgressHelperActivity import info.nightscout.androidaps.activities.ErrorHelperActivity import info.nightscout.androidaps.data.DetailedBolusInfo -import info.nightscout.androidaps.data.Profile +import info.nightscout.androidaps.data.ProfileSealed import info.nightscout.androidaps.data.PumpEnactResult +import info.nightscout.androidaps.database.AppRepository +import info.nightscout.androidaps.database.ValueWrapper +import info.nightscout.androidaps.database.entities.EffectiveProfileSwitch +import info.nightscout.androidaps.database.entities.ProfileSwitch +import info.nightscout.androidaps.database.interfaces.end import info.nightscout.androidaps.dialogs.BolusProgressDialog import info.nightscout.androidaps.events.EventBolusRequested import info.nightscout.androidaps.events.EventNewBasalProfile -import info.nightscout.androidaps.events.EventProfileNeedsUpdate -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.events.EventProfileSwitchChanged +import info.nightscout.androidaps.extensions.getCustomizedName +import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.plugins.bus.RxBusWrapper @@ -31,6 +33,7 @@ import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotifi import info.nightscout.androidaps.plugins.general.overview.notifications.Notification import info.nightscout.androidaps.queue.commands.* import info.nightscout.androidaps.queue.commands.Command.CommandType +import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.HtmlHelper import info.nightscout.androidaps.utils.buildHelper.BuildHelper @@ -38,51 +41,12 @@ 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 io.reactivex.rxkotlin.subscribeBy import java.util.* import javax.inject.Inject import javax.inject.Singleton -/** - * Created by mike on 08.11.2017. - * - * - * DATA FLOW: - * --------- - * - * - * (request) - > ConfigBuilder.getCommandQueue().bolus(...) - * - * - * app no longer waits for result but passes Callback - * - * - * request is added to queue, if another request of the same type already exists in queue, it's removed prior adding - * but if request of the same type is currently executed (probably important only for bolus which is running long time), new request is declined - * new QueueThread is created and started if current if finished - * CommandReadStatus is added automatically before command if queue is empty - * - * - * biggest change is we don't need exec pump commands in Handler because it's finished immediately - * command queueing if not realized by stacking in different Handlers and threads anymore but by internal queue with better control - * - * - * QueueThread calls ConfigBuilder#connect which is passed to getActivePump().connect - * connect should be executed on background and return immediately. afterwards isConnecting() is expected to be true - * - * - * while isConnecting() == true GUI is updated by posting connection progress - * - * - * if connect is successful: isConnected() becomes true, isConnecting() becomes false - * CommandQueue starts calling execute() of commands. execute() is expected to be blocking (return after finish). - * callback with result is called after finish automatically - * if connect failed: isConnected() becomes false, isConnecting() becomes false - * connect() is called again - * - * - * when queue is empty, disconnect is called - */ - @Singleton open class CommandQueue @Inject constructor( private val injector: HasAndroidInjector, @@ -92,10 +56,12 @@ open class CommandQueue @Inject constructor( private val resourceHelper: ResourceHelper, private val constraintChecker: ConstraintChecker, private val profileFunction: ProfileFunction, - private val activePlugin: Lazy, + private val activePlugin: ActivePlugin, private val context: Context, private val sp: SP, private val buildHelper: BuildHelper, + private val dateUtil: DateUtil, + private val repository: AppRepository, private val fabricPrivacy: FabricPrivacy ) : CommandQueueProvider { @@ -108,17 +74,37 @@ open class CommandQueue @Inject constructor( init { disposable.add(rxBus - .toObservable(EventProfileNeedsUpdate::class.java) + .toObservable(EventProfileSwitchChanged::class.java) .observeOn(aapsSchedulers.io) .subscribe({ aapsLogger.debug(LTag.PROFILE, "onProfileSwitch") - profileFunction.getProfile()?.let { - setProfile(it, object : Callback() { + profileFunction.getRequestedProfile()?.let { + val nonCustomized = ProfileSealed.PS(it).convertToNonCustomizedProfile(dateUtil) + setProfile(ProfileSealed.Pure(nonCustomized), it.interfaceIDs.nightscoutId != null, object : Callback() { override fun run() { if (!result.success) { ErrorHelperActivity.runAlarm(context, result.comment, resourceHelper.gs(R.string.failedupdatebasalprofile), R.raw.boluserror) } - if (result.enacted) rxBus.send(EventNewBasalProfile()) + if (result.enacted) { + rxBus.send(EventNewBasalProfile()) + repository.createEffectiveProfileSwitch( + EffectiveProfileSwitch( + timestamp = dateUtil.now(), + basalBlocks = nonCustomized.basalBlocks, + isfBlocks = nonCustomized.isfBlocks, + icBlocks = nonCustomized.icBlocks, + targetBlocks = nonCustomized.targetBlocks, + glucoseUnit = if (it.glucoseUnit == ProfileSwitch.GlucoseUnit.MGDL) EffectiveProfileSwitch.GlucoseUnit.MGDL else EffectiveProfileSwitch.GlucoseUnit.MMOL, + originalProfileName = it.profileName, + originalCustomizedName = it.getCustomizedName(), + originalTimeshift = it.timeshift, + originalPercentage = it.percentage, + originalDuration = it.duration, + originalEnd = it.end, + insulinConfiguration = it.insulinConfiguration + ) + ) + } } }) } @@ -190,7 +176,7 @@ open class CommandQueue @Inject constructor( open fun notifyAboutNewCommand() { waitForFinishedThread() if (thread == null || thread!!.state == Thread.State.TERMINATED) { - thread = QueueThread(this, context, aapsLogger, rxBus, activePlugin.get(), resourceHelper, sp) + thread = QueueThread(this, context, aapsLogger, rxBus, activePlugin, resourceHelper, sp) thread!!.start() aapsLogger.debug(LTag.PUMPQUEUE, "Starting new thread") } else { @@ -209,7 +195,7 @@ open class CommandQueue @Inject constructor( override fun independentConnect(reason: String, callback: Callback?) { aapsLogger.debug(LTag.PUMPQUEUE, "Starting new queue") - val tempCommandQueue = CommandQueue(injector, aapsLogger, rxBus, aapsSchedulers, resourceHelper, constraintChecker, profileFunction, activePlugin, context, sp, buildHelper, fabricPrivacy) + val tempCommandQueue = CommandQueue(injector, aapsLogger, rxBus, aapsSchedulers, resourceHelper, constraintChecker, profileFunction, activePlugin, context, sp, buildHelper, dateUtil, repository, fabricPrivacy) tempCommandQueue.readStatus(reason, callback) } @@ -229,13 +215,40 @@ open class CommandQueue @Inject constructor( // returns true if command is queued @Synchronized override fun bolus(detailedBolusInfo: DetailedBolusInfo, callback: Callback?): Boolean { - var type = if (detailedBolusInfo.isSMB) CommandType.SMB_BOLUS else CommandType.BOLUS + // Check if pump store carbs + // If not, it's not necessary add command to the queue and initiate connection + // Assuming carbs in the future and carbs with duration are NOT stores anyway + if ((detailedBolusInfo.carbs > 0) && + (!activePlugin.activePump.pumpDescription.storesCarbInfo || + detailedBolusInfo.carbsDuration != 0L || + (detailedBolusInfo.carbsTimestamp ?: detailedBolusInfo.timestamp) > dateUtil.now()) + ) { + disposable += repository.runTransactionForResult(detailedBolusInfo.insertCarbsTransaction()) + .subscribeBy( + onSuccess = { result -> + result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted carbs $it") } + callback?.result(PumpEnactResult(injector).enacted(false).success(true))?.run() + + }, + onError = { + aapsLogger.error(LTag.DATABASE, "Error while saving carbs", it) + callback?.result(PumpEnactResult(injector).enacted(false).success(false))?.run() + } + ) + // Do not process carbs anymore + detailedBolusInfo.carbs = 0.0 + // if no insulin just exit + if (detailedBolusInfo.insulin == 0.0) return true + + } + var type = if (detailedBolusInfo.bolusType == DetailedBolusInfo.BolusType.SMB) CommandType.SMB_BOLUS else CommandType.BOLUS if (type == CommandType.SMB_BOLUS) { if (isRunning(CommandType.BOLUS) || isRunning(CommandType.SMB_BOLUS) || bolusInQueue()) { aapsLogger.debug(LTag.PUMPQUEUE, "Rejecting SMB since a bolus is queue/running") return false } - if (detailedBolusInfo.lastKnownBolusTime < activePlugin.get().activeTreatments.lastBolusTime) { + val lastBolusTime = repository.getLastBolusRecord()?.timestamp ?: 0L + if (detailedBolusInfo.lastKnownBolusTime < lastBolusTime) { aapsLogger.debug(LTag.PUMPQUEUE, "Rejecting bolus, another bolus was issued since request time") return false } @@ -256,7 +269,7 @@ open class CommandQueue @Inject constructor( detailedBolusInfo.insulin = constraintChecker.applyBolusConstraints(Constraint(detailedBolusInfo.insulin)).value() detailedBolusInfo.carbs = constraintChecker.applyCarbsConstraints(Constraint(detailedBolusInfo.carbs.toInt())).value().toDouble() // add new command to queue - if (detailedBolusInfo.isSMB) { + if (detailedBolusInfo.bolusType == DetailedBolusInfo.BolusType.SMB) { add(CommandSMBBolus(injector, detailedBolusInfo, callback)) } else { add(CommandBolus(injector, detailedBolusInfo, callback, type)) @@ -293,11 +306,11 @@ open class CommandQueue @Inject constructor( } removeAll(CommandType.BOLUS) removeAll(CommandType.SMB_BOLUS) - Thread { activePlugin.get().activePump.stopBolusDelivering() }.run() + Thread { activePlugin.activePump.stopBolusDelivering() }.run() } // returns true if command is queued - override fun tempBasalAbsolute(absoluteRate: Double, durationInMinutes: Int, enforceNew: Boolean, profile: Profile, callback: Callback?): Boolean { + override fun tempBasalAbsolute(absoluteRate: Double, durationInMinutes: Int, enforceNew: Boolean, profile: Profile, tbrType: PumpSync.TemporaryBasalType, callback: Callback?): Boolean { if (!enforceNew && isRunning(CommandType.TEMPBASAL)) { callback?.result(executingNowError())?.run() return false @@ -306,13 +319,13 @@ open class CommandQueue @Inject constructor( removeAll(CommandType.TEMPBASAL) val rateAfterConstraints = constraintChecker.applyBasalConstraints(Constraint(absoluteRate), profile).value() // add new command to queue - add(CommandTempBasalAbsolute(injector, rateAfterConstraints, durationInMinutes, enforceNew, profile, callback)) + add(CommandTempBasalAbsolute(injector, rateAfterConstraints, durationInMinutes, enforceNew, profile, tbrType, callback)) notifyAboutNewCommand() return true } // returns true if command is queued - override fun tempBasalPercent(percent: Int, durationInMinutes: Int, enforceNew: Boolean, profile: Profile, callback: Callback?): Boolean { + override fun tempBasalPercent(percent: Int, durationInMinutes: Int, enforceNew: Boolean, profile: Profile, tbrType: PumpSync.TemporaryBasalType, callback: Callback?): Boolean { if (!enforceNew && isRunning(CommandType.TEMPBASAL)) { callback?.result(executingNowError())?.run() return false @@ -321,7 +334,7 @@ open class CommandQueue @Inject constructor( removeAll(CommandType.TEMPBASAL) val percentAfterConstraints = constraintChecker.applyBasalPercentConstraints(Constraint(percent), profile).value() // add new command to queue - add(CommandTempBasalPercent(injector, percentAfterConstraints, durationInMinutes, enforceNew, profile, callback)) + add(CommandTempBasalPercent(injector, percentAfterConstraints, durationInMinutes, enforceNew, profile, tbrType, callback)) notifyAboutNewCommand() return true } @@ -370,8 +383,8 @@ open class CommandQueue @Inject constructor( } // returns true if command is queued - override fun setProfile(profile: Profile, callback: Callback?): Boolean { - if (isThisProfileSet(profile)) { + override fun setProfile(profile: Profile, hasNsId: Boolean, callback: Callback?): Boolean { + if (isThisProfileSet(profile) && repository.getEffectiveProfileSwitchActiveAt(dateUtil.now()).blockingGet() is ValueWrapper.Existing) { aapsLogger.debug(LTag.PUMPQUEUE, "Correct profile already set") callback?.result(PumpEnactResult(injector).success(true).enacted(false))?.run() return false @@ -385,9 +398,9 @@ open class CommandQueue @Inject constructor( } */ // Compare with pump limits - val basalValues = profile.basalValues + val basalValues = profile.getBasalValues() for (basalValue in basalValues) { - if (basalValue.value < activePlugin.get().activePump.pumpDescription.basalMinimumRate) { + if (basalValue.value < activePlugin.activePump.pumpDescription.basalMinimumRate) { val notification = Notification(Notification.BASAL_VALUE_BELOW_MINIMUM, resourceHelper.gs(R.string.basalvaluebelowminimum), Notification.URGENT) rxBus.send(EventNewNotification(notification)) callback?.result(PumpEnactResult(injector).success(false).enacted(false).comment(R.string.basalvaluebelowminimum))?.run() @@ -398,7 +411,7 @@ open class CommandQueue @Inject constructor( // remove all unfinished removeAll(CommandType.BASAL_PROFILE) // add new command to queue - add(CommandSetProfile(injector, profile, callback)) + add(CommandSetProfile(injector, profile, hasNsId, callback)) notifyAboutNewCommand() return true } @@ -554,16 +567,12 @@ open class CommandQueue @Inject constructor( } override fun isThisProfileSet(profile: Profile): Boolean { - val activePump = activePlugin.get().activePump - val current = profileFunction.getProfile() - return if (current != null) { - val result = activePump.isThisProfileSet(profile) - if (!result) { - aapsLogger.debug(LTag.PUMPQUEUE, "Current profile: $current") - aapsLogger.debug(LTag.PUMPQUEUE, "New profile: $profile") - } - result - } else true + val result = activePlugin.activePump.isThisProfileSet(profile) + if (!result) { + aapsLogger.debug(LTag.PUMPQUEUE, "Current profile: ${profileFunction.getProfile()}") + aapsLogger.debug(LTag.PUMPQUEUE, "New profile: $profile") + } + return result } private fun showBolusProgressDialog(insulin: Double, ctx: Context?) { diff --git a/app/src/main/java/info/nightscout/androidaps/queue/QueueThread.kt b/app/src/main/java/info/nightscout/androidaps/queue/QueueThread.kt index c1b4fa7aa5..34b64b8501 100644 --- a/app/src/main/java/info/nightscout/androidaps/queue/QueueThread.kt +++ b/app/src/main/java/info/nightscout/androidaps/queue/QueueThread.kt @@ -7,7 +7,7 @@ import android.os.SystemClock import info.nightscout.androidaps.Constants import info.nightscout.androidaps.R import info.nightscout.androidaps.events.EventPumpStatusChanged -import info.nightscout.androidaps.interfaces.ActivePluginProvider +import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.plugins.bus.RxBusWrapper @@ -22,7 +22,7 @@ class QueueThread internal constructor( context: Context, private val aapsLogger: AAPSLogger, private val rxBus: RxBusWrapper, - private val activePlugin: ActivePluginProvider, + private val activePlugin: ActivePlugin, private val resourceHelper: ResourceHelper, private val sp: SP ) : Thread() { diff --git a/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandBolus.kt b/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandBolus.kt index 084eb12609..e8d779634e 100644 --- a/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandBolus.kt +++ b/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandBolus.kt @@ -4,7 +4,7 @@ import dagger.android.HasAndroidInjector import info.nightscout.androidaps.R import info.nightscout.androidaps.data.DetailedBolusInfo import info.nightscout.androidaps.dialogs.BolusProgressDialog -import info.nightscout.androidaps.interfaces.ActivePluginProvider +import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.general.overview.events.EventDismissBolusProgressIfRunning @@ -19,7 +19,7 @@ class CommandBolus( ) : Command(injector, type, callback) { @Inject lateinit var rxBus: RxBusWrapper - @Inject lateinit var activePlugin: ActivePluginProvider + @Inject lateinit var activePlugin: ActivePlugin override fun execute() { val r = activePlugin.activePump.deliverTreatment(detailedBolusInfo) diff --git a/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandCancelExtendedBolus.kt b/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandCancelExtendedBolus.kt index 40f9dfb972..c15d0e7914 100644 --- a/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandCancelExtendedBolus.kt +++ b/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandCancelExtendedBolus.kt @@ -1,7 +1,7 @@ package info.nightscout.androidaps.queue.commands import dagger.android.HasAndroidInjector -import info.nightscout.androidaps.interfaces.ActivePluginProvider +import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.queue.Callback import javax.inject.Inject @@ -11,7 +11,7 @@ class CommandCancelExtendedBolus constructor( callback: Callback? ) : Command(injector, CommandType.EXTENDEDBOLUS, callback) { - @Inject lateinit var activePlugin: ActivePluginProvider + @Inject lateinit var activePlugin: ActivePlugin override fun execute() { val r = activePlugin.activePump.cancelExtendedBolus() diff --git a/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandCancelTempBasal.kt b/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandCancelTempBasal.kt index ff7182df9a..79835a9ecd 100644 --- a/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandCancelTempBasal.kt +++ b/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandCancelTempBasal.kt @@ -1,7 +1,7 @@ package info.nightscout.androidaps.queue.commands import dagger.android.HasAndroidInjector -import info.nightscout.androidaps.interfaces.ActivePluginProvider +import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.queue.Callback import javax.inject.Inject @@ -12,7 +12,7 @@ class CommandCancelTempBasal( callback: Callback? ) : Command(injector, CommandType.TEMPBASAL, callback) { - @Inject lateinit var activePlugin: ActivePluginProvider + @Inject lateinit var activePlugin: ActivePlugin override fun execute() { val r = activePlugin.activePump.cancelTempBasal(enforceNew) diff --git a/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandCustomCommand.kt b/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandCustomCommand.kt index f0df2754d3..e9f058ece9 100644 --- a/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandCustomCommand.kt +++ b/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandCustomCommand.kt @@ -1,7 +1,7 @@ package info.nightscout.androidaps.queue.commands import dagger.android.HasAndroidInjector -import info.nightscout.androidaps.interfaces.ActivePluginProvider +import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.queue.Callback import javax.inject.Inject @@ -12,7 +12,7 @@ class CommandCustomCommand( callback: Callback? ) : Command(injector, CommandType.CUSTOM_COMMAND, callback) { - @Inject lateinit var activePlugin: ActivePluginProvider + @Inject lateinit var activePlugin: ActivePlugin override fun execute() { val result = activePlugin.activePump.executeCustomCommand(customCommand) diff --git a/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandExtendedBolus.kt b/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandExtendedBolus.kt index 2e81520ee4..c1c67bfac4 100644 --- a/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandExtendedBolus.kt +++ b/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandExtendedBolus.kt @@ -1,7 +1,7 @@ package info.nightscout.androidaps.queue.commands import dagger.android.HasAndroidInjector -import info.nightscout.androidaps.interfaces.ActivePluginProvider +import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.queue.Callback import javax.inject.Inject @@ -13,7 +13,7 @@ class CommandExtendedBolus constructor( callback: Callback? ) : Command(injector, CommandType.EXTENDEDBOLUS, callback) { - @Inject lateinit var activePlugin: ActivePluginProvider + @Inject lateinit var activePlugin: ActivePlugin override fun execute() { val r = activePlugin.activePump.setExtendedBolus(insulin, durationInMinutes) diff --git a/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandInsightSetTBROverNotification.kt b/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandInsightSetTBROverNotification.kt index 7cf02fed74..ab8b8bd3e2 100644 --- a/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandInsightSetTBROverNotification.kt +++ b/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandInsightSetTBROverNotification.kt @@ -1,7 +1,7 @@ package info.nightscout.androidaps.queue.commands import dagger.android.HasAndroidInjector -import info.nightscout.androidaps.interfaces.ActivePluginProvider +import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.plugins.pump.insight.LocalInsightPlugin import info.nightscout.androidaps.queue.Callback import javax.inject.Inject @@ -12,7 +12,7 @@ class CommandInsightSetTBROverNotification constructor( callback: Callback? ) : Command(injector, CommandType.INSIGHT_SET_TBR_OVER_ALARM, callback) { - @Inject lateinit var activePlugin: ActivePluginProvider + @Inject lateinit var activePlugin: ActivePlugin override fun execute() { val pump = activePlugin.activePump diff --git a/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandLoadEvents.kt b/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandLoadEvents.kt index 7f09441ecc..f959eff033 100644 --- a/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandLoadEvents.kt +++ b/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandLoadEvents.kt @@ -1,8 +1,8 @@ package info.nightscout.androidaps.queue.commands import dagger.android.HasAndroidInjector -import info.nightscout.androidaps.interfaces.ActivePluginProvider -import info.nightscout.androidaps.interfaces.DanaRInterface +import info.nightscout.androidaps.interfaces.ActivePlugin +import info.nightscout.androidaps.interfaces.Dana import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.queue.Callback import javax.inject.Inject @@ -12,12 +12,12 @@ class CommandLoadEvents( callback: Callback? ) : Command(injector, CommandType.LOAD_EVENTS, callback) { - @Inject lateinit var activePlugin: ActivePluginProvider + @Inject lateinit var activePlugin: ActivePlugin override fun execute() { val pump = activePlugin.activePump - if (pump is DanaRInterface) { - val danaPump = pump as DanaRInterface + if (pump is Dana) { + val danaPump = pump as Dana val r = danaPump.loadEvents() aapsLogger.debug(LTag.PUMPQUEUE, "Result success: ${r.success} enacted: ${r.enacted}") callback?.result(r)?.run() diff --git a/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandLoadHistory.kt b/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandLoadHistory.kt index fb7f455ed4..431b4fe12e 100644 --- a/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandLoadHistory.kt +++ b/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandLoadHistory.kt @@ -1,8 +1,8 @@ package info.nightscout.androidaps.queue.commands import dagger.android.HasAndroidInjector -import info.nightscout.androidaps.interfaces.ActivePluginProvider -import info.nightscout.androidaps.interfaces.DanaRInterface +import info.nightscout.androidaps.interfaces.ActivePlugin +import info.nightscout.androidaps.interfaces.Dana import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.queue.Callback import javax.inject.Inject @@ -13,12 +13,12 @@ class CommandLoadHistory( callback: Callback? ) : Command(injector, CommandType.LOAD_HISTORY, callback) { - @Inject lateinit var activePlugin: ActivePluginProvider + @Inject lateinit var activePlugin: ActivePlugin override fun execute() { val pump = activePlugin.activePump - if (pump is DanaRInterface) { - val danaPump = pump as DanaRInterface + if (pump is Dana) { + val danaPump = pump as Dana val r = danaPump.loadHistory(type) aapsLogger.debug(LTag.PUMPQUEUE, "Result success: " + r.success + " enacted: " + r.enacted) callback?.result(r)?.run() diff --git a/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandLoadTDDs.kt b/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandLoadTDDs.kt index 9bf4a1ab7d..a38ab32962 100644 --- a/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandLoadTDDs.kt +++ b/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandLoadTDDs.kt @@ -1,7 +1,7 @@ package info.nightscout.androidaps.queue.commands import dagger.android.HasAndroidInjector -import info.nightscout.androidaps.interfaces.ActivePluginProvider +import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.queue.Callback import javax.inject.Inject @@ -11,7 +11,7 @@ class CommandLoadTDDs( callback: Callback? ) : Command(injector, CommandType.LOAD_TDD, callback) { - @Inject lateinit var activePlugin: ActivePluginProvider + @Inject lateinit var activePlugin: ActivePlugin override fun execute() { val pump = activePlugin.activePump diff --git a/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandReadStatus.kt b/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandReadStatus.kt index 052ad8a515..096178b06e 100644 --- a/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandReadStatus.kt +++ b/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandReadStatus.kt @@ -2,7 +2,7 @@ package info.nightscout.androidaps.queue.commands import dagger.android.HasAndroidInjector import info.nightscout.androidaps.data.PumpEnactResult -import info.nightscout.androidaps.interfaces.ActivePluginProvider +import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.queue.Callback import info.nightscout.androidaps.utils.LocalAlertUtils @@ -15,7 +15,7 @@ class CommandReadStatus( callback: Callback? ) : Command(injector, CommandType.READSTATUS, callback) { - @Inject lateinit var activePlugin: ActivePluginProvider + @Inject lateinit var activePlugin: ActivePlugin @Inject lateinit var localAlertUtils: LocalAlertUtils override fun execute() { diff --git a/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandSMBBolus.kt b/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandSMBBolus.kt index f18ae1db9f..d43493059b 100644 --- a/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandSMBBolus.kt +++ b/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandSMBBolus.kt @@ -4,7 +4,7 @@ import dagger.android.HasAndroidInjector import info.nightscout.androidaps.R import info.nightscout.androidaps.data.DetailedBolusInfo import info.nightscout.androidaps.data.PumpEnactResult -import info.nightscout.androidaps.interfaces.ActivePluginProvider +import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.queue.Callback import info.nightscout.androidaps.utils.DateUtil @@ -18,19 +18,19 @@ class CommandSMBBolus( ) : Command(injector, CommandType.SMB_BOLUS, callback) { @Inject lateinit var dateUtil: DateUtil - @Inject lateinit var activePlugin: ActivePluginProvider + @Inject lateinit var activePlugin: ActivePlugin override fun execute() { val r: PumpEnactResult - val lastBolusTime = activePlugin.activeTreatments.lastBolusTime - if (lastBolusTime != 0L && lastBolusTime + T.mins(3).msecs() > DateUtil.now()) { + val lastBolusTime = repository.getLastBolusRecord()?.timestamp ?: 0L + if (lastBolusTime != 0L && lastBolusTime + T.mins(3).msecs() > dateUtil.now()) { aapsLogger.debug(LTag.PUMPQUEUE, "SMB requested but still in 3 min interval") r = PumpEnactResult(injector).enacted(false).success(false).comment("SMB requested but still in 3 min interval") - } else if (detailedBolusInfo.deliverAt != 0L && detailedBolusInfo.deliverAt + T.mins(1).msecs() > System.currentTimeMillis()) { + } else if (detailedBolusInfo.deliverAtTheLatest != 0L && detailedBolusInfo.deliverAtTheLatest + T.mins(1).msecs() > System.currentTimeMillis()) { r = activePlugin.activePump.deliverTreatment(detailedBolusInfo) } else { r = PumpEnactResult(injector).enacted(false).success(false).comment("SMB request too old") - aapsLogger.debug(LTag.PUMPQUEUE, "SMB bolus canceled. deliverAt: " + dateUtil.dateAndTimeString(detailedBolusInfo.deliverAt)) + aapsLogger.debug(LTag.PUMPQUEUE, "SMB bolus canceled. deliverAt: " + dateUtil.dateAndTimeString(detailedBolusInfo.deliverAtTheLatest)) } aapsLogger.debug(LTag.PUMPQUEUE, "Result success: ${r.success} enacted: ${r.enacted}") callback?.result(r)?.run() diff --git a/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandSetProfile.kt b/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandSetProfile.kt index 89521346fe..7759448f6f 100644 --- a/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandSetProfile.kt +++ b/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandSetProfile.kt @@ -2,29 +2,34 @@ package info.nightscout.androidaps.queue.commands import dagger.android.HasAndroidInjector import info.nightscout.androidaps.R -import info.nightscout.androidaps.data.Profile import info.nightscout.androidaps.data.PumpEnactResult -import info.nightscout.androidaps.db.Source -import info.nightscout.androidaps.interfaces.ActivePluginProvider +import info.nightscout.androidaps.database.ValueWrapper +import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.CommandQueueProvider +import info.nightscout.androidaps.interfaces.Config import info.nightscout.androidaps.interfaces.PluginType +import info.nightscout.androidaps.interfaces.Profile import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorPlugin import info.nightscout.androidaps.queue.Callback +import info.nightscout.androidaps.utils.DateUtil import javax.inject.Inject class CommandSetProfile constructor( injector: HasAndroidInjector, private val profile: Profile, + private val hasNsId: Boolean, callback: Callback? ) : Command(injector, CommandType.BASAL_PROFILE, callback) { @Inject lateinit var smsCommunicatorPlugin: SmsCommunicatorPlugin - @Inject lateinit var activePlugin: ActivePluginProvider + @Inject lateinit var activePlugin: ActivePlugin + @Inject lateinit var dateUtil: DateUtil @Inject lateinit var commandQueue: CommandQueueProvider + @Inject lateinit var config: Config override fun execute() { - if (commandQueue.isThisProfileSet(profile)) { + if (commandQueue.isThisProfileSet(profile) && repository.getEffectiveProfileSwitchActiveAt(dateUtil.now()).blockingGet() is ValueWrapper.Existing) { aapsLogger.debug(LTag.PUMPQUEUE, "Correct profile already set. profile: $profile") callback?.result(PumpEnactResult(injector).success(true).enacted(false))?.run() return @@ -33,11 +38,10 @@ class CommandSetProfile constructor( aapsLogger.debug(LTag.PUMPQUEUE, "Result success: ${r.success} enacted: ${r.enacted} profile: $profile") callback?.result(r)?.run() // Send SMS notification if ProfileSwitch is coming from NS - val profileSwitch = activePlugin.activeTreatments.getProfileSwitchFromHistory(System.currentTimeMillis()) - if (profileSwitch != null && r.enacted && profileSwitch.source == Source.NIGHTSCOUT) { - if (smsCommunicatorPlugin.isEnabled(PluginType.GENERAL)) { + val profileSwitch = repository.getEffectiveProfileSwitchActiveAt(dateUtil.now()).blockingGet() + if (profileSwitch is ValueWrapper.Existing && r.enacted && hasNsId && !config.NSCLIENT) { + if (smsCommunicatorPlugin.isEnabled(PluginType.GENERAL)) smsCommunicatorPlugin.sendNotificationToAllNumbers(resourceHelper.gs(R.string.profile_set_ok)) - } } } diff --git a/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandSetUserSettings.kt b/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandSetUserSettings.kt index c63d32fc1c..abefe7e00e 100644 --- a/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandSetUserSettings.kt +++ b/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandSetUserSettings.kt @@ -1,8 +1,8 @@ package info.nightscout.androidaps.queue.commands import dagger.android.HasAndroidInjector -import info.nightscout.androidaps.interfaces.ActivePluginProvider -import info.nightscout.androidaps.interfaces.DanaRInterface +import info.nightscout.androidaps.interfaces.ActivePlugin +import info.nightscout.androidaps.interfaces.Dana import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.queue.Callback import javax.inject.Inject @@ -12,11 +12,11 @@ class CommandSetUserSettings( callback: Callback? ) : Command(injector, CommandType.SET_USER_SETTINGS, callback) { - @Inject lateinit var activePlugin: ActivePluginProvider + @Inject lateinit var activePlugin: ActivePlugin override fun execute() { val pump = activePlugin.activePump - if (pump is DanaRInterface) { + if (pump is Dana) { val r = pump.setUserOptions() aapsLogger.debug(LTag.PUMPQUEUE, "Result success: ${r.success} enacted: ${r.enacted}") callback?.result(r)?.run() diff --git a/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandStartPump.kt b/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandStartPump.kt index da911775bf..8987208bab 100644 --- a/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandStartPump.kt +++ b/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandStartPump.kt @@ -1,7 +1,7 @@ package info.nightscout.androidaps.queue.commands import dagger.android.HasAndroidInjector -import info.nightscout.androidaps.interfaces.ActivePluginProvider +import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.plugins.pump.insight.LocalInsightPlugin import info.nightscout.androidaps.queue.Callback import javax.inject.Inject @@ -11,7 +11,7 @@ class CommandStartPump( callback: Callback? ) : Command(injector, CommandType.START_PUMP, callback) { - @Inject lateinit var activePlugin: ActivePluginProvider + @Inject lateinit var activePlugin: ActivePlugin override fun execute() { val pump = activePlugin.activePump diff --git a/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandStopPump.kt b/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandStopPump.kt index 1f0390e97f..894573f435 100644 --- a/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandStopPump.kt +++ b/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandStopPump.kt @@ -1,7 +1,7 @@ package info.nightscout.androidaps.queue.commands import dagger.android.HasAndroidInjector -import info.nightscout.androidaps.interfaces.ActivePluginProvider +import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.plugins.pump.insight.LocalInsightPlugin import info.nightscout.androidaps.queue.Callback import javax.inject.Inject @@ -11,7 +11,7 @@ class CommandStopPump( callback: Callback? ) : Command(injector, CommandType.STOP_PUMP, callback) { - @Inject lateinit var activePlugin: ActivePluginProvider + @Inject lateinit var activePlugin: ActivePlugin override fun execute() { val pump = activePlugin.activePump diff --git a/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandTempBasalAbsolute.kt b/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandTempBasalAbsolute.kt index 488aa3bb7f..647a6feee6 100644 --- a/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandTempBasalAbsolute.kt +++ b/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandTempBasalAbsolute.kt @@ -1,8 +1,9 @@ package info.nightscout.androidaps.queue.commands import dagger.android.HasAndroidInjector -import info.nightscout.androidaps.data.Profile -import info.nightscout.androidaps.interfaces.ActivePluginProvider +import info.nightscout.androidaps.interfaces.Profile +import info.nightscout.androidaps.interfaces.ActivePlugin +import info.nightscout.androidaps.interfaces.PumpSync import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.queue.Callback import javax.inject.Inject @@ -13,13 +14,14 @@ class CommandTempBasalAbsolute( private val durationInMinutes: Int, private val enforceNew: Boolean, private val profile: Profile, + private val tbrType: PumpSync.TemporaryBasalType, callback: Callback? ) : Command(injector, CommandType.TEMPBASAL, callback) { - @Inject lateinit var activePlugin: ActivePluginProvider + @Inject lateinit var activePlugin: ActivePlugin override fun execute() { - val r = activePlugin.activePump.setTempBasalAbsolute(absoluteRate, durationInMinutes, profile, enforceNew) + val r = activePlugin.activePump.setTempBasalAbsolute(absoluteRate, durationInMinutes, profile, enforceNew, tbrType) aapsLogger.debug(LTag.PUMPQUEUE, "Result rate: $absoluteRate durationInMinutes: $durationInMinutes success: ${r.success} enacted: ${r.enacted}") callback?.result(r)?.run() } diff --git a/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandTempBasalPercent.kt b/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandTempBasalPercent.kt index 8bfdbb73b7..259ae91b5f 100644 --- a/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandTempBasalPercent.kt +++ b/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandTempBasalPercent.kt @@ -1,8 +1,9 @@ package info.nightscout.androidaps.queue.commands import dagger.android.HasAndroidInjector -import info.nightscout.androidaps.data.Profile -import info.nightscout.androidaps.interfaces.ActivePluginProvider +import info.nightscout.androidaps.interfaces.Profile +import info.nightscout.androidaps.interfaces.ActivePlugin +import info.nightscout.androidaps.interfaces.PumpSync import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.queue.Callback import javax.inject.Inject @@ -13,13 +14,14 @@ class CommandTempBasalPercent( private val durationInMinutes: Int, private val enforceNew: Boolean, private val profile: Profile, + private val tbrType: PumpSync.TemporaryBasalType, callback: Callback? ) : Command(injector, CommandType.TEMPBASAL, callback) { - @Inject lateinit var activePlugin: ActivePluginProvider + @Inject lateinit var activePlugin: ActivePlugin override fun execute() { - val r = activePlugin.activePump.setTempBasalPercent(percent, durationInMinutes, profile, enforceNew) + val r = activePlugin.activePump.setTempBasalPercent(percent, durationInMinutes, profile, enforceNew, tbrType) aapsLogger.debug(LTag.PUMPQUEUE, "Result percent: $percent durationInMinutes: $durationInMinutes success: ${r.success} enacted: ${r.enacted}") callback?.result(r)?.run() } diff --git a/app/src/main/java/info/nightscout/androidaps/receivers/DataReceiver.kt b/app/src/main/java/info/nightscout/androidaps/receivers/DataReceiver.kt index 89a869837f..eb1c5f1837 100644 --- a/app/src/main/java/info/nightscout/androidaps/receivers/DataReceiver.kt +++ b/app/src/main/java/info/nightscout/androidaps/receivers/DataReceiver.kt @@ -26,7 +26,7 @@ open class DataReceiver : DaggerBroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { super.onReceive(context, intent) val bundle = intent.extras ?: return - aapsLogger.debug(LTag.DATASERVICE, "onReceive ${intent.action} ${BundleLogger.log(bundle)}") + aapsLogger.debug(LTag.DATABASE, "onReceive ${intent.action} ${BundleLogger.log(bundle)}") when (intent.action) { diff --git a/app/src/main/java/info/nightscout/androidaps/receivers/DataWorker.kt b/app/src/main/java/info/nightscout/androidaps/receivers/DataWorker.kt index d46491b15e..fed3db6598 100644 --- a/app/src/main/java/info/nightscout/androidaps/receivers/DataWorker.kt +++ b/app/src/main/java/info/nightscout/androidaps/receivers/DataWorker.kt @@ -32,6 +32,12 @@ class DataWorker @Inject constructor( return value as Bundle? } + @Synchronized fun pickupObject(key: Long): Any? { + val value = store[key] + store.remove(key) + return value + } + @Synchronized fun pickupString(key: Long): String? { val value = store[key] store.remove(key) diff --git a/app/src/main/java/info/nightscout/androidaps/receivers/KeepAliveReceiver.kt b/app/src/main/java/info/nightscout/androidaps/receivers/KeepAliveReceiver.kt index eb20d8acb4..1b78360966 100644 --- a/app/src/main/java/info/nightscout/androidaps/receivers/KeepAliveReceiver.kt +++ b/app/src/main/java/info/nightscout/androidaps/receivers/KeepAliveReceiver.kt @@ -3,23 +3,31 @@ package info.nightscout.androidaps.receivers import android.app.AlarmManager import android.app.PendingIntent import android.app.PendingIntent.CanceledException +import android.app.PendingIntent.FLAG_IMMUTABLE import android.content.Context import android.content.Intent -import android.os.PowerManager import android.os.SystemClock +import androidx.work.OneTimeWorkRequest +import androidx.work.WorkManager +import androidx.work.Worker +import androidx.work.WorkerParameters import dagger.android.DaggerBroadcastReceiver +import dagger.android.HasAndroidInjector import info.nightscout.androidaps.BuildConfig -import info.nightscout.androidaps.Config -import info.nightscout.androidaps.events.EventProfileNeedsUpdate -import info.nightscout.androidaps.interfaces.ActivePluginProvider +import info.nightscout.androidaps.data.ProfileSealed +import info.nightscout.androidaps.interfaces.Config +import info.nightscout.androidaps.database.AppRepository +import info.nightscout.androidaps.events.EventProfileSwitchChanged +import info.nightscout.androidaps.extensions.buildDeviceStatus +import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.CommandQueueProvider +import info.nightscout.androidaps.interfaces.IobCobCalculator import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin import info.nightscout.androidaps.plugins.bus.RxBusWrapper -import info.nightscout.androidaps.plugins.general.nsclient.NSUpload -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin +import info.nightscout.androidaps.plugins.configBuilder.RunningConfiguration import info.nightscout.androidaps.queue.commands.Command import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.FabricPrivacy @@ -31,42 +39,112 @@ import kotlin.math.abs class KeepAliveReceiver : DaggerBroadcastReceiver() { @Inject lateinit var aapsLogger: AAPSLogger - @Inject lateinit var rxBus: RxBusWrapper - @Inject lateinit var activePlugin: ActivePluginProvider - @Inject lateinit var commandQueue: CommandQueueProvider - @Inject lateinit var profileFunction: ProfileFunction - @Inject lateinit var loopPlugin: LoopPlugin - @Inject lateinit var iobCobCalculatorPlugin: IobCobCalculatorPlugin - @Inject lateinit var localAlertUtils: LocalAlertUtils - @Inject lateinit var fabricPrivacy: FabricPrivacy - @Inject lateinit var receiverStatusStore: ReceiverStatusStore - @Inject lateinit var config: Config - @Inject lateinit var nsUpload: NSUpload - @Inject lateinit var dateUtil: DateUtil companion object { private val KEEP_ALIVE_MILLISECONDS = T.mins(5).msecs() - private val STATUS_UPDATE_FREQUENCY = T.mins(15).msecs() - private val IOB_UPDATE_FREQUENCY_IN_MINS = 5L - - private var lastReadStatus: Long = 0 - private var lastRun: Long = 0 - private var lastIobUpload: Long = 0 - } override fun onReceive(context: Context, intent: Intent) { super.onReceive(context, intent) aapsLogger.debug(LTag.CORE, "KeepAlive received") - val pm = context.getSystemService(Context.POWER_SERVICE) as PowerManager - val wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "AndroidAPS:KeepAliveReceiver") - wl.acquire(T.mins(2).msecs()) - localAlertUtils.shortenSnoozeInterval() - localAlertUtils.checkStaleBGAlert() - checkPump() - checkAPS() - wl.release() + + WorkManager.getInstance(context) + .enqueue(OneTimeWorkRequest.Builder(KeepAliveWorker::class.java).build()) + } + + class KeepAliveWorker( + context: Context, + params: WorkerParameters + ) : Worker(context, params) { + + @Inject lateinit var aapsLogger: AAPSLogger + @Inject lateinit var localAlertUtils: LocalAlertUtils + @Inject lateinit var repository: AppRepository + @Inject lateinit var config: Config + @Inject lateinit var iobCobCalculator: IobCobCalculator + @Inject lateinit var loopPlugin: LoopPlugin + @Inject lateinit var dateUtil: DateUtil + @Inject lateinit var activePlugin: ActivePlugin + @Inject lateinit var profileFunction: ProfileFunction + @Inject lateinit var runningConfiguration: RunningConfiguration + @Inject lateinit var receiverStatusStore: ReceiverStatusStore + @Inject lateinit var rxBus: RxBusWrapper + @Inject lateinit var commandQueue: CommandQueueProvider + @Inject lateinit var fabricPrivacy: FabricPrivacy + + init { + (context.applicationContext as HasAndroidInjector).androidInjector().inject(this) + } + + companion object { + + private val STATUS_UPDATE_FREQUENCY = T.mins(15).msecs() + private const val IOB_UPDATE_FREQUENCY_IN_MINUTES = 5L + + private var lastReadStatus: Long = 0 + private var lastRun: Long = 0 + private var lastIobUpload: Long = 0 + + } + + override fun doWork(): Result { + localAlertUtils.shortenSnoozeInterval() + localAlertUtils.checkStaleBGAlert() + checkPump() + checkAPS() + return Result.success() + } + + // Usually deviceStatus is uploaded through LoopPlugin after every loop cycle. + // if there is no BG available, we have to upload anyway to have correct + // IOB displayed in NS + private fun checkAPS() { + var shouldUploadStatus = false + if (config.NSCLIENT) return + if (config.PUMPCONTROL) shouldUploadStatus = true + else if (!loopPlugin.isEnabled() || iobCobCalculator.ads.actualBg() == null) + shouldUploadStatus = true + else if (dateUtil.isOlderThan(activePlugin.activeAPS.lastAPSRun, 5)) shouldUploadStatus = true + if (dateUtil.isOlderThan(lastIobUpload, IOB_UPDATE_FREQUENCY_IN_MINUTES) && shouldUploadStatus) { + lastIobUpload = dateUtil.now() + buildDeviceStatus(dateUtil, loopPlugin, iobCobCalculator, profileFunction, + activePlugin.activePump, receiverStatusStore, runningConfiguration, + BuildConfig.VERSION_NAME + "-" + BuildConfig.BUILDVERSION)?.also { + repository.insert(it) + } + } + } + + private fun checkPump() { + val pump = activePlugin.activePump + val ps = profileFunction.getRequestedProfile() ?: return + val profile = ProfileSealed.PS(ps) + val lastConnection = pump.lastDataTime() + val isStatusOutdated = lastConnection + STATUS_UPDATE_FREQUENCY < System.currentTimeMillis() + val isBasalOutdated = abs(profile.getBasal() - pump.baseBasalRate) > pump.pumpDescription.basalStep + aapsLogger.debug(LTag.CORE, "Last connection: " + dateUtil.dateAndTimeString(lastConnection)) + // sometimes keep alive broadcast stops + // as as workaround test if readStatus was requested before an alarm is generated + if (lastReadStatus != 0L && lastReadStatus > System.currentTimeMillis() - T.mins(5).msecs()) { + localAlertUtils.checkPumpUnreachableAlarm(lastConnection, isStatusOutdated, loopPlugin.isDisconnected) + } + if (!pump.isThisProfileSet(profile) && !commandQueue.isRunning(Command.CommandType.BASAL_PROFILE) + || profileFunction.getProfile() == null) { + rxBus.send(EventProfileSwitchChanged()) + } else if (isStatusOutdated && !pump.isBusy()) { + lastReadStatus = System.currentTimeMillis() + commandQueue.readStatus("KeepAlive. Status outdated.", null) + } else if (isBasalOutdated && !pump.isBusy()) { + lastReadStatus = System.currentTimeMillis() + commandQueue.readStatus("KeepAlive. Basal outdated.", null) + } + if (lastRun != 0L && System.currentTimeMillis() - lastRun > T.mins(10).msecs()) { + aapsLogger.error(LTag.CORE, "KeepAlive fail") + fabricPrivacy.logCustom("KeepAliveFail") + } + lastRun = System.currentTimeMillis() + } } class KeepAliveManager @Inject constructor( @@ -82,7 +160,7 @@ class KeepAliveReceiver : DaggerBroadcastReceiver() { localAlertUtils.preSnoozeAlarms() val am = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager val i = Intent(context, KeepAliveReceiver::class.java) - val pi = PendingIntent.getBroadcast(context, 0, i, 0) + val pi = PendingIntent.getBroadcast(context, 0, i, FLAG_IMMUTABLE) try { pi.send() } catch (e: CanceledException) { @@ -94,53 +172,9 @@ class KeepAliveReceiver : DaggerBroadcastReceiver() { fun cancelAlarm(context: Context) { aapsLogger.debug(LTag.CORE, "KeepAlive canceled") val intent = Intent(context, KeepAliveReceiver::class.java) - val sender = PendingIntent.getBroadcast(context, 0, intent, 0) + val sender = PendingIntent.getBroadcast(context, 0, intent, FLAG_IMMUTABLE) val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager alarmManager.cancel(sender) } } - - // Usually deviceStatus is uploaded through LoopPlugin after every loop cycle. - // if there is no BG available, we have to upload anyway to have correct - // IOB displayed in NS - private fun checkAPS() { - var shouldUploadStatus = false - if (config.NSCLIENT) return - if (config.PUMPCONTROL) shouldUploadStatus = true - else if (!loopPlugin.isEnabled() || iobCobCalculatorPlugin.actualBg() == null) - shouldUploadStatus = true - else if (DateUtil.isOlderThan(activePlugin.activeAPS.lastAPSRun, 5)) shouldUploadStatus = true - if (DateUtil.isOlderThan(lastIobUpload, IOB_UPDATE_FREQUENCY_IN_MINS) && shouldUploadStatus) { - lastIobUpload = DateUtil.now() - nsUpload.uploadDeviceStatus(loopPlugin, iobCobCalculatorPlugin, profileFunction, activePlugin.activePump, receiverStatusStore, BuildConfig.VERSION_NAME + "-" + BuildConfig.BUILDVERSION) - } - } - - private fun checkPump() { - val pump = activePlugin.activePump - val profile = profileFunction.getProfile() ?: return - val lastConnection = pump.lastDataTime() - val isStatusOutdated = lastConnection + STATUS_UPDATE_FREQUENCY < System.currentTimeMillis() - val isBasalOutdated = abs(profile.basal - pump.baseBasalRate) > pump.pumpDescription.basalStep - aapsLogger.debug(LTag.CORE, "Last connection: " + dateUtil.dateAndTimeString(lastConnection)) - // sometimes keep alive broadcast stops - // as as workaround test if readStatus was requested before an alarm is generated - if (lastReadStatus != 0L && lastReadStatus > System.currentTimeMillis() - T.mins(5).msecs()) { - localAlertUtils.checkPumpUnreachableAlarm(lastConnection, isStatusOutdated, loopPlugin.isDisconnected) - } - if (!pump.isThisProfileSet(profile) && !commandQueue.isRunning(Command.CommandType.BASAL_PROFILE)) { - rxBus.send(EventProfileNeedsUpdate()) - } else if (isStatusOutdated && !pump.isBusy()) { - lastReadStatus = System.currentTimeMillis() - commandQueue.readStatus("KeepAlive. Status outdated.", null) - } else if (isBasalOutdated && !pump.isBusy()) { - lastReadStatus = System.currentTimeMillis() - commandQueue.readStatus("KeepAlive. Basal outdated.", null) - } - if (lastRun != 0L && System.currentTimeMillis() - lastRun > T.mins(10).msecs()) { - aapsLogger.error(LTag.CORE, "KeepAlive fail") - fabricPrivacy.logCustom("KeepAliveFail") - } - lastRun = System.currentTimeMillis() - } } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/receivers/TimeDateOrTZChangeReceiver.kt b/app/src/main/java/info/nightscout/androidaps/receivers/TimeDateOrTZChangeReceiver.kt index bcf3091c86..51bf087cd0 100644 --- a/app/src/main/java/info/nightscout/androidaps/receivers/TimeDateOrTZChangeReceiver.kt +++ b/app/src/main/java/info/nightscout/androidaps/receivers/TimeDateOrTZChangeReceiver.kt @@ -4,8 +4,8 @@ import android.content.Context import android.content.Intent import com.google.gson.Gson import dagger.android.DaggerBroadcastReceiver -import info.nightscout.androidaps.interfaces.ActivePluginProvider -import info.nightscout.androidaps.interfaces.PumpInterface +import info.nightscout.androidaps.interfaces.ActivePlugin +import info.nightscout.androidaps.interfaces.Pump import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.BundleLogger import info.nightscout.androidaps.logging.LTag @@ -15,7 +15,7 @@ import javax.inject.Inject class TimeDateOrTZChangeReceiver : DaggerBroadcastReceiver() { @Inject lateinit var aapsLogger: AAPSLogger - @Inject lateinit var activePlugin: ActivePluginProvider + @Inject lateinit var activePlugin: ActivePlugin val gson: Gson = Gson() private var isDST = false @@ -37,7 +37,7 @@ class TimeDateOrTZChangeReceiver : DaggerBroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { super.onReceive(context, intent) val action = intent.action - val activePump: PumpInterface = activePlugin.activePump + val activePump: Pump = activePlugin.activePump aapsLogger.debug(LTag.PUMP, "TimeDateOrTZChangeReceiver::Date, Time and/or TimeZone changed. [action={}]", action) aapsLogger.debug(LTag.PUMP, "TimeDateOrTZChangeReceiver::Intent::{}", BundleLogger.log(intent.extras)) diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/SWDefinition.kt b/app/src/main/java/info/nightscout/androidaps/setupwizard/SWDefinition.kt index 600a4842e3..b65a284149 100644 --- a/app/src/main/java/info/nightscout/androidaps/setupwizard/SWDefinition.kt +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/SWDefinition.kt @@ -7,28 +7,20 @@ import android.net.Uri import android.provider.Settings import androidx.appcompat.app.AppCompatActivity import dagger.android.HasAndroidInjector -import info.nightscout.androidaps.Config import info.nightscout.androidaps.Constants import info.nightscout.androidaps.R +import info.nightscout.androidaps.data.ProfileSealed import info.nightscout.androidaps.dialogs.ProfileSwitchDialog import info.nightscout.androidaps.events.EventPumpStatusChanged -import info.nightscout.androidaps.interfaces.ActivePluginProvider -import info.nightscout.androidaps.interfaces.CommandQueueProvider -import info.nightscout.androidaps.interfaces.ImportExportPrefsInterface -import info.nightscout.androidaps.interfaces.PluginType -import info.nightscout.androidaps.interfaces.ProfileFunction +import info.nightscout.androidaps.interfaces.* 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.constraints.objectives.ObjectivesFragment import info.nightscout.androidaps.plugins.constraints.objectives.ObjectivesPlugin import info.nightscout.androidaps.plugins.general.nsclient.NSClientPlugin import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientStatus -import info.nightscout.androidaps.plugins.general.nsclient.services.NSClientService import info.nightscout.androidaps.plugins.profile.local.LocalProfileFragment import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin -import info.nightscout.androidaps.plugins.profile.ns.NSProfileFragment -import info.nightscout.androidaps.plugins.profile.ns.NSProfilePlugin import info.nightscout.androidaps.plugins.pump.common.events.EventRileyLinkDeviceStatusChange import info.nightscout.androidaps.plugins.pump.omnipod.dash.OmnipodDashPumpPlugin import info.nightscout.androidaps.plugins.pump.omnipod.eros.OmnipodErosPumpPlugin @@ -52,14 +44,13 @@ class SWDefinition @Inject constructor( private val sp: SP, private val profileFunction: ProfileFunction, private val localProfilePlugin: LocalProfilePlugin, - private val activePlugin: ActivePluginProvider, + private val activePlugin: ActivePlugin, private val commandQueue: CommandQueueProvider, private val objectivesPlugin: ObjectivesPlugin, - private val configBuilderPlugin: ConfigBuilderPlugin, + private val configBuilder: ConfigBuilder, private val loopPlugin: LoopPlugin, private val nsClientPlugin: NSClientPlugin, - private val nsProfilePlugin: NSProfilePlugin, - private val importExportPrefs: ImportExportPrefsInterface, + private val importExportPrefs: ImportExportPrefs, private val androidPermission: AndroidPermission, private val cryptoUtil: CryptoUtil, private val config: Config @@ -174,7 +165,7 @@ class SWDefinition @Inject constructor( .add(SWButton(injector) .text(R.string.enable_nsclient) .action { - configBuilderPlugin.performPluginSwitch(nsClientPlugin, true, PluginType.GENERAL) + configBuilder.performPluginSwitch(nsClientPlugin, true, PluginType.GENERAL) rxBus.send(EventSWUpdate(true)) } .visibility { !nsClientPlugin.isEnabled(PluginType.GENERAL) }) @@ -194,8 +185,8 @@ class SWDefinition @Inject constructor( .label(R.string.status) .initialStatus(nsClientPlugin.status) ) - .validator { nsClientPlugin.nsClientService != null && NSClientService.isConnected && NSClientService.hasWriteAuth } - .visibility { !(nsClientPlugin.nsClientService != null && NSClientService.isConnected && NSClientService.hasWriteAuth) } + .validator { nsClientPlugin.nsClientService?.isConnected == true && nsClientPlugin.nsClientService?.hasWriteAuth == true } + .visibility { !(nsClientPlugin.nsClientService?.isConnected == true && nsClientPlugin.nsClientService?.hasWriteAuth == true) } private val screenPatientName = SWScreen(injector, R.string.patient_name) .skippable(true) .add(SWInfoText(injector) @@ -259,28 +250,15 @@ class SWDefinition @Inject constructor( .option(PluginType.BGSOURCE, R.string.configbuilder_bgsource_description) .label(R.string.configbuilder_bgsource)) .add(SWBreak(injector)) - private val screenProfile = SWScreen(injector, R.string.configbuilder_profile) - .skippable(false) - .add(SWInfoText(injector) - .label(R.string.setupwizard_profile_description)) - .add(SWBreak(injector)) - .add(SWPlugin(injector, this) - .option(PluginType.PROFILE, R.string.configbuilder_profile_description) - .label(R.string.configbuilder_profile)) - private val screenNsProfile = SWScreen(injector, R.string.nsprofile) - .skippable(false) - .add(SWInfoText(injector) - .label(R.string.adjustprofileinns)) - .add(SWFragment(injector, this) - .add(NSProfileFragment())) - .validator { nsProfilePlugin.profile != null && nsProfilePlugin.profile!!.getDefaultProfile() != null && nsProfilePlugin.profile!!.getDefaultProfile()!!.isValid("StartupWizard") } - .visibility { nsProfilePlugin.isEnabled(PluginType.PROFILE) } private val screenLocalProfile = SWScreen(injector, R.string.localprofile) .skippable(false) .add(SWFragment(injector, this) .add(LocalProfileFragment())) - .validator { localProfilePlugin.profile?.getDefaultProfile()?.isValid("StartupWizard") == true } - .visibility { localProfilePlugin.isEnabled(PluginType.PROFILE) } + .validator { + localProfilePlugin.profile?.getDefaultProfile()?.let { ProfileSealed.Pure(it).isValid("StartupWizard", activePlugin.activePump, config, resourceHelper, rxBus) } + ?: false + } + .visibility { localProfilePlugin.isEnabled() } private val screenProfileSwitch = SWScreen(injector, R.string.careportal_profileswitch) .skippable(false) .add(SWInfoText(injector) @@ -360,7 +338,7 @@ class SWDefinition @Inject constructor( .add(SWButton(injector) .text(R.string.enableloop) .action { - configBuilderPlugin.performPluginSwitch(loopPlugin, true, PluginType.LOOP) + configBuilder.performPluginSwitch(loopPlugin, true, PluginType.LOOP) rxBus.send(EventSWUpdate(true)) } .visibility { !loopPlugin.isEnabled(PluginType.LOOP) }) @@ -404,8 +382,6 @@ class SWDefinition @Inject constructor( .add(screenAge) .add(screenInsulin) .add(screenBgSource) - .add(screenProfile) - .add(screenNsProfile) .add(screenLocalProfile) .add(screenProfileSwitch) .add(screenPump) @@ -433,8 +409,6 @@ class SWDefinition @Inject constructor( .add(screenAge) .add(screenInsulin) .add(screenBgSource) - .add(screenProfile) - .add(screenNsProfile) .add(screenLocalProfile) .add(screenProfileSwitch) .add(screenPump) diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/SetupWizardActivity.kt b/app/src/main/java/info/nightscout/androidaps/setupwizard/SetupWizardActivity.kt index 78b21df59d..a1a0a76c5a 100644 --- a/app/src/main/java/info/nightscout/androidaps/setupwizard/SetupWizardActivity.kt +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/SetupWizardActivity.kt @@ -9,7 +9,7 @@ import info.nightscout.androidaps.MainActivity import info.nightscout.androidaps.R import info.nightscout.androidaps.activities.NoSplashAppCompatActivity import info.nightscout.androidaps.databinding.ActivitySetupwizardBinding -import info.nightscout.androidaps.events.EventProfileNeedsUpdate +import info.nightscout.androidaps.events.EventProfileSwitchChanged import info.nightscout.androidaps.events.EventProfileStoreChanged import info.nightscout.androidaps.events.EventPumpStatusChanged import info.nightscout.androidaps.logging.UserEntryLogger @@ -94,7 +94,7 @@ class SetupWizardActivity : NoSplashAppCompatActivity() { .subscribe({ updateButtons() }, fabricPrivacy::logException) ) disposable.add(rxBus - .toObservable(EventProfileNeedsUpdate::class.java) + .toObservable(EventProfileSwitchChanged::class.java) .observeOn(aapsSchedulers.main) .subscribe({ updateButtons() }, fabricPrivacy::logException) ) diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWEditNumberWithUnits.kt b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWEditNumberWithUnits.kt index 7f9934f2c8..c6f6f87935 100644 --- a/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWEditNumberWithUnits.kt +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWEditNumberWithUnits.kt @@ -7,12 +7,12 @@ import android.view.View import android.widget.LinearLayout import android.widget.TextView import dagger.android.HasAndroidInjector -import info.nightscout.androidaps.Constants -import info.nightscout.androidaps.data.Profile +import info.nightscout.androidaps.interfaces.GlucoseUnit +import info.nightscout.androidaps.interfaces.Profile import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.setupwizard.SWNumberValidator -import info.nightscout.androidaps.utils.ui.NumberPicker import info.nightscout.androidaps.utils.SafeParse +import info.nightscout.androidaps.utils.ui.NumberPicker import java.text.DecimalFormat import javax.inject.Inject @@ -43,7 +43,7 @@ class SWEditNumberWithUnits(injector: HasAndroidInjector, private val init: Doub var initValue = sp.getDouble(preferenceId, init) initValue = Profile.toCurrentUnits(profileFunction.getUnits(), initValue) val numberPicker = NumberPicker(context) - if (profileFunction.getUnits() == Constants.MMOL) numberPicker.setParams(initValue, min, max, 0.1, DecimalFormat("0.0"), false, null, watcher) else numberPicker.setParams(initValue, min * 18, max * 18, 1.0, DecimalFormat("0"), false, null, watcher) + if (profileFunction.getUnits() == GlucoseUnit.MMOL) numberPicker.setParams(initValue, min, max, 0.1, DecimalFormat("0.0"), false, null, watcher) else numberPicker.setParams(initValue, min * 18, max * 18, 1.0, DecimalFormat("0"), false, null, watcher) layout.addView(numberPicker) val c = TextView(context) diff --git a/app/src/main/java/info/nightscout/androidaps/skins/SkinButtonsOn.kt b/app/src/main/java/info/nightscout/androidaps/skins/SkinButtonsOn.kt index 829c1e6d63..8c3595d069 100644 --- a/app/src/main/java/info/nightscout/androidaps/skins/SkinButtonsOn.kt +++ b/app/src/main/java/info/nightscout/androidaps/skins/SkinButtonsOn.kt @@ -1,6 +1,6 @@ package info.nightscout.androidaps.skins -import info.nightscout.androidaps.Config +import info.nightscout.androidaps.interfaces.Config import info.nightscout.androidaps.R import javax.inject.Inject import javax.inject.Singleton diff --git a/app/src/main/java/info/nightscout/androidaps/skins/SkinClassic.kt b/app/src/main/java/info/nightscout/androidaps/skins/SkinClassic.kt index 3e27443e74..a6ae3b51b9 100644 --- a/app/src/main/java/info/nightscout/androidaps/skins/SkinClassic.kt +++ b/app/src/main/java/info/nightscout/androidaps/skins/SkinClassic.kt @@ -3,7 +3,7 @@ package info.nightscout.androidaps.skins import android.util.DisplayMetrics import android.view.View import android.widget.LinearLayout -import info.nightscout.androidaps.Config +import info.nightscout.androidaps.interfaces.Config import info.nightscout.androidaps.R import javax.inject.Inject import javax.inject.Singleton diff --git a/app/src/main/java/info/nightscout/androidaps/skins/SkinLargeDisplay.kt b/app/src/main/java/info/nightscout/androidaps/skins/SkinLargeDisplay.kt index 62764b00bc..751e0b3ad0 100644 --- a/app/src/main/java/info/nightscout/androidaps/skins/SkinLargeDisplay.kt +++ b/app/src/main/java/info/nightscout/androidaps/skins/SkinLargeDisplay.kt @@ -3,7 +3,7 @@ package info.nightscout.androidaps.skins import android.util.DisplayMetrics import android.view.View import android.widget.LinearLayout -import info.nightscout.androidaps.Config +import info.nightscout.androidaps.interfaces.Config import info.nightscout.androidaps.R import javax.inject.Inject import javax.inject.Singleton diff --git a/app/src/main/java/info/nightscout/androidaps/skins/SkinLowRes.kt b/app/src/main/java/info/nightscout/androidaps/skins/SkinLowRes.kt index c2fbc2aba1..c4384db93b 100644 --- a/app/src/main/java/info/nightscout/androidaps/skins/SkinLowRes.kt +++ b/app/src/main/java/info/nightscout/androidaps/skins/SkinLowRes.kt @@ -3,7 +3,7 @@ package info.nightscout.androidaps.skins import android.util.DisplayMetrics import android.view.View import android.widget.LinearLayout -import info.nightscout.androidaps.Config +import info.nightscout.androidaps.interfaces.Config import info.nightscout.androidaps.R import javax.inject.Inject import javax.inject.Singleton diff --git a/app/src/main/java/info/nightscout/androidaps/utils/ActivityMonitor.kt b/app/src/main/java/info/nightscout/androidaps/utils/ActivityMonitor.kt index 7364bef9a5..313d82308b 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/ActivityMonitor.kt +++ b/app/src/main/java/info/nightscout/androidaps/utils/ActivityMonitor.kt @@ -16,7 +16,8 @@ import javax.inject.Singleton class ActivityMonitor @Inject constructor( private var aapsLogger: AAPSLogger, private val resourceHelper: ResourceHelper, - private var sp: SP + private val sp: SP, + private val dateUtil: DateUtil ) : Application.ActivityLifecycleCallbacks { override fun onActivityPaused(activity: Activity?) { @@ -26,10 +27,10 @@ class ActivityMonitor @Inject constructor( aapsLogger.debug(LTag.UI, "onActivityPaused: $name resumed == 0") return } - val elapsed = DateUtil.now() - resumed + val elapsed = dateUtil.now() - resumed val total = sp.getLong("Monitor_" + name + "_total", 0) if (total == 0L) { - sp.putLong("Monitor_" + name + "_start", DateUtil.now()) + sp.putLong("Monitor_" + name + "_start", dateUtil.now()) } sp.putLong("Monitor_" + name + "_total", total + elapsed) aapsLogger.debug(LTag.UI, "onActivityPaused: $name elapsed=$elapsed total=${total + elapsed}") @@ -38,7 +39,7 @@ class ActivityMonitor @Inject constructor( override fun onActivityResumed(activity: Activity?) { val name = activity?.javaClass?.simpleName ?: return aapsLogger.debug(LTag.UI, "onActivityResumed: $name") - sp.putLong("Monitor_" + name + "_" + "resumed", DateUtil.now()) + sp.putLong("Monitor_" + name + "_" + "resumed", dateUtil.now()) } override fun onActivityStarted(activity: Activity?) { @@ -63,9 +64,9 @@ class ActivityMonitor @Inject constructor( if (key.startsWith("Monitor") && key.endsWith("total")) { val v = if (value is Long) value else SafeParse.stringToLong(value as String) val activity = key.split("_")[1].replace("Activity", "") - val duration = DateUtil.niceTimeScalar(v as Long, resourceHelper) + val duration = dateUtil.niceTimeScalar(v as Long, resourceHelper) val start = sp.getLong(key.replace("total", "start"), 0) - val days = T.msecs(DateUtil.now() - start).days() + val days = T.msecs(dateUtil.now() - start).days() result += resourceHelper.gs(R.string.activitymonitorformat, activity, duration, days) } return result diff --git a/app/src/main/java/info/nightscout/androidaps/utils/CarbTimer.kt b/app/src/main/java/info/nightscout/androidaps/utils/CarbTimer.kt index 7bc1205ebc..3509e820df 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/CarbTimer.kt +++ b/app/src/main/java/info/nightscout/androidaps/utils/CarbTimer.kt @@ -1,8 +1,8 @@ package info.nightscout.androidaps.utils import dagger.android.HasAndroidInjector -import info.nightscout.androidaps.Constants import info.nightscout.androidaps.R +import info.nightscout.androidaps.interfaces.GlucoseUnit import info.nightscout.androidaps.plugins.general.automation.AutomationEvent import info.nightscout.androidaps.plugins.general.automation.AutomationPlugin import info.nightscout.androidaps.plugins.general.automation.actions.ActionAlarm @@ -37,21 +37,21 @@ class CarbTimer @Inject constructor( // Bg under 180 mgdl and dropping by 15 mgdl list.add(TriggerConnector(injector, TriggerConnector.Type.AND).apply { - list.add(TriggerBg(injector, 180.0, Constants.MGDL, Comparator.Compare.IS_LESSER)) - list.add(TriggerDelta(injector, InputDelta(resourceHelper, -15.0, -360.0, 360.0, 1.0, DecimalFormat("0"), InputDelta.DeltaType.DELTA), Constants.MGDL, Comparator.Compare.IS_EQUAL_OR_LESSER)) - list.add(TriggerDelta(injector, InputDelta(resourceHelper, -8.0, -360.0, 360.0, 1.0, DecimalFormat("0"), InputDelta.DeltaType.SHORT_AVERAGE), Constants.MGDL, Comparator.Compare.IS_EQUAL_OR_LESSER)) + list.add(TriggerBg(injector, 180.0, GlucoseUnit.MGDL, Comparator.Compare.IS_LESSER)) + list.add(TriggerDelta(injector, InputDelta(resourceHelper, -15.0, -360.0, 360.0, 1.0, DecimalFormat("0"), InputDelta.DeltaType.DELTA), GlucoseUnit.MGDL, Comparator.Compare.IS_EQUAL_OR_LESSER)) + list.add(TriggerDelta(injector, InputDelta(resourceHelper, -8.0, -360.0, 360.0, 1.0, DecimalFormat("0"), InputDelta.DeltaType.SHORT_AVERAGE), GlucoseUnit.MGDL, Comparator.Compare.IS_EQUAL_OR_LESSER)) }) // Bg under 160 mgdl and dropping by 9 mgdl list.add(TriggerConnector(injector, TriggerConnector.Type.AND).apply { - list.add(TriggerBg(injector, 160.0, Constants.MGDL, Comparator.Compare.IS_LESSER)) - list.add(TriggerDelta(injector, InputDelta(resourceHelper, -9.0, -360.0, 360.0, 1.0, DecimalFormat("0"), InputDelta.DeltaType.DELTA), Constants.MGDL, Comparator.Compare.IS_EQUAL_OR_LESSER)) - list.add(TriggerDelta(injector, InputDelta(resourceHelper, -5.0, -360.0, 360.0, 1.0, DecimalFormat("0"), InputDelta.DeltaType.SHORT_AVERAGE), Constants.MGDL, Comparator.Compare.IS_EQUAL_OR_LESSER)) + list.add(TriggerBg(injector, 160.0, GlucoseUnit.MGDL, Comparator.Compare.IS_LESSER)) + list.add(TriggerDelta(injector, InputDelta(resourceHelper, -9.0, -360.0, 360.0, 1.0, DecimalFormat("0"), InputDelta.DeltaType.DELTA), GlucoseUnit.MGDL, Comparator.Compare.IS_EQUAL_OR_LESSER)) + list.add(TriggerDelta(injector, InputDelta(resourceHelper, -5.0, -360.0, 360.0, 1.0, DecimalFormat("0"), InputDelta.DeltaType.SHORT_AVERAGE), GlucoseUnit.MGDL, Comparator.Compare.IS_EQUAL_OR_LESSER)) }) // Bg under 145 mgdl and dropping list.add(TriggerConnector(injector, TriggerConnector.Type.AND).apply { - list.add(TriggerBg(injector, 145.0, Constants.MGDL, Comparator.Compare.IS_LESSER)) - list.add(TriggerDelta(injector, InputDelta(resourceHelper, 0.0, -360.0, 360.0, 1.0, DecimalFormat("0"), InputDelta.DeltaType.DELTA), Constants.MGDL, Comparator.Compare.IS_EQUAL_OR_LESSER)) - list.add(TriggerDelta(injector, InputDelta(resourceHelper, 0.0, -360.0, 360.0, 1.0, DecimalFormat("0"), InputDelta.DeltaType.SHORT_AVERAGE), Constants.MGDL, Comparator.Compare.IS_EQUAL_OR_LESSER)) + list.add(TriggerBg(injector, 145.0, GlucoseUnit.MGDL, Comparator.Compare.IS_LESSER)) + list.add(TriggerDelta(injector, InputDelta(resourceHelper, 0.0, -360.0, 360.0, 1.0, DecimalFormat("0"), InputDelta.DeltaType.DELTA), GlucoseUnit.MGDL, Comparator.Compare.IS_EQUAL_OR_LESSER)) + list.add(TriggerDelta(injector, InputDelta(resourceHelper, 0.0, -360.0, 360.0, 1.0, DecimalFormat("0"), InputDelta.DeltaType.SHORT_AVERAGE), GlucoseUnit.MGDL, Comparator.Compare.IS_EQUAL_OR_LESSER)) }) } actions.add(ActionAlarm(injector, resourceHelper.gs(R.string.time_to_eat))) diff --git a/app/src/main/java/info/nightscout/androidaps/utils/HardLimits.kt b/app/src/main/java/info/nightscout/androidaps/utils/HardLimits.kt index 95bd7983ae..0389b34283 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/HardLimits.kt +++ b/app/src/main/java/info/nightscout/androidaps/utils/HardLimits.kt @@ -2,11 +2,14 @@ package info.nightscout.androidaps.utils import android.content.Context import info.nightscout.androidaps.R +import info.nightscout.androidaps.database.AppRepository +import info.nightscout.androidaps.database.transactions.InsertTherapyEventAnnouncementTransaction import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.plugins.bus.RxBusWrapper -import info.nightscout.androidaps.plugins.general.nsclient.NSUpload import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.sharedPreferences.SP +import io.reactivex.disposables.CompositeDisposable +import io.reactivex.rxkotlin.plusAssign import javax.inject.Inject import javax.inject.Singleton import kotlin.math.max @@ -19,9 +22,11 @@ class HardLimits @Inject constructor( private val sp: SP, private val resourceHelper: ResourceHelper, private val context: Context, - private val nsUpload: NSUpload + private val repository: AppRepository ) { + private val disposable = CompositeDisposable() + companion object { private const val CHILD = 0 @@ -88,7 +93,7 @@ class HardLimits @Inject constructor( msg += ".\n" msg += String.format(resourceHelper.gs(R.string.valuelimitedto), value, newValue) aapsLogger.error(msg) - nsUpload.uploadError(msg) + disposable += repository.runTransaction(InsertTherapyEventAnnouncementTransaction(msg)).subscribe() ToastUtils.showToastInUiThread(context, rxBus, msg, R.raw.error) } return newValue diff --git a/app/src/main/java/info/nightscout/androidaps/utils/JSONFormatter.java b/app/src/main/java/info/nightscout/androidaps/utils/JSONFormatter.java deleted file mode 100644 index dae7f61252..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/utils/JSONFormatter.java +++ /dev/null @@ -1,126 +0,0 @@ -package info.nightscout.androidaps.utils; - -import android.text.Html; -import android.text.Spanned; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; -import org.slf4j.Logger; - -import java.util.Iterator; -import java.util.Date; -import java.text.SimpleDateFormat; -import java.text.DateFormat; - -import info.nightscout.androidaps.logging.StacktraceLoggerWrapper; - -/** - * Created by mike on 11.07.2016. - */ -public class JSONFormatter { - private static final Logger log = StacktraceLoggerWrapper.getLogger(JSONFormatter.class); - - public static Spanned format(final String jsonString) { - final JsonVisitor visitor = new JsonVisitor(1, '\t'); - try { - if (jsonString.equals("undefined")) - return HtmlHelper.INSTANCE.fromHtml("undefined"); - else if (jsonString.getBytes()[0] == '[') - return HtmlHelper.INSTANCE.fromHtml(visitor.visit(new JSONArray(jsonString), 0)); - else - return HtmlHelper.INSTANCE.fromHtml(visitor.visit(new JSONObject(jsonString), 0)); - } catch (JSONException e) { - log.error("Unhandled exception", e); - return HtmlHelper.INSTANCE.fromHtml(""); - } - } - - public static Spanned format(final JSONObject object) { - final JsonVisitor visitor = new JsonVisitor(1, '\t'); - try { - return HtmlHelper.INSTANCE.fromHtml(visitor.visit(object, 0)); - } catch (JSONException e) { - log.error("Unhandled exception", e); - return HtmlHelper.INSTANCE.fromHtml(""); - } - } - - private static class JsonVisitor { - private final int indentationSize; - private final char indentationChar; - - public JsonVisitor(final int indentationSize, final char indentationChar) { - this.indentationSize = indentationSize; - this.indentationChar = indentationChar; - } - - private String visit(final JSONArray array, final int indent) throws JSONException { - String ret = ""; - final int length = array.length(); - if (length == 0) { - } else { - ret += write("[", indent); - for (int i = 0; i < length; i++) { - ret += visit(array.get(i), indent); - } - ret += write("]", indent); - } - return ret; - } - - private String visit(final JSONObject obj, final int indent) throws JSONException { - String ret = ""; - final int length = obj.length(); - if (length == 0) { - } else { - final Iterator keys = obj.keys(); - while (keys.hasNext()) { - final String key = keys.next(); - ret += write("" + key + ": ", indent); - ret += visit(obj.get(key), indent + 1); - ret += "
"; - } - } - return ret; - } - - private String visit(final Object object, final int indent) throws JSONException { - String ret = ""; - Long n; - if (object instanceof JSONArray) { - ret += visit((JSONArray) object, indent); - } else if (object instanceof JSONObject) { - ret += "
" + visit((JSONObject) object, indent); - } else { - if (object instanceof String) { - ret += write("\"" + ((String) object).replace("<", "<").replace(">", ">") + "\"", indent); - } else { - // try to detect Date as milliseconds - if (object instanceof Long) { - n = (Long) object; - if (n > 1580000000000L && n < 2000000000000L) { // from 2020.01.26 to 2033.05.18 it is with high probability a date object - Date date = new Date(n); - DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - ret += write(formatter.format(date), indent); - } else { - ret += write(String.valueOf(object), indent); - } - } else { - ret += write(String.valueOf(object), indent); - } - } - } - return ret; - } - - private String write(final String data, final int indent) { - String ret = ""; - for (int i = 0; i < (indent * indentationSize); i++) { - ret += indentationChar; - } - ret += data; - return ret; - } - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/utils/JSONFormatter.kt b/app/src/main/java/info/nightscout/androidaps/utils/JSONFormatter.kt new file mode 100644 index 0000000000..7d5247c885 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/utils/JSONFormatter.kt @@ -0,0 +1,113 @@ +package info.nightscout.androidaps.utils + +import android.text.Spanned +import info.nightscout.androidaps.logging.AAPSLogger +import info.nightscout.androidaps.utils.HtmlHelper.fromHtml +import org.json.JSONArray +import org.json.JSONException +import org.json.JSONObject +import java.text.DateFormat +import java.text.SimpleDateFormat +import java.util.* +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class JSONFormatter @Inject constructor( + private val aapsLogger: AAPSLogger +) { + + fun format(jsonString: String?): Spanned { + jsonString ?: return fromHtml("") + val visitor = JsonVisitor(1, '\t') + return try { + when { + jsonString == "undefined" -> fromHtml("undefined") + jsonString.toByteArray()[0] == '['.toByte() -> fromHtml(visitor.visit(JSONArray(jsonString), 0)) + else -> fromHtml(visitor.visit(JSONObject(jsonString), 0)) + } + } catch (e: JSONException) { + aapsLogger.error("Unhandled exception", e) + fromHtml("") + } + } + + fun format(jsonObject: JSONObject?): Spanned { + jsonObject ?: return fromHtml("") + val visitor = JsonVisitor(1, '\t') + return try { + fromHtml(visitor.visit(jsonObject, 0)) + } catch (e: JSONException) { + aapsLogger.error("Unhandled exception", e) + fromHtml("") + } + } + + private class JsonVisitor(private val indentationSize: Int, private val indentationChar: Char) { + + fun visit(array: JSONArray, indent: Int): String { + var ret = "" + val length = array.length() + if (length != 0) { + ret += write("[", indent) + for (i in 0 until length) { + ret += visit(array[i], indent) + } + ret += write("]", indent) + } + return ret + } + + fun visit(obj: JSONObject, indent: Int): String { + var ret = "" + val length = obj.length() + if (length != 0) { + val keys = obj.keys() + while (keys.hasNext()) { + val key = keys.next() + ret += write("$key: ", indent) + ret += visit(obj[key], indent + 1) + ret += "
" + } + } + return ret + } + + private fun visit(any: Any, indent: Int): String { + var ret = "" + val n: Long + if (any is JSONArray) { + ret += visit(any, indent) + } else if (any is JSONObject) { + ret += "
" + visit(any, indent) + } else { + if (any is String) { + ret += write("\"" + any.replace("<", "<").replace(">", ">") + "\"", indent) + } else { + // try to detect Date as milliseconds + if (any is Long) { + n = any + ret += if (n in 1580000000001..1999999999999) { // from 2020.01.26 to 2033.05.18 it is with high probability a date object + val formatter: DateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()) + write(formatter.format(Date(n)), indent) + } else { + write(any.toString(), indent) + } + } else { + ret += write(any.toString(), indent) + } + } + } + return ret + } + + private fun write(data: String, indent: Int): String { + var ret = "" + for (i in 0 until indent * indentationSize) { + ret += indentationChar + } + ret += data + return ret + } + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/utils/LocalAlertUtils.kt b/app/src/main/java/info/nightscout/androidaps/utils/LocalAlertUtils.kt index 908f759f41..a8bdb9e92b 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/LocalAlertUtils.kt +++ b/app/src/main/java/info/nightscout/androidaps/utils/LocalAlertUtils.kt @@ -1,21 +1,24 @@ package info.nightscout.androidaps.utils -import info.nightscout.androidaps.Config +import info.nightscout.androidaps.interfaces.Config import info.nightscout.androidaps.Constants import info.nightscout.androidaps.R -import info.nightscout.androidaps.interfaces.ActivePluginProvider +import info.nightscout.androidaps.database.AppRepository +import info.nightscout.androidaps.database.transactions.InsertTherapyEventAnnouncementTransaction +import info.nightscout.androidaps.interfaces.ActivePlugin +import info.nightscout.androidaps.interfaces.IobCobCalculator import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.plugins.bus.RxBusWrapper -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.smsCommunicator.SmsCommunicatorPlugin -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.sharedPreferences.SP +import io.reactivex.disposables.CompositeDisposable +import io.reactivex.rxkotlin.plusAssign import javax.inject.Inject import javax.inject.Singleton import kotlin.math.min @@ -29,15 +32,17 @@ class LocalAlertUtils @Inject constructor( private val sp: SP, private val rxBus: RxBusWrapper, private val resourceHelper: ResourceHelper, - private val activePlugin: ActivePluginProvider, + private val activePlugin: ActivePlugin, private val profileFunction: ProfileFunction, - private val iobCobCalculatorPlugin: IobCobCalculatorPlugin, + private val iobCobCalculator: IobCobCalculator, private val smsCommunicatorPlugin: SmsCommunicatorPlugin, private val config: Config, - private val nsUpload: NSUpload, + private val repository: AppRepository, private val dateUtil: DateUtil ) { + private val disposable = CompositeDisposable() + private fun missedReadingsThreshold(): Long { return T.mins(sp.getInt(R.string.key_missed_bg_readings_threshold_minutes, Constants.DEFAULT_MISSED_BG_READINGS_THRESHOLD_MINUTES).toLong()).msecs() } @@ -55,7 +60,7 @@ class LocalAlertUtils @Inject constructor( sp.putLong("nextPumpDisconnectedAlarm", System.currentTimeMillis() + pumpUnreachableThreshold()) rxBus.send(EventNewNotification(Notification(Notification.PUMP_UNREACHABLE, resourceHelper.gs(R.string.pump_unreachable), Notification.URGENT).also { it.soundId = R.raw.alarm })) if (sp.getBoolean(R.string.key_ns_create_announcements_from_errors, true)) - nsUpload.uploadError(resourceHelper.gs(R.string.pump_unreachable)) + disposable += repository.runTransaction(InsertTherapyEventAnnouncementTransaction(resourceHelper.gs(R.string.pump_unreachable))).subscribe() } if (sp.getBoolean(R.string.key_smscommunicator_report_pump_ureachable, true)) smsCommunicatorPlugin.sendNotificationToAllNumbers(resourceHelper.gs(R.string.pump_unreachable)) @@ -105,7 +110,7 @@ class LocalAlertUtils @Inject constructor( } fun checkStaleBGAlert() { - val bgReading = iobCobCalculatorPlugin.lastBg() + val bgReading = iobCobCalculator.ads.lastBg() if (sp.getBoolean(R.string.key_enable_missed_bg_readings_alert, false) && bgReading != null && bgReading.timestamp + missedReadingsThreshold() < System.currentTimeMillis() && sp.getLong("nextMissedReadingsAlarm", 0L) < System.currentTimeMillis()) { val n = Notification(Notification.BG_READINGS_MISSED, resourceHelper.gs(R.string.missed_bg_readings), Notification.URGENT) @@ -113,7 +118,7 @@ class LocalAlertUtils @Inject constructor( sp.putLong("nextMissedReadingsAlarm", System.currentTimeMillis() + missedReadingsThreshold()) rxBus.send(EventNewNotification(n)) if (sp.getBoolean(R.string.key_ns_create_announcements_from_errors, true)) { - nsUpload.uploadError(n.text) + n.text?.let { disposable += repository.runTransaction(InsertTherapyEventAnnouncementTransaction(it)).subscribe() } } } } diff --git a/app/src/main/java/info/nightscout/androidaps/utils/NumberPickerVertical.java b/app/src/main/java/info/nightscout/androidaps/utils/NumberPickerVertical.java deleted file mode 100644 index 5244c5c4ce..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/utils/NumberPickerVertical.java +++ /dev/null @@ -1,26 +0,0 @@ -package info.nightscout.androidaps.utils; - -import android.content.Context; -import android.util.AttributeSet; -import android.view.LayoutInflater; - -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.utils.ui.NumberPicker; - -/** - * Created by mike on 28.06.2016. - */ -public class NumberPickerVertical extends NumberPicker { - public NumberPickerVertical(Context context) { - super(context); - } - - public NumberPickerVertical(Context context, AttributeSet attrs) { - super(context, attrs); - } - - @Override - protected void inflate(Context context) { - LayoutInflater.from(context).inflate(R.layout.number_picker_layout_vertical, this, true); - } - } diff --git a/app/src/main/java/info/nightscout/androidaps/utils/NumberPickerVertical.kt b/app/src/main/java/info/nightscout/androidaps/utils/NumberPickerVertical.kt new file mode 100644 index 0000000000..5e7a0d7e90 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/utils/NumberPickerVertical.kt @@ -0,0 +1,17 @@ +package info.nightscout.androidaps.utils + +import android.content.Context +import android.util.AttributeSet +import android.view.LayoutInflater +import info.nightscout.androidaps.R +import info.nightscout.androidaps.utils.ui.NumberPicker + +class NumberPickerVertical : NumberPicker { + + constructor(context: Context?) : super(context) + constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) + + override fun inflate(context: Context) { + LayoutInflater.from(context).inflate(R.layout.number_picker_layout_vertical, this, true) + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/utils/PercentageSplitter.java b/app/src/main/java/info/nightscout/androidaps/utils/PercentageSplitter.java deleted file mode 100644 index b3f8156cfe..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/utils/PercentageSplitter.java +++ /dev/null @@ -1,22 +0,0 @@ -package info.nightscout.androidaps.utils; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Created by mike on 22.12.2017. - */ - -public class PercentageSplitter { - // Matches "Profile name (200%,-2h)", "Profile name (50%) - private static final Pattern splitPattern = Pattern.compile("(.+)\\(\\d+%(,-?\\d+h)?\\)"); - - /** Removes the suffix for percentage and timeshift from a profile name. This is the inverse of what - * {@link info.nightscout.androidaps.db.ProfileSwitch#getCustomizedName()} does. - * Since the customized name is used for the PS upload to NS, this is needed get the original profile name - * when retrieving the PS from NS again. */ - public static String pureName(String name) { - Matcher percentageMatch = splitPattern.matcher(name); - return percentageMatch.find() ? percentageMatch.group(1).trim() : name; - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/utils/PercentageSplitter.kt b/app/src/main/java/info/nightscout/androidaps/utils/PercentageSplitter.kt new file mode 100644 index 0000000000..3e38a2088c --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/utils/PercentageSplitter.kt @@ -0,0 +1,20 @@ +package info.nightscout.androidaps.utils + +import java.util.regex.Pattern + +object PercentageSplitter { + + // Matches "Profile name (200%,-2h)", "Profile name (50%) + private val splitPattern = Pattern.compile("(.+)\\(\\d+%(,-?\\d+h)?\\)") + + /** + * Removes the suffix for percentage and timeshift from a profile name. This is the inverse of what + * [ProfileSwitch.getCustomizedName()] does. + * Since the customized name is used for the PS upload to NS, this is needed get the original profile name + * when retrieving the PS from NS again. + */ + fun pureName(name: String): String { + val percentageMatch = splitPattern.matcher(name) + return if (percentageMatch.find()) percentageMatch.group(1).trim { it <= ' ' } else name + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/utils/SPBackupAgent.java b/app/src/main/java/info/nightscout/androidaps/utils/SPBackupAgent.java deleted file mode 100644 index 098b90c5d1..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/utils/SPBackupAgent.java +++ /dev/null @@ -1,17 +0,0 @@ -package info.nightscout.androidaps.utils; - -import android.app.backup.BackupAgentHelper; -import android.app.backup.SharedPreferencesBackupHelper; - -public class SPBackupAgent extends BackupAgentHelper { - - @Override - public void onCreate() { - // API 24 - final String PREFS = getApplicationContext().getPackageName() + "_preferences"; - final String PREFS_BACKUP_KEY = "SP"; - SharedPreferencesBackupHelper helper = - new SharedPreferencesBackupHelper(this, PREFS); - addHelper(PREFS_BACKUP_KEY, helper); - } -} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/utils/SPBackupAgent.kt b/app/src/main/java/info/nightscout/androidaps/utils/SPBackupAgent.kt new file mode 100644 index 0000000000..bf3a4a5516 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/utils/SPBackupAgent.kt @@ -0,0 +1,16 @@ +package info.nightscout.androidaps.utils + +import android.app.backup.BackupAgentHelper +import android.app.backup.SharedPreferencesBackupHelper + +@Suppress("LocalVariableName") +class SPBackupAgent : BackupAgentHelper() { + + override fun onCreate() { + // API 24 + val PREFS = applicationContext.packageName + "_preferences" + val PREFS_BACKUP_KEY = "SP" + val helper = SharedPreferencesBackupHelper(this, PREFS) + addHelper(PREFS_BACKUP_KEY, helper) + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/utils/TimeListEdit.java b/app/src/main/java/info/nightscout/androidaps/utils/TimeListEdit.java index af6fd3e742..33a24e3244 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/TimeListEdit.java +++ b/app/src/main/java/info/nightscout/androidaps/utils/TimeListEdit.java @@ -95,7 +95,6 @@ public class TimeListEdit { LinearLayout.LayoutParams llp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); llp.setMargins(0, 5, 0, 5); textlabel.setLayoutParams(llp); - //textlabel.setBackgroundColor(ContextCompat.getColor(MainApp.instance(), R.color.linearBlockBackground)); TextViewCompat.setTextAppearance(textlabel, android.R.style.TextAppearance_Medium); layout.addView(textlabel); diff --git a/app/src/main/java/info/nightscout/androidaps/utils/TrendCalculator.kt b/app/src/main/java/info/nightscout/androidaps/utils/TrendCalculator.kt index ac7738043d..a5f2dc2dcb 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/TrendCalculator.kt +++ b/app/src/main/java/info/nightscout/androidaps/utils/TrendCalculator.kt @@ -10,9 +10,12 @@ class TrendCalculator @Inject constructor( private val repository: AppRepository ) { - fun getTrendArrow(glucoseValue: GlucoseValue): GlucoseValue.TrendArrow = - if (glucoseValue.trendArrow != GlucoseValue.TrendArrow.NONE) glucoseValue.trendArrow - else calculateDirection(glucoseValue) + fun getTrendArrow(glucoseValue: GlucoseValue?): GlucoseValue.TrendArrow = + when { + glucoseValue?.trendArrow == null -> GlucoseValue.TrendArrow.NONE + glucoseValue.trendArrow != GlucoseValue.TrendArrow.NONE -> glucoseValue.trendArrow + else -> calculateDirection(glucoseValue) + } private fun calculateDirection(glucoseValue: GlucoseValue): GlucoseValue.TrendArrow { diff --git a/app/src/main/java/info/nightscout/androidaps/utils/XdripCalibrations.kt b/app/src/main/java/info/nightscout/androidaps/utils/XdripCalibrations.kt index af13dadb1e..bc7d5ff479 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/XdripCalibrations.kt +++ b/app/src/main/java/info/nightscout/androidaps/utils/XdripCalibrations.kt @@ -3,10 +3,10 @@ package info.nightscout.androidaps.utils import android.content.Context import android.content.Intent import android.os.Bundle -import info.nightscout.androidaps.Constants import info.nightscout.androidaps.R -import info.nightscout.androidaps.logging.AAPSLogger +import info.nightscout.androidaps.interfaces.GlucoseUnit import info.nightscout.androidaps.interfaces.ProfileFunction +import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.services.Intents import info.nightscout.androidaps.utils.resources.ResourceHelper import javax.inject.Inject @@ -23,7 +23,7 @@ class XdripCalibrations @Inject constructor( fun sendIntent(bg: Double): Boolean { val bundle = Bundle() bundle.putDouble("glucose_number", bg) - bundle.putString("units", if (profileFunction.getUnits() == Constants.MGDL) "mgdl" else "mmol") + bundle.putString("units", if (profileFunction.getUnits() == GlucoseUnit.MGDL) "mgdl" else "mmol") bundle.putLong("timestamp", System.currentTimeMillis()) val intent = Intent(Intents.ACTION_REMOTE_CALIBRATION) intent.putExtras(bundle) diff --git a/app/src/main/java/info/nightscout/androidaps/utils/alertDialogs/PrefImportSummaryDialog.kt b/app/src/main/java/info/nightscout/androidaps/utils/alertDialogs/PrefImportSummaryDialog.kt index 976feb9ed1..7a2aa6f513 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/alertDialogs/PrefImportSummaryDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/utils/alertDialogs/PrefImportSummaryDialog.kt @@ -20,7 +20,7 @@ import info.nightscout.androidaps.R import info.nightscout.androidaps.plugins.general.maintenance.formats.Prefs import info.nightscout.androidaps.plugins.general.maintenance.formats.PrefsStatus import info.nightscout.androidaps.utils.ToastUtils -import info.nightscout.androidaps.utils.extensions.runOnUiThread +import info.nightscout.androidaps.extensions.runOnUiThread import java.util.* object PrefImportSummaryDialog { diff --git a/app/src/main/java/info/nightscout/androidaps/utils/alertDialogs/TwoMessagesAlertDialog.kt b/app/src/main/java/info/nightscout/androidaps/utils/alertDialogs/TwoMessagesAlertDialog.kt index c3049a3597..b8bc6466a2 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/alertDialogs/TwoMessagesAlertDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/utils/alertDialogs/TwoMessagesAlertDialog.kt @@ -9,7 +9,7 @@ import android.view.View import android.widget.TextView import androidx.annotation.DrawableRes import info.nightscout.androidaps.R -import info.nightscout.androidaps.utils.extensions.runOnUiThread +import info.nightscout.androidaps.extensions.runOnUiThread object TwoMessagesAlertDialog { diff --git a/app/src/main/java/info/nightscout/androidaps/utils/androidNotification/NotificationHolder.kt b/app/src/main/java/info/nightscout/androidaps/utils/androidNotification/NotificationHolderImpl.kt similarity index 85% rename from app/src/main/java/info/nightscout/androidaps/utils/androidNotification/NotificationHolder.kt rename to app/src/main/java/info/nightscout/androidaps/utils/androidNotification/NotificationHolderImpl.kt index 72dbb6430f..dfe9ed21c4 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/androidNotification/NotificationHolder.kt +++ b/app/src/main/java/info/nightscout/androidaps/utils/androidNotification/NotificationHolderImpl.kt @@ -9,18 +9,18 @@ import androidx.core.app.NotificationCompat import androidx.core.app.TaskStackBuilder import info.nightscout.androidaps.MainActivity import info.nightscout.androidaps.core.R -import info.nightscout.androidaps.interfaces.IconsProviderInterface -import info.nightscout.androidaps.interfaces.NotificationHolderInterface +import info.nightscout.androidaps.interfaces.IconsProvider +import info.nightscout.androidaps.interfaces.NotificationHolder import info.nightscout.androidaps.utils.resources.ResourceHelper import javax.inject.Inject import javax.inject.Singleton @Singleton -class NotificationHolder @Inject constructor( +class NotificationHolderImpl @Inject constructor( resourceHelper: ResourceHelper, context: Context, - iconsProvider: IconsProviderInterface -) : NotificationHolderInterface { + iconsProvider: IconsProvider +) : NotificationHolder { override val channelID = "AndroidAPS-Ongoing" override val notificationID = 4711 diff --git a/app/src/main/java/info/nightscout/androidaps/utils/buildHelper/BuildHelper.kt b/app/src/main/java/info/nightscout/androidaps/utils/buildHelper/BuildHelper.kt index 2eafc12ebb..0cb6b5c772 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/buildHelper/BuildHelper.kt +++ b/app/src/main/java/info/nightscout/androidaps/utils/buildHelper/BuildHelper.kt @@ -1,7 +1,7 @@ package info.nightscout.androidaps.utils.buildHelper import info.nightscout.androidaps.BuildConfig -import info.nightscout.androidaps.Config +import info.nightscout.androidaps.interfaces.Config import info.nightscout.androidaps.plugins.general.maintenance.LoggerUtils import java.io.File import javax.inject.Inject diff --git a/app/src/main/java/info/nightscout/androidaps/Config.kt b/app/src/main/java/info/nightscout/androidaps/utils/buildHelper/ConfigImpl.kt similarity index 77% rename from app/src/main/java/info/nightscout/androidaps/Config.kt rename to app/src/main/java/info/nightscout/androidaps/utils/buildHelper/ConfigImpl.kt index a598adc440..1f8ee377c5 100644 --- a/app/src/main/java/info/nightscout/androidaps/Config.kt +++ b/app/src/main/java/info/nightscout/androidaps/utils/buildHelper/ConfigImpl.kt @@ -1,12 +1,13 @@ -package info.nightscout.androidaps +package info.nightscout.androidaps.utils.buildHelper import android.os.Build -import info.nightscout.androidaps.interfaces.ConfigInterface +import info.nightscout.androidaps.BuildConfig +import info.nightscout.androidaps.interfaces.Config import javax.inject.Inject import javax.inject.Singleton @Singleton -class Config @Inject constructor() : ConfigInterface { +class ConfigImpl @Inject constructor() : Config { override val SUPPORTEDNSVERSION = 1002 // 0.10.00 override val APS = BuildConfig.FLAVOR == "full" diff --git a/app/src/main/java/info/nightscout/androidaps/utils/extensions/DoubleToSignedString.kt b/app/src/main/java/info/nightscout/androidaps/utils/extensions/DoubleToSignedString.kt index af942b871e..bbad911a7c 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/extensions/DoubleToSignedString.kt +++ b/app/src/main/java/info/nightscout/androidaps/utils/extensions/DoubleToSignedString.kt @@ -1,9 +1,9 @@ package info.nightscout.androidaps.utils.extensions -import info.nightscout.androidaps.interfaces.PumpInterface +import info.nightscout.androidaps.interfaces.Pump import info.nightscout.androidaps.utils.DecimalFormatter -fun Double.toSignedString(pump: PumpInterface): String { +fun Double.toSignedString(pump: Pump): String { val formatted = DecimalFormatter.toPumpSupportedBolus(this, pump) return if (this > 0) "+$formatted" else formatted } diff --git a/app/src/main/java/info/nightscout/androidaps/utils/extensions/GlucoseValueUtils.kt b/app/src/main/java/info/nightscout/androidaps/utils/extensions/GlucoseValueUtils.kt deleted file mode 100644 index b02ba88e39..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/utils/extensions/GlucoseValueUtils.kt +++ /dev/null @@ -1,13 +0,0 @@ -package info.nightscout.androidaps.utils.extensions - -import info.nightscout.androidaps.Constants -import info.nightscout.androidaps.database.entities.GlucoseValue -import info.nightscout.androidaps.utils.DecimalFormatter - -fun GlucoseValue.valueToUnits(units: String): Double = - if (units == Constants.MGDL) value - else value * Constants.MGDL_TO_MMOLL - -fun GlucoseValue.valueToUnitsString(units: String): String = - if (units == Constants.MGDL) DecimalFormatter.to0Decimal(value) - else DecimalFormatter.to1Decimal(value * Constants.MGDL_TO_MMOLL) diff --git a/app/src/main/java/info/nightscout/androidaps/utils/resources/IconsProvider.kt b/app/src/main/java/info/nightscout/androidaps/utils/resources/IconsProviderImplementation.kt similarity index 73% rename from app/src/main/java/info/nightscout/androidaps/utils/resources/IconsProvider.kt rename to app/src/main/java/info/nightscout/androidaps/utils/resources/IconsProviderImplementation.kt index beab1f4330..74f39d3572 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/resources/IconsProvider.kt +++ b/app/src/main/java/info/nightscout/androidaps/utils/resources/IconsProviderImplementation.kt @@ -1,13 +1,13 @@ package info.nightscout.androidaps.utils.resources import info.nightscout.androidaps.R -import info.nightscout.androidaps.interfaces.ConfigInterface -import info.nightscout.androidaps.interfaces.IconsProviderInterface +import info.nightscout.androidaps.interfaces.Config +import info.nightscout.androidaps.interfaces.IconsProvider import javax.inject.Inject import javax.inject.Singleton @Singleton -class IconsProvider @Inject constructor(private val config: ConfigInterface) : IconsProviderInterface { +class IconsProviderImplementation @Inject constructor(private val config: Config) : IconsProvider { override fun getIcon(): Int = when { diff --git a/app/src/main/java/info/nightscout/androidaps/utils/stats/TddCalculator.kt b/app/src/main/java/info/nightscout/androidaps/utils/stats/TddCalculator.kt index b9da317bc6..f00d391026 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/stats/TddCalculator.kt +++ b/app/src/main/java/info/nightscout/androidaps/utils/stats/TddCalculator.kt @@ -1,107 +1,91 @@ package info.nightscout.androidaps.utils.stats -import android.content.Context import android.text.Spanned import android.util.LongSparseArray -import dagger.android.HasAndroidInjector import info.nightscout.androidaps.R import info.nightscout.androidaps.database.AppRepository -import info.nightscout.androidaps.db.TDD -import info.nightscout.androidaps.interfaces.ActivePluginProvider -import info.nightscout.androidaps.interfaces.DatabaseHelperInterface +import info.nightscout.androidaps.database.entities.Bolus +import info.nightscout.androidaps.database.entities.TotalDailyDose +import info.nightscout.androidaps.interfaces.ActivePlugin +import info.nightscout.androidaps.interfaces.IobCobCalculator import info.nightscout.androidaps.interfaces.ProfileFunction -import info.nightscout.androidaps.interfaces.UploadQueueInterface import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag -import info.nightscout.androidaps.plugins.bus.RxBusWrapper -import info.nightscout.androidaps.plugins.general.nsclient.NSUpload -import info.nightscout.androidaps.plugins.treatments.TreatmentService -import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin import info.nightscout.androidaps.utils.DateUtil -import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.HtmlHelper import info.nightscout.androidaps.utils.MidnightTime import info.nightscout.androidaps.utils.T +import info.nightscout.androidaps.extensions.convertedToAbsolute +import info.nightscout.androidaps.extensions.toText import info.nightscout.androidaps.utils.resources.ResourceHelper -import info.nightscout.androidaps.utils.rx.AapsSchedulers -import info.nightscout.androidaps.utils.sharedPreferences.SP import javax.inject.Inject class TddCalculator @Inject constructor( - injector: HasAndroidInjector, - aapsLogger: AAPSLogger, - rxBus: RxBusWrapper, - resourceHelper: ResourceHelper, - context: Context, - aapsSchedulers: AapsSchedulers, - sp: SP, - private val activePlugin: ActivePluginProvider, + private val aapsLogger: AAPSLogger, + private val resourceHelper: ResourceHelper, + private val activePlugin: ActivePlugin, private val profileFunction: ProfileFunction, - fabricPrivacy: FabricPrivacy, - nsUpload: NSUpload, private val dateUtil: DateUtil, - uploadQueue: UploadQueueInterface, - databaseHelper: DatabaseHelperInterface, - repository: AppRepository -) : TreatmentsPlugin(injector, aapsLogger, rxBus, aapsSchedulers, resourceHelper, context, sp, profileFunction, activePlugin, nsUpload, fabricPrivacy, dateUtil, uploadQueue, databaseHelper, repository) { + private val iobCobCalculator: IobCobCalculator, + private val repository: AppRepository +) { - init { - service = TreatmentService(injector) // plugin is not started - } + fun calculate(days: Long): LongSparseArray { + val startTime = MidnightTime.calc(dateUtil.now() - T.days(days).msecs()) + val endTime = MidnightTime.calc(dateUtil.now()) - fun calculate(days: Long): LongSparseArray { - val range = T.days(days + 1).msecs() - val startTime = MidnightTime.calc(DateUtil.now() - T.days(days).msecs()) - val endTime = MidnightTime.calc(DateUtil.now()) - initializeData(range) - - val result = LongSparseArray() - for (t in treatmentsFromHistory) { - if (!t.isValid) continue - if (t.date < startTime || t.date > endTime) continue - val midnight = MidnightTime.calc(t.date) - val tdd = result[midnight] ?: TDD(midnight, 0.0, 0.0, 0.0) - tdd.bolus += t.insulin - tdd.carbs += t.carbs + val result = LongSparseArray() + repository.getBolusesDataFromTimeToTime(startTime, endTime, true).blockingGet() + .filter { it.type != Bolus.Type.PRIMING } + .forEach { t -> + val midnight = MidnightTime.calc(t.timestamp) + val tdd = result[midnight] ?: TotalDailyDose(timestamp = midnight) + tdd.bolusAmount += t.amount + result.put(midnight, tdd) + } + repository.getCarbsDataFromTimeToTimeExpanded(startTime, endTime, true).blockingGet().forEach { t -> + val midnight = MidnightTime.calc(t.timestamp) + val tdd = result[midnight] ?: TotalDailyDose(timestamp = midnight) + tdd.carbs += t.amount result.put(midnight, tdd) } for (t in startTime until endTime step T.mins(5).msecs()) { val midnight = MidnightTime.calc(t) - val tdd = result[midnight] ?: TDD(midnight, 0.0, 0.0, 0.0) - val tbr = getTempBasalFromHistory(t) - val profile = profileFunction.getProfile(t, this) ?: continue - val absoluteRate = tbr?.tempBasalConvertedToAbsolute(t, profile) ?: profile.getBasal(t) - tdd.basal += absoluteRate / 60.0 * 5.0 + val tdd = result[midnight] ?: TotalDailyDose(timestamp = midnight) + val tbr = iobCobCalculator.getTempBasalIncludingConvertedExtended(t) + val profile = profileFunction.getProfile(t) ?: continue + val absoluteRate = tbr?.convertedToAbsolute(t, profile) ?: profile.getBasal(t) + tdd.basalAmount += absoluteRate / 60.0 * 5.0 if (!activePlugin.activePump.isFakingTempsByExtendedBoluses) { // they are not included in TBRs - val eb = getExtendedBolusFromHistory(t) - val absoluteEbRate = eb?.absoluteRate() ?: 0.0 - tdd.bolus += absoluteEbRate / 60.0 * 5.0 + val eb = iobCobCalculator.getExtendedBolus(t) + val absoluteEbRate = eb?.rate ?: 0.0 + tdd.bolusAmount += absoluteEbRate / 60.0 * 5.0 } result.put(midnight, tdd) } for (i in 0 until result.size()) { val tdd = result.valueAt(i) - tdd.total = tdd.bolus + tdd.basal + tdd.totalAmount = tdd.bolusAmount + tdd.basalAmount } aapsLogger.debug(LTag.CORE, result.toString()) return result } - private fun averageTDD(tdds: LongSparseArray): TDD { - val totalTdd = TDD() + private fun averageTDD(tdds: LongSparseArray): TotalDailyDose { + val totalTdd = TotalDailyDose(timestamp = dateUtil.now()) for (i in 0 until tdds.size()) { val tdd = tdds.valueAt(i) - totalTdd.basal += tdd.basal - totalTdd.bolus += tdd.bolus - totalTdd.total += tdd.total + totalTdd.basalAmount += tdd.basalAmount + totalTdd.bolusAmount += tdd.bolusAmount + totalTdd.totalAmount += tdd.totalAmount totalTdd.carbs += tdd.carbs } - totalTdd.basal /= tdds.size().toDouble() - totalTdd.bolus /= tdds.size().toDouble() - totalTdd.total /= tdds.size().toDouble() + totalTdd.basalAmount /= tdds.size().toDouble() + totalTdd.bolusAmount /= tdds.size().toDouble() + totalTdd.totalAmount /= tdds.size().toDouble() totalTdd.carbs /= tdds.size().toDouble() return totalTdd } @@ -118,7 +102,7 @@ class TddCalculator @Inject constructor( } @Suppress("SameParameterValue") - private fun toText(tdds: LongSparseArray, includeCarbs: Boolean): String { + private fun toText(tdds: LongSparseArray, includeCarbs: Boolean): String { var t = "" for (i in 0 until tdds.size()) { t += "${tdds.valueAt(i).toText(resourceHelper, dateUtil, includeCarbs)}
" diff --git a/app/src/main/java/info/nightscout/androidaps/utils/stats/TirCalculator.kt b/app/src/main/java/info/nightscout/androidaps/utils/stats/TirCalculator.kt index d1f7c74bf1..6e890bfacb 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/stats/TirCalculator.kt +++ b/app/src/main/java/info/nightscout/androidaps/utils/stats/TirCalculator.kt @@ -4,7 +4,7 @@ import android.text.Spanned import android.util.LongSparseArray import info.nightscout.androidaps.Constants import info.nightscout.androidaps.R -import info.nightscout.androidaps.data.Profile +import info.nightscout.androidaps.interfaces.Profile import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.utils.DateUtil @@ -26,8 +26,8 @@ class TirCalculator @Inject constructor( fun calculate(days: Long, lowMgdl: Double, highMgdl: Double): LongSparseArray { if (lowMgdl < 39) throw RuntimeException("Low below 39") if (lowMgdl > highMgdl) throw RuntimeException("Low > High") - val startTime = MidnightTime.calc(DateUtil.now() - T.days(days).msecs()) - val endTime = MidnightTime.calc(DateUtil.now()) + val startTime = MidnightTime.calc(dateUtil.now() - T.days(days).msecs()) + val endTime = MidnightTime.calc(dateUtil.now()) val bgReadings = repository.compatGetBgReadingsDataFromTime(startTime, endTime, true).blockingGet() val result = LongSparseArray() diff --git a/app/src/main/java/info/nightscout/androidaps/utils/wizard/BolusWizard.kt b/app/src/main/java/info/nightscout/androidaps/utils/wizard/BolusWizard.kt index 05c441b369..a9f2462bc6 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/wizard/BolusWizard.kt +++ b/app/src/main/java/info/nightscout/androidaps/utils/wizard/BolusWizard.kt @@ -5,15 +5,18 @@ import android.content.Intent import android.text.Spanned import com.google.common.base.Joiner import dagger.android.HasAndroidInjector -import info.nightscout.androidaps.Config +import info.nightscout.androidaps.interfaces.Config 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.interfaces.Profile +import info.nightscout.androidaps.database.AppRepository +import info.nightscout.androidaps.database.entities.BolusCalculatorResult +import info.nightscout.androidaps.database.entities.ValueWithUnit import info.nightscout.androidaps.database.entities.TemporaryTarget -import info.nightscout.androidaps.database.entities.TherapyEvent -import info.nightscout.androidaps.database.entities.UserEntry.* -import info.nightscout.androidaps.db.Source +import info.nightscout.androidaps.database.entities.UserEntry.Action +import info.nightscout.androidaps.database.entities.UserEntry.Sources +import info.nightscout.androidaps.database.transactions.InsertOrUpdateBolusCalculatorResultTransaction import info.nightscout.androidaps.events.EventRefreshOverview import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.logging.AAPSLogger @@ -24,7 +27,6 @@ import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatusProvider -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin import info.nightscout.androidaps.queue.Callback import info.nightscout.androidaps.utils.CarbTimer import info.nightscout.androidaps.utils.DateUtil @@ -32,11 +34,11 @@ import info.nightscout.androidaps.utils.HtmlHelper import info.nightscout.androidaps.utils.Round import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.alertDialogs.OKDialog -import info.nightscout.androidaps.utils.extensions.formatColor +import info.nightscout.androidaps.extensions.formatColor import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.sharedPreferences.SP -import org.json.JSONException -import org.json.JSONObject +import io.reactivex.disposables.CompositeDisposable +import io.reactivex.rxkotlin.plusAssign import java.util.* import javax.inject.Inject import kotlin.math.abs @@ -51,15 +53,18 @@ class BolusWizard @Inject constructor( @Inject lateinit var sp: SP @Inject lateinit var profileFunction: ProfileFunction @Inject lateinit var constraintChecker: ConstraintChecker - @Inject lateinit var activePlugin: ActivePluginProvider + @Inject lateinit var activePlugin: ActivePlugin @Inject lateinit var commandQueue: CommandQueueProvider @Inject lateinit var loopPlugin: LoopPlugin - @Inject lateinit var iobCobCalculatorPlugin: IobCobCalculatorPlugin + @Inject lateinit var iobCobCalculator: IobCobCalculator @Inject lateinit var dateUtil: DateUtil @Inject lateinit var config: Config @Inject lateinit var uel: UserEntryLogger @Inject lateinit var carbTimer: CarbTimer @Inject lateinit var glucoseStatusProvider: GlucoseStatusProvider + @Inject lateinit var repository: AppRepository + + private val disposable = CompositeDisposable() init { injector.androidInjector().inject(this) @@ -114,7 +119,7 @@ class BolusWizard @Inject constructor( var cob: Double = 0.0 var bg: Double = 0.0 private var correction: Double = 0.0 - private var percentageCorrection: Double = 0.0 + private var percentageCorrection: Int = 0 private var useBg: Boolean = false private var useCob: Boolean = false private var includeBolusIOB: Boolean = false @@ -125,6 +130,7 @@ class BolusWizard @Inject constructor( private var useAlarm = false var notes: String = "" private var carbTime: Int = 0 + private var quickWizard: Boolean = true @JvmOverloads fun doCalc(profile: Profile, @@ -134,7 +140,7 @@ class BolusWizard @Inject constructor( cob: Double, bg: Double, correction: Double, - percentageCorrection: Double = 100.0, + percentageCorrection: Int = 100, useBg: Boolean, useCob: Boolean, includeBolusIOB: Boolean, @@ -144,7 +150,8 @@ class BolusWizard @Inject constructor( useTrend: Boolean, useAlarm: Boolean, notes: String = "", - carbTime: Int = 0 + carbTime: Int = 0, + quickWizard: Boolean = false ): BolusWizard { this.profile = profile @@ -165,11 +172,12 @@ class BolusWizard @Inject constructor( this.useAlarm = useAlarm this.notes = notes this.carbTime = carbTime + this.quickWizard = quickWizard // Insulin from BG - sens = Profile.fromMgdlToUnits(profile.isfMgdl, profileFunction.getUnits()) - targetBGLow = Profile.fromMgdlToUnits(profile.targetLowMgdl, profileFunction.getUnits()) - targetBGHigh = Profile.fromMgdlToUnits(profile.targetHighMgdl, profileFunction.getUnits()) + sens = Profile.fromMgdlToUnits(profile.getIsfMgdl(), profileFunction.getUnits()) + targetBGLow = Profile.fromMgdlToUnits(profile.getTargetLowMgdl(), profileFunction.getUnits()) + targetBGHigh = Profile.fromMgdlToUnits(profile.getTargetHighMgdl(), profileFunction.getUnits()) if (useTT && tempTarget != null) { targetBGLow = Profile.fromMgdlToUnits(tempTarget.lowTarget, profileFunction.getUnits()) targetBGHigh = Profile.fromMgdlToUnits(tempTarget.highTarget, profileFunction.getUnits()) @@ -193,16 +201,14 @@ class BolusWizard @Inject constructor( } // Insulin from carbs - ic = profile.ic + ic = profile.getIc() insulinFromCarbs = carbs / ic insulinFromCOB = if (useCob) (cob / ic) else 0.0 // Insulin from IOB // IOB calculation - activePlugin.activeTreatments.updateTotalIOBTreatments() - val bolusIob = activePlugin.activeTreatments.lastCalculationTreatments.round() - activePlugin.activeTreatments.updateTotalIOBTempBasals() - val basalIob = activePlugin.activeTreatments.lastCalculationTempBasals.round() + val bolusIob = iobCobCalculator.calculateIobFromBolus().round() + val basalIob = iobCobCalculator.calculateIobFromTempBasalsIncludingConvertedExtended().round() insulinFromBolusIOB = if (includeBolusIOB) -bolusIob.iob else 0.0 insulinFromBasalIOB = if (includeBasalIOB) -basalIob.basaliob else 0.0 @@ -212,7 +218,7 @@ class BolusWizard @Inject constructor( // Insulin from superbolus for 2h. Get basal rate now and after 1h if (useSuperBolus) { - insulinFromSuperBolus = profile.basal + insulinFromSuperBolus = profile.getBasal() var timeAfter1h = System.currentTimeMillis() timeAfter1h += T.hours(1).msecs() insulinFromSuperBolus += profile.getBasal(timeAfter1h) @@ -241,53 +247,45 @@ class BolusWizard @Inject constructor( return this } - @Suppress("SpellCheckingInspection") - private fun nsJSON(): JSONObject { - val bolusCalcJSON = JSONObject() - try { - bolusCalcJSON.put("profile", profileName) - bolusCalcJSON.put("notes", notes) - bolusCalcJSON.put("eventTime", DateUtil.toISOString(Date())) - bolusCalcJSON.put("targetBGLow", targetBGLow) - bolusCalcJSON.put("targetBGHigh", targetBGHigh) - bolusCalcJSON.put("isf", sens) - bolusCalcJSON.put("ic", ic) - bolusCalcJSON.put("iob", -(insulinFromBolusIOB + insulinFromBasalIOB)) - bolusCalcJSON.put("bolusiob", insulinFromBolusIOB) - bolusCalcJSON.put("basaliob", insulinFromBasalIOB) - bolusCalcJSON.put("bolusiobused", includeBolusIOB) - bolusCalcJSON.put("basaliobused", includeBasalIOB) - bolusCalcJSON.put("bg", bg) - bolusCalcJSON.put("insulinbg", insulinFromBG) - bolusCalcJSON.put("insulinbgused", useBg) - bolusCalcJSON.put("bgdiff", bgDiff) - bolusCalcJSON.put("insulincarbs", insulinFromCarbs) - bolusCalcJSON.put("carbs", carbs) - bolusCalcJSON.put("cob", cob) - bolusCalcJSON.put("cobused", useCob) - bolusCalcJSON.put("insulincob", insulinFromCOB) - bolusCalcJSON.put("othercorrection", correction) - bolusCalcJSON.put("insulinsuperbolus", insulinFromSuperBolus) - bolusCalcJSON.put("insulintrend", insulinFromTrend) - bolusCalcJSON.put("insulin", calculatedTotalInsulin) - bolusCalcJSON.put("superbolusused", useSuperBolus) - bolusCalcJSON.put("insulinsuperbolus", insulinFromSuperBolus) - bolusCalcJSON.put("trendused", useTrend) - bolusCalcJSON.put("insulintrend", insulinFromTrend) - bolusCalcJSON.put("trend", trend) - bolusCalcJSON.put("ttused", useTT) - bolusCalcJSON.put("percentageCorrection", percentageCorrection) - } catch (e: JSONException) { - aapsLogger.error("Unhandled exception", e) - } - return bolusCalcJSON - } + private fun createBolusCalculatorResult(): BolusCalculatorResult = + BolusCalculatorResult( + timestamp = dateUtil.now(), + targetBGLow = targetBGLow, + targetBGHigh = targetBGHigh, + isf = sens, + ic = ic, + bolusIOB = insulinFromBolusIOB, + wasBolusIOBUsed = includeBolusIOB, + basalIOB = insulinFromBasalIOB, + wasBasalIOBUsed = includeBasalIOB, + glucoseValue = bg, + wasGlucoseUsed = useBg && bg > 0, + glucoseDifference = bgDiff, + glucoseInsulin = insulinFromBG, + glucoseTrend = trend, + wasTrendUsed = useTrend, + trendInsulin = insulinFromTrend, + cob = cob, + wasCOBUsed = useCob, + cobInsulin = insulinFromCOB, + carbs = carbs.toDouble(), + wereCarbsUsed = cob > 0, + carbsInsulin = insulinFromCarbs, + otherCorrection = correction, + wasSuperbolusUsed = useSuperBolus, + superbolusInsulin = insulinFromSuperBolus, + wasTempTargetUsed = useTT, + totalInsulin = calculatedTotalInsulin, + percentageCorrection = percentageCorrection, + profileName = profileName, + note = notes + ) private fun confirmMessageAfterConstraints(advisor: Boolean): Spanned { val actions: LinkedList = LinkedList() if (insulinAfterConstraints > 0) { - val pct = if (percentageCorrection != 100.0) " (" + percentageCorrection.toInt() + "%)" else "" + val pct = if (percentageCorrection != 100) " ($percentageCorrection%)" else "" actions.add(resourceHelper.gs(R.string.bolus) + ": " + resourceHelper.gs(R.string.formatinsulinunits, insulinAfterConstraints).formatColor(resourceHelper, R.color.bolus) + pct) } if (carbs > 0 && !advisor) { @@ -301,7 +299,7 @@ class BolusWizard @Inject constructor( } if (insulinFromCOB > 0) { actions.add(resourceHelper.gs(R.string.cobvsiob) + ": " + resourceHelper.gs(R.string.formatsignedinsulinunits, insulinFromBolusIOB + insulinFromBasalIOB + insulinFromCOB + insulinFromBG).formatColor(resourceHelper, R.color.cobAlert)) - val absorptionRate = iobCobCalculatorPlugin.slowAbsorptionPercentage(60) + val absorptionRate = iobCobCalculator.ads.slowAbsorptionPercentage(60) if (absorptionRate > .25) actions.add(resourceHelper.gs(R.string.slowabsorptiondetected, resourceHelper.gc(R.color.cobAlert), (absorptionRate * 100).toInt())) } @@ -339,17 +337,19 @@ class BolusWizard @Inject constructor( val confirmMessage = confirmMessageAfterConstraints(advisor = true) OKDialog.showConfirmation(ctx, resourceHelper.gs(R.string.boluswizard), confirmMessage, { DetailedBolusInfo().apply { - eventType = TherapyEvent.Type.CORRECTION_BOLUS.text + eventType = DetailedBolusInfo.EventType.CORRECTION_BOLUS insulin = insulinAfterConstraints carbs = 0.0 context = ctx - glucose = bg - glucoseType = "Manual" + mgdlGlucose = Profile.toMgdl(bg, profile.units) + glucoseType = DetailedBolusInfo.MeterType.MANUAL carbTime = 0 - boluscalc = nsJSON() - source = Source.USER + bolusCalculatorResult = createBolusCalculatorResult() notes = this@BolusWizard.notes - uel.log(Action.BOLUS_ADVISOR, notes, ValueWithUnit(eventType, Units.TherapyEvent), ValueWithUnit(insulinAfterConstraints, Units.U)) + uel.log(Action.BOLUS_ADVISOR, if (quickWizard) Sources.QuickWizard else Sources.WizardDialog, + notes, + ValueWithUnit.TherapyEventType(eventType.toDBbEventType()), + ValueWithUnit.Insulin(insulinAfterConstraints)) if (insulin > 0) { commandQueue.bolus(this, object : Callback() { override fun run() { @@ -372,14 +372,14 @@ class BolusWizard @Inject constructor( OKDialog.showConfirmation(ctx, resourceHelper.gs(R.string.boluswizard), confirmMessage, { if (insulinAfterConstraints > 0 || carbs > 0) { if (useSuperBolus) { - uel.log(Action.SUPERBOLUS_TBR) + uel.log(Action.SUPERBOLUS_TBR, Sources.WizardDialog) if (loopPlugin.isEnabled(PluginType.LOOP)) { loopPlugin.superBolusTo(System.currentTimeMillis() + 2 * 60L * 60 * 1000) rxBus.send(EventRefreshOverview("WizardDialog")) } if (pump.pumpDescription.tempBasalStyle == PumpDescription.ABSOLUTE) { - commandQueue.tempBasalAbsolute(0.0, 120, true, profile, object : Callback() { + commandQueue.tempBasalAbsolute(0.0, 120, true, profile, PumpSync.TemporaryBasalType.NORMAL, object : Callback() { override fun run() { if (!result.success) { ErrorHelperActivity.runAlarm(ctx, result.comment, resourceHelper.gs(R.string.tempbasaldeliveryerror), R.raw.boluserror) @@ -387,8 +387,7 @@ class BolusWizard @Inject constructor( } }) } else { - - commandQueue.tempBasalPercent(0, 120, true, profile, object : Callback() { + commandQueue.tempBasalPercent(0, 120, true, profile, PumpSync.TemporaryBasalType.NORMAL, object : Callback() { override fun run() { if (!result.success) { val i = Intent(ctx, ErrorHelperActivity::class.java) @@ -403,18 +402,27 @@ class BolusWizard @Inject constructor( } } DetailedBolusInfo().apply { - eventType = TherapyEvent.Type.BOLUS_WIZARD.text + eventType = DetailedBolusInfo.EventType.BOLUS_WIZARD insulin = insulinAfterConstraints carbs = this@BolusWizard.carbs.toDouble() context = ctx - glucose = bg - glucoseType = "Manual" + mgdlGlucose = Profile.toMgdl(bg, profile.units) + glucoseType = DetailedBolusInfo.MeterType.MANUAL carbTime = this@BolusWizard.carbTime - boluscalc = nsJSON() - source = Source.USER + bolusCalculatorResult = createBolusCalculatorResult() notes = this@BolusWizard.notes - uel.log(Action.BOLUS, notes, ValueWithUnit(eventType,Units.TherapyEvent), ValueWithUnit(insulinAfterConstraints, Units.U), ValueWithUnit(this@BolusWizard.carbs, Units.G, this@BolusWizard.carbs != 0), ValueWithUnit(carbTime, Units.M, carbTime != 0)) - if (insulin > 0 || pump.pumpDescription.storesCarbInfo) { + if (insulin > 0 || carbs > 0) { + val action = when { + insulinAfterConstraints.equals(0.0) -> Action.CARBS + carbs.equals(0.0) -> Action.BOLUS + else -> Action.TREATMENT + } + uel.log(action, if (quickWizard) Sources.QuickWizard else Sources.WizardDialog, + notes, + ValueWithUnit.TherapyEventType(eventType.toDBbEventType()), + ValueWithUnit.Insulin(insulinAfterConstraints).takeIf { insulinAfterConstraints != 0.0 }, + ValueWithUnit.Gram(this@BolusWizard.carbs).takeIf { this@BolusWizard.carbs != 0 }, + ValueWithUnit.Minute(carbTime).takeIf { carbTime != 0 }) commandQueue.bolus(this, object : Callback() { override fun run() { if (!result.success) { @@ -422,12 +430,16 @@ class BolusWizard @Inject constructor( } } }) - } else { - activePlugin.activeTreatments.addToHistoryTreatment(this, false) } + disposable += repository.runTransactionForResult(InsertOrUpdateBolusCalculatorResultTransaction(bolusCalculatorResult!!)) + .subscribe( + { result -> result.inserted.forEach { inserted -> aapsLogger.debug(LTag.DATABASE, "Inserted bolusCalculatorResult $inserted") } }, + { aapsLogger.error(LTag.DATABASE, "Error while saving bolusCalculatorResult", it) } + ) + } if (useAlarm && carbs > 0 && carbTime > 0) { - carbTimer.scheduleReminder(dateUtil._now() + T.mins(carbTime.toLong()).msecs()) + carbTimer.scheduleReminder(dateUtil.now() + T.mins(carbTime.toLong()).msecs()) } } }) diff --git a/app/src/main/java/info/nightscout/androidaps/utils/wizard/QuickWizardEntry.kt b/app/src/main/java/info/nightscout/androidaps/utils/wizard/QuickWizardEntry.kt index 471bd0898a..9e9ae6a87a 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/wizard/QuickWizardEntry.kt +++ b/app/src/main/java/info/nightscout/androidaps/utils/wizard/QuickWizardEntry.kt @@ -2,25 +2,22 @@ package info.nightscout.androidaps.utils.wizard import dagger.android.HasAndroidInjector import info.nightscout.androidaps.R -import info.nightscout.androidaps.data.Profile import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.ValueWrapper import info.nightscout.androidaps.database.entities.GlucoseValue +import info.nightscout.androidaps.extensions.valueToUnits +import info.nightscout.androidaps.interfaces.IobCobCalculator +import info.nightscout.androidaps.interfaces.Profile import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatusProvider -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin -import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.JsonHelper.safeGetInt import info.nightscout.androidaps.utils.JsonHelper.safeGetString -import info.nightscout.androidaps.utils.extensions.valueToUnits import info.nightscout.androidaps.utils.sharedPreferences.SP import org.json.JSONException import org.json.JSONObject -import java.util.* import javax.inject.Inject class QuickWizardEntry @Inject constructor(private val injector: HasAndroidInjector) { @@ -28,9 +25,8 @@ class QuickWizardEntry @Inject constructor(private val injector: HasAndroidInjec @Inject lateinit var aapsLogger: AAPSLogger @Inject lateinit var sp: SP @Inject lateinit var profileFunction: ProfileFunction - @Inject lateinit var treatmentsPlugin: TreatmentsPlugin @Inject lateinit var loopPlugin: LoopPlugin - @Inject lateinit var iobCobCalculatorPlugin: IobCobCalculatorPlugin + @Inject lateinit var iobCobCalculator: IobCobCalculator @Inject lateinit var repository: AppRepository @Inject lateinit var dateUtil: DateUtil @Inject lateinit var glucoseStatusProvider: GlucoseStatusProvider @@ -77,10 +73,10 @@ class QuickWizardEntry @Inject constructor(private val injector: HasAndroidInjec return this } - fun isActive(): Boolean = Profile.secondsFromMidnight() >= validFrom() && Profile.secondsFromMidnight() <= validTo() + fun isActive(): Boolean = profileFunction.secondsFromMidnight() >= validFrom() && profileFunction.secondsFromMidnight() <= validTo() fun doCalc(profile: Profile, profileName: String, lastBG: GlucoseValue, _synchronized: Boolean): BolusWizard { - val dbRecord = repository.getTemporaryTargetActiveAt(dateUtil._now()).blockingGet() + val dbRecord = repository.getTemporaryTargetActiveAt(dateUtil.now()).blockingGet() val tempTarget = if (dbRecord is ValueWrapper.Existing) dbRecord.value else null //BG var bg = 0.0 @@ -90,7 +86,7 @@ class QuickWizardEntry @Inject constructor(private val injector: HasAndroidInjec // COB var cob = 0.0 if (useCOB() == YES) { - val cobInfo = iobCobCalculatorPlugin.getCobInfo(_synchronized, "QuickWizard COB") + val cobInfo = iobCobCalculator.getCobInfo(_synchronized, "QuickWizard COB") if (cobInfo.displayCob != null) cob = cobInfo.displayCob!! } // Bolus IOB @@ -99,8 +95,7 @@ class QuickWizardEntry @Inject constructor(private val injector: HasAndroidInjec bolusIOB = true } // Basal IOB - treatmentsPlugin.updateTotalIOBTempBasals() - val basalIob = treatmentsPlugin.lastCalculationTempBasals.round() + val basalIob = iobCobCalculator.calculateIobFromTempBasalsIncludingConvertedExtended().round() var basalIOB = false if (useBasalIOB() == YES) { basalIOB = true @@ -125,17 +120,17 @@ class QuickWizardEntry @Inject constructor(private val injector: HasAndroidInjec } else if (useTrend() == NEGATIVE_ONLY && glucoseStatus != null && glucoseStatus.shortAvgDelta < 0) { trend = true } - val percentage = sp.getDouble(R.string.key_boluswizard_percentage, 100.0) - return BolusWizard(injector).doCalc(profile, profileName, tempTarget, carbs(), cob, bg, 0.0, percentage, true, useCOB() == YES, bolusIOB, basalIOB, superBolus, useTempTarget() == YES, trend, false, "QuickWizard") + val percentage = sp.getInt(R.string.key_boluswizard_percentage, 100) + return BolusWizard(injector).doCalc(profile, profileName, tempTarget, carbs(), cob, bg, 0.0, percentage, true, useCOB() == YES, bolusIOB, basalIOB, superBolus, useTempTarget() == YES, trend, false, buttonText(), quickWizard = true) //tbc, ok if only quickwizard, but if other sources elsewhere use Sources.QuickWiard } fun buttonText(): String = safeGetString(storage, "buttonText", "") fun carbs(): Int = safeGetInt(storage, "carbs") - fun validFromDate(): Date = DateUtil.toDate(validFrom()) + fun validFromDate(): Long = dateUtil.secondsOfTheDayToMilliseconds(validFrom()) - fun validToDate(): Date = DateUtil.toDate(validTo()) + fun validToDate(): Long = dateUtil.secondsOfTheDayToMilliseconds(validTo()) fun validFrom(): Int = safeGetInt(storage, "validFrom") diff --git a/app/src/main/res/layout/dialog_loop.xml b/app/src/main/res/layout/dialog_loop.xml index b0658e56e1..594d1855e3 100644 --- a/app/src/main/res/layout/dialog_loop.xml +++ b/app/src/main/res/layout/dialog_loop.xml @@ -51,6 +51,7 @@ diff --git a/app/src/main/res/layout/dialog_temptarget.xml b/app/src/main/res/layout/dialog_temptarget.xml index 7a53ea8496..61f92c402f 100644 --- a/app/src/main/res/layout/dialog_temptarget.xml +++ b/app/src/main/res/layout/dialog_temptarget.xml @@ -141,8 +141,6 @@ - - + android:paddingBottom="10dp" + android:weightSum="5"> + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="vertical"> + + + + + + + android:text="@string/activate_profile" /> - - + android:autoLink="web" + tools:ignore="RtlHardcoded" /> @@ -42,18 +39,19 @@ android:gravity="center_horizontal"> + android:text="@string/nsclientinternal_autoscroll" + tools:ignore="RtlHardcoded" /> @@ -68,22 +66,15 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginRight="5dp" - android:text="@string/status" /> + android:text="@string/status" + tools:ignore="RtlHardcoded" /> - - - - @@ -148,20 +129,17 @@ - - - - + diff --git a/app/src/main/res/layout/nsprofile_fragment.xml b/app/src/main/res/layout/nsprofile_fragment.xml deleted file mode 100644 index 9f4d9525ae..0000000000 --- a/app/src/main/res/layout/nsprofile_fragment.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - -