This commit is contained in:
Milos Kozak 2022-04-05 13:52:36 +02:00
commit 4bf3879377
69 changed files with 1816 additions and 1318 deletions

View file

@ -109,7 +109,7 @@ android {
defaultConfig { defaultConfig {
multiDexEnabled true multiDexEnabled true
versionCode 1500 versionCode 1500
version "3.0.0.1-dev-h" version "3.0.0.1-dev-i"
buildConfigField "String", "VERSION", '"' + version + '"' buildConfigField "String", "VERSION", '"' + version + '"'
buildConfigField "String", "BUILDVERSION", '"' + generateGitBuild() + '-' + generateDate() + '"' buildConfigField "String", "BUILDVERSION", '"' + generateGitBuild() + '-' + generateDate() + '"'
buildConfigField "String", "REMOTE", '"' + generateGitRemote() + '"' buildConfigField "String", "REMOTE", '"' + generateGitRemote() + '"'

View file

@ -13,7 +13,6 @@ import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.data.ProfileSealed import info.nightscout.androidaps.data.ProfileSealed
import info.nightscout.androidaps.database.interfaces.end import info.nightscout.androidaps.database.interfaces.end
import info.nightscout.androidaps.extensions.directionToIcon import info.nightscout.androidaps.extensions.directionToIcon
import info.nightscout.androidaps.extensions.isInProgress
import info.nightscout.androidaps.extensions.toVisibility import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.extensions.valueToUnitsString import info.nightscout.androidaps.extensions.valueToUnitsString
import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.interfaces.*
@ -78,7 +77,7 @@ class Widget : AppWidgetProvider() {
// Create an Intent to launch MainActivity when clicked // Create an Intent to launch MainActivity when clicked
val intent = Intent(context, MainActivity::class.java).also { it.action = intentAction } val intent = Intent(context, MainActivity::class.java).also { it.action = intentAction }
val pendingIntent = PendingIntent.getActivity(context, 0, intent, 0) val pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT)
// Widgets allow click handlers to only launch pending intents // Widgets allow click handlers to only launch pending intents
views.setOnClickPendingIntent(R.id.widget_layout, pendingIntent) views.setOnClickPendingIntent(R.id.widget_layout, pendingIntent)
@ -105,7 +104,11 @@ class Widget : AppWidgetProvider() {
} }
) )
views.setImageViewResource(R.id.arrow, trendCalculator.getTrendArrow(overviewData.lastBg).directionToIcon()) views.setImageViewResource(R.id.arrow, trendCalculator.getTrendArrow(overviewData.lastBg).directionToIcon())
//binding.infoLayout.arrow.setColorFilter(overviewData.lastBgColor(context)) views.setInt(R.id.arrow, "setColorFilter", when {
overviewData.isLow -> rh.gc(R.color.low)
overviewData.isHigh -> rh.gc(R.color.high)
else -> rh.gc(R.color.inrange)
})
val glucoseStatus = glucoseStatusProvider.glucoseStatusData val glucoseStatus = glucoseStatusProvider.glucoseStatusData
if (glucoseStatus != null) { if (glucoseStatus != null) {
@ -127,21 +130,21 @@ class Widget : AppWidgetProvider() {
} }
private fun updateTemporaryBasal(views: RemoteViews) { private fun updateTemporaryBasal(views: RemoteViews) {
views.setTextViewText(R.id.base_basal, overviewData.temporaryBasalText) views.setTextViewText(R.id.base_basal, overviewData.temporaryBasalText(iobCobCalculator))
views.setTextColor(R.id.base_basal, overviewData.temporaryBasalColor) views.setTextColor(R.id.base_basal, overviewData.temporaryBasalColor(iobCobCalculator))
views.setImageViewResource(R.id.base_basal_icon, overviewData.temporaryBasalIcon) views.setImageViewResource(R.id.base_basal_icon, overviewData.temporaryBasalIcon(iobCobCalculator))
} }
private fun updateExtendedBolus(views: RemoteViews) { private fun updateExtendedBolus(views: RemoteViews) {
val pump = activePlugin.activePump val pump = activePlugin.activePump
views.setTextViewText(R.id.extended_bolus, overviewData.extendedBolusText) views.setTextViewText(R.id.extended_bolus, overviewData.extendedBolusText(iobCobCalculator))
views.setViewVisibility(R.id.extended_layout, (iobCobCalculator.getExtendedBolus(dateUtil.now()) != null && !pump.isFakingTempsByExtendedBoluses).toVisibility()) views.setViewVisibility(R.id.extended_layout, (iobCobCalculator.getExtendedBolus(dateUtil.now()) != null && !pump.isFakingTempsByExtendedBoluses).toVisibility())
} }
private fun updateIobCob(views: RemoteViews) { private fun updateIobCob(views: RemoteViews) {
views.setTextViewText(R.id.iob, overviewData.iobText) views.setTextViewText(R.id.iob, overviewData.iobText(iobCobCalculator))
// cob // cob
var cobText = overviewData.cobInfo?.displayText(rh, dateUtil, isDev = false) ?: rh.gs(R.string.value_unavailable_short) var cobText = overviewData.cobInfo(iobCobCalculator).displayText(rh, dateUtil, isDev = false) ?: rh.gs(R.string.value_unavailable_short)
val constraintsProcessed = loop.lastRun?.constraintsProcessed val constraintsProcessed = loop.lastRun?.constraintsProcessed
val lastRun = loop.lastRun val lastRun = loop.lastRun
@ -158,7 +161,6 @@ class Widget : AppWidgetProvider() {
private fun updateTemporaryTarget(views: RemoteViews) { private fun updateTemporaryTarget(views: RemoteViews) {
val units = profileFunction.getUnits() val units = profileFunction.getUnits()
if (overviewData.temporaryTarget?.isInProgress(dateUtil) == false) overviewData.temporaryTarget = null
val tempTarget = overviewData.temporaryTarget val tempTarget = overviewData.temporaryTarget
if (tempTarget != null) { if (tempTarget != null) {
// this is crashing, use background as text for now // this is crashing, use background as text for now
@ -210,12 +212,12 @@ class Widget : AppWidgetProvider() {
views.setTextColor(R.id.active_profile, profileTextColor) views.setTextColor(R.id.active_profile, profileTextColor)
} }
fun updateSensitivity(views: RemoteViews) { private fun updateSensitivity(views: RemoteViews) {
if (sp.getBoolean(R.string.key_openapsama_useautosens, false) && constraintChecker.isAutosensModeEnabled().value()) if (sp.getBoolean(R.string.key_openapsama_useautosens, false) && constraintChecker.isAutosensModeEnabled().value())
views.setImageViewResource(R.id.sensitivity_icon, R.drawable.ic_swap_vert_black_48dp_green) views.setImageViewResource(R.id.sensitivity_icon, R.drawable.ic_swap_vert_black_48dp_green)
else else
views.setImageViewResource(R.id.sensitivity_icon, R.drawable.ic_x_swap_vert) views.setImageViewResource(R.id.sensitivity_icon, R.drawable.ic_x_swap_vert)
views.setTextViewText(R.id.sensitivity, overviewData.lastAutosensData?.let { autosensData -> views.setTextViewText(R.id.sensitivity, overviewData.lastAutosensData(iobCobCalculator)?.let { autosensData ->
String.format(Locale.ENGLISH, "%.0f%%", autosensData.autosensResult.ratio * 100) String.format(Locale.ENGLISH, "%.0f%%", autosensData.autosensResult.ratio * 100)
} ?: "") } ?: "")

View file

@ -3,7 +3,6 @@ package info.nightscout.androidaps.activities
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.DatePickerDialog import android.app.DatePickerDialog
import android.content.Context import android.content.Context
import android.graphics.Color
import android.os.Bundle import android.os.Bundle
import android.util.DisplayMetrics import android.util.DisplayMetrics
import android.view.ViewGroup import android.view.ViewGroup
@ -19,21 +18,18 @@ import info.nightscout.androidaps.events.EventAutosensCalculationFinished
import info.nightscout.androidaps.events.EventCustomCalculationFinished import info.nightscout.androidaps.events.EventCustomCalculationFinished
import info.nightscout.androidaps.events.EventRefreshOverview import info.nightscout.androidaps.events.EventRefreshOverview
import info.nightscout.androidaps.extensions.toVisibility import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.extensions.toVisibilityKeepSpace
import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.Config import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.interfaces.Loop import info.nightscout.androidaps.interfaces.Loop
import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.general.nsclient.data.NSDeviceStatus import info.nightscout.androidaps.plugins.general.nsclient.data.NSDeviceStatus
import info.nightscout.androidaps.plugins.general.overview.OverviewData import info.nightscout.androidaps.plugins.general.overview.OverviewData
import info.nightscout.androidaps.plugins.general.overview.OverviewMenus import info.nightscout.androidaps.plugins.general.overview.OverviewMenus
import info.nightscout.androidaps.plugins.general.overview.events.EventUpdateOverviewGraph
import info.nightscout.androidaps.plugins.general.overview.graphData.GraphData import info.nightscout.androidaps.plugins.general.overview.graphData.GraphData
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventBucketedDataCreated
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventIobCalculationProgress import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventIobCalculationProgress
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.receivers.DataWorker import info.nightscout.androidaps.receivers.DataWorker
import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.DefaultValueHelper import info.nightscout.androidaps.utils.DefaultValueHelper
@ -42,6 +38,7 @@ import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.Translator import info.nightscout.androidaps.utils.Translator
import info.nightscout.androidaps.utils.buildHelper.BuildHelper import info.nightscout.androidaps.utils.buildHelper.BuildHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.androidaps.workflow.CalculationWorkflow
import info.nightscout.shared.logging.LTag import info.nightscout.shared.logging.LTag
import info.nightscout.shared.sharedPreferences.SP import info.nightscout.shared.sharedPreferences.SP
import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.disposables.CompositeDisposable
@ -59,9 +56,6 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
@Inject lateinit var defaultValueHelper: DefaultValueHelper @Inject lateinit var defaultValueHelper: DefaultValueHelper
@Inject lateinit var activePlugin: ActivePlugin @Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var buildHelper: BuildHelper @Inject lateinit var buildHelper: BuildHelper
@Inject lateinit var sensitivityOref1Plugin: SensitivityOref1Plugin
@Inject lateinit var sensitivityAAPSPlugin: SensitivityAAPSPlugin
@Inject lateinit var sensitivityWeightedAveragePlugin: SensitivityWeightedAveragePlugin
@Inject lateinit var repository: AppRepository @Inject lateinit var repository: AppRepository
@Inject lateinit var fabricPrivacy: FabricPrivacy @Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var overviewMenus: OverviewMenus @Inject lateinit var overviewMenus: OverviewMenus
@ -72,6 +66,7 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
@Inject lateinit var translator: Translator @Inject lateinit var translator: Translator
@Inject lateinit var context: Context @Inject lateinit var context: Context
@Inject lateinit var dataWorker: DataWorker @Inject lateinit var dataWorker: DataWorker
@Inject lateinit var calculationWorkflow: CalculationWorkflow
private val disposable = CompositeDisposable() private val disposable = CompositeDisposable()
@ -94,6 +89,17 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
setContentView(binding.root) setContentView(binding.root)
// We don't want to use injected singletons but own instance working on top of different data // We don't want to use injected singletons but own instance working on top of different data
overviewData =
OverviewData(
aapsLogger,
rh,
dateUtil,
sp,
activePlugin,
defaultValueHelper,
profileFunction,
repository
)
iobCobCalculator = iobCobCalculator =
IobCobCalculatorPlugin( IobCobCalculatorPlugin(
injector, injector,
@ -104,32 +110,11 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
rh, rh,
profileFunction, profileFunction,
activePlugin, activePlugin,
sensitivityOref1Plugin,
sensitivityAAPSPlugin,
sensitivityWeightedAveragePlugin,
fabricPrivacy, fabricPrivacy,
dateUtil, dateUtil,
repository, repository,
context, overviewData,
dataWorker calculationWorkflow
)
overviewData =
OverviewData(
injector,
aapsLogger,
rh,
dateUtil,
sp,
activePlugin,
defaultValueHelper,
profileFunction,
config,
loop,
nsDeviceStatus,
repository,
overviewMenus,
iobCobCalculator,
translator
) )
binding.left.setOnClickListener { binding.left.setOnClickListener {
@ -201,7 +186,7 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
axisWidth = if (dm.densityDpi <= 120) 3 else if (dm.densityDpi <= 160) 10 else if (dm.densityDpi <= 320) 35 else if (dm.densityDpi <= 420) 50 else if (dm.densityDpi <= 560) 70 else 80 axisWidth = if (dm.densityDpi <= 120) 3 else if (dm.densityDpi <= 160) 10 else if (dm.densityDpi <= 320) 35 else if (dm.densityDpi <= 420) 50 else if (dm.densityDpi <= 560) 70 else 80
binding.bgGraph.gridLabelRenderer?.gridColor = rh.gac(this, R.attr.graphgrid) binding.bgGraph.gridLabelRenderer?.gridColor = rh.gac(this, R.attr.graphgrid)
binding.bgGraph.gridLabelRenderer?.reloadStyles() binding.bgGraph.gridLabelRenderer?.reloadStyles()
binding.bgGraph.gridLabelRenderer?.labelVerticalWidth = axisWidth binding.bgGraph.gridLabelRenderer?.labelVerticalWidth = axisWidth
@ -217,7 +202,7 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
override fun onPause() { override fun onPause() {
super.onPause() super.onPause()
disposable.clear() disposable.clear()
iobCobCalculator.stopCalculation("onPause") calculationWorkflow.stopCalculation(CalculationWorkflow.HISTORY_CALCULATION, "onPause")
} }
@Synchronized @Synchronized
@ -239,22 +224,11 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
disposable += rxBus disposable += rxBus
.toObservable(EventIobCalculationProgress::class.java) .toObservable(EventIobCalculationProgress::class.java)
.observeOn(aapsSchedulers.main) .observeOn(aapsSchedulers.main)
.subscribe({ .subscribe({ updateCalcProgress(it.pass.finalPercent(it.progressPct)) }, fabricPrivacy::logException)
if (it.cause is EventCustomCalculationFinished)
binding.overviewIobcalculationprogess.text = it.progressPct.toString() + "%"
}, fabricPrivacy::logException)
disposable += rxBus disposable += rxBus
.toObservable(EventRefreshOverview::class.java) .toObservable(EventUpdateOverviewGraph::class.java)
.observeOn(aapsSchedulers.main) .observeOn(aapsSchedulers.main)
.subscribe({ updateGUI("EventRefreshOverview") }, fabricPrivacy::logException) .subscribe({ updateGUI("EventRefreshOverview") }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventBucketedDataCreated::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({
overviewData.prepareBucketedData("EventBucketedDataCreated")
overviewData.prepareBgData("EventBucketedDataCreated")
rxBus.send(EventRefreshOverview("EventBucketedDataCreated"))
}, fabricPrivacy::logException)
if (overviewData.fromTime == 0L) { if (overviewData.fromTime == 0L) {
// set start of current day // set start of current day
@ -285,12 +259,12 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
val graph = GraphView(this) val graph = GraphView(this)
graph.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, rh.dpToPx(100)).also { it.setMargins(0, rh.dpToPx(15), 0, rh.dpToPx(10)) } graph.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, rh.dpToPx(100)).also { it.setMargins(0, rh.dpToPx(15), 0, rh.dpToPx(10)) }
graph.gridLabelRenderer?.gridColor = rh.gac( R.attr.graphgrid) graph.gridLabelRenderer?.gridColor = rh.gac(R.attr.graphgrid)
graph.gridLabelRenderer?.reloadStyles() graph.gridLabelRenderer?.reloadStyles()
graph.gridLabelRenderer?.isHorizontalLabelsVisible = false graph.gridLabelRenderer?.isHorizontalLabelsVisible = false
graph.gridLabelRenderer?.labelVerticalWidth = axisWidth graph.gridLabelRenderer?.labelVerticalWidth = axisWidth
graph.gridLabelRenderer?.numVerticalLabels = 3 graph.gridLabelRenderer?.numVerticalLabels = 3
graph.viewport.backgroundColor =rh.gac(this , R.attr.viewPortbackgroundColor) graph.viewport.backgroundColor = rh.gac(this, R.attr.viewPortbackgroundColor)
relativeLayout.addView(graph) relativeLayout.addView(graph)
val label = TextView(this) val label = TextView(this)
@ -310,17 +284,7 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
@Suppress("SameParameterValue") @Suppress("SameParameterValue")
private fun loadAll(from: String) { private fun loadAll(from: String) {
updateDate() updateDate()
Thread { runCalculation(from)
overviewData.prepareBgData(from)
overviewData.prepareTreatmentsData(from)
rxBus.send(EventRefreshOverview("loadAll_$from"))
overviewData.prepareTemporaryTargetData(from)
rxBus.send(EventRefreshOverview("loadAll_$from"))
overviewData.prepareBasalData(from)
rxBus.send(EventRefreshOverview(from))
aapsLogger.debug(LTag.UI, "loadAll $from finished")
runCalculation(from)
}.start()
} }
private fun setTime(start: Long) { private fun setTime(start: Long) {
@ -341,22 +305,32 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
} }
private fun runCalculation(from: String) { private fun runCalculation(from: String) {
iobCobCalculator.runCalculation(from, overviewData.toTime, bgDataReload = true, limitDataToOldestAvailable = false, cause = EventCustomCalculationFinished()) calculationWorkflow.runCalculation(
CalculationWorkflow.HISTORY_CALCULATION,
iobCobCalculator,
overviewData,
from,
overviewData.toTime,
bgDataReload = true,
limitDataToOldestAvailable = false,
cause = EventCustomCalculationFinished(),
runLoop = false
)
} }
@Volatile @Volatile
var runningRefresh = false var runningRefresh = false
@Suppress("SameParameterValue") @Suppress("SameParameterValue")
private fun refreshLoop(from: String) { private fun refreshLoop(from: String) {
if (runningRefresh) return if (runningRefresh) return
runningRefresh = true runningRefresh = true
overviewData.prepareIobAutosensData(from)
rxBus.send(EventRefreshOverview(from)) rxBus.send(EventRefreshOverview(from))
aapsLogger.debug(LTag.UI, "refreshLoop finished") aapsLogger.debug(LTag.UI, "refreshLoop finished")
runningRefresh = false runningRefresh = false
} }
fun updateDate() { private fun updateDate() {
binding.date.text = dateUtil.dateAndTimeString(overviewData.fromTime) binding.date.text = dateUtil.dateAndTimeString(overviewData.fromTime)
binding.zoom.text = rangeToDisplay.toString() binding.zoom.text = rangeToDisplay.toString()
} }
@ -403,12 +377,12 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
var useDSForScale = false var useDSForScale = false
var useBGIForScale = false var useBGIForScale = false
when { when {
menuChartSettings[g + 1][OverviewMenus.CharType.ABS.ordinal] -> useABSForScale = true menuChartSettings[g + 1][OverviewMenus.CharType.ABS.ordinal] -> useABSForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.IOB.ordinal] -> useIobForScale = true menuChartSettings[g + 1][OverviewMenus.CharType.IOB.ordinal] -> useIobForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.COB.ordinal] -> useCobForScale = true menuChartSettings[g + 1][OverviewMenus.CharType.COB.ordinal] -> useCobForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.DEV.ordinal] -> useDevForScale = true menuChartSettings[g + 1][OverviewMenus.CharType.DEV.ordinal] -> useDevForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.BGI.ordinal] -> useBGIForScale = true menuChartSettings[g + 1][OverviewMenus.CharType.BGI.ordinal] -> useBGIForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.SEN.ordinal] -> useRatioForScale = true menuChartSettings[g + 1][OverviewMenus.CharType.SEN.ordinal] -> useRatioForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.DEVSLOPE.ordinal] -> useDSForScale = 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] val alignDevBgiScale = menuChartSettings[g + 1][OverviewMenus.CharType.DEV.ordinal] && menuChartSettings[g + 1][OverviewMenus.CharType.BGI.ordinal]
@ -440,4 +414,9 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
secondaryGraphsData[g].performUpdate() secondaryGraphsData[g].performUpdate()
} }
} }
private fun updateCalcProgress(percent: Int) {
binding.progressBar.progress = percent
binding.progressBar.visibility = (percent != 100).toVisibilityKeepSpace()
}
} }

View file

@ -24,9 +24,7 @@ import info.nightscout.androidaps.database.transactions.InvalidateCarbsTransacti
import info.nightscout.androidaps.databinding.TreatmentsBolusCarbsFragmentBinding import info.nightscout.androidaps.databinding.TreatmentsBolusCarbsFragmentBinding
import info.nightscout.androidaps.databinding.TreatmentsBolusCarbsItemBinding import info.nightscout.androidaps.databinding.TreatmentsBolusCarbsItemBinding
import info.nightscout.androidaps.dialogs.WizardInfoDialog import info.nightscout.androidaps.dialogs.WizardInfoDialog
import info.nightscout.androidaps.events.EventAutosensCalculationFinished
import info.nightscout.androidaps.events.EventTreatmentChange import info.nightscout.androidaps.events.EventTreatmentChange
import info.nightscout.androidaps.events.EventTreatmentUpdateGui
import info.nightscout.androidaps.extensions.iobCalc import info.nightscout.androidaps.extensions.iobCalc
import info.nightscout.androidaps.extensions.toVisibility import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.ActivePlugin
@ -170,16 +168,6 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() {
.observeOn(aapsSchedulers.main) .observeOn(aapsSchedulers.main)
.debounce(1L, TimeUnit.SECONDS) .debounce(1L, TimeUnit.SECONDS)
.subscribe({ swapAdapter() }, fabricPrivacy::logException) .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 @Synchronized
@ -334,7 +322,7 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() {
showInvalidated = true showInvalidated = true
updateMenuVisibility() updateMenuVisibility()
ToastUtils.showToastInUiThread(context, rh.gs(R.string.show_invalidated_records)) ToastUtils.showToastInUiThread(context, rh.gs(R.string.show_invalidated_records))
rxBus.send(EventTreatmentUpdateGui()) swapAdapter()
true true
} }
@ -342,7 +330,7 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() {
showInvalidated = false showInvalidated = false
updateMenuVisibility() updateMenuVisibility()
ToastUtils.showToastInUiThread(context, rh.gs(R.string.hide_invalidated_records)) ToastUtils.showToastInUiThread(context, rh.gs(R.string.hide_invalidated_records))
rxBus.send(EventTreatmentUpdateGui()) swapAdapter()
true true
} }

View file

@ -19,7 +19,6 @@ import info.nightscout.androidaps.database.transactions.InvalidateTherapyEventTr
import info.nightscout.androidaps.databinding.TreatmentsCareportalFragmentBinding import info.nightscout.androidaps.databinding.TreatmentsCareportalFragmentBinding
import info.nightscout.androidaps.databinding.TreatmentsCareportalItemBinding import info.nightscout.androidaps.databinding.TreatmentsCareportalItemBinding
import info.nightscout.androidaps.events.EventTherapyEventChange import info.nightscout.androidaps.events.EventTherapyEventChange
import info.nightscout.androidaps.events.EventTreatmentUpdateGui
import info.nightscout.androidaps.extensions.toVisibility import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.plugins.bus.RxBus
@ -128,11 +127,6 @@ class TreatmentsCareportalFragment : DaggerFragment() {
.observeOn(aapsSchedulers.main) .observeOn(aapsSchedulers.main)
.debounce(1L, TimeUnit.SECONDS) .debounce(1L, TimeUnit.SECONDS)
.subscribe({ swapAdapter() }, fabricPrivacy::logException) .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)
} }
@Synchronized @Synchronized
@ -216,7 +210,7 @@ class TreatmentsCareportalFragment : DaggerFragment() {
showInvalidated = true showInvalidated = true
updateMenuVisibility() updateMenuVisibility()
ToastUtils.showToastInUiThread(context, rh.gs(R.string.show_invalidated_records)) ToastUtils.showToastInUiThread(context, rh.gs(R.string.show_invalidated_records))
rxBus.send(EventTreatmentUpdateGui()) swapAdapter()
true true
} }
@ -224,7 +218,7 @@ class TreatmentsCareportalFragment : DaggerFragment() {
showInvalidated = false showInvalidated = false
updateMenuVisibility() updateMenuVisibility()
ToastUtils.showToastInUiThread(context, rh.gs(R.string.hide_invalidated_records)) ToastUtils.showToastInUiThread(context, rh.gs(R.string.hide_invalidated_records))
rxBus.send(EventTreatmentUpdateGui()) swapAdapter()
true true
} }

View file

@ -20,7 +20,6 @@ import info.nightscout.androidaps.database.transactions.InvalidateExtendedBolusT
import info.nightscout.androidaps.databinding.TreatmentsExtendedbolusFragmentBinding import info.nightscout.androidaps.databinding.TreatmentsExtendedbolusFragmentBinding
import info.nightscout.androidaps.databinding.TreatmentsExtendedbolusItemBinding import info.nightscout.androidaps.databinding.TreatmentsExtendedbolusItemBinding
import info.nightscout.androidaps.events.EventExtendedBolusChange import info.nightscout.androidaps.events.EventExtendedBolusChange
import info.nightscout.androidaps.events.EventTreatmentUpdateGui
import info.nightscout.androidaps.extensions.iobCalc import info.nightscout.androidaps.extensions.iobCalc
import info.nightscout.androidaps.extensions.isInProgress import info.nightscout.androidaps.extensions.isInProgress
import info.nightscout.androidaps.extensions.toVisibility import info.nightscout.androidaps.extensions.toVisibility
@ -105,11 +104,6 @@ class TreatmentsExtendedBolusesFragment : DaggerFragment() {
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
.debounce(1L, TimeUnit.SECONDS) .debounce(1L, TimeUnit.SECONDS)
.subscribe({ swapAdapter() }, fabricPrivacy::logException) .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)
} }
@Synchronized @Synchronized
@ -203,7 +197,7 @@ class TreatmentsExtendedBolusesFragment : DaggerFragment() {
showInvalidated = true showInvalidated = true
updateMenuVisibility() updateMenuVisibility()
ToastUtils.showToastInUiThread(context, rh.gs(R.string.show_invalidated_records)) ToastUtils.showToastInUiThread(context, rh.gs(R.string.show_invalidated_records))
rxBus.send(EventTreatmentUpdateGui()) swapAdapter()
true true
} }
@ -211,7 +205,7 @@ class TreatmentsExtendedBolusesFragment : DaggerFragment() {
showInvalidated = false showInvalidated = false
updateMenuVisibility() updateMenuVisibility()
ToastUtils.showToastInUiThread(context, rh.gs(R.string.hide_invalidated_records)) ToastUtils.showToastInUiThread(context, rh.gs(R.string.hide_invalidated_records))
rxBus.send(EventTreatmentUpdateGui()) swapAdapter()
true true
} }

View file

@ -22,7 +22,6 @@ import info.nightscout.androidaps.databinding.TreatmentsProfileswitchItemBinding
import info.nightscout.androidaps.dialogs.ProfileViewerDialog import info.nightscout.androidaps.dialogs.ProfileViewerDialog
import info.nightscout.androidaps.events.EventEffectiveProfileSwitchChanged import info.nightscout.androidaps.events.EventEffectiveProfileSwitchChanged
import info.nightscout.androidaps.events.EventProfileSwitchChanged import info.nightscout.androidaps.events.EventProfileSwitchChanged
import info.nightscout.androidaps.events.EventTreatmentUpdateGui
import info.nightscout.androidaps.extensions.getCustomizedName import info.nightscout.androidaps.extensions.getCustomizedName
import info.nightscout.androidaps.extensions.toVisibility import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.logging.UserEntryLogger
@ -47,7 +46,6 @@ import io.reactivex.rxjava3.core.Completable
import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign import io.reactivex.rxjava3.kotlin.plusAssign
import io.reactivex.rxjava3.kotlin.subscribeBy import io.reactivex.rxjava3.kotlin.subscribeBy
import java.util.concurrent.TimeUnit
import javax.inject.Inject import javax.inject.Inject
class TreatmentsProfileSwitchFragment : DaggerFragment() { class TreatmentsProfileSwitchFragment : DaggerFragment() {
@ -162,11 +160,6 @@ class TreatmentsProfileSwitchFragment : DaggerFragment() {
.toObservable(EventEffectiveProfileSwitchChanged::class.java) .toObservable(EventEffectiveProfileSwitchChanged::class.java)
.observeOn(aapsSchedulers.main) .observeOn(aapsSchedulers.main)
.subscribe({ swapAdapter() }, fabricPrivacy::logException) .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)
} }
@Synchronized @Synchronized
@ -306,7 +299,7 @@ class TreatmentsProfileSwitchFragment : DaggerFragment() {
showInvalidated = true showInvalidated = true
updateMenuVisibility() updateMenuVisibility()
ToastUtils.showToastInUiThread(context, rh.gs(R.string.show_invalidated_records)) ToastUtils.showToastInUiThread(context, rh.gs(R.string.show_invalidated_records))
rxBus.send(EventTreatmentUpdateGui()) swapAdapter()
true true
} }
@ -314,7 +307,7 @@ class TreatmentsProfileSwitchFragment : DaggerFragment() {
showInvalidated = false showInvalidated = false
updateMenuVisibility() updateMenuVisibility()
ToastUtils.showToastInUiThread(context, rh.gs(R.string.hide_invalidated_records)) ToastUtils.showToastInUiThread(context, rh.gs(R.string.hide_invalidated_records))
rxBus.send(EventTreatmentUpdateGui()) swapAdapter()
true true
} }

View file

@ -23,7 +23,6 @@ import info.nightscout.androidaps.databinding.TreatmentsTemptargetItemBinding
import info.nightscout.androidaps.events.EventEffectiveProfileSwitchChanged import info.nightscout.androidaps.events.EventEffectiveProfileSwitchChanged
import info.nightscout.androidaps.events.EventProfileSwitchChanged import info.nightscout.androidaps.events.EventProfileSwitchChanged
import info.nightscout.androidaps.events.EventTempTargetChange import info.nightscout.androidaps.events.EventTempTargetChange
import info.nightscout.androidaps.events.EventTreatmentUpdateGui
import info.nightscout.androidaps.extensions.friendlyDescription import info.nightscout.androidaps.extensions.friendlyDescription
import info.nightscout.androidaps.extensions.highValueToUnitsToString import info.nightscout.androidaps.extensions.highValueToUnitsToString
import info.nightscout.androidaps.extensions.lowValueToUnitsToString import info.nightscout.androidaps.extensions.lowValueToUnitsToString
@ -134,11 +133,6 @@ class TreatmentsTempTargetFragment : DaggerFragment() {
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
.debounce(1L, TimeUnit.SECONDS) .debounce(1L, TimeUnit.SECONDS)
.subscribe({ swapAdapter() }, fabricPrivacy::logException) .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)
} }
@Synchronized @Synchronized
@ -235,7 +229,7 @@ class TreatmentsTempTargetFragment : DaggerFragment() {
showInvalidated = true showInvalidated = true
updateMenuVisibility() updateMenuVisibility()
ToastUtils.showToastInUiThread(context, rh.gs(R.string.show_invalidated_records)) ToastUtils.showToastInUiThread(context, rh.gs(R.string.show_invalidated_records))
rxBus.send(EventTreatmentUpdateGui()) swapAdapter()
true true
} }
@ -243,7 +237,7 @@ class TreatmentsTempTargetFragment : DaggerFragment() {
showInvalidated = false showInvalidated = false
updateMenuVisibility() updateMenuVisibility()
ToastUtils.showToastInUiThread(context, rh.gs(R.string.show_invalidated_records)) ToastUtils.showToastInUiThread(context, rh.gs(R.string.show_invalidated_records))
rxBus.send(EventTreatmentUpdateGui()) swapAdapter()
true true
} }

View file

@ -23,9 +23,7 @@ import info.nightscout.androidaps.database.transactions.InvalidateExtendedBolusT
import info.nightscout.androidaps.database.transactions.InvalidateTemporaryBasalTransaction import info.nightscout.androidaps.database.transactions.InvalidateTemporaryBasalTransaction
import info.nightscout.androidaps.databinding.TreatmentsTempbasalsFragmentBinding import info.nightscout.androidaps.databinding.TreatmentsTempbasalsFragmentBinding
import info.nightscout.androidaps.databinding.TreatmentsTempbasalsItemBinding import info.nightscout.androidaps.databinding.TreatmentsTempbasalsItemBinding
import info.nightscout.androidaps.events.EventAutosensCalculationFinished
import info.nightscout.androidaps.events.EventTempBasalChange import info.nightscout.androidaps.events.EventTempBasalChange
import info.nightscout.androidaps.events.EventTreatmentUpdateGui
import info.nightscout.androidaps.extensions.iobCalc import info.nightscout.androidaps.extensions.iobCalc
import info.nightscout.androidaps.extensions.toStringFull import info.nightscout.androidaps.extensions.toStringFull
import info.nightscout.androidaps.extensions.toTemporaryBasal import info.nightscout.androidaps.extensions.toTemporaryBasal
@ -141,15 +139,6 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() {
.toObservable(EventTempBasalChange::class.java) .toObservable(EventTempBasalChange::class.java)
.observeOn(aapsSchedulers.main) .observeOn(aapsSchedulers.main)
.subscribe({ swapAdapter() }, fabricPrivacy::logException) .subscribe({ swapAdapter() }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventAutosensCalculationFinished::class.java)
.observeOn(aapsSchedulers.main)
.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)
} }
@Synchronized @Synchronized
@ -183,7 +172,7 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() {
holder.binding.date.text = if (newDay) dateUtil.dateStringRelative(tempBasal.timestamp, rh) else "" holder.binding.date.text = if (newDay) dateUtil.dateStringRelative(tempBasal.timestamp, rh) else ""
if (tempBasal.isInProgress) { if (tempBasal.isInProgress) {
holder.binding.time.text = dateUtil.timeString(tempBasal.timestamp) holder.binding.time.text = dateUtil.timeString(tempBasal.timestamp)
holder.binding.time.setTextColor(rh.gac(context , R.attr.activeColor)) holder.binding.time.setTextColor(rh.gac(context, R.attr.activeColor))
} else { } else {
holder.binding.time.text = dateUtil.timeRangeString(tempBasal.timestamp, tempBasal.end) holder.binding.time.text = dateUtil.timeRangeString(tempBasal.timestamp, tempBasal.end)
holder.binding.time.setTextColor(holder.binding.duration.currentTextColor) holder.binding.time.setTextColor(holder.binding.duration.currentTextColor)
@ -200,7 +189,7 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() {
holder.binding.suspendFlag.visibility = (tempBasal.type == TemporaryBasal.Type.PUMP_SUSPEND).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.emulatedSuspendFlag.visibility = (tempBasal.type == TemporaryBasal.Type.EMULATED_PUMP_SUSPEND).toVisibility()
holder.binding.superBolusFlag.visibility = (tempBasal.type == TemporaryBasal.Type.SUPERBOLUS).toVisibility() holder.binding.superBolusFlag.visibility = (tempBasal.type == TemporaryBasal.Type.SUPERBOLUS).toVisibility()
if (abs(iob.basaliob) > 0.01) holder.binding.iob.setTextColor(rh.gac(context , R.attr.activeColor)) else holder.binding.iob.setTextColor(holder.binding.duration.currentTextColor) if (abs(iob.basaliob) > 0.01) holder.binding.iob.setTextColor(rh.gac(context, R.attr.activeColor)) else holder.binding.iob.setTextColor(holder.binding.duration.currentTextColor)
holder.binding.cbRemove.visibility = (tempBasal.isValid && actionHelper.isRemoving).toVisibility() holder.binding.cbRemove.visibility = (tempBasal.isValid && actionHelper.isRemoving).toVisibility()
if (actionHelper.isRemoving) { if (actionHelper.isRemoving) {
holder.binding.cbRemove.setOnCheckedChangeListener { _, value -> holder.binding.cbRemove.setOnCheckedChangeListener { _, value ->
@ -251,7 +240,7 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() {
showInvalidated = true showInvalidated = true
updateMenuVisibility() updateMenuVisibility()
ToastUtils.showToastInUiThread(context, rh.gs(R.string.show_invalidated_records)) ToastUtils.showToastInUiThread(context, rh.gs(R.string.show_invalidated_records))
rxBus.send(EventTreatmentUpdateGui()) swapAdapter()
true true
} }
@ -259,7 +248,7 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() {
showInvalidated = false showInvalidated = false
updateMenuVisibility() updateMenuVisibility()
ToastUtils.showToastInUiThread(context, rh.gs(R.string.hide_invalidated_records)) ToastUtils.showToastInUiThread(context, rh.gs(R.string.hide_invalidated_records))
rxBus.send(EventTreatmentUpdateGui()) swapAdapter()
true true
} }
@ -281,41 +270,41 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() {
private fun removeSelected(selectedItems: SparseArray<TemporaryBasal>) { private fun removeSelected(selectedItems: SparseArray<TemporaryBasal>) {
if (selectedItems.size() > 0) if (selectedItems.size() > 0)
activity?.let { activity -> activity?.let { activity ->
OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), getConfirmationText(selectedItems), Runnable { OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), getConfirmationText(selectedItems), Runnable {
selectedItems.forEach {_, tempBasal -> selectedItems.forEach { _, tempBasal ->
var extendedBolus: ExtendedBolus? = null var extendedBolus: ExtendedBolus? = null
val isFakeExtended = tempBasal.type == TemporaryBasal.Type.FAKE_EXTENDED val isFakeExtended = tempBasal.type == TemporaryBasal.Type.FAKE_EXTENDED
if (isFakeExtended) { if (isFakeExtended) {
val eb = repository.getExtendedBolusActiveAt(tempBasal.timestamp).blockingGet() val eb = repository.getExtendedBolusActiveAt(tempBasal.timestamp).blockingGet()
extendedBolus = if (eb is ValueWrapper.Existing) eb.value else null extendedBolus = if (eb is ValueWrapper.Existing) eb.value else null
}
if (isFakeExtended && extendedBolus != null) {
uel.log(
Action.EXTENDED_BOLUS_REMOVED, Sources.Treatments,
ValueWithUnit.Timestamp(extendedBolus.timestamp),
ValueWithUnit.Insulin(extendedBolus.amount),
ValueWithUnit.UnitPerHour(extendedBolus.rate),
ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(extendedBolus.duration).toInt())
)
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) })
} else if (!isFakeExtended) {
uel.log(
Action.TEMP_BASAL_REMOVED, Sources.Treatments,
ValueWithUnit.Timestamp(tempBasal.timestamp),
if (tempBasal.isAbsolute) ValueWithUnit.UnitPerHour(tempBasal.rate) else ValueWithUnit.Percent(tempBasal.rate.toInt()),
ValueWithUnit.Minute(T.msecs(tempBasal.duration).mins().toInt())
)
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) })
}
} }
if (isFakeExtended && extendedBolus != null) { actionHelper.finish()
uel.log( })
Action.EXTENDED_BOLUS_REMOVED, Sources.Treatments, }
ValueWithUnit.Timestamp(extendedBolus.timestamp),
ValueWithUnit.Insulin(extendedBolus.amount),
ValueWithUnit.UnitPerHour(extendedBolus.rate),
ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(extendedBolus.duration).toInt())
)
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) })
} else if (!isFakeExtended) {
uel.log(
Action.TEMP_BASAL_REMOVED, Sources.Treatments,
ValueWithUnit.Timestamp(tempBasal.timestamp),
if (tempBasal.isAbsolute) ValueWithUnit.UnitPerHour(tempBasal.rate) else ValueWithUnit.Percent(tempBasal.rate.toInt()),
ValueWithUnit.Minute(T.msecs(tempBasal.duration).mins().toInt())
)
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) })
}
}
actionHelper.finish()
})
}
} }
} }

View file

@ -13,7 +13,6 @@ import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.databinding.TreatmentsUserEntryFragmentBinding import info.nightscout.androidaps.databinding.TreatmentsUserEntryFragmentBinding
import info.nightscout.androidaps.databinding.TreatmentsUserEntryItemBinding import info.nightscout.androidaps.databinding.TreatmentsUserEntryItemBinding
import info.nightscout.androidaps.events.EventPreferenceChange import info.nightscout.androidaps.events.EventPreferenceChange
import info.nightscout.androidaps.events.EventTreatmentUpdateGui
import info.nightscout.androidaps.extensions.toVisibility import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.interfaces.ImportExportPrefs import info.nightscout.androidaps.interfaces.ImportExportPrefs
import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.interfaces.ProfileFunction
@ -30,7 +29,6 @@ import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.androidaps.utils.userEntry.UserEntryPresentationHelper import info.nightscout.androidaps.utils.userEntry.UserEntryPresentationHelper
import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign import io.reactivex.rxjava3.kotlin.plusAssign
import java.util.concurrent.TimeUnit
import javax.inject.Inject import javax.inject.Inject
class TreatmentsUserEntryFragment : DaggerFragment() { class TreatmentsUserEntryFragment : DaggerFragment() {
@ -99,11 +97,6 @@ class TreatmentsUserEntryFragment : DaggerFragment() {
.toObservable(EventPreferenceChange::class.java) .toObservable(EventPreferenceChange::class.java)
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
.subscribe({ swapAdapter() }, fabricPrivacy::logException) .subscribe({ swapAdapter() }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventTreatmentUpdateGui::class.java)
.observeOn(aapsSchedulers.io)
.debounce(1L, TimeUnit.SECONDS)
.subscribe({ swapAdapter() }, fabricPrivacy::logException)
} }
@Synchronized @Synchronized
@ -172,7 +165,7 @@ class TreatmentsUserEntryFragment : DaggerFragment() {
showLoop = true showLoop = true
updateMenuVisibility() updateMenuVisibility()
ToastUtils.showToastInUiThread(context, rh.gs(R.string.show_loop_records)) ToastUtils.showToastInUiThread(context, rh.gs(R.string.show_loop_records))
rxBus.send(EventTreatmentUpdateGui()) swapAdapter()
true true
} }
@ -180,7 +173,7 @@ class TreatmentsUserEntryFragment : DaggerFragment() {
showLoop = false showLoop = false
updateMenuVisibility() updateMenuVisibility()
ToastUtils.showToastInUiThread(context, rh.gs(R.string.show_hide_records)) ToastUtils.showToastInUiThread(context, rh.gs(R.string.show_hide_records))
rxBus.send(EventTreatmentUpdateGui()) swapAdapter()
true true
} }

View file

@ -60,6 +60,11 @@ class CompatDBHelper @Inject constructor(
rxBus.send(EventExtendedBolusChange()) rxBus.send(EventExtendedBolusChange())
rxBus.send(EventNewHistoryData(timestamp, false)) rxBus.send(EventNewHistoryData(timestamp, false))
} }
it.filterIsInstance<EffectiveProfileSwitch>().firstOrNull()?.let { eps ->
aapsLogger.debug(LTag.DATABASE, "Firing EventEffectiveProfileSwitchChanged $eps")
rxBus.send(EventEffectiveProfileSwitchChanged(eps))
rxBus.send(EventNewHistoryData(eps.timestamp, false))
}
it.filterIsInstance<TemporaryTarget>().firstOrNull()?.let { tt -> it.filterIsInstance<TemporaryTarget>().firstOrNull()?.let { tt ->
aapsLogger.debug(LTag.DATABASE, "Firing EventTempTargetChange $tt") aapsLogger.debug(LTag.DATABASE, "Firing EventTempTargetChange $tt")
rxBus.send(EventTempTargetChange()) rxBus.send(EventTempTargetChange())
@ -76,10 +81,6 @@ class CompatDBHelper @Inject constructor(
aapsLogger.debug(LTag.DATABASE, "Firing EventProfileSwitchChanged $ps") aapsLogger.debug(LTag.DATABASE, "Firing EventProfileSwitchChanged $ps")
rxBus.send(EventProfileSwitchChanged()) rxBus.send(EventProfileSwitchChanged())
} }
it.filterIsInstance<EffectiveProfileSwitch>().firstOrNull()?.let { eps ->
aapsLogger.debug(LTag.DATABASE, "Firing EventEffectiveProfileSwitchChanged $eps")
rxBus.send(EventEffectiveProfileSwitchChanged(eps))
}
it.filterIsInstance<OfflineEvent>().firstOrNull()?.let { oe -> it.filterIsInstance<OfflineEvent>().firstOrNull()?.let { oe ->
aapsLogger.debug(LTag.DATABASE, "Firing EventOfflineChange $oe") aapsLogger.debug(LTag.DATABASE, "Firing EventOfflineChange $oe")
rxBus.send(EventOfflineChange()) rxBus.send(EventOfflineChange())

View file

@ -8,8 +8,6 @@ import info.nightscout.androidaps.plugins.aps.openAPSAMA.DetermineBasalResultAMA
import info.nightscout.androidaps.plugins.aps.openAPSSMB.DetermineBasalAdapterSMBJS import info.nightscout.androidaps.plugins.aps.openAPSSMB.DetermineBasalAdapterSMBJS
import info.nightscout.androidaps.plugins.aps.openAPSSMB.DetermineBasalResultSMB import info.nightscout.androidaps.plugins.aps.openAPSSMB.DetermineBasalResultSMB
import info.nightscout.androidaps.plugins.aps.openAPSSMBDynamicISF.DetermineBasalAdapterSMBDynamicISFJS import info.nightscout.androidaps.plugins.aps.openAPSSMBDynamicISF.DetermineBasalAdapterSMBDynamicISFJS
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobOref1Worker
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobOrefWorker
@Module @Module
@Suppress("unused") @Suppress("unused")
@ -21,6 +19,4 @@ abstract class APSModule {
@ContributesAndroidInjector abstract fun determineBasalAdapterAMAJSInjector(): DetermineBasalAdapterAMAJS @ContributesAndroidInjector abstract fun determineBasalAdapterAMAJSInjector(): DetermineBasalAdapterAMAJS
@ContributesAndroidInjector abstract fun determineBasalAdapterSMBJSInjector(): DetermineBasalAdapterSMBJS @ContributesAndroidInjector abstract fun determineBasalAdapterSMBJSInjector(): DetermineBasalAdapterSMBJS
@ContributesAndroidInjector abstract fun determineBasalAdapterSMBAutoISFJSInjector(): DetermineBasalAdapterSMBDynamicISFJS @ContributesAndroidInjector abstract fun determineBasalAdapterSMBAutoISFJSInjector(): DetermineBasalAdapterSMBDynamicISFJS
@ContributesAndroidInjector abstract fun iobCobWorkerInjector(): IobCobOrefWorker
@ContributesAndroidInjector abstract fun iobCobOref1WorkerInjector(): IobCobOref1Worker
} }

View file

@ -46,6 +46,7 @@ import javax.inject.Singleton
OmnipodDashModule::class, OmnipodDashModule::class,
OmnipodErosModule::class, OmnipodErosModule::class,
APSModule::class, APSModule::class,
WorkflowModule::class,
PreferencesModule::class, PreferencesModule::class,
OverviewModule::class, OverviewModule::class,
DataClassesModule::class, DataClassesModule::class,

View file

@ -0,0 +1,25 @@
package info.nightscout.androidaps.di
import dagger.Module
import dagger.android.ContributesAndroidInjector
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.*
import info.nightscout.androidaps.workflow.*
@Module
@Suppress("unused")
abstract class WorkflowModule {
@ContributesAndroidInjector abstract fun iobCobWorkerInjector(): IobCobOrefWorker
@ContributesAndroidInjector abstract fun iobCobOref1WorkerInjector(): IobCobOref1Worker
@ContributesAndroidInjector abstract fun prepareIobAutosensDataWorkerInjector(): PrepareIobAutosensGraphDataWorker
@ContributesAndroidInjector abstract fun prepareBasalDataWorkerInjector(): PrepareBasalDataWorker
@ContributesAndroidInjector abstract fun prepareTemporaryTargetDataWorkerInjector(): PrepareTemporaryTargetDataWorker
@ContributesAndroidInjector abstract fun prepareTreatmentsDataWorkerInjector(): PrepareTreatmentsDataWorker
@ContributesAndroidInjector abstract fun loadIobCobResultsWorkerInjector(): UpdateIobCobSensWorker
@ContributesAndroidInjector abstract fun preparePredictionsWorkerInjector(): PreparePredictionsWorker
@ContributesAndroidInjector abstract fun updateGraphAndIobWorkerInjector(): UpdateGraphWorker
@ContributesAndroidInjector abstract fun prepareBgDataWorkerInjector(): PrepareBgDataWorker
@ContributesAndroidInjector abstract fun prepareBucketedDataWorkerInjector(): PrepareBucketedDataWorker
@ContributesAndroidInjector abstract fun loadBgDataWorkerInjector(): LoadBgDataWorker
@ContributesAndroidInjector abstract fun invokeLoopWorkerInjector(): InvokeLoopWorker
}

View file

@ -1,3 +0,0 @@
package info.nightscout.androidaps.events
class EventReloadProfileSwitchData : Event()

View file

@ -1,3 +0,0 @@
package info.nightscout.androidaps.events
class EventTreatmentUpdateGui : EventUpdateGui()

View file

@ -1,5 +0,0 @@
package info.nightscout.androidaps.plugins.aps.events
import info.nightscout.androidaps.events.Event
class EventLoopInvoked : Event()

View file

@ -10,11 +10,13 @@ import android.content.Intent
import android.os.SystemClock import android.os.SystemClock
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.* import info.nightscout.androidaps.BuildConfig
import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.MainActivity
import info.nightscout.androidaps.R
import info.nightscout.androidaps.activities.ErrorHelperActivity import info.nightscout.androidaps.activities.ErrorHelperActivity
import info.nightscout.androidaps.annotations.OpenForTesting import info.nightscout.androidaps.annotations.OpenForTesting
import info.nightscout.androidaps.data.DetailedBolusInfo import info.nightscout.androidaps.data.DetailedBolusInfo
import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.data.PumpEnactResult import info.nightscout.androidaps.data.PumpEnactResult
import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.ValueWrapper import info.nightscout.androidaps.database.ValueWrapper
@ -25,13 +27,13 @@ import info.nightscout.androidaps.database.entities.ValueWithUnit
import info.nightscout.androidaps.database.transactions.InsertAndCancelCurrentOfflineEventTransaction import info.nightscout.androidaps.database.transactions.InsertAndCancelCurrentOfflineEventTransaction
import info.nightscout.androidaps.database.transactions.InsertTherapyEventAnnouncementTransaction import info.nightscout.androidaps.database.transactions.InsertTherapyEventAnnouncementTransaction
import info.nightscout.androidaps.events.EventAcceptOpenLoopChange import info.nightscout.androidaps.events.EventAcceptOpenLoopChange
import info.nightscout.androidaps.events.EventAutosensCalculationFinished
import info.nightscout.androidaps.events.EventNewBG
import info.nightscout.androidaps.events.EventTempTargetChange import info.nightscout.androidaps.events.EventTempTargetChange
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.interfaces.* import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.interfaces.Loop.LastRun import info.nightscout.androidaps.interfaces.Loop.LastRun
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.aps.loop.events.EventLoopSetLastRunGui 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.EventLoopUpdateGui
@ -51,13 +53,10 @@ import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.HardLimits import info.nightscout.androidaps.utils.HardLimits
import info.nightscout.androidaps.utils.T 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.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import info.nightscout.shared.sharedPreferences.SP import info.nightscout.shared.sharedPreferences.SP
import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign import io.reactivex.rxjava3.kotlin.plusAssign
@ -88,20 +87,21 @@ class LoopPlugin @Inject constructor(
private val uel: UserEntryLogger, private val uel: UserEntryLogger,
private val repository: AppRepository, private val repository: AppRepository,
private val runningConfiguration: RunningConfiguration private val runningConfiguration: RunningConfiguration
) : PluginBase(PluginDescription() ) : PluginBase(
.mainType(PluginType.LOOP) PluginDescription()
.fragmentClass(LoopFragment::class.java.name) .mainType(PluginType.LOOP)
.pluginIcon(R.drawable.ic_loop_closed_white) .fragmentClass(LoopFragment::class.java.name)
.pluginName(R.string.loop) .pluginIcon(R.drawable.ic_loop_closed_white)
.shortName(R.string.loop_shortname) .pluginName(R.string.loop)
.preferencesId(R.xml.pref_loop) .shortName(R.string.loop_shortname)
.enableByDefault(config.APS) .preferencesId(R.xml.pref_loop)
.description(R.string.description_loop), .enableByDefault(config.APS)
.description(R.string.description_loop),
aapsLogger, rh, injector aapsLogger, rh, injector
), Loop { ), Loop {
private val disposable = CompositeDisposable() private val disposable = CompositeDisposable()
private var lastBgTriggeredRun: Long = 0 override var lastBgTriggeredRun: Long = 0
private var carbsSuggestionsSuspendedUntil: Long = 0 private var carbsSuggestionsSuspendedUntil: Long = 0
private var prevCarbsreq = 0 private var prevCarbsreq = 0
override var lastRun: LastRun? = null override var lastRun: LastRun? = null
@ -109,39 +109,19 @@ class LoopPlugin @Inject constructor(
override fun onStart() { override fun onStart() {
createNotificationChannel() createNotificationChannel()
super.onStart() super.onStart()
disposable.add(rxBus disposable += rxBus
.toObservable(EventTempTargetChange::class.java) .toObservable(EventTempTargetChange::class.java)
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
.subscribe({ invoke("EventTempTargetChange", true) }, fabricPrivacy::logException) .subscribe({ invoke("EventTempTargetChange", true) }, fabricPrivacy::logException)
)
/*
This method is triggered once autosens calculation has completed, so the LoopPlugin
has current data to work with. However, autosens calculation can be triggered by multiple
sources and currently only a new BG should trigger a loop run. Hence we return early if
the event causing the calculation is not EventNewBg.
<p>
*/
disposable.add(rxBus
.toObservable(EventAutosensCalculationFinished::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ event: EventAutosensCalculationFinished ->
// Autosens calculation not triggered by a new BG
if (event.cause !is EventNewBG) return@subscribe
val glucoseValue = iobCobCalculator.ads.actualBg() ?: return@subscribe
// BG outdated
// already looped with that value
if (glucoseValue.timestamp <= lastBgTriggeredRun) return@subscribe
lastBgTriggeredRun = glucoseValue.timestamp
invoke("AutosenseCalculation for $glucoseValue", true)
}, fabricPrivacy::logException)
)
} }
private fun createNotificationChannel() { private fun createNotificationChannel() {
val mNotificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager val mNotificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
@SuppressLint("WrongConstant") val channel = NotificationChannel(CHANNEL_ID, @SuppressLint("WrongConstant") val channel = NotificationChannel(
CHANNEL_ID, CHANNEL_ID,
NotificationManager.IMPORTANCE_HIGH) CHANNEL_ID,
NotificationManager.IMPORTANCE_HIGH
)
mNotificationManager.createNotificationChannel(channel) mNotificationManager.createNotificationChannel(channel)
} }
@ -255,7 +235,7 @@ class LoopPlugin @Inject constructor(
if (apsResult == null) { if (apsResult == null) {
rxBus.send(EventLoopSetLastRunGui(rh.gs(R.string.noapsselected))) rxBus.send(EventLoopSetLastRunGui(rh.gs(R.string.noapsselected)))
return return
} else rxBus.send(EventLoopInvoked()) }
if (!isEmptyQueue()) { if (!isEmptyQueue()) {
aapsLogger.debug(LTag.APS, rh.gs(R.string.pumpbusy)) aapsLogger.debug(LTag.APS, rh.gs(R.string.pumpbusy))
@ -296,9 +276,11 @@ class LoopPlugin @Inject constructor(
lastRun.lastTBRRequest = 0 lastRun.lastTBRRequest = 0
lastRun.lastSMBEnact = 0 lastRun.lastSMBEnact = 0
lastRun.lastSMBRequest = 0 lastRun.lastSMBRequest = 0
buildDeviceStatus(dateUtil, this, iobCobCalculator, profileFunction, buildDeviceStatus(
dateUtil, this, iobCobCalculator, profileFunction,
activePlugin.activePump, receiverStatusStore, runningConfiguration, activePlugin.activePump, receiverStatusStore, runningConfiguration,
BuildConfig.VERSION_NAME + "-" + BuildConfig.BUILDVERSION)?.also { BuildConfig.VERSION_NAME + "-" + BuildConfig.BUILDVERSION
)?.also {
repository.insert(it) repository.insert(it)
} }
@ -316,7 +298,11 @@ class LoopPlugin @Inject constructor(
if (closedLoopEnabled.value()) { if (closedLoopEnabled.value()) {
if (allowNotification) { if (allowNotification) {
if (resultAfterConstraints.isCarbsRequired if (resultAfterConstraints.isCarbsRequired
&& resultAfterConstraints.carbsReq >= sp.getInt(R.string.key_smb_enable_carbs_suggestions_threshold, 0) && carbsSuggestionsSuspendedUntil < System.currentTimeMillis() && !treatmentTimeThreshold(-15)) { && 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)) { 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) val carbReqLocal = Notification(Notification.CARBS_REQUIRED, resultAfterConstraints.carbsRequiredText, Notification.NORMAL)
rxBus.send(EventNewNotification(carbReqLocal)) rxBus.send(EventNewNotification(carbReqLocal))
@ -353,9 +339,11 @@ class LoopPlugin @Inject constructor(
// mId allows you to update the notification later on. // mId allows you to update the notification later on.
mNotificationManager.notify(Constants.notificationID, builder.build()) mNotificationManager.notify(Constants.notificationID, builder.build())
uel.log(Action.CAREPORTAL, Sources.Loop, rh.gs(R.string.carbsreq, resultAfterConstraints.carbsReq, resultAfterConstraints.carbsReqWithin), uel.log(
ValueWithUnit.Gram(resultAfterConstraints.carbsReq), Action.CAREPORTAL, Sources.Loop, rh.gs(R.string.carbsreq, resultAfterConstraints.carbsReq, resultAfterConstraints.carbsReqWithin),
ValueWithUnit.Minute(resultAfterConstraints.carbsReqWithin)) ValueWithUnit.Gram(resultAfterConstraints.carbsReq),
ValueWithUnit.Minute(resultAfterConstraints.carbsReqWithin)
)
rxBus.send(EventNewOpenLoopNotification()) rxBus.send(EventNewOpenLoopNotification())
//only send to wear if Native notifications are turned off //only send to wear if Native notifications are turned off
@ -373,7 +361,8 @@ class LoopPlugin @Inject constructor(
} }
} }
if (resultAfterConstraints.isChangeRequested if (resultAfterConstraints.isChangeRequested
&& !commandQueue.bolusInQueue()) { && !commandQueue.bolusInQueue()
) {
val waiting = PumpEnactResult(injector) val waiting = PumpEnactResult(injector)
waiting.queued = true waiting.queued = true
if (resultAfterConstraints.tempBasalRequested) lastRun.tbrSetByPump = waiting if (resultAfterConstraints.tempBasalRequested) lastRun.tbrSetByPump = waiting
@ -486,9 +475,11 @@ class LoopPlugin @Inject constructor(
lastRun.lastTBRRequest = lastRun.lastAPSRun lastRun.lastTBRRequest = lastRun.lastAPSRun
lastRun.lastTBREnact = dateUtil.now() lastRun.lastTBREnact = dateUtil.now()
lastRun.lastOpenModeAccept = dateUtil.now() lastRun.lastOpenModeAccept = dateUtil.now()
buildDeviceStatus(dateUtil, this@LoopPlugin, iobCobCalculator, profileFunction, buildDeviceStatus(
dateUtil, this@LoopPlugin, iobCobCalculator, profileFunction,
activePlugin.activePump, receiverStatusStore, runningConfiguration, activePlugin.activePump, receiverStatusStore, runningConfiguration,
BuildConfig.VERSION_NAME + "-" + BuildConfig.BUILDVERSION)?.also { BuildConfig.VERSION_NAME + "-" + BuildConfig.BUILDVERSION
)?.also {
repository.insert(it) repository.insert(it)
} }
sp.incInt(R.string.key_ObjectivesmanualEnacts) sp.incInt(R.string.key_ObjectivesmanualEnacts)
@ -531,8 +522,10 @@ class LoopPlugin @Inject constructor(
commandQueue.cancelTempBasal(false, callback) commandQueue.cancelTempBasal(false, callback)
} else { } else {
aapsLogger.debug(LTag.APS, "applyAPSRequest: Basal set correctly") aapsLogger.debug(LTag.APS, "applyAPSRequest: Basal set correctly")
callback?.result(PumpEnactResult(injector).absolute(request.rate).duration(0) callback?.result(
.enacted(false).success(true).comment(R.string.basal_set_correctly))?.run() PumpEnactResult(injector).absolute(request.rate).duration(0)
.enacted(false).success(true).comment(R.string.basal_set_correctly)
)?.run()
} }
} else if (request.usePercent && allowPercentage()) { } else if (request.usePercent && allowPercentage()) {
if (request.percent == 100 && request.duration == 0) { if (request.percent == 100 && request.duration == 0) {
@ -542,32 +535,52 @@ class LoopPlugin @Inject constructor(
commandQueue.cancelTempBasal(false, callback) commandQueue.cancelTempBasal(false, callback)
} else { } else {
aapsLogger.debug(LTag.APS, "applyAPSRequest: Basal set correctly") aapsLogger.debug(LTag.APS, "applyAPSRequest: Basal set correctly")
callback?.result(PumpEnactResult(injector).percent(request.percent).duration(0) callback?.result(
.enacted(false).success(true).comment(R.string.basal_set_correctly))?.run() 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.convertedToPercent(now, profile)) { } 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") aapsLogger.debug(LTag.APS, "applyAPSRequest: Temp basal set correctly")
callback?.result(PumpEnactResult(injector).percent(request.percent) callback?.result(
.enacted(false).success(true).duration(activeTemp.plannedRemainingMinutes) PumpEnactResult(injector).percent(request.percent)
.comment(R.string.let_temp_basal_run))?.run() .enacted(false).success(true).duration(activeTemp.plannedRemainingMinutes)
.comment(R.string.let_temp_basal_run)
)?.run()
} else { } else {
aapsLogger.debug(LTag.APS, "applyAPSRequest: tempBasalPercent()") aapsLogger.debug(LTag.APS, "applyAPSRequest: tempBasalPercent()")
uel.log(Action.TEMP_BASAL, Sources.Loop, uel.log(
Action.TEMP_BASAL, Sources.Loop,
ValueWithUnit.Percent(request.percent), ValueWithUnit.Percent(request.percent),
ValueWithUnit.Minute(request.duration)) ValueWithUnit.Minute(request.duration)
)
commandQueue.tempBasalPercent(request.percent, request.duration, false, profile, PumpSync.TemporaryBasalType.NORMAL, callback) commandQueue.tempBasalPercent(request.percent, request.duration, false, profile, PumpSync.TemporaryBasalType.NORMAL, callback)
} }
} else { } else {
if (activeTemp != null && activeTemp.plannedRemainingMinutes > 5 && request.duration - activeTemp.plannedRemainingMinutes < 30 && abs(request.rate - activeTemp.convertedToAbsolute(now, profile)) < pump.pumpDescription.basalStep) { 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") aapsLogger.debug(LTag.APS, "applyAPSRequest: Temp basal set correctly")
callback?.result(PumpEnactResult(injector).absolute(activeTemp.convertedToAbsolute(now, profile)) callback?.result(
.enacted(false).success(true).duration(activeTemp.plannedRemainingMinutes) PumpEnactResult(injector).absolute(activeTemp.convertedToAbsolute(now, profile))
.comment(R.string.let_temp_basal_run))?.run() .enacted(false).success(true).duration(activeTemp.plannedRemainingMinutes)
.comment(R.string.let_temp_basal_run)
)?.run()
} else { } else {
aapsLogger.debug(LTag.APS, "applyAPSRequest: setTempBasalAbsolute()") aapsLogger.debug(LTag.APS, "applyAPSRequest: setTempBasalAbsolute()")
uel.log(Action.TEMP_BASAL, Sources.Loop, uel.log(
Action.TEMP_BASAL, Sources.Loop,
ValueWithUnit.UnitPerHour(request.rate), ValueWithUnit.UnitPerHour(request.rate),
ValueWithUnit.Minute(request.duration)) ValueWithUnit.Minute(request.duration)
)
commandQueue.tempBasalAbsolute(request.rate, request.duration, false, profile, PumpSync.TemporaryBasalType.NORMAL, callback) commandQueue.tempBasalAbsolute(request.rate, request.duration, false, profile, PumpSync.TemporaryBasalType.NORMAL, callback)
} }
} }
@ -581,9 +594,11 @@ class LoopPlugin @Inject constructor(
val lastBolusTime = repository.getLastBolusRecord()?.timestamp ?: 0L val lastBolusTime = repository.getLastBolusRecord()?.timestamp ?: 0L
if (lastBolusTime != 0L && lastBolusTime + 3 * 60 * 1000 > System.currentTimeMillis()) { if (lastBolusTime != 0L && lastBolusTime + 3 * 60 * 1000 > System.currentTimeMillis()) {
aapsLogger.debug(LTag.APS, "SMB requested but still in 3 min interval") aapsLogger.debug(LTag.APS, "SMB requested but still in 3 min interval")
callback?.result(PumpEnactResult(injector) callback?.result(
.comment(R.string.smb_frequency_exceeded) PumpEnactResult(injector)
.enacted(false).success(false))?.run() .comment(R.string.smb_frequency_exceeded)
.enacted(false).success(false)
)?.run()
return return
} }
if (!pump.isInitialized()) { if (!pump.isInitialized()) {
@ -619,11 +634,11 @@ class LoopPlugin @Inject constructor(
val pump = activePlugin.activePump val pump = activePlugin.activePump
disposable += repository.runTransactionForResult(InsertAndCancelCurrentOfflineEventTransaction(dateUtil.now(), T.mins(durationInMinutes.toLong()).msecs(), reason)) disposable += repository.runTransactionForResult(InsertAndCancelCurrentOfflineEventTransaction(dateUtil.now(), T.mins(durationInMinutes.toLong()).msecs(), reason))
.subscribe({ result -> .subscribe({ result ->
result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") } result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") }
result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted OfflineEvent $it") } result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted OfflineEvent $it") }
}, { }, {
aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it) aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it)
}) })
if (pump.pumpDescription.tempBasalStyle == PumpDescription.ABSOLUTE) { if (pump.pumpDescription.tempBasalStyle == PumpDescription.ABSOLUTE) {
commandQueue.tempBasalAbsolute(0.0, durationInMinutes, true, profile, PumpSync.TemporaryBasalType.EMULATED_PUMP_SUSPEND, object : Callback() { commandQueue.tempBasalAbsolute(0.0, durationInMinutes, true, profile, PumpSync.TemporaryBasalType.EMULATED_PUMP_SUSPEND, object : Callback() {
override fun run() { override fun run() {
@ -655,11 +670,11 @@ class LoopPlugin @Inject constructor(
override fun suspendLoop(durationInMinutes: Int) { override fun suspendLoop(durationInMinutes: Int) {
disposable += repository.runTransactionForResult(InsertAndCancelCurrentOfflineEventTransaction(dateUtil.now(), T.mins(durationInMinutes.toLong()).msecs(), OfflineEvent.Reason.SUSPEND)) disposable += repository.runTransactionForResult(InsertAndCancelCurrentOfflineEventTransaction(dateUtil.now(), T.mins(durationInMinutes.toLong()).msecs(), OfflineEvent.Reason.SUSPEND))
.subscribe({ result -> .subscribe({ result ->
result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") } result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") }
result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted OfflineEvent $it") } result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted OfflineEvent $it") }
}, { }, {
aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it) aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it)
}) })
commandQueue.cancelTempBasal(true, object : Callback() { commandQueue.cancelTempBasal(true, object : Callback() {
override fun run() { override fun run() {
if (!result.success) { if (!result.success) {

View file

@ -169,6 +169,9 @@ class PluginStore @Inject constructor(
override val activeSafety: Safety override val activeSafety: Safety
get() = getSpecificPluginsListByInterface(Safety::class.java).first() as Safety get() = getSpecificPluginsListByInterface(Safety::class.java).first() as Safety
override val activeIobCobCalculator: IobCobCalculator
get() = getSpecificPluginsListByInterface(IobCobCalculator::class.java).first() as IobCobCalculator
override fun getPluginsList(): ArrayList<PluginBase> = ArrayList(plugins) override fun getPluginsList(): ArrayList<PluginBase> = ArrayList(plugins)
} }

View file

@ -6,12 +6,11 @@ import android.content.pm.ResolveInfo
import android.os.Bundle import android.os.Bundle
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.events.* import info.nightscout.androidaps.events.Event
import info.nightscout.androidaps.events.EventAutosensCalculationFinished
import info.nightscout.androidaps.extensions.durationInMinutes import info.nightscout.androidaps.extensions.durationInMinutes
import info.nightscout.androidaps.extensions.toStringFull import info.nightscout.androidaps.extensions.toStringFull
import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.interfaces.*
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import info.nightscout.androidaps.plugins.aps.events.EventOpenAPSUpdateGui import info.nightscout.androidaps.plugins.aps.events.EventOpenAPSUpdateGui
import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.general.nsclient.data.DeviceStatusData import info.nightscout.androidaps.plugins.general.nsclient.data.DeviceStatusData
@ -25,6 +24,8 @@ import info.nightscout.androidaps.utils.DefaultValueHelper
import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.disposables.CompositeDisposable
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -68,26 +69,6 @@ class DataBroadcastPlugin @Inject constructor(
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
.subscribe({ sendData(it) }, fabricPrivacy::logException) .subscribe({ sendData(it) }, fabricPrivacy::logException)
) )
disposable.add(rxBus
.toObservable(EventExtendedBolusChange::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ sendData(it) }, fabricPrivacy::logException)
)
disposable.add(rxBus
.toObservable(EventTempBasalChange::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ sendData(it) }, fabricPrivacy::logException)
)
disposable.add(rxBus
.toObservable(EventTreatmentChange::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ sendData(it) }, fabricPrivacy::logException)
)
disposable.add(rxBus
.toObservable(EventEffectiveProfileSwitchChanged::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ sendData(it) }, fabricPrivacy::logException)
)
disposable.add(rxBus disposable.add(rxBus
.toObservable(EventAutosensCalculationFinished::class.java) .toObservable(EventAutosensCalculationFinished::class.java)
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)

View file

@ -170,8 +170,7 @@ class NSClientPlugin @Inject constructor(
override fun onServiceConnected(name: ComponentName, service: IBinder) { override fun onServiceConnected(name: ComponentName, service: IBinder) {
aapsLogger.debug(LTag.NSCLIENT, "Service is connected") aapsLogger.debug(LTag.NSCLIENT, "Service is connected")
val mLocalBinder = service as NSClientService.LocalBinder val mLocalBinder = service as NSClientService.LocalBinder?
@Suppress("UNNECESSARY_SAFE_CALL")
nsClientService = mLocalBinder?.serviceInstance // is null when running in roboelectric nsClientService = mLocalBinder?.serviceInstance // is null when running in roboelectric
} }
} }

View file

@ -1,46 +1,42 @@
package info.nightscout.androidaps.plugins.general.overview package info.nightscout.androidaps.plugins.general.overview
import android.content.Context import android.content.Context
import android.graphics.DashPathEffect
import android.graphics.Paint
import androidx.annotation.ColorInt import androidx.annotation.ColorInt
import com.jjoe64.graphview.series.BarGraphSeries import com.jjoe64.graphview.series.BarGraphSeries
import com.jjoe64.graphview.series.DataPoint import com.jjoe64.graphview.series.DataPoint
import com.jjoe64.graphview.series.LineGraphSeries import com.jjoe64.graphview.series.LineGraphSeries
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.IobTotal import info.nightscout.androidaps.data.IobTotal
import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.ValueWrapper import info.nightscout.androidaps.database.ValueWrapper
import info.nightscout.androidaps.database.entities.Bolus
import info.nightscout.androidaps.database.entities.GlucoseValue import info.nightscout.androidaps.database.entities.GlucoseValue
import info.nightscout.androidaps.database.entities.TemporaryTarget import info.nightscout.androidaps.database.entities.TemporaryTarget
import info.nightscout.androidaps.database.entities.TherapyEvent import info.nightscout.androidaps.extensions.convertedToPercent
import info.nightscout.androidaps.extensions.* import info.nightscout.androidaps.extensions.isInProgress
import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.extensions.toStringFull
import info.nightscout.shared.logging.AAPSLogger import info.nightscout.androidaps.extensions.toStringShort
import info.nightscout.shared.logging.LTag import info.nightscout.androidaps.extensions.valueToUnits
import info.nightscout.androidaps.plugins.aps.openAPSSMB.SMBDefaults import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.plugins.general.nsclient.data.NSDeviceStatus import info.nightscout.androidaps.interfaces.IobCobCalculator
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.* import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult 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.CobInfo
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.data.AutosensData import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.* import info.nightscout.androidaps.utils.DefaultValueHelper
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.sharedPreferences.SP import info.nightscout.shared.sharedPreferences.SP
import java.util.* import java.util.*
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
import kotlin.collections.ArrayList
import kotlin.math.abs
import kotlin.math.ceil
import kotlin.math.max
import kotlin.math.min
@Singleton @Singleton
class OverviewData @Inject constructor( class OverviewData @Inject constructor(
private val injector: HasAndroidInjector,
private val aapsLogger: AAPSLogger, private val aapsLogger: AAPSLogger,
private val rh: ResourceHelper, private val rh: ResourceHelper,
private val dateUtil: DateUtil, private val dateUtil: DateUtil,
@ -48,13 +44,7 @@ class OverviewData @Inject constructor(
private val activePlugin: ActivePlugin, private val activePlugin: ActivePlugin,
private val defaultValueHelper: DefaultValueHelper, private val defaultValueHelper: DefaultValueHelper,
private val profileFunction: ProfileFunction, private val profileFunction: ProfileFunction,
private val config: Config, private val repository: AppRepository
private val loop: Loop,
private val nsDeviceStatus: NSDeviceStatus,
private val repository: AppRepository,
private val overviewMenus: OverviewMenus,
private val iobCobCalculator: IobCobCalculator,
private val translator: Translator
) { ) {
var rangeToDisplay = 6 // for graph var rangeToDisplay = 6 // for graph
@ -65,13 +55,6 @@ class OverviewData @Inject constructor(
fun reset() { fun reset() {
pumpStatus = "" pumpStatus = ""
calcProgressPct = 100 calcProgressPct = 100
lastBg = null
bolusIob = null
basalIob = null
cobInfo = null
lastCarbsTime = 0L
temporaryTarget = null
lastAutosensData = null
bgReadingsArray = ArrayList() bgReadingsArray = ArrayList()
bucketedGraphSeries = PointsWithLabelGraphSeries() bucketedGraphSeries = PointsWithLabelGraphSeries()
bgReadingGraphSeries = PointsWithLabelGraphSeries() bgReadingGraphSeries = PointsWithLabelGraphSeries()
@ -128,7 +111,12 @@ class OverviewData @Inject constructor(
* BG * BG
*/ */
var lastBg: GlucoseValue? = null val lastBg: GlucoseValue?
get() =
repository.getLastGlucoseValueWrapped().blockingGet().let { gvWrapped ->
if (gvWrapped is ValueWrapper.Existing) gvWrapped.value
else null
}
val isLow: Boolean val isLow: Boolean
get() = lastBg?.let { lastBg -> get() = lastBg?.let { lastBg ->
@ -165,17 +153,16 @@ class OverviewData @Inject constructor(
* TEMPORARY BASAL * TEMPORARY BASAL
*/ */
val temporaryBasalText: String fun temporaryBasalText(iobCobCalculator: IobCobCalculator): String =
get() = profileFunction.getProfile()?.let { profile ->
profileFunction.getProfile()?.let { profile -> var temporaryBasal = iobCobCalculator.getTempBasalIncludingConvertedExtended(dateUtil.now())
var temporaryBasal = iobCobCalculator.getTempBasalIncludingConvertedExtended(dateUtil.now()) if (temporaryBasal?.isInProgress == false) temporaryBasal = null
if (temporaryBasal?.isInProgress == false) temporaryBasal = null temporaryBasal?.let { "T:" + it.toStringShort() }
temporaryBasal?.let { "T:" + it.toStringShort() } ?: rh.gs(R.string.pump_basebasalrate, profile.getBasal())
?: rh.gs(R.string.pump_basebasalrate, profile.getBasal()) } ?: rh.gs(R.string.notavailable)
} ?: rh.gs(R.string.notavailable)
val temporaryBasalDialogText: String fun temporaryBasalDialogText(iobCobCalculator: IobCobCalculator): String =
get() = profileFunction.getProfile()?.let { profile -> profileFunction.getProfile()?.let { profile ->
iobCobCalculator.getTempBasalIncludingConvertedExtended(dateUtil.now())?.let { temporaryBasal -> iobCobCalculator.getTempBasalIncludingConvertedExtended(dateUtil.now())?.let { temporaryBasal ->
"${rh.gs(R.string.basebasalrate_label)}: ${rh.gs(R.string.pump_basebasalrate, profile.getBasal())}" + "${rh.gs(R.string.basebasalrate_label)}: ${rh.gs(R.string.pump_basebasalrate, profile.getBasal())}" +
"\n" + rh.gs(R.string.tempbasal_label) + ": " + temporaryBasal.toStringFull(profile, dateUtil) "\n" + rh.gs(R.string.tempbasal_label) + ": " + temporaryBasal.toStringFull(profile, dateUtil)
@ -183,76 +170,78 @@ class OverviewData @Inject constructor(
?: "${rh.gs(R.string.basebasalrate_label)}: ${rh.gs(R.string.pump_basebasalrate, profile.getBasal())}" ?: "${rh.gs(R.string.basebasalrate_label)}: ${rh.gs(R.string.pump_basebasalrate, profile.getBasal())}"
} ?: rh.gs(R.string.notavailable) } ?: rh.gs(R.string.notavailable)
val temporaryBasalIcon: Int fun temporaryBasalIcon(iobCobCalculator: IobCobCalculator): Int =
get() = profileFunction.getProfile()?.let { profile ->
profileFunction.getProfile()?.let { profile -> iobCobCalculator.getTempBasalIncludingConvertedExtended(dateUtil.now())?.let { temporaryBasal ->
iobCobCalculator.getTempBasalIncludingConvertedExtended(dateUtil.now())?.let { temporaryBasal -> val percentRate = temporaryBasal.convertedToPercent(dateUtil.now(), profile)
val percentRate = temporaryBasal.convertedToPercent(dateUtil.now(), profile) when {
when { percentRate > 100 -> R.drawable.ic_cp_basal_tbr_high
percentRate > 100 -> R.drawable.ic_cp_basal_tbr_high percentRate < 100 -> R.drawable.ic_cp_basal_tbr_low
percentRate < 100 -> R.drawable.ic_cp_basal_tbr_low else -> R.drawable.ic_cp_basal_no_tbr
else -> R.drawable.ic_cp_basal_no_tbr
}
} }
} ?: R.drawable.ic_cp_basal_no_tbr }
} ?: R.drawable.ic_cp_basal_no_tbr
val temporaryBasalColor: Int // will be removed if a solution of getting the right color for widget is solved
get() = iobCobCalculator.getTempBasalIncludingConvertedExtended(dateUtil.now())?.let { rh.gc(R.color.basal) } fun temporaryBasalColor(iobCobCalculator: IobCobCalculator): Int =
?: rh.gc(R.color.defaulttextcolor) iobCobCalculator.getTempBasalIncludingConvertedExtended(dateUtil.now())?.let { rh.gc(R.color.basal) }
?: rh.gc(R.color.textAppearancemediumDark)
fun temporaryBasalColor(context: Context?, iobCobCalculator: IobCobCalculator): Int = iobCobCalculator.getTempBasalIncludingConvertedExtended(dateUtil.now())?.let { rh.gac(context , R.attr.basal) }
?: rh.gac(context, R.attr.textAppearancemediumColor)
/* /*
* EXTENDED BOLUS * EXTENDED BOLUS
*/ */
val extendedBolusText: String fun extendedBolusText(iobCobCalculator: IobCobCalculator): String =
get() = iobCobCalculator.getExtendedBolus(dateUtil.now())?.let { extendedBolus ->
iobCobCalculator.getExtendedBolus(dateUtil.now())?.let { extendedBolus -> if (!extendedBolus.isInProgress(dateUtil)) ""
if (!extendedBolus.isInProgress(dateUtil)) "" else if (!activePlugin.activePump.isFakingTempsByExtendedBoluses) rh.gs(R.string.pump_basebasalrate, extendedBolus.rate)
else if (!activePlugin.activePump.isFakingTempsByExtendedBoluses) rh.gs(R.string.pump_basebasalrate, extendedBolus.rate) else ""
else "" } ?: ""
} ?: ""
val extendedBolusDialogText: String fun extendedBolusDialogText(iobCobCalculator: IobCobCalculator): String =
get() = iobCobCalculator.getExtendedBolus(dateUtil.now())?.toStringFull(dateUtil) ?: "" iobCobCalculator.getExtendedBolus(dateUtil.now())?.toStringFull(dateUtil) ?: ""
/* /*
* IOB, COB * IOB, COB
*/ */
var bolusIob: IobTotal? = null fun bolusIob(iobCobCalculator: IobCobCalculator): IobTotal = iobCobCalculator.calculateIobFromBolus().round()
var basalIob: IobTotal? = null fun basalIob(iobCobCalculator: IobCobCalculator): IobTotal = iobCobCalculator.calculateIobFromTempBasalsIncludingConvertedExtended().round()
var cobInfo: CobInfo? = null fun cobInfo(iobCobCalculator: IobCobCalculator): CobInfo = iobCobCalculator.getCobInfo(true, "Overview COB")
var lastCarbsTime: Long = 0L
val iobText: String val lastCarbsTime: Long
get() = get() = repository.getLastCarbsRecordWrapped().blockingGet().let { lastCarbs ->
bolusIob?.let { bolusIob -> if (lastCarbs is ValueWrapper.Existing) lastCarbs.value.timestamp else 0L
basalIob?.let { basalIob -> }
rh.gs(R.string.formatinsulinunits, bolusIob.iob + basalIob.basaliob)
} ?: rh.gs(R.string.value_unavailable_short)
} ?: rh.gs(R.string.value_unavailable_short)
val iobDialogText: String fun iobText(iobCobCalculator: IobCobCalculator): String =
get() = rh.gs(R.string.formatinsulinunits, bolusIob(iobCobCalculator).iob + basalIob(iobCobCalculator).basaliob)
bolusIob?.let { bolusIob ->
basalIob?.let { basalIob -> fun iobDialogText(iobCobCalculator: IobCobCalculator): String =
rh.gs(R.string.formatinsulinunits, bolusIob.iob + basalIob.basaliob) + "\n" + rh.gs(R.string.formatinsulinunits, bolusIob(iobCobCalculator).iob + basalIob(iobCobCalculator).basaliob) + "\n" +
rh.gs(R.string.bolus) + ": " + rh.gs(R.string.formatinsulinunits, bolusIob.iob) + "\n" + rh.gs(R.string.bolus) + ": " + rh.gs(R.string.formatinsulinunits, bolusIob(iobCobCalculator).iob) + "\n" +
rh.gs(R.string.basal) + ": " + rh.gs(R.string.formatinsulinunits, basalIob.basaliob) rh.gs(R.string.basal) + ": " + rh.gs(R.string.formatinsulinunits, basalIob(iobCobCalculator).basaliob)
} ?: rh.gs(R.string.value_unavailable_short)
} ?: rh.gs(R.string.value_unavailable_short)
/* /*
* TEMP TARGET * TEMP TARGET
*/ */
var temporaryTarget: TemporaryTarget? = null val temporaryTarget: TemporaryTarget?
get() =
repository.getTemporaryTargetActiveAt(dateUtil.now()).blockingGet().let { tempTarget ->
if (tempTarget is ValueWrapper.Existing) tempTarget.value
else null
}
/* /*
* SENSITIVITY * SENSITIVITY
*/ */
var lastAutosensData: AutosensData? = null fun lastAutosensData(iobCobCalculator: IobCobCalculator) = iobCobCalculator.ads.getLastAutosensData("Overview", aapsLogger, dateUtil)
/* /*
* Graphs * Graphs
*/ */
@ -312,532 +301,4 @@ class OverviewData @Inject constructor(
val dsMinScale = Scale() val dsMinScale = Scale()
var dsMaxSeries: LineGraphSeries<ScaledDataPoint> = LineGraphSeries() var dsMaxSeries: LineGraphSeries<ScaledDataPoint> = LineGraphSeries()
var dsMinSeries: LineGraphSeries<ScaledDataPoint> = LineGraphSeries() var dsMinSeries: LineGraphSeries<ScaledDataPoint> = LineGraphSeries()
@Synchronized
@Suppress("SameParameterValue", "UNUSED_PARAMETER")
fun prepareBgData(from: String) {
// val start = dateUtil.now()
maxBgValue = Double.MIN_VALUE
bgReadingsArray = repository.compatGetBgReadingsDataFromTime(fromTime, toTime, false).blockingGet()
val bgListArray: MutableList<DataPointWithLabelInterface> = java.util.ArrayList()
for (bg in bgReadingsArray) {
if (bg.timestamp < fromTime || bg.timestamp > toTime) continue
if (bg.value > maxBgValue) maxBgValue = bg.value
bgListArray.add(GlucoseValueDataPoint(bg, defaultValueHelper, profileFunction, rh))
}
bgListArray.sortWith { o1: DataPointWithLabelInterface, o2: DataPointWithLabelInterface -> o1.x.compareTo(o2.x) }
bgReadingGraphSeries = PointsWithLabelGraphSeries(Array(bgListArray.size) { i -> bgListArray[i] })
maxBgValue = Profile.fromMgdlToUnits(maxBgValue, profileFunction.getUnits())
if (defaultValueHelper.determineHighLine() > maxBgValue) maxBgValue = defaultValueHelper.determineHighLine()
maxBgValue = addUpperChartMargin(maxBgValue)
// profiler.log(LTag.UI, "prepareBgData() $from", start)
}
@Suppress("UNUSED_PARAMETER")
@Synchronized
fun preparePredictions(from: String) {
// val start = dateUtil.now()
val apsResult = if (config.APS) loop.lastRun?.constraintsProcessed else nsDeviceStatus.getAPSResult(injector)
val predictionsAvailable = if (config.APS) loop.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 = 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()
} else {
toTime = calendar.timeInMillis + 100000 // little bit more to avoid wrong rounding - GraphView specific
fromTime = toTime - T.hours(rangeToDisplay.toLong()).msecs()
endTime = toTime
}
val bgListArray: MutableList<DataPointWithLabelInterface> = java.util.ArrayList()
val predictions: MutableList<GlucoseValueDataPoint>? = apsResult?.predictions
?.map { bg -> GlucoseValueDataPoint(bg, defaultValueHelper, profileFunction, rh) }
?.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)
}
predictionsGraphSeries = PointsWithLabelGraphSeries(Array(bgListArray.size) { i -> bgListArray[i] })
// profiler.log(LTag.UI, "preparePredictions() $from", start)
}
@Synchronized
@Suppress("SameParameterValue", "UNUSED_PARAMETER")
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<DataPointWithLabelInterface> = java.util.ArrayList()
for (inMemoryGlucoseValue in bucketedData) {
if (inMemoryGlucoseValue.timestamp < fromTime || inMemoryGlucoseValue.timestamp > toTime) continue
bucketedListArray.add(InMemoryGlucoseValueDataPoint(inMemoryGlucoseValue, profileFunction, rh))
}
bucketedListArray.sortWith { o1: DataPointWithLabelInterface, o2: DataPointWithLabelInterface -> o1.x.compareTo(o2.x) }
bucketedGraphSeries = PointsWithLabelGraphSeries(Array(bucketedListArray.size) { i -> bucketedListArray[i] })
// profiler.log(LTag.UI, "prepareBucketedData() $from", start)
}
@Suppress("UNUSED_PARAMETER")
@Synchronized
fun prepareBasalData(from: String) {
// val start = dateUtil.now()
maxBasalValueFound = 0.0
val baseBasalArray: MutableList<ScaledDataPoint> = java.util.ArrayList()
val tempBasalArray: MutableList<ScaledDataPoint> = java.util.ArrayList()
val basalLineArray: MutableList<ScaledDataPoint> = java.util.ArrayList()
val absoluteBasalLineArray: MutableList<ScaledDataPoint> = java.util.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 = 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, 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
baseBasalGraphSeries = LineGraphSeries(Array(baseBasalArray.size) { i -> baseBasalArray[i] }).also {
it.isDrawBackground = true
it.backgroundColor = rh.gc(R.color.basebasal)
it.thickness = 0
}
tempBasalGraphSeries = LineGraphSeries(Array(tempBasalArray.size) { i -> tempBasalArray[i] }).also {
it.isDrawBackground = true
it.backgroundColor = rh.gc(R.color.tempbasal)
it.thickness = 0
}
basalLineGraphSeries = LineGraphSeries(Array(basalLineArray.size) { i -> basalLineArray[i] }).also {
it.setCustomPaint(Paint().also { paint ->
paint.style = Paint.Style.STROKE
paint.strokeWidth = rh.getDisplayMetrics().scaledDensity * 2
paint.pathEffect = DashPathEffect(floatArrayOf(2f, 4f), 0f)
paint.color = rh.gc(R.color.basal)
})
}
absoluteBasalGraphSeries = LineGraphSeries(Array(absoluteBasalLineArray.size) { i -> absoluteBasalLineArray[i] }).also {
it.setCustomPaint(Paint().also { absolutePaint ->
absolutePaint.style = Paint.Style.STROKE
absolutePaint.strokeWidth = rh.getDisplayMetrics().scaledDensity * 2
absolutePaint.color = rh.gc(R.color.basal)
})
}
// profiler.log(LTag.UI, "prepareBasalData() $from", start)
}
@Suppress("UNUSED_PARAMETER")
@Synchronized
fun prepareTemporaryTargetData(from: String) {
// val start = dateUtil.now()
val profile = profileFunction.getProfile() ?: return
val units = profileFunction.getUnits()
var toTime = toTime
val targetsSeriesArray: MutableList<DataPoint> = java.util.ArrayList()
var lastTarget = -1.0
loop.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
temporaryTargetSeries = LineGraphSeries(Array(targetsSeriesArray.size) { i -> targetsSeriesArray[i] }).also {
it.isDrawBackground = false
it.color = rh.gc(R.color.tempTargetBackground)
it.thickness = 2
}
// profiler.log(LTag.UI, "prepareTemporaryTargetData() $from", start)
}
@Suppress("UNUSED_PARAMETER")
@Synchronized
fun prepareTreatmentsData(from: String) {
// val start = dateUtil.now()
maxTreatmentsValue = 0.0
val filteredTreatments: MutableList<DataPointWithLabelInterface> = java.util.ArrayList()
repository.getBolusesDataFromTimeToTime(fromTime, endTime, true).blockingGet()
.map { BolusDataPoint(it, rh, activePlugin, defaultValueHelper) }
.filter { it.data.type == Bolus.Type.NORMAL || it.data.type == Bolus.Type.SMB }
.forEach {
it.y = getNearestBg(it.x.toLong())
filteredTreatments.add(it)
}
repository.getCarbsDataFromTimeToTimeExpanded(fromTime, endTime, true).blockingGet()
.map { CarbsDataPoint(it, rh) }
.forEach {
it.y = getNearestBg(it.x.toLong())
filteredTreatments.add(it)
}
// ProfileSwitch
repository.getEffectiveProfileSwitchDataFromTimeToTime(fromTime, endTime, true).blockingGet()
.map { EffectiveProfileSwitchDataPoint(it,rh) }
.forEach(filteredTreatments::add)
// OfflineEvent
repository.getOfflineEventDataFromTimeToTime(fromTime, endTime, true).blockingGet()
.map {
TherapyEventDataPoint(
TherapyEvent(timestamp = it.timestamp, duration = it.duration, type = TherapyEvent.Type.APS_OFFLINE, glucoseUnit = TherapyEvent.GlucoseUnit.MMOL),
rh,
profileFunction,
translator
)
}
.forEach(filteredTreatments::add)
// Extended bolus
if (!activePlugin.activePump.isFakingTempsByExtendedBoluses) {
repository.getExtendedBolusDataFromTimeToTime(fromTime, endTime, true).blockingGet()
.map { ExtendedBolusDataPoint(it, rh) }
.filter { it.duration != 0L }
.forEach {
it.y = getNearestBg(it.x.toLong())
filteredTreatments.add(it)
}
}
// Careportal
repository.compatGetTherapyEventDataFromToTime(fromTime - T.hours(6).msecs(), endTime).blockingGet()
.map { TherapyEventDataPoint(it, rh, profileFunction, translator) }
.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 { maxTreatmentsValue = maxOf(maxTreatmentsValue, it) }
treatmentsSeries = PointsWithLabelGraphSeries(filteredTreatments.toTypedArray())
// profiler.log(LTag.UI, "prepareTreatmentsData() $from", start)
}
@Suppress("UNUSED_PARAMETER")
@Synchronized
fun prepareIobAutosensData(from: String) {
// val start = dateUtil.now()
val iobArray: MutableList<ScaledDataPoint> = java.util.ArrayList()
val absIobArray: MutableList<ScaledDataPoint> = java.util.ArrayList()
maxIobValueFound = Double.MIN_VALUE
var lastIob = 0.0
var absLastIob = 0.0
var time = fromTime
val minFailOverActiveList: MutableList<DataPointWithLabelInterface> = java.util.ArrayList()
val cobArray: MutableList<ScaledDataPoint> = java.util.ArrayList()
maxCobValueFound = Double.MIN_VALUE
var lastCob = 0
val actArrayHist: MutableList<ScaledDataPoint> = java.util.ArrayList()
val actArrayPrediction: MutableList<ScaledDataPoint> = java.util.ArrayList()
val now = dateUtil.now().toDouble()
maxIAValue = 0.0
val bgiArrayHist: MutableList<ScaledDataPoint> = java.util.ArrayList()
val bgiArrayPrediction: MutableList<ScaledDataPoint> = java.util.ArrayList()
maxBGIValue = Double.MIN_VALUE
val devArray: MutableList<OverviewPlugin.DeviationDataPoint> = java.util.ArrayList()
maxDevValueFound = Double.MIN_VALUE
val ratioArray: MutableList<ScaledDataPoint> = java.util.ArrayList()
maxRatioValueFound = 5.0 //even if sens data equals 0 for all the period, minimum scale is between 95% and 105%
minRatioValueFound = -5.0
val dsMaxArray: MutableList<ScaledDataPoint> = java.util.ArrayList()
val dsMinArray: MutableList<ScaledDataPoint> = java.util.ArrayList()
maxFromMaxValueFound = Double.MIN_VALUE
maxFromMinValueFound = Double.MIN_VALUE
val adsData = iobCobCalculator.ads.clone()
while (time <= 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, iobScale))
iobArray.add(ScaledDataPoint(time, iob.iob, iobScale))
maxIobValueFound = maxOf(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, iobScale))
absIobArray.add(ScaledDataPoint(time, absIob.iob, iobScale))
maxIobValueFound = maxOf(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(), cobScale))
cobArray.add(ScaledDataPoint(time, cob.toDouble(), cobScale))
maxCobValueFound = max(maxCobValueFound, cob.toDouble())
lastCob = cob
}
if (autosensData.failOverToMinAbsorptionRate) {
autosensData.scale = cobScale
autosensData.chartTime = time
minFailOverActiveList.add(autosensData)
}
}
// ACTIVITY
if (time <= now) actArrayHist.add(ScaledDataPoint(time, iob.activity, actScale))
else actArrayPrediction.add(ScaledDataPoint(time, iob.activity, actScale))
maxIAValue = max(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, bgiScale))
else bgiArrayPrediction.add(ScaledDataPoint(time, bgi, bgiScale))
maxBGIValue = max(maxBGIValue, max(abs(bgi), deviation))
// DEVIATIONS
if (autosensData != null) {
var color = rh.gc(R.color.deviationblack) // "="
if (autosensData.type == "" || autosensData.type == "non-meal") {
if (autosensData.pastSensitivity == "C") color = rh.gc(R.color.deviationgrey)
if (autosensData.pastSensitivity == "+") color = rh.gc(R.color.deviationgreen)
if (autosensData.pastSensitivity == "-") color = rh.gc(R.color.deviationred)
} else if (autosensData.type == "uam") {
color = rh.gc(R.color.uam)
} else if (autosensData.type == "csf") {
color = rh.gc(R.color.deviationgrey)
}
devArray.add(OverviewPlugin.DeviationDataPoint(time.toDouble(), autosensData.deviation, color, devScale))
maxDevValueFound = maxOf(maxDevValueFound, abs(autosensData.deviation), abs(bgi))
}
// RATIO
if (autosensData != null) {
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))
}
// DEV SLOPE
if (autosensData != null) {
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
}
// IOB
iobSeries = FixedLineGraphSeries(Array(iobArray.size) { i -> iobArray[i] }).also {
it.isDrawBackground = true
it.backgroundColor = -0x7f000001 and rh.gc(R.color.iob) //50%
it.color = rh.gc(R.color.iob)
it.thickness = 3
}
absIobSeries = FixedLineGraphSeries(Array(absIobArray.size) { i -> absIobArray[i] }).also {
it.isDrawBackground = true
it.backgroundColor = -0x7f000001 and rh.gc(R.color.iob) //50%
it.color = rh.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<DataPointWithLabelInterface> = java.util.ArrayList()
val iobPredictionArray = iobCobCalculator.calculateIobArrayForSMB(lastAutosensResult, SMBDefaults.exercise_mode, SMBDefaults.half_basal_exercise_target, isTempTarget)
for (i in iobPredictionArray) {
iobPrediction.add(i.setColor(rh.gc(R.color.iobPredAS)))
maxIobValueFound = max(maxIobValueFound, abs(i.iob))
}
iobPredictions1Series = PointsWithLabelGraphSeries(Array(iobPrediction.size) { i -> iobPrediction[i] })
aapsLogger.debug(LTag.AUTOSENS, "IOB prediction for AS=" + DecimalFormatter.to2Decimal(lastAutosensResult.ratio) + ": " + iobCobCalculator.iobArrayToString(iobPredictionArray))
/*
val iobPrediction2: MutableList<DataPointWithLabelInterface> = java.util.ArrayList()
val iobPredictionArray2 = iobCobCalculator.calculateIobArrayForSMB(AutosensResult(), SMBDefaults.exercise_mode, SMBDefaults.half_basal_exercise_target, isTempTarget)
for (i in iobPredictionArray2) {
iobPrediction2.add(i.setColor(rh.gc(R.color.iobPred)))
maxIobValueFound = max(maxIobValueFound, abs(i.iob))
}
iobPredictions2Series = PointsWithLabelGraphSeries(Array(iobPrediction2.size) { i -> iobPrediction2[i] })
aapsLogger.debug(LTag.AUTOSENS, "IOB prediction for AS=" + DecimalFormatter.to2Decimal(1.0) + ": " + iobCobCalculator.iobArrayToString(iobPredictionArray2))
*/
} else {
iobPredictions1Series = PointsWithLabelGraphSeries()
//iobPredictions2Series = PointsWithLabelGraphSeries()
}
// COB
cobSeries = FixedLineGraphSeries(Array(cobArray.size) { i -> cobArray[i] }).also {
it.isDrawBackground = true
it.backgroundColor = -0x7f000001 and rh.gc(R.color.cob) //50%
it.color = rh.gc(R.color.cob)
it.thickness = 3
}
cobMinFailOverSeries = PointsWithLabelGraphSeries(Array(minFailOverActiveList.size) { i -> minFailOverActiveList[i] })
// ACTIVITY
activitySeries = FixedLineGraphSeries(Array(actArrayHist.size) { i -> actArrayHist[i] }).also {
it.isDrawBackground = false
it.color = rh.gc(R.color.activity)
it.thickness = 3
}
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 = rh.gc(R.color.activity)
})
}
// BGI
minusBgiSeries = FixedLineGraphSeries(Array(bgiArrayHist.size) { i -> bgiArrayHist[i] }).also {
it.isDrawBackground = false
it.color = rh.gc(R.color.bgi)
it.thickness = 3
}
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 = rh.gc(R.color.bgi)
})
}
// DEVIATIONS
deviationsSeries = BarGraphSeries(Array(devArray.size) { i -> devArray[i] }).also {
it.setValueDependentColor { data: OverviewPlugin.DeviationDataPoint -> data.color }
}
// RATIO
ratioSeries = LineGraphSeries(Array(ratioArray.size) { i -> ratioArray[i] }).also {
it.color = rh.gc(R.color.ratio)
it.thickness = 3
}
// DEV SLOPE
dsMaxSeries = LineGraphSeries(Array(dsMaxArray.size) { i -> dsMaxArray[i] }).also {
it.color = rh.gc(R.color.devslopepos)
it.thickness = 3
}
dsMinSeries = LineGraphSeries(Array(dsMinArray.size) { i -> dsMinArray[i] }).also {
it.color = rh.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 {
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 <E : DataPointWithLabelInterface> List<E>.filterTimeframe(fromTime: Long, endTime: Long): List<E> =
filter { it.x + it.duration >= fromTime && it.x <= endTime }
} }

View file

@ -36,13 +36,9 @@ import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.database.interfaces.end import info.nightscout.androidaps.database.interfaces.end
import info.nightscout.androidaps.databinding.OverviewFragmentBinding import info.nightscout.androidaps.databinding.OverviewFragmentBinding
import info.nightscout.androidaps.dialogs.* import info.nightscout.androidaps.dialogs.*
import info.nightscout.androidaps.events.EventAcceptOpenLoopChange import info.nightscout.androidaps.events.*
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.directionToIcon
import info.nightscout.androidaps.extensions.isInProgress import info.nightscout.androidaps.extensions.runOnUiThread
import info.nightscout.androidaps.extensions.toVisibility import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.extensions.valueToUnitsString import info.nightscout.androidaps.extensions.valueToUnitsString
import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.interfaces.*
@ -188,10 +184,6 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
overviewData.rangeToDisplay += 6 overviewData.rangeToDisplay += 6
overviewData.rangeToDisplay = if (overviewData.rangeToDisplay > 24) 6 else overviewData.rangeToDisplay overviewData.rangeToDisplay = if (overviewData.rangeToDisplay > 24) 6 else overviewData.rangeToDisplay
sp.putInt(R.string.key_rangetodisplay, overviewData.rangeToDisplay) sp.putInt(R.string.key_rangetodisplay, overviewData.rangeToDisplay)
overviewData.initRange()
overviewData.prepareBucketedData("EventBucketedDataCreated")
overviewData.prepareBgData("EventBucketedDataCreated")
updateGraph("rangeChange")
rxBus.send(EventPreferenceChange(rh, R.string.key_rangetodisplay)) rxBus.send(EventPreferenceChange(rh, R.string.key_rangetodisplay))
sp.putBoolean(R.string.key_objectiveusescale, true) sp.putBoolean(R.string.key_objectiveusescale, true)
false false
@ -227,113 +219,106 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
@Synchronized @Synchronized
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
disposable += activePlugin.activeOverview.overviewBus
.toObservable(EventUpdateOverviewTime::class.java)
.debounce(1L, TimeUnit.SECONDS)
.observeOn(aapsSchedulers.main)
.subscribe({ updateTime(it.from) }, fabricPrivacy::logException)
disposable += activePlugin.activeOverview.overviewBus disposable += activePlugin.activeOverview.overviewBus
.toObservable(EventUpdateOverviewCalcProgress::class.java) .toObservable(EventUpdateOverviewCalcProgress::class.java)
.observeOn(aapsSchedulers.main) .observeOn(aapsSchedulers.main)
.subscribe({ updateCalcProgress(it.from) }, fabricPrivacy::logException) .subscribe({ updateCalcProgress() }, fabricPrivacy::logException)
disposable += activePlugin.activeOverview.overviewBus
.toObservable(EventUpdateOverviewProfile::class.java)
.debounce(1L, TimeUnit.SECONDS)
.observeOn(aapsSchedulers.main)
.subscribe({ updateProfile(it.from) }, fabricPrivacy::logException)
disposable += activePlugin.activeOverview.overviewBus
.toObservable(EventUpdateOverviewTemporaryBasal::class.java)
.debounce(1L, TimeUnit.SECONDS)
.observeOn(aapsSchedulers.main)
.subscribe({ updateTemporaryBasal(it.from) }, fabricPrivacy::logException)
disposable += activePlugin.activeOverview.overviewBus
.toObservable(EventUpdateOverviewExtendedBolus::class.java)
.debounce(1L, TimeUnit.SECONDS)
.observeOn(aapsSchedulers.main)
.subscribe({ updateExtendedBolus(it.from) }, fabricPrivacy::logException)
disposable += activePlugin.activeOverview.overviewBus
.toObservable(EventUpdateOverviewTemporaryTarget::class.java)
.debounce(1L, TimeUnit.SECONDS)
.observeOn(aapsSchedulers.main)
.subscribe({ updateTemporaryTarget(it.from) }, fabricPrivacy::logException)
disposable += activePlugin.activeOverview.overviewBus
.toObservable(EventUpdateOverviewBg::class.java)
.debounce(1L, TimeUnit.SECONDS)
.observeOn(aapsSchedulers.main)
.subscribe({ updateBg(it.from) }, fabricPrivacy::logException)
disposable += activePlugin.activeOverview.overviewBus disposable += activePlugin.activeOverview.overviewBus
.toObservable(EventUpdateOverviewIobCob::class.java) .toObservable(EventUpdateOverviewIobCob::class.java)
.debounce(1L, TimeUnit.SECONDS) .debounce(1L, TimeUnit.SECONDS)
.observeOn(aapsSchedulers.main) .observeOn(aapsSchedulers.main)
.subscribe({ updateIobCob(it.from) }, fabricPrivacy::logException) .subscribe({ updateIobCob() }, fabricPrivacy::logException)
disposable += activePlugin.activeOverview.overviewBus disposable += activePlugin.activeOverview.overviewBus
.toObservable(EventUpdateOverviewSensitivity::class.java) .toObservable(EventUpdateOverviewSensitivity::class.java)
.debounce(1L, TimeUnit.SECONDS) .debounce(1L, TimeUnit.SECONDS)
.observeOn(aapsSchedulers.main) .observeOn(aapsSchedulers.main)
.subscribe({ updateSensitivity(it.from) }, fabricPrivacy::logException) .subscribe({ updateSensitivity() }, fabricPrivacy::logException)
disposable += activePlugin.activeOverview.overviewBus disposable += activePlugin.activeOverview.overviewBus
.toObservable(EventUpdateOverviewGraph::class.java) .toObservable(EventUpdateOverviewGraph::class.java)
.debounce(1L, TimeUnit.SECONDS) .debounce(1L, TimeUnit.SECONDS)
.observeOn(aapsSchedulers.main) .observeOn(aapsSchedulers.main)
.subscribe({ updateGraph(it.from) }, fabricPrivacy::logException) .subscribe({ updateGraph() }, fabricPrivacy::logException)
disposable += activePlugin.activeOverview.overviewBus disposable += activePlugin.activeOverview.overviewBus
.toObservable(EventUpdateOverviewPumpStatus::class.java) .toObservable(EventUpdateOverviewPumpStatus::class.java)
.observeOn(aapsSchedulers.main) .observeOn(aapsSchedulers.main)
.subscribe({ updatePumpStatus(it.from) }, fabricPrivacy::logException) .subscribe({ updatePumpStatus() }, fabricPrivacy::logException)
disposable += activePlugin.activeOverview.overviewBus disposable += activePlugin.activeOverview.overviewBus
.toObservable(EventUpdateOverviewNotification::class.java) .toObservable(EventUpdateOverviewNotification::class.java)
.observeOn(aapsSchedulers.main) .observeOn(aapsSchedulers.main)
.subscribe({ updateNotification(it.from) }, fabricPrivacy::logException) .subscribe({ updateNotification() }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventNewBG::class.java)
.debounce(1L, TimeUnit.SECONDS)
.observeOn(aapsSchedulers.main)
.subscribe({ updateBg() }, fabricPrivacy::logException)
disposable += rxBus disposable += rxBus
.toObservable(EventRefreshOverview::class.java) .toObservable(EventRefreshOverview::class.java)
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
.subscribe({ .subscribe({
if (it.now) overviewPlugin.refreshLoop(it.from) if (it.now) refreshAll()
else scheduleUpdateGUI(it.from) else scheduleUpdateGUI()
}, fabricPrivacy::logException) }, fabricPrivacy::logException)
disposable += rxBus disposable += rxBus
.toObservable(EventAcceptOpenLoopChange::class.java) .toObservable(EventAcceptOpenLoopChange::class.java)
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
.subscribe({ scheduleUpdateGUI("EventAcceptOpenLoopChange") }, fabricPrivacy::logException) .subscribe({ scheduleUpdateGUI() }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventInitializationChanged::class.java)
.observeOn(aapsSchedulers.main)
.subscribe({ updateTime("EventInitializationChanged") }, fabricPrivacy::logException)
disposable += rxBus disposable += rxBus
.toObservable(EventPreferenceChange::class.java) .toObservable(EventPreferenceChange::class.java)
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
.subscribe({ scheduleUpdateGUI("EventPreferenceChange") }, fabricPrivacy::logException) .subscribe({ scheduleUpdateGUI() }, fabricPrivacy::logException)
disposable += rxBus disposable += rxBus
.toObservable(EventNewOpenLoopNotification::class.java) .toObservable(EventNewOpenLoopNotification::class.java)
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
.subscribe({ scheduleUpdateGUI("EventNewOpenLoopNotification") }, fabricPrivacy::logException) .subscribe({ scheduleUpdateGUI() }, fabricPrivacy::logException)
disposable += rxBus disposable += rxBus
.toObservable(EventPumpStatusChanged::class.java) .toObservable(EventPumpStatusChanged::class.java)
.observeOn(aapsSchedulers.main) .observeOn(aapsSchedulers.main)
.delay(30, TimeUnit.MILLISECONDS, aapsSchedulers.main) .delay(30, TimeUnit.MILLISECONDS, aapsSchedulers.main)
.subscribe({ .subscribe({
overviewData.pumpStatus = it.getStatus(rh) overviewData.pumpStatus = it.getStatus(rh)
updatePumpStatus("EventPumpStatusChanged") updatePumpStatus()
}, fabricPrivacy::logException) }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventEffectiveProfileSwitchChanged::class.java)
.observeOn(aapsSchedulers.main)
.subscribe({ updateProfile() }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventTempTargetChange::class.java)
.observeOn(aapsSchedulers.main)
.subscribe({ updateTemporaryTarget() }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventExtendedBolusChange::class.java)
.observeOn(aapsSchedulers.main)
.subscribe({ updateExtendedBolus() }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventTempBasalChange::class.java)
.observeOn(aapsSchedulers.main)
.subscribe({ updateTemporaryBasal() }, fabricPrivacy::logException)
refreshLoop = Runnable { refreshLoop = Runnable {
overviewPlugin.refreshLoop("refreshLoop") refreshAll()
handler.postDelayed(refreshLoop, 60 * 1000L) handler.postDelayed(refreshLoop, 60 * 1000L)
} }
handler.postDelayed(refreshLoop, 60 * 1000L) handler.postDelayed(refreshLoop, 60 * 1000L)
updateTime("onResume") refreshAll()
updateCalcProgress("onResume") updatePumpStatus()
updateProfile("onResume") updateCalcProgress()
updateTemporaryBasal("onResume") }
updateExtendedBolus("onResume")
updateTemporaryTarget("onResume") fun refreshAll() {
updateBg("onResume") runOnUiThread {
updateIobCob("onResume") updateBg()
updateSensitivity("onResume") updateTime()
updateGraph("onResume") updateProfile()
updatePumpStatus("onResume") updateTemporaryBasal()
updateNotification("onResume") updateExtendedBolus()
updateTemporaryTarget()
updateIobCob()
updateSensitivity()
updateGraph()
updateNotification()
}
} }
@Synchronized @Synchronized
@ -563,14 +548,14 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
binding.buttonsLayout.cgmButton.setCompoundDrawablesWithIntrinsicBounds(null, rh.gd(R.drawable.ic_byoda), null, null) binding.buttonsLayout.cgmButton.setCompoundDrawablesWithIntrinsicBounds(null, rh.gd(R.drawable.ic_byoda), null, null)
for (drawable in binding.buttonsLayout.cgmButton.compoundDrawables) { for (drawable in binding.buttonsLayout.cgmButton.compoundDrawables) {
drawable?.mutate() drawable?.mutate()
drawable?.colorFilter = PorterDuffColorFilter(rh.gac( context,R.attr.cgmdexColor ), PorterDuff.Mode.SRC_IN) drawable?.colorFilter = PorterDuffColorFilter(rh.gac(context, R.attr.cgmdexColor), PorterDuff.Mode.SRC_IN)
} }
binding.buttonsLayout.cgmButton.setTextColor(rh.gac(context, R.attr.cgmdexColor)) binding.buttonsLayout.cgmButton.setTextColor(rh.gac(context, R.attr.cgmdexColor))
} else if (xDripIsBgSource) { } else if (xDripIsBgSource) {
binding.buttonsLayout.cgmButton.setCompoundDrawablesWithIntrinsicBounds(null, rh.gd(R.drawable.ic_xdrip), null, null) binding.buttonsLayout.cgmButton.setCompoundDrawablesWithIntrinsicBounds(null, rh.gd(R.drawable.ic_xdrip), null, null)
for (drawable in binding.buttonsLayout.cgmButton.compoundDrawables) { for (drawable in binding.buttonsLayout.cgmButton.compoundDrawables) {
drawable?.mutate() drawable?.mutate()
drawable?.colorFilter = PorterDuffColorFilter(rh.gac( context,R.attr.cgmxdripColor ), PorterDuff.Mode.SRC_IN) drawable?.colorFilter = PorterDuffColorFilter(rh.gac(context, R.attr.cgmxdripColor), PorterDuff.Mode.SRC_IN)
} }
binding.buttonsLayout.cgmButton.setTextColor(rh.gac(context, R.attr.cgmxdripColor)) binding.buttonsLayout.cgmButton.setTextColor(rh.gac(context, R.attr.cgmxdripColor))
} }
@ -752,11 +737,11 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
var task: Runnable? = null var task: Runnable? = null
private fun scheduleUpdateGUI(from: String) { private fun scheduleUpdateGUI() {
class UpdateRunnable : Runnable { class UpdateRunnable : Runnable {
override fun run() { override fun run() {
overviewPlugin.refreshLoop(from) refreshAll()
task = null task = null
} }
} }
@ -766,8 +751,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
} }
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
@Suppress("UNUSED_PARAMETER") fun updateBg() {
fun updateBg(from: String) {
val units = profileFunction.getUnits() val units = profileFunction.getUnits()
binding.infoLayout.bg.text = overviewData.lastBg?.valueToUnitsString(units) binding.infoLayout.bg.text = overviewData.lastBg?.valueToUnitsString(units)
?: rh.gs(R.string.notavailable) ?: rh.gs(R.string.notavailable)
@ -816,8 +800,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
} }
} }
@Suppress("UNUSED_PARAMETER") fun updateProfile() {
fun updateProfile(from: String) {
val profileBackgroundColor = val profileBackgroundColor =
profileFunction.getProfile()?.let { profileFunction.getProfile()?.let {
if (it is ProfileSealed.EPS) { if (it is ProfileSealed.EPS) {
@ -849,28 +832,25 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
binding.activeProfile.setTextColor(profileTextColor) binding.activeProfile.setTextColor(profileTextColor)
} }
@Suppress("UNUSED_PARAMETER") private fun updateTemporaryBasal() {
fun updateTemporaryBasal(from: String) { binding.infoLayout.baseBasal.text = overviewData.temporaryBasalText(iobCobCalculator)
binding.infoLayout.baseBasal.text = overviewData.temporaryBasalText binding.infoLayout.baseBasal.setTextColor(overviewData.temporaryBasalColor(context, iobCobCalculator))
binding.infoLayout.baseBasal.setTextColor(overviewData.temporaryBasalColor) binding.infoLayout.baseBasalIcon.setImageResource(overviewData.temporaryBasalIcon(iobCobCalculator))
binding.infoLayout.baseBasalIcon.setImageResource(overviewData.temporaryBasalIcon)
binding.infoLayout.basalLayout.setOnClickListener { binding.infoLayout.basalLayout.setOnClickListener {
activity?.let { OKDialog.show(it, rh.gs(R.string.basal), overviewData.temporaryBasalDialogText) } activity?.let { OKDialog.show(it, rh.gs(R.string.basal), overviewData.temporaryBasalDialogText(iobCobCalculator)) }
} }
} }
@Suppress("UNUSED_PARAMETER") private fun updateExtendedBolus() {
fun updateExtendedBolus(from: String) {
val pump = activePlugin.activePump val pump = activePlugin.activePump
binding.infoLayout.extendedBolus.text = overviewData.extendedBolusText binding.infoLayout.extendedBolus.text = overviewData.extendedBolusText(iobCobCalculator)
binding.infoLayout.extendedLayout.setOnClickListener { binding.infoLayout.extendedLayout.setOnClickListener {
activity?.let { OKDialog.show(it, rh.gs(R.string.extended_bolus), overviewData.extendedBolusDialogText) } activity?.let { OKDialog.show(it, rh.gs(R.string.extended_bolus), overviewData.extendedBolusDialogText(iobCobCalculator)) }
} }
binding.infoLayout.extendedLayout.visibility = (iobCobCalculator.getExtendedBolus(dateUtil.now()) != null && !pump.isFakingTempsByExtendedBoluses).toVisibility() binding.infoLayout.extendedLayout.visibility = (iobCobCalculator.getExtendedBolus(dateUtil.now()) != null && !pump.isFakingTempsByExtendedBoluses).toVisibility()
} }
@Suppress("UNUSED_PARAMETER") fun updateTime() {
fun updateTime(from: String) {
binding.infoLayout.time.text = dateUtil.timeString(dateUtil.now()) binding.infoLayout.time.text = dateUtil.timeString(dateUtil.now())
// Status lights // Status lights
val pump = activePlugin.activePump val pump = activePlugin.activePump
@ -901,14 +881,13 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
processAps() processAps()
} }
@Suppress("UNUSED_PARAMETER") fun updateIobCob() {
fun updateIobCob(from: String) { binding.infoLayout.iob.text = overviewData.iobText(iobCobCalculator)
binding.infoLayout.iob.text = overviewData.iobText
binding.infoLayout.iobLayout.setOnClickListener { binding.infoLayout.iobLayout.setOnClickListener {
activity?.let { OKDialog.show(it, rh.gs(R.string.iob), overviewData.iobDialogText) } activity?.let { OKDialog.show(it, rh.gs(R.string.iob), overviewData.iobDialogText(iobCobCalculator)) }
} }
// cob // cob
var cobText = overviewData.cobInfo?.displayText(rh, dateUtil, buildHelper.isEngineeringMode()) ?: rh.gs(R.string.value_unavailable_short) var cobText = overviewData.cobInfo(iobCobCalculator).displayText(rh, dateUtil, buildHelper.isEngineeringMode()) ?: rh.gs(R.string.value_unavailable_short)
val constraintsProcessed = loop.lastRun?.constraintsProcessed val constraintsProcessed = loop.lastRun?.constraintsProcessed
val lastRun = loop.lastRun val lastRun = loop.lastRun
@ -929,10 +908,8 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
} }
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
@Suppress("UNUSED_PARAMETER") fun updateTemporaryTarget() {
fun updateTemporaryTarget(from: String) {
val units = profileFunction.getUnits() val units = profileFunction.getUnits()
if (overviewData.temporaryTarget?.isInProgress(dateUtil) == false) overviewData.temporaryTarget = null
val tempTarget = overviewData.temporaryTarget val tempTarget = overviewData.temporaryTarget
if (tempTarget != null) { if (tempTarget != null) {
binding.tempTarget.setTextColor(rh.gac(context, R.attr.ribbonTextWarningColor)) binding.tempTarget.setTextColor(rh.gac(context, R.attr.ribbonTextWarningColor))
@ -957,8 +934,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
} }
} }
@Suppress("UNUSED_PARAMETER") private fun updateGraph() {
fun updateGraph(from: String) {
val pump = activePlugin.activePump val pump = activePlugin.activePump
val graphData = GraphData(injector, binding.graphsLayout.bgGraph, overviewData) val graphData = GraphData(injector, binding.graphsLayout.bgGraph, overviewData)
val menuChartSettings = overviewMenus.setting val menuChartSettings = overviewMenus.setting
@ -1036,14 +1012,12 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
} }
} }
@Suppress("UNUSED_PARAMETER") private fun updateCalcProgress() {
fun updateCalcProgress(from: String) {
binding.progressBar.progress = overviewData.calcProgressPct binding.progressBar.progress = overviewData.calcProgressPct
binding.progressBar.visibility = (overviewData.calcProgressPct != 100).toVisibility() binding.progressBar.visibility = (overviewData.calcProgressPct != 100).toVisibility()
} }
@Suppress("UNUSED_PARAMETER") private fun updateSensitivity() {
fun updateSensitivity(from: String) {
if (sp.getBoolean(R.string.key_openapsama_useautosens, false) && constraintChecker.isAutosensModeEnabled().value()) { if (sp.getBoolean(R.string.key_openapsama_useautosens, false) && constraintChecker.isAutosensModeEnabled().value()) {
binding.infoLayout.sensitivityIcon.setImageResource(R.drawable.ic_swap_vert_black_48dp_green) binding.infoLayout.sensitivityIcon.setImageResource(R.drawable.ic_swap_vert_black_48dp_green)
} else { } else {
@ -1051,20 +1025,18 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
} }
binding.infoLayout.sensitivity.text = binding.infoLayout.sensitivity.text =
overviewData.lastAutosensData?.let { autosensData -> overviewData.lastAutosensData(iobCobCalculator)?.let { autosensData ->
String.format(Locale.ENGLISH, "%.0f%%", autosensData.autosensResult.ratio * 100) String.format(Locale.ENGLISH, "%.0f%%", autosensData.autosensResult.ratio * 100)
} ?: "" } ?: ""
} }
@Suppress("UNUSED_PARAMETER") private fun updatePumpStatus() {
fun updatePumpStatus(from: String) {
val status = overviewData.pumpStatus val status = overviewData.pumpStatus
binding.pumpStatus.text = status binding.pumpStatus.text = status
binding.pumpStatusLayout.visibility = (status != "").toVisibility() binding.pumpStatusLayout.visibility = (status != "").toVisibility()
} }
@Suppress("UNUSED_PARAMETER") private fun updateNotification() {
fun updateNotification(from: String) {
binding.notifications.let { notificationStore.updateNotifications(it) } binding.notifications.let { notificationStore.updateNotifications(it) }
} }
} }

View file

@ -4,25 +4,26 @@ import androidx.preference.PreferenceFragmentCompat
import androidx.preference.SwitchPreference import androidx.preference.SwitchPreference
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.events.EventPumpStatusChanged
import info.nightscout.androidaps.database.ValueWrapper
import info.nightscout.androidaps.events.*
import info.nightscout.androidaps.extensions.* import info.nightscout.androidaps.extensions.*
import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.interfaces.Config
import info.nightscout.shared.logging.AAPSLogger import info.nightscout.androidaps.interfaces.Overview
import info.nightscout.shared.logging.LTag import info.nightscout.androidaps.interfaces.PluginBase
import info.nightscout.androidaps.plugins.aps.events.EventLoopInvoked import info.nightscout.androidaps.interfaces.PluginDescription
import info.nightscout.androidaps.interfaces.PluginType
import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.general.overview.events.* 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.EventUpdateOverviewCalcProgress
import info.nightscout.androidaps.plugins.general.overview.events.EventUpdateOverviewNotification
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.Scale import info.nightscout.androidaps.plugins.general.overview.graphExtensions.Scale
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.ScaledDataPoint import info.nightscout.androidaps.plugins.general.overview.graphExtensions.ScaledDataPoint
import info.nightscout.androidaps.plugins.general.overview.notifications.NotificationStore import info.nightscout.androidaps.plugins.general.overview.notifications.NotificationStore
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventBucketedDataCreated
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventIobCalculationProgress import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventIobCalculationProgress
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.sharedPreferences.SP import info.nightscout.shared.sharedPreferences.SP
import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign import io.reactivex.rxjava3.kotlin.plusAssign
@ -41,9 +42,6 @@ class OverviewPlugin @Inject constructor(
private val aapsSchedulers: AapsSchedulers, private val aapsSchedulers: AapsSchedulers,
rh: ResourceHelper, rh: ResourceHelper,
private val config: Config, private val config: Config,
private val dateUtil: DateUtil,
private val iobCobCalculator: IobCobCalculator,
private val repository: AppRepository,
private val overviewData: OverviewData, private val overviewData: OverviewData,
private val overviewMenus: OverviewMenus private val overviewMenus: OverviewMenus
) : PluginBase( ) : PluginBase(
@ -89,62 +87,9 @@ class OverviewPlugin @Inject constructor(
disposable += rxBus disposable += rxBus
.toObservable(EventIobCalculationProgress::class.java) .toObservable(EventIobCalculationProgress::class.java)
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
.subscribe({ overviewData.calcProgressPct = it.progressPct; overviewBus.send(EventUpdateOverviewCalcProgress("EventIobCalculationProgress")) }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventTempBasalChange::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ overviewBus.send(EventUpdateOverviewTemporaryBasal("EventTempBasalChange")) }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventExtendedBolusChange::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ overviewBus.send(EventUpdateOverviewExtendedBolus("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({ .subscribe({
loadIobCobResults("EventTreatmentChange") overviewData.calcProgressPct = it.pass.finalPercent(it.progressPct)
overviewData.prepareTreatmentsData("EventTreatmentChange") overviewBus.send(EventUpdateOverviewCalcProgress("EventIobCalculationProgress"))
overviewBus.send(EventUpdateOverviewGraph("EventTreatmentChange"))
}, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventTherapyEventChange::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({
overviewData.prepareTreatmentsData("EventTherapyEventChange")
overviewBus.send(EventUpdateOverviewGraph("EventTherapyEventChange"))
}, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventBucketedDataCreated::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({
overviewData.prepareBucketedData("EventBucketedDataCreated")
overviewData.prepareBgData("EventBucketedDataCreated")
overviewBus.send(EventUpdateOverviewGraph("EventBucketedDataCreated"))
}, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventLoopInvoked::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ overviewData.preparePredictions("EventLoopInvoked") }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventEffectiveProfileSwitchChanged::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({
loadProfile("EventEffectiveProfileSwitchChanged")
overviewData.prepareBasalData("EventEffectiveProfileSwitchChanged")
}, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventAutosensCalculationFinished::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({
if (it.cause !is EventCustomCalculationFinished) refreshLoop("EventAutosensCalculationFinished")
}, fabricPrivacy::logException) }, fabricPrivacy::logException)
disposable += rxBus disposable += rxBus
.toObservable(EventPumpStatusChanged::class.java) .toObservable(EventPumpStatusChanged::class.java)
@ -152,20 +97,7 @@ class OverviewPlugin @Inject constructor(
.subscribe({ .subscribe({
overviewData.pumpStatus = it.getStatus(rh) overviewData.pumpStatus = it.getStatus(rh)
}, fabricPrivacy::logException) }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventPreferenceChange::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ event ->
if (event.isChanged(rh, R.string.key_units)) {
overviewData.reset()
overviewData.prepareBucketedData("EventBucketedDataCreated")
overviewData.prepareBgData("EventBucketedDataCreated")
overviewBus.send(EventUpdateOverviewGraph("EventBucketedDataCreated"))
loadAll("EventPreferenceChange")
}
}, fabricPrivacy::logException)
Thread { loadAll("onResume") }.start()
} }
override fun onStop() { override fun onStop() {
@ -243,7 +175,7 @@ class OverviewPlugin @Inject constructor(
.storeDouble(R.string.key_statuslights_bat_critical, sp, rh) .storeDouble(R.string.key_statuslights_bat_critical, sp, rh)
.storeInt(R.string.key_boluswizard_percentage, sp, rh) .storeInt(R.string.key_boluswizard_percentage, sp, rh)
} }
/*
@Volatile @Volatile
var runningRefresh = false var runningRefresh = false
override fun refreshLoop(from: String) { override fun refreshLoop(from: String) {
@ -284,38 +216,5 @@ class OverviewPlugin @Inject constructor(
overviewBus.send(EventUpdateOverviewGraph(from)) overviewBus.send(EventUpdateOverviewGraph(from))
aapsLogger.debug(LTag.UI, "loadAll finished") aapsLogger.debug(LTag.UI, "loadAll finished")
} }
*/
private fun loadProfile(from: String) {
overviewBus.send(EventUpdateOverviewProfile(from))
}
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(EventUpdateOverviewTemporaryTarget(from))
}
private fun loadAsData(from: String) {
overviewData.lastAutosensData = iobCobCalculator.ads.getLastAutosensData("Overview", aapsLogger, dateUtil)
overviewBus.send(EventUpdateOverviewSensitivity(from))
}
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(EventUpdateOverviewBg(from))
}
private fun loadIobCobResults(from: String) {
overviewData.bolusIob = iobCobCalculator.calculateIobFromBolus().round()
overviewData.basalIob = iobCobCalculator.calculateIobFromTempBasalsIncludingConvertedExtended().round()
overviewData.cobInfo = iobCobCalculator.getCobInfo(true, "Overview COB")
val lastCarbs = repository.getLastCarbsRecordWrapped().blockingGet()
overviewData.lastCarbsTime = if (lastCarbs is ValueWrapper.Existing) lastCarbs.value.timestamp else 0L
overviewBus.send(EventUpdateOverviewIobCob(from))
}
} }

View file

@ -1,5 +0,0 @@
package info.nightscout.androidaps.plugins.general.overview.events
import info.nightscout.androidaps.events.Event
class EventUpdateOverviewBg(val from: String) : Event()

View file

@ -1,5 +0,0 @@
package info.nightscout.androidaps.plugins.general.overview.events
import info.nightscout.androidaps.events.Event
class EventUpdateOverviewExtendedBolus(val from: String) : Event()

View file

@ -1,5 +0,0 @@
package info.nightscout.androidaps.plugins.general.overview.events
import info.nightscout.androidaps.events.Event
class EventUpdateOverviewProfile(val from: String) : Event()

View file

@ -1,5 +0,0 @@
package info.nightscout.androidaps.plugins.general.overview.events
import info.nightscout.androidaps.events.Event
class EventUpdateOverviewTemporaryBasal(val from: String) : Event()

View file

@ -1,5 +0,0 @@
package info.nightscout.androidaps.plugins.general.overview.events
import info.nightscout.androidaps.events.Event
class EventUpdateOverviewTemporaryTarget(val from: String) : Event()

View file

@ -1,5 +0,0 @@
package info.nightscout.androidaps.plugins.general.overview.events
import info.nightscout.androidaps.events.Event
class EventUpdateOverviewTime(val from: String) : Event()

View file

@ -9,17 +9,20 @@ import androidx.core.app.RemoteInput
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.Constants import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.events.* import info.nightscout.androidaps.events.EventAutosensCalculationFinished
import info.nightscout.androidaps.events.EventInitializationChanged
import info.nightscout.androidaps.events.EventPreferenceChange
import info.nightscout.androidaps.events.EventRefreshOverview
import info.nightscout.androidaps.extensions.toStringShort import info.nightscout.androidaps.extensions.toStringShort
import info.nightscout.androidaps.extensions.valueToUnitsString import info.nightscout.androidaps.extensions.valueToUnitsString
import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.interfaces.*
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatusProvider import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatusProvider
import info.nightscout.androidaps.utils.DecimalFormatter import info.nightscout.androidaps.utils.DecimalFormatter
import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.shared.logging.AAPSLogger
import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign import io.reactivex.rxjava3.kotlin.plusAssign
import javax.inject.Inject import javax.inject.Inject
@ -72,26 +75,10 @@ class PersistentNotificationPlugin @Inject constructor(
.toObservable(EventRefreshOverview::class.java) .toObservable(EventRefreshOverview::class.java)
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
.subscribe({ triggerNotificationUpdate() }, fabricPrivacy::logException) .subscribe({ triggerNotificationUpdate() }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventExtendedBolusChange::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ triggerNotificationUpdate() }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventTempBasalChange::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ triggerNotificationUpdate() }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventTreatmentChange::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ triggerNotificationUpdate() }, fabricPrivacy::logException)
disposable += rxBus disposable += rxBus
.toObservable(EventInitializationChanged::class.java) .toObservable(EventInitializationChanged::class.java)
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
.subscribe({ triggerNotificationUpdate() }, fabricPrivacy::logException) .subscribe({ triggerNotificationUpdate() }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventEffectiveProfileSwitchChanged::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ triggerNotificationUpdate() }, fabricPrivacy::logException)
disposable += rxBus disposable += rxBus
.toObservable(EventAutosensCalculationFinished::class.java) .toObservable(EventAutosensCalculationFinished::class.java)
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)

View file

@ -1,12 +1,6 @@
package info.nightscout.androidaps.plugins.iob.iobCobCalculator package info.nightscout.androidaps.plugins.iob.iobCobCalculator
import android.content.Context
import android.os.SystemClock
import androidx.collection.LongSparseArray import androidx.collection.LongSparseArray
import androidx.work.ExistingWorkPolicy
import androidx.work.OneTimeWorkRequest
import androidx.work.WorkInfo
import androidx.work.WorkManager
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.Constants import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
@ -24,21 +18,19 @@ import info.nightscout.androidaps.extensions.convertedToAbsolute
import info.nightscout.androidaps.extensions.iobCalc import info.nightscout.androidaps.extensions.iobCalc
import info.nightscout.androidaps.extensions.toTemporaryBasal import info.nightscout.androidaps.extensions.toTemporaryBasal
import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.interfaces.*
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.general.overview.OverviewData
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.data.AutosensData import info.nightscout.androidaps.plugins.iob.iobCobCalculator.data.AutosensData
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventNewHistoryData 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.receivers.DataWorker
import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.DecimalFormatter import info.nightscout.androidaps.utils.DecimalFormatter
import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.androidaps.workflow.CalculationWorkflow
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import info.nightscout.shared.sharedPreferences.SP import info.nightscout.shared.sharedPreferences.SP
import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign import io.reactivex.rxjava3.kotlin.plusAssign
@ -63,14 +55,11 @@ class IobCobCalculatorPlugin @Inject constructor(
rh: ResourceHelper, rh: ResourceHelper,
private val profileFunction: ProfileFunction, private val profileFunction: ProfileFunction,
private val activePlugin: ActivePlugin, private val activePlugin: ActivePlugin,
private val sensitivityOref1Plugin: SensitivityOref1Plugin,
private val sensitivityAAPSPlugin: SensitivityAAPSPlugin,
private val sensitivityWeightedAveragePlugin: SensitivityWeightedAveragePlugin,
private val fabricPrivacy: FabricPrivacy, private val fabricPrivacy: FabricPrivacy,
private val dateUtil: DateUtil, private val dateUtil: DateUtil,
private val repository: AppRepository, private val repository: AppRepository,
private val context: Context, val overviewData: OverviewData,
private val dataWorker: DataWorker private val calculationWorkflow: CalculationWorkflow
) : PluginBase( ) : PluginBase(
PluginDescription() PluginDescription()
.mainType(PluginType.GENERAL) .mainType(PluginType.GENERAL)
@ -91,8 +80,6 @@ class IobCobCalculatorPlugin @Inject constructor(
private val dataLock = Any() private val dataLock = Any()
private var thread: Thread? = null private var thread: Thread? = null
private val jobGroupName = "calculation"
override fun onStart() { override fun onStart() {
super.onStart() super.onStart()
// EventConfigBuilderChange // EventConfigBuilderChange
@ -126,14 +113,6 @@ class IobCobCalculatorPlugin @Inject constructor(
resetDataAndRunCalculation("onEventPreferenceChange", event) resetDataAndRunCalculation("onEventPreferenceChange", event)
} }
}, fabricPrivacy::logException) }, fabricPrivacy::logException)
// EventAppInitialized
disposable += rxBus
.toObservable(EventAppInitialized::class.java)
.observeOn(aapsSchedulers.io)
.subscribe(
{ event -> runCalculation("onEventAppInitialized", System.currentTimeMillis(), bgDataReload = true, limitDataToOldestAvailable = true, cause = event) },
fabricPrivacy::logException
)
// EventNewHistoryData // EventNewHistoryData
disposable += rxBus disposable += rxBus
.toObservable(EventNewHistoryData::class.java) .toObservable(EventNewHistoryData::class.java)
@ -147,10 +126,10 @@ class IobCobCalculatorPlugin @Inject constructor(
} }
private fun resetDataAndRunCalculation(reason: String, event: Event?) { private fun resetDataAndRunCalculation(reason: String, event: Event?) {
stopCalculation(reason) calculationWorkflow.stopCalculation(CalculationWorkflow.MAIN_CALCULATION,reason)
clearCache() clearCache()
ads.reset() ads.reset()
runCalculation(reason, System.currentTimeMillis(), bgDataReload = false, limitDataToOldestAvailable = true, cause = event) calculationWorkflow.runCalculation(CalculationWorkflow.MAIN_CALCULATION,this, overviewData, reason, System.currentTimeMillis(), bgDataReload = false, limitDataToOldestAvailable = true, cause = event, runLoop = true)
} }
override fun clearCache() { override fun clearCache() {
@ -310,11 +289,7 @@ class IobCobCalculatorPlugin @Inject constructor(
override fun getMealDataWithWaitingForCalculationFinish(): MealData { override fun getMealDataWithWaitingForCalculationFinish(): MealData {
val result = MealData() val result = MealData()
val now = System.currentTimeMillis() val now = System.currentTimeMillis()
val maxAbsorptionHours: Double = if (sensitivityAAPSPlugin.isEnabled() || sensitivityWeightedAveragePlugin.isEnabled()) { val maxAbsorptionHours: Double = activePlugin.activeSensitivity.maxAbsorptionHours()
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 - (maxAbsorptionHours * T.hours(1).msecs()).toLong() val absorptionTimeAgo = now - (maxAbsorptionHours * T.hours(1).msecs()).toLong()
repository.getCarbsDataFromTimeToTimeExpanded(absorptionTimeAgo + 1, now, true) repository.getCarbsDataFromTimeToTimeExpanded(absorptionTimeAgo + 1, now, true)
.blockingGet() .blockingGet()
@ -374,34 +349,6 @@ class IobCobCalculatorPlugin @Inject constructor(
return sb.toString() return sb.toString()
} }
fun stopCalculation(from: String) {
aapsLogger.debug(LTag.AUTOSENS, "Stopping calculation thread: $from")
WorkManager.getInstance(context).cancelUniqueWork(jobGroupName)
val workStatus = WorkManager.getInstance(context).getWorkInfosForUniqueWork(jobGroupName).get()
while (workStatus.size >= 1 && workStatus[0].state == WorkInfo.State.RUNNING)
SystemClock.sleep(100)
aapsLogger.debug(LTag.AUTOSENS, "Calculation thread stopped: $from")
}
fun runCalculation(from: String, end: Long, bgDataReload: Boolean, limitDataToOldestAvailable: Boolean, cause: Event?) {
aapsLogger.debug(LTag.AUTOSENS, "Starting calculation worker: $from to ${dateUtil.dateAndTimeAndSecondsString(end)}")
val iobCalculation =
if (sensitivityOref1Plugin.isEnabled())
OneTimeWorkRequest.Builder(IobCobOref1Worker::class.java)
.setInputData(
dataWorker.storeInputData(IobCobOref1Worker.IobCobOref1WorkerData(injector, this, from, end, bgDataReload, limitDataToOldestAvailable, cause))
).build()
else
OneTimeWorkRequest.Builder(IobCobOref1Worker::class.java)
.setInputData(
dataWorker.storeInputData(IobCobOrefWorker.IobCobOrefWorkerData(injector, this, from, end, bgDataReload, limitDataToOldestAvailable, cause))
).build()
WorkManager.getInstance(context)
.enqueueUniqueWork(jobGroupName, ExistingWorkPolicy.REPLACE, iobCalculation)
}
// Limit rate of EventNewHistoryData // Limit rate of EventNewHistoryData
private val historyWorker = Executors.newSingleThreadScheduledExecutor() private val historyWorker = Executors.newSingleThreadScheduledExecutor()
private var scheduledHistoryPost: ScheduledFuture<*>? = null private var scheduledHistoryPost: ScheduledFuture<*>? = null
@ -443,7 +390,7 @@ class IobCobCalculatorPlugin @Inject constructor(
// When historical data is changed (coming from NS etc) finished calculations after this date must be invalidated // When historical data is changed (coming from NS etc) finished calculations after this date must be invalidated
private fun newHistoryData(oldDataTimestamp: Long, bgDataReload: Boolean, event: Event) { private fun newHistoryData(oldDataTimestamp: Long, bgDataReload: Boolean, event: Event) {
//log.debug("Locking onNewHistoryData"); //log.debug("Locking onNewHistoryData");
stopCalculation("onEventNewHistoryData") calculationWorkflow.stopCalculation(CalculationWorkflow.MAIN_CALCULATION,"onEventNewHistoryData")
synchronized(dataLock) { synchronized(dataLock) {
// clear up 5 min back for proper COB calculation // clear up 5 min back for proper COB calculation
@ -467,7 +414,7 @@ class IobCobCalculatorPlugin @Inject constructor(
} }
ads.newHistoryData(time, aapsLogger, dateUtil) ads.newHistoryData(time, aapsLogger, dateUtil)
} }
runCalculation(event.javaClass.simpleName, System.currentTimeMillis(), bgDataReload, true, event) calculationWorkflow.runCalculation(CalculationWorkflow.MAIN_CALCULATION,this, overviewData, event.javaClass.simpleName, System.currentTimeMillis(), bgDataReload, true, event, runLoop = true)
//log.debug("Releasing onNewHistoryData"); //log.debug("Releasing onNewHistoryData");
} }

View file

@ -32,6 +32,7 @@ import info.nightscout.androidaps.utils.Profiler
import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.buildHelper.BuildHelper import info.nightscout.androidaps.utils.buildHelper.BuildHelper
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.workflow.CalculationWorkflow
import info.nightscout.shared.logging.AAPSLogger import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag import info.nightscout.shared.logging.LTag
import info.nightscout.shared.sharedPreferences.SP import info.nightscout.shared.sharedPreferences.SP
@ -62,6 +63,7 @@ class IobCobOref1Worker(
@Inject lateinit var dateUtil: DateUtil @Inject lateinit var dateUtil: DateUtil
@Inject lateinit var repository: AppRepository @Inject lateinit var repository: AppRepository
@Inject lateinit var dataWorker: DataWorker @Inject lateinit var dataWorker: DataWorker
@Inject lateinit var calculationWorkflow: CalculationWorkflow
init { init {
(context.applicationContext as HasAndroidInjector).androidInjector().inject(this) (context.applicationContext as HasAndroidInjector).androidInjector().inject(this)
@ -69,10 +71,9 @@ class IobCobOref1Worker(
class IobCobOref1WorkerData( class IobCobOref1WorkerData(
val injector: HasAndroidInjector, val injector: HasAndroidInjector,
val iobCobCalculatorPlugin: IobCobCalculator, // cannot be injected : HistoryBrowser uses different instance val iobCobCalculator: IobCobCalculator, // cannot be injected : HistoryBrowser uses different instance
val from: String, val from: String,
val end: Long, val end: Long,
val bgDataReload: Boolean,
val limitDataToOldestAvailable: Boolean, val limitDataToOldestAvailable: Boolean,
val cause: Event? val cause: Event?
) )
@ -90,13 +91,9 @@ class IobCobOref1Worker(
return Result.failure(workDataOf("Error" to "app still initializing")) return Result.failure(workDataOf("Error" to "app still initializing"))
} }
//log.debug("Locking calculateSensitivityData"); //log.debug("Locking calculateSensitivityData");
val oldestTimeWithData = data.iobCobCalculatorPlugin.calculateDetectionStart(data.end, data.limitDataToOldestAvailable) val oldestTimeWithData = data.iobCobCalculator.calculateDetectionStart(data.end, data.limitDataToOldestAvailable)
if (data.bgDataReload) {
data.iobCobCalculatorPlugin.ads.loadBgData(data.end, repository, aapsLogger, dateUtil, rxBus)
data.iobCobCalculatorPlugin.clearCache()
}
// work on local copy and set back when finished // work on local copy and set back when finished
val ads = data.iobCobCalculatorPlugin.ads.clone() val ads = data.iobCobCalculator.ads.clone()
val bucketedData = ads.bucketedData val bucketedData = ads.bucketedData
val autosensDataTable = ads.autosensDataTable val autosensDataTable = ads.autosensDataTable
if (bucketedData == null || bucketedData.size < 3) { if (bucketedData == null || bucketedData.size < 3) {
@ -108,8 +105,7 @@ class IobCobOref1Worker(
var previous = autosensDataTable[prevDataTime] var previous = autosensDataTable[prevDataTime]
// start from oldest to be able sub cob // start from oldest to be able sub cob
for (i in bucketedData.size - 4 downTo 0) { for (i in bucketedData.size - 4 downTo 0) {
val progress = i.toString() + if (buildHelper.isDev()) " (${data.from})" else "" rxBus.send(EventIobCalculationProgress(CalculationWorkflow.ProgressData.IOB_COB_OREF, 100 - (100.0 * i / bucketedData.size).toInt(), data.cause))
rxBus.send(EventIobCalculationProgress(100 - (100.0 * i / bucketedData.size).toInt(), data.cause))
if (isStopped) { if (isStopped) {
aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (trigger): ${data.from}") aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (trigger): ${data.from}")
return Result.failure(workDataOf("Error" to "Aborting calculation thread (trigger): ${data.from}")) return Result.failure(workDataOf("Error" to "Aborting calculation thread (trigger): ${data.from}"))
@ -145,7 +141,7 @@ class IobCobOref1Worker(
autosensData.bg = bg autosensData.bg = bg
delta = bg - bucketedData[i + 1].value delta = bg - bucketedData[i + 1].value
avgDelta = (bg - bucketedData[i + 3].value) / 3 avgDelta = (bg - bucketedData[i + 3].value) / 3
val iob = data.iobCobCalculatorPlugin.calculateFromTreatmentsAndTemps(bgTime, profile) val iob = data.iobCobCalculator.calculateFromTreatmentsAndTemps(bgTime, profile)
val bgi = -iob.activity * sens * 5 val bgi = -iob.activity * sens * 5
val deviation = delta - bgi val deviation = delta - bgi
val avgDeviation = ((avgDelta - bgi) * 1000).roundToLong() / 1000.0 val avgDeviation = ((avgDelta - bgi) * 1000).roundToLong() / 1000.0
@ -332,13 +328,13 @@ class IobCobOref1Worker(
autosensData.autosensResult = sensitivity autosensData.autosensResult = sensitivity
aapsLogger.debug(LTag.AUTOSENS, autosensData.toString()) aapsLogger.debug(LTag.AUTOSENS, autosensData.toString())
} }
data.iobCobCalculatorPlugin.ads = ads data.iobCobCalculator.ads = ads
Thread { Thread {
SystemClock.sleep(1000) SystemClock.sleep(1000)
rxBus.send(EventAutosensCalculationFinished(data.cause)) rxBus.send(EventAutosensCalculationFinished(data.cause))
}.start() }.start()
} finally { } finally {
rxBus.send(EventIobCalculationProgress(100, data.cause)) rxBus.send(EventIobCalculationProgress(CalculationWorkflow.ProgressData.IOB_COB_OREF, 100, data.cause))
aapsLogger.debug(LTag.AUTOSENS, "AUTOSENSDATA thread ended: ${data.from}") aapsLogger.debug(LTag.AUTOSENS, "AUTOSENSDATA thread ended: ${data.from}")
profiler.log(LTag.AUTOSENS, "IobCobOref1Thread", start) profiler.log(LTag.AUTOSENS, "IobCobOref1Thread", start)
} }

View file

@ -30,6 +30,7 @@ import info.nightscout.androidaps.utils.Profiler
import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.buildHelper.BuildHelper import info.nightscout.androidaps.utils.buildHelper.BuildHelper
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.workflow.CalculationWorkflow
import info.nightscout.shared.logging.AAPSLogger import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag import info.nightscout.shared.logging.LTag
import info.nightscout.shared.sharedPreferences.SP import info.nightscout.shared.sharedPreferences.SP
@ -69,7 +70,6 @@ class IobCobOrefWorker @Inject internal constructor(
val iobCobCalculatorPlugin: IobCobCalculator, // cannot be injected : HistoryBrowser uses different instance val iobCobCalculatorPlugin: IobCobCalculator, // cannot be injected : HistoryBrowser uses different instance
val from: String, val from: String,
val end: Long, val end: Long,
val bgDataReload: Boolean,
val limitDataToOldestAvailable: Boolean, val limitDataToOldestAvailable: Boolean,
val cause: Event? val cause: Event?
) )
@ -87,10 +87,6 @@ class IobCobOrefWorker @Inject internal constructor(
} }
//log.debug("Locking calculateSensitivityData"); //log.debug("Locking calculateSensitivityData");
val oldestTimeWithData = data.iobCobCalculatorPlugin.calculateDetectionStart(data.end, data.limitDataToOldestAvailable) val oldestTimeWithData = data.iobCobCalculatorPlugin.calculateDetectionStart(data.end, data.limitDataToOldestAvailable)
if (data.bgDataReload) {
data.iobCobCalculatorPlugin.ads.loadBgData(data.end, repository, aapsLogger, dateUtil, rxBus)
data.iobCobCalculatorPlugin.clearCache()
}
// work on local copy and set back when finished // work on local copy and set back when finished
val ads = data.iobCobCalculatorPlugin.ads.clone() val ads = data.iobCobCalculatorPlugin.ads.clone()
val bucketedData = ads.bucketedData val bucketedData = ads.bucketedData
@ -104,8 +100,7 @@ class IobCobOrefWorker @Inject internal constructor(
var previous = autosensDataTable[prevDataTime] var previous = autosensDataTable[prevDataTime]
// start from oldest to be able sub cob // start from oldest to be able sub cob
for (i in bucketedData.size - 4 downTo 0) { for (i in bucketedData.size - 4 downTo 0) {
val progress = i.toString() + if (buildHelper.isDev()) " (${data.from})" else "" rxBus.send(EventIobCalculationProgress(CalculationWorkflow.ProgressData.IOB_COB_OREF, 100 - (100.0 * i / bucketedData.size).toInt(), data.cause))
rxBus.send(EventIobCalculationProgress(100 - (100.0 * i / bucketedData.size).toInt(), data.cause))
if (isStopped) { if (isStopped) {
aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (trigger): ${data.from}") aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (trigger): ${data.from}")
return Result.failure(workDataOf("Error" to "Aborting calculation thread (trigger): ${data.from}")) return Result.failure(workDataOf("Error" to "Aborting calculation thread (trigger): ${data.from}"))
@ -282,7 +277,7 @@ class IobCobOrefWorker @Inject internal constructor(
rxBus.send(EventAutosensCalculationFinished(data.cause)) rxBus.send(EventAutosensCalculationFinished(data.cause))
}.start() }.start()
} finally { } finally {
rxBus.send(EventIobCalculationProgress(100, data.cause)) rxBus.send(EventIobCalculationProgress(CalculationWorkflow.ProgressData.IOB_COB_OREF, 100, data.cause))
aapsLogger.debug(LTag.AUTOSENS, "AUTOSENSDATA thread ended: ${data.from}") aapsLogger.debug(LTag.AUTOSENS, "AUTOSENSDATA thread ended: ${data.from}")
profiler.log(LTag.AUTOSENS, "IobCobThread", start) profiler.log(LTag.AUTOSENS, "IobCobThread", start)
} }

View file

@ -1,5 +1,6 @@
package info.nightscout.androidaps.plugins.iob.iobCobCalculator.events package info.nightscout.androidaps.plugins.iob.iobCobCalculator.events
import info.nightscout.androidaps.events.Event import info.nightscout.androidaps.events.Event
import info.nightscout.androidaps.workflow.CalculationWorkflow
class EventIobCalculationProgress(val progressPct: Int, val cause: Event?) : Event() class EventIobCalculationProgress(val pass: CalculationWorkflow.ProgressData, val progressPct: Int, val cause: Event?) : Event()

View file

@ -137,6 +137,8 @@ class SensitivityAAPSPlugin @Inject constructor(
return output return output
} }
override fun maxAbsorptionHours(): Double = sp.getDouble(R.string.key_absorption_maxtime, Constants.DEFAULT_MAX_ABSORPTION_TIME)
override val id: SensitivityType override val id: SensitivityType
get() = SensitivityType.SENSITIVITY_AAPS get() = SensitivityType.SENSITIVITY_AAPS

View file

@ -204,6 +204,8 @@ class SensitivityOref1Plugin @Inject constructor(
return output return output
} }
override fun maxAbsorptionHours(): Double = sp.getDouble(R.string.key_absorption_cutoff, Constants.DEFAULT_MAX_ABSORPTION_TIME)
override fun configuration(): JSONObject { override fun configuration(): JSONObject {
val c = JSONObject() val c = JSONObject()
try { try {

View file

@ -157,6 +157,8 @@ class SensitivityWeightedAveragePlugin @Inject constructor(
return output return output
} }
override fun maxAbsorptionHours(): Double = sp.getDouble(R.string.key_absorption_maxtime, Constants.DEFAULT_MAX_ABSORPTION_TIME)
override val id: SensitivityType override val id: SensitivityType
get() = SensitivityType.SENSITIVITY_WEIGHTED get() = SensitivityType.SENSITIVITY_WEIGHTED

View file

@ -0,0 +1,270 @@
package info.nightscout.androidaps.workflow
import android.content.Context
import android.os.SystemClock
import androidx.work.*
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.events.Event
import info.nightscout.androidaps.events.EventAppInitialized
import info.nightscout.androidaps.events.EventOfflineChange
import info.nightscout.androidaps.events.EventPreferenceChange
import info.nightscout.androidaps.events.EventTherapyEventChange
import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.IobCobCalculator
import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.general.overview.OverviewData
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobOref1Worker
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobOrefWorker
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventNewHistoryData
import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref1Plugin
import info.nightscout.androidaps.receivers.DataWorker
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.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class CalculationWorkflow @Inject constructor(
aapsSchedulers: AapsSchedulers,
rh: ResourceHelper,
rxBus: RxBus,
private val context: Context,
private val injector: HasAndroidInjector,
private val aapsLogger: AAPSLogger,
private val fabricPrivacy: FabricPrivacy,
private val dateUtil: DateUtil,
private val sensitivityOref1Plugin: SensitivityOref1Plugin,
private val dataWorker: DataWorker,
private val activePlugin: ActivePlugin
) {
companion object {
const val MAIN_CALCULATION = "calculation"
const val HISTORY_CALCULATION = "history_calculation"
const val JOB = "job"
}
private var disposable: CompositeDisposable = CompositeDisposable()
private val iobCobCalculator: IobCobCalculator
get() = activePlugin.activeIobCobCalculator // cross-dependency CalculationWorkflow x IobCobCalculator
private val overviewData: OverviewData
get() = (iobCobCalculator as IobCobCalculatorPlugin).overviewData
enum class ProgressData(val pass: Int, val percentOfTotal: Int) {
PREPARE_BASAL_DATA(0, 5),
PREPARE_TEMPORARY_TARGET_DATA(1, 5),
PREPARE_TREATMENTS_DATA(2, 5),
IOB_COB_OREF(3, 75),
PREPARE_IOB_AUTOSENS_DATA(4, 10);
fun finalPercent(progress: Int): Int {
var total = 0
for (i in values()) if (i.pass < pass) total += i.percentOfTotal
total += (percentOfTotal.toDouble() * progress / 100.0).toInt()
return total
}
}
init {
// Verify definition
var sumPercent = 0
for (pass in ProgressData.values()) sumPercent += pass.percentOfTotal
require(sumPercent == 100)
disposable += rxBus
.toObservable(EventTherapyEventChange::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ runOnEventTherapyEventChange() }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventOfflineChange::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ runOnEventTherapyEventChange() }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventPreferenceChange::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ event ->
if (event.isChanged(rh, R.string.key_units)) {
overviewData.reset()
rxBus.send(EventNewHistoryData(0, false))
}
if (event.isChanged(rh, R.string.key_rangetodisplay)) {
overviewData.initRange()
runOnScaleChanged()
rxBus.send(EventNewHistoryData(0, false))
}
}, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventAppInitialized::class.java)
.observeOn(aapsSchedulers.io)
.subscribe(
{
runCalculation(
MAIN_CALCULATION,
iobCobCalculator,
overviewData,
"onEventAppInitialized",
System.currentTimeMillis(),
bgDataReload = true,
limitDataToOldestAvailable = true,
cause = it,
runLoop = true
)
},
fabricPrivacy::logException
)
}
fun stopCalculation(job: String, from: String) {
aapsLogger.debug(LTag.AUTOSENS, "Stopping calculation thread: $from")
WorkManager.getInstance(context).cancelUniqueWork(job)
val workStatus = WorkManager.getInstance(context).getWorkInfosForUniqueWork(job).get()
while (workStatus.size >= 1 && workStatus[0].state == WorkInfo.State.RUNNING)
SystemClock.sleep(100)
aapsLogger.debug(LTag.AUTOSENS, "Calculation thread stopped: $from")
}
fun runCalculation(
job: String,
iobCobCalculator: IobCobCalculator,
overviewData: OverviewData,
from: String,
end: Long,
bgDataReload: Boolean,
limitDataToOldestAvailable: Boolean,
cause: Event?,
runLoop: Boolean
) {
aapsLogger.debug(LTag.AUTOSENS, "Starting calculation worker: $from to ${dateUtil.dateAndTimeAndSecondsString(end)}")
WorkManager.getInstance(context)
.beginUniqueWork(
job, ExistingWorkPolicy.REPLACE,
if (bgDataReload) OneTimeWorkRequest.Builder(LoadBgDataWorker::class.java).setInputData(dataWorker.storeInputData(LoadBgDataWorker.LoadBgData(iobCobCalculator, end))).build()
else OneTimeWorkRequest.Builder(DummyWorker::class.java).build()
)
.then(
OneTimeWorkRequest.Builder(PrepareBucketedDataWorker::class.java)
.setInputData(dataWorker.storeInputData(PrepareBucketedDataWorker.PrepareBucketedData(iobCobCalculator, overviewData)))
.build()
)
.then(
OneTimeWorkRequest.Builder(PrepareBgDataWorker::class.java)
.setInputData(dataWorker.storeInputData(PrepareBgDataWorker.PrepareBgData(iobCobCalculator, overviewData)))
.build()
)
.then(
OneTimeWorkRequest.Builder(UpdateGraphWorker::class.java)
.setInputData(Data.Builder().putString(JOB, job).build())
.build()
)
.then(
OneTimeWorkRequest.Builder(PrepareTreatmentsDataWorker::class.java)
.setInputData(dataWorker.storeInputData(PrepareTreatmentsDataWorker.PrepareTreatmentsData(overviewData)))
.build()
)
.then(
OneTimeWorkRequest.Builder(PrepareBasalDataWorker::class.java)
.setInputData(dataWorker.storeInputData(PrepareBasalDataWorker.PrepareBasalData(iobCobCalculator, overviewData)))
.build()
)
.then(
OneTimeWorkRequest.Builder(PrepareTemporaryTargetDataWorker::class.java)
.setInputData(dataWorker.storeInputData(PrepareTemporaryTargetDataWorker.PrepareTemporaryTargetData(overviewData)))
.build()
)
.then(
OneTimeWorkRequest.Builder(UpdateGraphWorker::class.java)
.setInputData(Data.Builder().putString(JOB, job).build())
.build()
)
.then(
if (sensitivityOref1Plugin.isEnabled())
OneTimeWorkRequest.Builder(IobCobOref1Worker::class.java)
.setInputData(dataWorker.storeInputData(IobCobOref1Worker.IobCobOref1WorkerData(injector, iobCobCalculator, from, end, limitDataToOldestAvailable, cause)))
.build()
else
OneTimeWorkRequest.Builder(IobCobOrefWorker::class.java)
.setInputData(dataWorker.storeInputData(IobCobOrefWorker.IobCobOrefWorkerData(injector, iobCobCalculator, from, end, limitDataToOldestAvailable, cause)))
.build()
)
.then(OneTimeWorkRequest.Builder(UpdateIobCobSensWorker::class.java).build())
.then(
OneTimeWorkRequest.Builder(PrepareIobAutosensGraphDataWorker::class.java)
.setInputData(dataWorker.storeInputData(PrepareIobAutosensGraphDataWorker.PrepareIobAutosensData(iobCobCalculator, overviewData)))
.build()
)
.then(
OneTimeWorkRequest.Builder(UpdateGraphWorker::class.java)
.setInputData(Data.Builder().putString(JOB, job).build())
.build()
)
.then(
runLoop,
OneTimeWorkRequest.Builder(InvokeLoopWorker::class.java)
.setInputData(dataWorker.storeInputData(InvokeLoopWorker.InvokeLoopData(cause)))
.build()
)
.then(
runLoop,
OneTimeWorkRequest.Builder(PreparePredictionsWorker::class.java)
.setInputData(dataWorker.storeInputData(PreparePredictionsWorker.PreparePredictionsData(overviewData)))
.build()
)
.then(
runLoop, OneTimeWorkRequest.Builder(UpdateGraphWorker::class.java)
.setInputData(Data.Builder().putString(JOB, job).build())
.build()
)
.enqueue()
}
fun WorkContinuation.then(shouldAdd: Boolean, work: OneTimeWorkRequest): WorkContinuation =
if (shouldAdd) then(work) else this
private fun runOnEventTherapyEventChange() {
WorkManager.getInstance(context)
.beginUniqueWork(
MAIN_CALCULATION, ExistingWorkPolicy.APPEND,
OneTimeWorkRequest.Builder(PrepareTreatmentsDataWorker::class.java)
.setInputData(dataWorker.storeInputData(PrepareTreatmentsDataWorker.PrepareTreatmentsData(overviewData)))
.build()
)
.then(
OneTimeWorkRequest.Builder(UpdateGraphWorker::class.java)
.build()
)
.enqueue()
}
private fun runOnScaleChanged() {
WorkManager.getInstance(context)
.beginUniqueWork(
MAIN_CALCULATION, ExistingWorkPolicy.APPEND,
OneTimeWorkRequest.Builder(PrepareBucketedDataWorker::class.java)
.setInputData(dataWorker.storeInputData(PrepareBucketedDataWorker.PrepareBucketedData(iobCobCalculator, overviewData)))
.build()
)
.then(
OneTimeWorkRequest.Builder(PrepareBgDataWorker::class.java)
.setInputData(dataWorker.storeInputData(PrepareBgDataWorker.PrepareBgData(iobCobCalculator, overviewData)))
.build()
)
.then(
OneTimeWorkRequest.Builder(UpdateGraphWorker::class.java)
.build()
)
.enqueue()
}
}

View file

@ -0,0 +1,13 @@
package info.nightscout.androidaps.workflow
import android.content.Context
import androidx.work.Worker
import androidx.work.WorkerParameters
class DummyWorker(
context: Context,
params: WorkerParameters
) : Worker(context, params) {
override fun doWork(): Result = Result.success()
}

View file

@ -0,0 +1,51 @@
package info.nightscout.androidaps.workflow
import android.content.Context
import androidx.work.Worker
import androidx.work.WorkerParameters
import androidx.work.workDataOf
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.events.Event
import info.nightscout.androidaps.events.EventNewBG
import info.nightscout.androidaps.interfaces.IobCobCalculator
import info.nightscout.androidaps.interfaces.Loop
import info.nightscout.androidaps.receivers.DataWorker
import javax.inject.Inject
class InvokeLoopWorker(
context: Context,
params: WorkerParameters
) : Worker(context, params) {
@Inject lateinit var dataWorker: DataWorker
@Inject lateinit var iobCobCalculator: IobCobCalculator
@Inject lateinit var loop: Loop
init {
(context.applicationContext as HasAndroidInjector).androidInjector().inject(this)
}
class InvokeLoopData(
val cause: Event?
)
/*
This method is triggered once autosens calculation has completed, so the LoopPlugin
has current data to work with. However, autosens calculation can be triggered by multiple
sources and currently only a new BG should trigger a loop run. Hence we return early if
the event causing the calculation is not EventNewBg.
<p>
*/
override fun doWork(): Result {
val data = dataWorker.pickupObject(inputData.getLong(DataWorker.STORE_KEY, -1)) as InvokeLoopData?
?: return Result.failure(workDataOf("Error" to "missing input data"))
if (data.cause !is EventNewBG) return Result.success(workDataOf("Result" to "no calculation needed"))
val glucoseValue = iobCobCalculator.ads.actualBg() ?: return Result.success(workDataOf("Result" to "bg outdated"))
if (glucoseValue.timestamp <= loop.lastBgTriggeredRun) return Result.success(workDataOf("Result" to "already looped with that value"))
loop.lastBgTriggeredRun = glucoseValue.timestamp
loop.invoke("Calculation for $glucoseValue", true)
return Result.success()
}
}

View file

@ -0,0 +1,45 @@
package info.nightscout.androidaps.workflow
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.IobCobCalculator
import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.receivers.DataWorker
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.shared.logging.AAPSLogger
import javax.inject.Inject
class LoadBgDataWorker(
context: Context,
params: WorkerParameters
) : Worker(context, params) {
@Inject lateinit var dataWorker: DataWorker
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var rxBus: RxBus
@Inject lateinit var repository: AppRepository
init {
(context.applicationContext as HasAndroidInjector).androidInjector().inject(this)
}
class LoadBgData(
val iobCobCalculator: IobCobCalculator,
val end: Long
)
override fun doWork(): Result {
val data = dataWorker.pickupObject(inputData.getLong(DataWorker.STORE_KEY, -1)) as LoadBgData?
?: return Result.failure(workDataOf("Error" to "missing input data"))
data.iobCobCalculator.ads.loadBgData(data.end, repository, aapsLogger, dateUtil, rxBus)
data.iobCobCalculator.clearCache()
return Result.success()
}
}

View file

@ -0,0 +1,145 @@
package info.nightscout.androidaps.workflow
import android.content.Context
import android.graphics.DashPathEffect
import android.graphics.Paint
import androidx.work.Worker
import androidx.work.WorkerParameters
import androidx.work.workDataOf
import com.jjoe64.graphview.series.LineGraphSeries
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.interfaces.IobCobCalculator
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.general.overview.OverviewData
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.ScaledDataPoint
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventIobCalculationProgress
import info.nightscout.androidaps.receivers.DataWorker
import info.nightscout.androidaps.utils.resources.ResourceHelper
import java.util.ArrayList
import javax.inject.Inject
import kotlin.math.max
class PrepareBasalDataWorker(
context: Context,
params: WorkerParameters
) : Worker(context, params) {
@Inject lateinit var dataWorker: DataWorker
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var rh: ResourceHelper
@Inject lateinit var rxBus: RxBus
init {
(context.applicationContext as HasAndroidInjector).androidInjector().inject(this)
}
class PrepareBasalData(
val iobCobCalculator: IobCobCalculator, // cannot be injected : HistoryBrowser uses different instance
val overviewData: OverviewData
)
override fun doWork(): Result {
val data = dataWorker.pickupObject(inputData.getLong(DataWorker.STORE_KEY, -1)) as PrepareBasalData?
?: return Result.failure(workDataOf("Error" to "missing input data"))
rxBus.send(EventIobCalculationProgress(CalculationWorkflow.ProgressData.PREPARE_BASAL_DATA, 0, null))
data.overviewData.maxBasalValueFound = 0.0
val baseBasalArray: MutableList<ScaledDataPoint> = ArrayList()
val tempBasalArray: MutableList<ScaledDataPoint> = ArrayList()
val basalLineArray: MutableList<ScaledDataPoint> = ArrayList()
val absoluteBasalLineArray: MutableList<ScaledDataPoint> = ArrayList()
var lastLineBasal = 0.0
var lastAbsoluteLineBasal = -1.0
var lastBaseBasal = 0.0
var lastTempBasal = 0.0
var time = data.overviewData.fromTime
while (time < data.overviewData.toTime) {
val progress = (time - data.overviewData.fromTime).toDouble() / (data.overviewData.toTime - data.overviewData.fromTime) * 100.0
rxBus.send(EventIobCalculationProgress(CalculationWorkflow.ProgressData.PREPARE_BASAL_DATA, progress.toInt(), null))
val profile = profileFunction.getProfile(time)
if (profile == null) {
time += 60 * 1000L
continue
}
val basalData = data.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, data.overviewData.basalScale))
tempBasalArray.add(ScaledDataPoint(time, tempBasalValue.also { basal = it }, data.overviewData.basalScale))
}
if (lastBaseBasal != 0.0) {
baseBasalArray.add(ScaledDataPoint(time, lastBaseBasal, data.overviewData.basalScale))
baseBasalArray.add(ScaledDataPoint(time, 0.0, data.overviewData.basalScale))
lastBaseBasal = 0.0
}
} else {
if (baseBasalValue != lastBaseBasal) {
baseBasalArray.add(ScaledDataPoint(time, lastBaseBasal, data.overviewData.basalScale))
baseBasalArray.add(ScaledDataPoint(time, baseBasalValue.also { basal = it }, data.overviewData.basalScale))
lastBaseBasal = baseBasalValue
}
if (lastTempBasal != 0.0) {
tempBasalArray.add(ScaledDataPoint(time, lastTempBasal, data.overviewData.basalScale))
tempBasalArray.add(ScaledDataPoint(time, 0.0, data.overviewData.basalScale))
}
}
if (baseBasalValue != lastLineBasal) {
basalLineArray.add(ScaledDataPoint(time, lastLineBasal, data.overviewData.basalScale))
basalLineArray.add(ScaledDataPoint(time, baseBasalValue, data.overviewData.basalScale))
}
if (absoluteLineValue != lastAbsoluteLineBasal) {
absoluteBasalLineArray.add(ScaledDataPoint(time, lastAbsoluteLineBasal, data.overviewData.basalScale))
absoluteBasalLineArray.add(ScaledDataPoint(time, basal, data.overviewData.basalScale))
}
lastAbsoluteLineBasal = absoluteLineValue
lastLineBasal = baseBasalValue
lastTempBasal = tempBasalValue
data.overviewData.maxBasalValueFound = max(data.overviewData.maxBasalValueFound, max(tempBasalValue, baseBasalValue))
time += 60 * 1000L
}
// final points
basalLineArray.add(ScaledDataPoint(data.overviewData.toTime, lastLineBasal, data.overviewData.basalScale))
baseBasalArray.add(ScaledDataPoint(data.overviewData.toTime, lastBaseBasal, data.overviewData.basalScale))
tempBasalArray.add(ScaledDataPoint(data.overviewData.toTime, lastTempBasal, data.overviewData.basalScale))
absoluteBasalLineArray.add(ScaledDataPoint(data.overviewData.toTime, lastAbsoluteLineBasal, data.overviewData.basalScale))
// create series
data.overviewData.baseBasalGraphSeries = LineGraphSeries(Array(baseBasalArray.size) { i -> baseBasalArray[i] }).also {
it.isDrawBackground = true
it.backgroundColor = rh.gc(R.color.basebasal)
it.thickness = 0
}
data.overviewData.tempBasalGraphSeries = LineGraphSeries(Array(tempBasalArray.size) { i -> tempBasalArray[i] }).also {
it.isDrawBackground = true
it.backgroundColor = rh.gc(R.color.tempbasal)
it.thickness = 0
}
data.overviewData.basalLineGraphSeries = LineGraphSeries(Array(basalLineArray.size) { i -> basalLineArray[i] }).also {
it.setCustomPaint(Paint().also { paint ->
paint.style = Paint.Style.STROKE
paint.strokeWidth = rh.getDisplayMetrics().scaledDensity * 2
paint.pathEffect = DashPathEffect(floatArrayOf(2f, 4f), 0f)
paint.color = rh.gc(R.color.basal)
})
}
data.overviewData.absoluteBasalGraphSeries = LineGraphSeries(Array(absoluteBasalLineArray.size) { i -> absoluteBasalLineArray[i] }).also {
it.setCustomPaint(Paint().also { absolutePaint ->
absolutePaint.style = Paint.Style.STROKE
absolutePaint.strokeWidth = rh.getDisplayMetrics().scaledDensity * 2
absolutePaint.color = rh.gc(R.color.basal)
})
}
rxBus.send(EventIobCalculationProgress(CalculationWorkflow.ProgressData.PREPARE_BASAL_DATA, 100, null))
return Result.success()
}
}

View file

@ -0,0 +1,67 @@
package info.nightscout.androidaps.workflow
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.GlucoseUnit
import info.nightscout.androidaps.interfaces.IobCobCalculator
import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.plugins.general.overview.OverviewData
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.DataPointWithLabelInterface
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.GlucoseValueDataPoint
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.PointsWithLabelGraphSeries
import info.nightscout.androidaps.receivers.DataWorker
import info.nightscout.androidaps.utils.DefaultValueHelper
import info.nightscout.androidaps.utils.Round
import info.nightscout.androidaps.utils.resources.ResourceHelper
import java.util.ArrayList
import javax.inject.Inject
class PrepareBgDataWorker(
context: Context,
params: WorkerParameters
) : Worker(context, params) {
@Inject lateinit var dataWorker: DataWorker
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var rh: ResourceHelper
@Inject lateinit var defaultValueHelper: DefaultValueHelper
@Inject lateinit var repository: AppRepository
init {
(context.applicationContext as HasAndroidInjector).androidInjector().inject(this)
}
class PrepareBgData(
val iobCobCalculator: IobCobCalculator, // cannot be injected : HistoryBrowser uses different instance
val overviewData: OverviewData
)
override fun doWork(): Result {
val data = dataWorker.pickupObject(inputData.getLong(DataWorker.STORE_KEY, -1)) as PrepareBgData?
?: return Result.failure(workDataOf("Error" to "missing input data"))
data.overviewData.maxBgValue = Double.MIN_VALUE
data.overviewData.bgReadingsArray = repository.compatGetBgReadingsDataFromTime(data.overviewData.fromTime, data.overviewData.toTime, false).blockingGet()
val bgListArray: MutableList<DataPointWithLabelInterface> = ArrayList()
for (bg in data.overviewData.bgReadingsArray) {
if (bg.timestamp < data.overviewData.fromTime || bg.timestamp > data.overviewData.toTime) continue
if (bg.value > data.overviewData.maxBgValue) data.overviewData.maxBgValue = bg.value
bgListArray.add(GlucoseValueDataPoint(bg, defaultValueHelper, profileFunction, rh))
}
bgListArray.sortWith { o1: DataPointWithLabelInterface, o2: DataPointWithLabelInterface -> o1.x.compareTo(o2.x) }
data.overviewData.bgReadingGraphSeries = PointsWithLabelGraphSeries(Array(bgListArray.size) { i -> bgListArray[i] })
data.overviewData.maxBgValue = Profile.fromMgdlToUnits(data.overviewData.maxBgValue, profileFunction.getUnits())
if (defaultValueHelper.determineHighLine() > data.overviewData.maxBgValue) data.overviewData.maxBgValue = defaultValueHelper.determineHighLine()
data.overviewData.maxBgValue = addUpperChartMargin(data.overviewData.maxBgValue)
return Result.success()
}
private fun addUpperChartMargin(maxBgValue: Double) =
if (profileFunction.getUnits() == GlucoseUnit.MGDL) Round.roundTo(maxBgValue, 40.0) + 80 else Round.roundTo(maxBgValue, 2.0) + 4
}

View file

@ -0,0 +1,57 @@
package info.nightscout.androidaps.workflow
import android.content.Context
import androidx.work.Worker
import androidx.work.WorkerParameters
import androidx.work.workDataOf
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.interfaces.IobCobCalculator
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.plugins.general.overview.OverviewData
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.DataPointWithLabelInterface
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.InMemoryGlucoseValueDataPoint
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.PointsWithLabelGraphSeries
import info.nightscout.androidaps.receivers.DataWorker
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.shared.logging.AAPSLogger
import javax.inject.Inject
class PrepareBucketedDataWorker(
context: Context,
params: WorkerParameters
) : Worker(context, params) {
@Inject lateinit var dataWorker: DataWorker
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var rh: ResourceHelper
@Inject lateinit var aapsLogger: AAPSLogger
init {
(context.applicationContext as HasAndroidInjector).androidInjector().inject(this)
}
class PrepareBucketedData(
val iobCobCalculator: IobCobCalculator, // cannot be injected : HistoryBrowser uses different instance
val overviewData: OverviewData
)
override fun doWork(): Result {
val data = dataWorker.pickupObject(inputData.getLong(DataWorker.STORE_KEY, -1)) as PrepareBucketedData?
?: return Result.failure(workDataOf("Error" to "missing input data"))
val bucketedData = data.iobCobCalculator.ads.getBucketedDataTableCopy() ?: return Result.success()
if (bucketedData.isEmpty()) {
aapsLogger.debug("No bucketed data.")
return Result.success()
}
val bucketedListArray: MutableList<DataPointWithLabelInterface> = ArrayList()
for (inMemoryGlucoseValue in bucketedData) {
if (inMemoryGlucoseValue.timestamp < data.overviewData.fromTime || inMemoryGlucoseValue.timestamp > data.overviewData.toTime) continue
bucketedListArray.add(InMemoryGlucoseValueDataPoint(inMemoryGlucoseValue, profileFunction, rh))
}
bucketedListArray.sortWith { o1: DataPointWithLabelInterface, o2: DataPointWithLabelInterface -> o1.x.compareTo(o2.x) }
data.overviewData.bucketedGraphSeries = PointsWithLabelGraphSeries(Array(bucketedListArray.size) { i -> bucketedListArray[i] })
return Result.success()
}
}

View file

@ -0,0 +1,284 @@
package info.nightscout.androidaps.workflow
import android.content.Context
import android.graphics.DashPathEffect
import android.graphics.Paint
import androidx.work.Worker
import androidx.work.WorkerParameters
import androidx.work.workDataOf
import com.jjoe64.graphview.series.BarGraphSeries
import com.jjoe64.graphview.series.LineGraphSeries
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.IobTotal
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.ValueWrapper
import info.nightscout.androidaps.interfaces.IobCobCalculator
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.plugins.aps.openAPSSMB.SMBDefaults
import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.general.overview.OverviewData
import info.nightscout.androidaps.plugins.general.overview.OverviewMenus
import info.nightscout.androidaps.plugins.general.overview.OverviewPlugin
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.DataPointWithLabelInterface
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.FixedLineGraphSeries
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.PointsWithLabelGraphSeries
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.ScaledDataPoint
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventIobCalculationProgress
import info.nightscout.androidaps.receivers.DataWorker
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.DecimalFormatter
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import java.util.ArrayList
import javax.inject.Inject
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
class PrepareIobAutosensGraphDataWorker(
context: Context,
params: WorkerParameters
) : Worker(context, params) {
@Inject lateinit var dataWorker: DataWorker
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var rh: ResourceHelper
@Inject lateinit var overviewMenus: OverviewMenus
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var repository: AppRepository
@Inject lateinit var rxBus: RxBus
init {
(context.applicationContext as HasAndroidInjector).androidInjector().inject(this)
}
class PrepareIobAutosensData(
val iobCobCalculator: IobCobCalculator, // cannot be injected : HistoryBrowser uses different instance
val overviewData: OverviewData
)
override fun doWork(): Result {
val data = dataWorker.pickupObject(inputData.getLong(DataWorker.STORE_KEY, -1)) as PrepareIobAutosensData?
?: return Result.failure(workDataOf("Error" to "missing input data"))
rxBus.send(EventIobCalculationProgress(CalculationWorkflow.ProgressData.PREPARE_IOB_AUTOSENS_DATA, 0, null))
val iobArray: MutableList<ScaledDataPoint> = ArrayList()
val absIobArray: MutableList<ScaledDataPoint> = ArrayList()
data.overviewData.maxIobValueFound = Double.MIN_VALUE
var lastIob = 0.0
var absLastIob = 0.0
var time = data.overviewData.fromTime
val minFailOverActiveList: MutableList<DataPointWithLabelInterface> = ArrayList()
val cobArray: MutableList<ScaledDataPoint> = ArrayList()
data.overviewData.maxCobValueFound = Double.MIN_VALUE
var lastCob = 0
val actArrayHist: MutableList<ScaledDataPoint> = ArrayList()
val actArrayPrediction: MutableList<ScaledDataPoint> = ArrayList()
val now = dateUtil.now().toDouble()
data.overviewData.maxIAValue = 0.0
val bgiArrayHist: MutableList<ScaledDataPoint> = ArrayList()
val bgiArrayPrediction: MutableList<ScaledDataPoint> = ArrayList()
data.overviewData.maxBGIValue = Double.MIN_VALUE
val devArray: MutableList<OverviewPlugin.DeviationDataPoint> = ArrayList()
data.overviewData.maxDevValueFound = Double.MIN_VALUE
val ratioArray: MutableList<ScaledDataPoint> = ArrayList()
data.overviewData.maxRatioValueFound = 5.0 //even if sens data equals 0 for all the period, minimum scale is between 95% and 105%
data.overviewData.minRatioValueFound = -5.0
val dsMaxArray: MutableList<ScaledDataPoint> = ArrayList()
val dsMinArray: MutableList<ScaledDataPoint> = ArrayList()
data.overviewData.maxFromMaxValueFound = Double.MIN_VALUE
data.overviewData.maxFromMinValueFound = Double.MIN_VALUE
val adsData = data.iobCobCalculator.ads.clone()
while (time <= data.overviewData.toTime) {
val progress = (time - data.overviewData.fromTime).toDouble() / (data.overviewData.toTime - data.overviewData.fromTime) * 100.0
rxBus.send(EventIobCalculationProgress(CalculationWorkflow.ProgressData.PREPARE_IOB_AUTOSENS_DATA, progress.toInt(), null))
val profile = profileFunction.getProfile(time)
if (profile == null) {
time += 5 * 60 * 1000L
continue
}
// IOB
val iob = data.iobCobCalculator.calculateFromTreatmentsAndTemps(time, profile)
val baseBasalIob = data.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, data.overviewData.iobScale))
iobArray.add(ScaledDataPoint(time, iob.iob, data.overviewData.iobScale))
data.overviewData.maxIobValueFound = maxOf(data.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, data.overviewData.iobScale))
absIobArray.add(ScaledDataPoint(time, absIob.iob, data.overviewData.iobScale))
data.overviewData.maxIobValueFound = maxOf(data.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(), data.overviewData.cobScale))
cobArray.add(ScaledDataPoint(time, cob.toDouble(), data.overviewData.cobScale))
data.overviewData.maxCobValueFound = max(data.overviewData.maxCobValueFound, cob.toDouble())
lastCob = cob
}
if (autosensData.failOverToMinAbsorptionRate) {
autosensData.scale = data.overviewData.cobScale
autosensData.chartTime = time
minFailOverActiveList.add(autosensData)
}
}
// ACTIVITY
if (time <= now) actArrayHist.add(ScaledDataPoint(time, iob.activity, data.overviewData.actScale))
else actArrayPrediction.add(ScaledDataPoint(time, iob.activity, data.overviewData.actScale))
data.overviewData.maxIAValue = max(data.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, data.overviewData.bgiScale))
else bgiArrayPrediction.add(ScaledDataPoint(time, bgi, data.overviewData.bgiScale))
data.overviewData.maxBGIValue = max(data.overviewData.maxBGIValue, max(abs(bgi), deviation))
// DEVIATIONS
if (autosensData != null) {
var color = rh.gc(R.color.deviationblack) // "="
if (autosensData.type == "" || autosensData.type == "non-meal") {
if (autosensData.pastSensitivity == "C") color = rh.gc(R.color.deviationgrey)
if (autosensData.pastSensitivity == "+") color = rh.gc(R.color.deviationgreen)
if (autosensData.pastSensitivity == "-") color = rh.gc(R.color.deviationred)
} else if (autosensData.type == "uam") {
color = rh.gc(R.color.uam)
} else if (autosensData.type == "csf") {
color = rh.gc(R.color.deviationgrey)
}
devArray.add(OverviewPlugin.DeviationDataPoint(time.toDouble(), autosensData.deviation, color, data.overviewData.devScale))
data.overviewData.maxDevValueFound = maxOf(data.overviewData.maxDevValueFound, abs(autosensData.deviation), abs(bgi))
}
// RATIO
if (autosensData != null) {
ratioArray.add(ScaledDataPoint(time, 100.0 * (autosensData.autosensResult.ratio - 1), data.overviewData.ratioScale))
data.overviewData.maxRatioValueFound = max(data.overviewData.maxRatioValueFound, 100.0 * (autosensData.autosensResult.ratio - 1))
data.overviewData.minRatioValueFound = min(data.overviewData.minRatioValueFound, 100.0 * (autosensData.autosensResult.ratio - 1))
}
// DEV SLOPE
if (autosensData != null) {
dsMaxArray.add(ScaledDataPoint(time, autosensData.slopeFromMaxDeviation, data.overviewData.dsMaxScale))
dsMinArray.add(ScaledDataPoint(time, autosensData.slopeFromMinDeviation, data.overviewData.dsMinScale))
data.overviewData.maxFromMaxValueFound = max(data.overviewData.maxFromMaxValueFound, abs(autosensData.slopeFromMaxDeviation))
data.overviewData.maxFromMinValueFound = max(data.overviewData.maxFromMinValueFound, abs(autosensData.slopeFromMinDeviation))
}
time += 5 * 60 * 1000L
}
// IOB
data.overviewData.iobSeries = FixedLineGraphSeries(Array(iobArray.size) { i -> iobArray[i] }).also {
it.isDrawBackground = true
it.backgroundColor = -0x7f000001 and rh.gc(R.color.iob) //50%
it.color = rh.gc(R.color.iob)
it.thickness = 3
}
data.overviewData.absIobSeries = FixedLineGraphSeries(Array(absIobArray.size) { i -> absIobArray[i] }).also {
it.isDrawBackground = true
it.backgroundColor = -0x7f000001 and rh.gc(R.color.iob) //50%
it.color = rh.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<DataPointWithLabelInterface> = ArrayList()
val iobPredictionArray = data.iobCobCalculator.calculateIobArrayForSMB(lastAutosensResult, SMBDefaults.exercise_mode, SMBDefaults.half_basal_exercise_target, isTempTarget)
for (i in iobPredictionArray) {
iobPrediction.add(i.setColor(rh.gc(R.color.iobPredAS)))
data.overviewData.maxIobValueFound = max(data.overviewData.maxIobValueFound, abs(i.iob))
}
data.overviewData.iobPredictions1Series = PointsWithLabelGraphSeries(Array(iobPrediction.size) { i -> iobPrediction[i] })
aapsLogger.debug(LTag.AUTOSENS, "IOB prediction for AS=" + DecimalFormatter.to2Decimal(lastAutosensResult.ratio) + ": " + data.iobCobCalculator.iobArrayToString(iobPredictionArray))
} else {
data.overviewData.iobPredictions1Series = PointsWithLabelGraphSeries()
}
// COB
data.overviewData.cobSeries = FixedLineGraphSeries(Array(cobArray.size) { i -> cobArray[i] }).also {
it.isDrawBackground = true
it.backgroundColor = -0x7f000001 and rh.gc(R.color.cob) //50%
it.color = rh.gc(R.color.cob)
it.thickness = 3
}
data.overviewData.cobMinFailOverSeries = PointsWithLabelGraphSeries(Array(minFailOverActiveList.size) { i -> minFailOverActiveList[i] })
// ACTIVITY
data.overviewData.activitySeries = FixedLineGraphSeries(Array(actArrayHist.size) { i -> actArrayHist[i] }).also {
it.isDrawBackground = false
it.color = rh.gc(R.color.activity)
it.thickness = 3
}
data.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 = rh.gc(R.color.activity)
})
}
// BGI
data.overviewData.minusBgiSeries = FixedLineGraphSeries(Array(bgiArrayHist.size) { i -> bgiArrayHist[i] }).also {
it.isDrawBackground = false
it.color = rh.gc(R.color.bgi)
it.thickness = 3
}
data.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 = rh.gc(R.color.bgi)
})
}
// DEVIATIONS
data.overviewData.deviationsSeries = BarGraphSeries(Array(devArray.size) { i -> devArray[i] }).also {
it.setValueDependentColor { data: OverviewPlugin.DeviationDataPoint -> data.color }
}
// RATIO
data.overviewData.ratioSeries = LineGraphSeries(Array(ratioArray.size) { i -> ratioArray[i] }).also {
it.color = rh.gc(R.color.ratio)
it.thickness = 3
}
// DEV SLOPE
data.overviewData.dsMaxSeries = LineGraphSeries(Array(dsMaxArray.size) { i -> dsMaxArray[i] }).also {
it.color = rh.gc(R.color.devslopepos)
it.thickness = 3
}
data.overviewData.dsMinSeries = LineGraphSeries(Array(dsMinArray.size) { i -> dsMinArray[i] }).also {
it.color = rh.gc(R.color.devslopeneg)
it.thickness = 3
}
rxBus.send(EventIobCalculationProgress(CalculationWorkflow.ProgressData.PREPARE_IOB_AUTOSENS_DATA, 100, null))
return Result.success()
}
}

View file

@ -0,0 +1,95 @@
package info.nightscout.androidaps.workflow
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.Config
import info.nightscout.androidaps.interfaces.Loop
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.general.nsclient.data.NSDeviceStatus
import info.nightscout.androidaps.plugins.general.overview.OverviewData
import info.nightscout.androidaps.plugins.general.overview.OverviewMenus
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.DataPointWithLabelInterface
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.GlucoseValueDataPoint
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.PointsWithLabelGraphSeries
import info.nightscout.androidaps.receivers.DataWorker
import info.nightscout.androidaps.utils.DefaultValueHelper
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.resources.ResourceHelper
import java.util.*
import javax.inject.Inject
import kotlin.math.ceil
import kotlin.math.max
import kotlin.math.min
class PreparePredictionsWorker(
context: Context,
params: WorkerParameters
) : Worker(context, params) {
@Inject lateinit var injector: HasAndroidInjector
@Inject lateinit var overviewData: OverviewData
@Inject lateinit var repository: AppRepository
@Inject lateinit var rxBus: RxBus
@Inject lateinit var config: Config
@Inject lateinit var nsDeviceStatus: NSDeviceStatus
@Inject lateinit var loop: Loop
@Inject lateinit var overviewMenus: OverviewMenus
@Inject lateinit var dataWorker: DataWorker
@Inject lateinit var defaultValueHelper: DefaultValueHelper
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var rh: ResourceHelper
init {
(context.applicationContext as HasAndroidInjector).androidInjector().inject(this)
}
class PreparePredictionsData(
val overviewData: OverviewData
)
override fun doWork(): Result {
val data = dataWorker.pickupObject(inputData.getLong(DataWorker.STORE_KEY, -1)) as PreparePredictionsData?
?: return Result.failure(workDataOf("Error" to "missing input data"))
val apsResult = if (config.APS) loop.lastRun?.constraintsProcessed else nsDeviceStatus.getAPSResult(injector)
val predictionsAvailable = if (config.APS) loop.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 = data.overviewData.rangeToDisplay - predictionHours
data.overviewData.toTime = calendar.timeInMillis + 100000 // little bit more to avoid wrong rounding - GraphView specific
data.overviewData.fromTime = data.overviewData.toTime - T.hours(hoursToFetch.toLong()).msecs()
data.overviewData.endTime = data.overviewData.toTime + T.hours(predictionHours.toLong()).msecs()
} else {
data.overviewData.toTime = calendar.timeInMillis + 100000 // little bit more to avoid wrong rounding - GraphView specific
data.overviewData.fromTime = data.overviewData.toTime - T.hours(data.overviewData.rangeToDisplay.toLong()).msecs()
data.overviewData.endTime = data.overviewData.toTime
}
val bgListArray: MutableList<DataPointWithLabelInterface> = ArrayList()
val predictions: MutableList<GlucoseValueDataPoint>? = apsResult?.predictions
?.map { bg -> GlucoseValueDataPoint(bg, defaultValueHelper, profileFunction, rh) }
?.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)
}
data.overviewData.predictionsGraphSeries = PointsWithLabelGraphSeries(Array(bgListArray.size) { i -> bgListArray[i] })
return Result.success()
}
}

View file

@ -0,0 +1,85 @@
package info.nightscout.androidaps.workflow
import android.content.Context
import androidx.work.Worker
import androidx.work.WorkerParameters
import androidx.work.workDataOf
import com.jjoe64.graphview.series.DataPoint
import com.jjoe64.graphview.series.LineGraphSeries
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.ValueWrapper
import info.nightscout.androidaps.extensions.target
import info.nightscout.androidaps.interfaces.Loop
import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.general.overview.OverviewData
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventIobCalculationProgress
import info.nightscout.androidaps.receivers.DataWorker
import info.nightscout.androidaps.utils.resources.ResourceHelper
import javax.inject.Inject
import kotlin.math.max
class PrepareTemporaryTargetDataWorker(
context: Context,
params: WorkerParameters
) : Worker(context, params) {
@Inject lateinit var dataWorker: DataWorker
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var rh: ResourceHelper
@Inject lateinit var repository: AppRepository
@Inject lateinit var loop: Loop
@Inject lateinit var rxBus: RxBus
init {
(context.applicationContext as HasAndroidInjector).androidInjector().inject(this)
}
class PrepareTemporaryTargetData(
val overviewData: OverviewData
)
override fun doWork(): Result {
val data = dataWorker.pickupObject(inputData.getLong(DataWorker.STORE_KEY, -1)) as PrepareTemporaryTargetData?
?: return Result.failure(workDataOf("Error" to "missing input data"))
rxBus.send(EventIobCalculationProgress(CalculationWorkflow.ProgressData.PREPARE_TEMPORARY_TARGET_DATA, 0, null))
val profile = profileFunction.getProfile() ?: return Result.failure(workDataOf("Error" to "missing profile"))
val units = profileFunction.getUnits()
var toTime = data.overviewData.toTime
val targetsSeriesArray: MutableList<DataPoint> = ArrayList()
var lastTarget = -1.0
loop.lastRun?.constraintsProcessed?.let { toTime = max(it.latestPredictionsTime, toTime) }
var time = data.overviewData.fromTime
while (time < toTime) {
val progress = (time - data.overviewData.fromTime).toDouble() / (data.overviewData.toTime - data.overviewData.fromTime) * 100.0
rxBus.send(EventIobCalculationProgress(CalculationWorkflow.ProgressData.PREPARE_TEMPORARY_TARGET_DATA, progress.toInt(), null))
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
data.overviewData.temporaryTargetSeries = LineGraphSeries(Array(targetsSeriesArray.size) { i -> targetsSeriesArray[i] }).also {
it.isDrawBackground = false
it.color = rh.gc(R.color.tempTargetBackground)
it.thickness = 2
}
rxBus.send(EventIobCalculationProgress(CalculationWorkflow.ProgressData.PREPARE_TEMPORARY_TARGET_DATA, 100, null))
return Result.success()
}
}

View file

@ -0,0 +1,136 @@
package info.nightscout.androidaps.workflow
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.Bolus
import info.nightscout.androidaps.database.entities.TherapyEvent
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.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.general.overview.OverviewData
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.*
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventIobCalculationProgress
import info.nightscout.androidaps.receivers.DataWorker
import info.nightscout.androidaps.utils.DefaultValueHelper
import info.nightscout.androidaps.utils.Round
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.Translator
import info.nightscout.androidaps.utils.resources.ResourceHelper
import javax.inject.Inject
class PrepareTreatmentsDataWorker(
context: Context,
params: WorkerParameters
) : Worker(context, params) {
@Inject lateinit var dataWorker: DataWorker
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var rh: ResourceHelper
@Inject lateinit var rxBus: RxBus
@Inject lateinit var translator: Translator
@Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var repository: AppRepository
@Inject lateinit var defaultValueHelper: DefaultValueHelper
init {
(context.applicationContext as HasAndroidInjector).androidInjector().inject(this)
}
class PrepareTreatmentsData(
val overviewData: OverviewData
)
override fun doWork(): Result {
val data = dataWorker.pickupObject(inputData.getLong(DataWorker.STORE_KEY, -1)) as PrepareTreatmentsData?
?: return Result.failure(workDataOf("Error" to "missing input data"))
rxBus.send(EventIobCalculationProgress(CalculationWorkflow.ProgressData.PREPARE_TREATMENTS_DATA, 0, null))
data.overviewData.maxTreatmentsValue = 0.0
val filteredTreatments: MutableList<DataPointWithLabelInterface> = ArrayList()
repository.getBolusesDataFromTimeToTime(data.overviewData.fromTime, data.overviewData.endTime, true).blockingGet()
.map { BolusDataPoint(it, rh, activePlugin, defaultValueHelper) }
.filter { it.data.type == Bolus.Type.NORMAL || it.data.type == Bolus.Type.SMB }
.forEach {
it.y = getNearestBg(data.overviewData, it.x.toLong())
filteredTreatments.add(it)
}
repository.getCarbsDataFromTimeToTimeExpanded(data.overviewData.fromTime, data.overviewData.endTime, true).blockingGet()
.map { CarbsDataPoint(it, rh) }
.forEach {
it.y = getNearestBg(data.overviewData, it.x.toLong())
filteredTreatments.add(it)
}
// ProfileSwitch
repository.getEffectiveProfileSwitchDataFromTimeToTime(data.overviewData.fromTime, data.overviewData.endTime, true).blockingGet()
.map { EffectiveProfileSwitchDataPoint(it, rh) }
.forEach(filteredTreatments::add)
// OfflineEvent
repository.getOfflineEventDataFromTimeToTime(data.overviewData.fromTime, data.overviewData.endTime, true).blockingGet()
.map {
TherapyEventDataPoint(
TherapyEvent(timestamp = it.timestamp, duration = it.duration, type = TherapyEvent.Type.APS_OFFLINE, glucoseUnit = TherapyEvent.GlucoseUnit.MMOL),
rh,
profileFunction,
translator
)
}
.forEach(filteredTreatments::add)
// Extended bolus
if (!activePlugin.activePump.isFakingTempsByExtendedBoluses) {
repository.getExtendedBolusDataFromTimeToTime(data.overviewData.fromTime, data.overviewData.endTime, true).blockingGet()
.map { ExtendedBolusDataPoint(it, rh) }
.filter { it.duration != 0L }
.forEach {
it.y = getNearestBg(data.overviewData, it.x.toLong())
filteredTreatments.add(it)
}
}
// Careportal
repository.compatGetTherapyEventDataFromToTime(data.overviewData.fromTime - T.hours(6).msecs(), data.overviewData.endTime).blockingGet()
.map { TherapyEventDataPoint(it, rh, profileFunction, translator) }
.filterTimeframe(data.overviewData.fromTime, data.overviewData.endTime)
.forEach {
if (it.y == 0.0) it.y = getNearestBg(data.overviewData, 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 { data.overviewData.maxTreatmentsValue = maxOf(data.overviewData.maxTreatmentsValue, it) }
data.overviewData.treatmentsSeries = PointsWithLabelGraphSeries(filteredTreatments.toTypedArray())
rxBus.send(EventIobCalculationProgress(CalculationWorkflow.ProgressData.PREPARE_TREATMENTS_DATA, 100, null))
return Result.success()
}
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(overviewData: OverviewData, 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 <E : DataPointWithLabelInterface> List<E>.filterTimeframe(fromTime: Long, endTime: Long): List<E> =
filter { it.x + it.duration >= fromTime && it.x <= endTime }
}

View file

@ -0,0 +1,31 @@
package info.nightscout.androidaps.workflow
import android.content.Context
import androidx.work.Worker
import androidx.work.WorkerParameters
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.general.overview.OverviewPlugin
import info.nightscout.androidaps.plugins.general.overview.events.EventUpdateOverviewGraph
import javax.inject.Inject
class UpdateGraphWorker(
context: Context,
params: WorkerParameters
) : Worker(context, params) {
@Inject lateinit var rxBus: RxBus
@Inject lateinit var overviewPlugin: OverviewPlugin
init {
(context.applicationContext as HasAndroidInjector).androidInjector().inject(this)
}
override fun doWork(): Result {
if (inputData.getString(CalculationWorkflow.JOB) == CalculationWorkflow.MAIN_CALCULATION)
overviewPlugin.overviewBus.send(EventUpdateOverviewGraph("UpdateGraphWorker"))
else
rxBus.send(EventUpdateOverviewGraph("UpdateGraphWorker"))
return Result.success()
}
}

View file

@ -0,0 +1,30 @@
package info.nightscout.androidaps.workflow
import android.content.Context
import androidx.work.Worker
import androidx.work.WorkerParameters
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.general.overview.OverviewPlugin
import info.nightscout.androidaps.plugins.general.overview.events.EventUpdateOverviewIobCob
import info.nightscout.androidaps.plugins.general.overview.events.EventUpdateOverviewSensitivity
import javax.inject.Inject
class UpdateIobCobSensWorker(
context: Context,
params: WorkerParameters
) : Worker(context, params) {
@Inject lateinit var rxBus: RxBus
@Inject lateinit var overviewPlugin: OverviewPlugin
init {
(context.applicationContext as HasAndroidInjector).androidInjector().inject(this)
}
override fun doWork(): Result {
overviewPlugin.overviewBus.send(EventUpdateOverviewIobCob("UpdateIobCobSensWorker"))
overviewPlugin.overviewBus.send(EventUpdateOverviewSensitivity("UpdateIobCobSensWorker"))
return Result.success()
}
}

View file

@ -63,6 +63,17 @@
</LinearLayout> </LinearLayout>
<ProgressBar
android:id="@+id/progress_bar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginEnd="10dp"
android:indeterminate="false"
android:max="100"
android:progress="100" />
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -88,13 +99,6 @@
android:paddingTop="5dp" android:paddingTop="5dp"
app:srcCompat="@drawable/ic_arrow_drop_down_white_24dp" /> app:srcCompat="@drawable/ic_arrow_drop_down_white_24dp" />
<TextView
android:id="@+id/overview_iobcalculationprogess"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:textSize="15sp" />
</RelativeLayout> </RelativeLayout>
<LinearLayout <LinearLayout

View file

@ -30,6 +30,8 @@ class AbstractSensitivityPluginTest : TestBase() {
override val id: Sensitivity.SensitivityType override val id: Sensitivity.SensitivityType
get() = Sensitivity.SensitivityType.UNKNOWN get() = Sensitivity.SensitivityType.UNKNOWN
override fun maxAbsorptionHours(): Double = 8.0
override fun configuration(): JSONObject = JSONObject() override fun configuration(): JSONObject = JSONObject()
override fun applyConfiguration(configuration: JSONObject) {} override fun applyConfiguration(configuration: JSONObject) {}

View file

@ -7,7 +7,11 @@ import android.os.SystemClock
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.annotations.OpenForTesting import info.nightscout.androidaps.annotations.OpenForTesting
import info.nightscout.androidaps.automation.R import info.nightscout.androidaps.automation.R
import info.nightscout.androidaps.events.* import info.nightscout.androidaps.events.EventBTChange
import info.nightscout.androidaps.events.EventChargingState
import info.nightscout.androidaps.events.EventLocationChange
import info.nightscout.androidaps.events.EventNetworkChange
import info.nightscout.androidaps.events.EventPreferenceChange
import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
@ -33,7 +37,6 @@ import org.json.JSONObject
import java.util.* import java.util.*
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
import kotlin.collections.ArrayList
@OpenForTesting @OpenForTesting
@Singleton @Singleton
@ -127,10 +130,6 @@ class AutomationPlugin @Inject constructor(
.toObservable(EventNetworkChange::class.java) .toObservable(EventNetworkChange::class.java)
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
.subscribe({ processActions() }, fabricPrivacy::logException) .subscribe({ processActions() }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventAutosensCalculationFinished::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ processActions() }, fabricPrivacy::logException)
disposable += rxBus disposable += rxBus
.toObservable(EventBTChange::class.java) .toObservable(EventBTChange::class.java)
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
@ -150,7 +149,7 @@ class AutomationPlugin @Inject constructor(
private fun storeToSP() { private fun storeToSP() {
val array = JSONArray() val array = JSONArray()
val iterator = ArrayList<AutomationEvent>(automationEvents).iterator() val iterator = ArrayList(automationEvents).iterator()
try { try {
while (iterator.hasNext()) { while (iterator.hasNext()) {
val event = iterator.next() val event = iterator.next()

View file

@ -38,6 +38,7 @@ ActionsTestBase : TestBaseWithProfile() {
override var enabled: Boolean override var enabled: Boolean
get() = true get() = true
set(_) {} set(_) {}
override var lastBgTriggeredRun: Long = 0
override fun invoke(initiator: String, allowNotification: Boolean, tempBasalFallback: Boolean) {} override fun invoke(initiator: String, allowNotification: Boolean, tempBasalFallback: Boolean) {}
override fun acceptChangeRequest() {} override fun acceptChangeRequest() {}

View file

@ -1,6 +1,3 @@
package info.nightscout.androidaps.events package info.nightscout.androidaps.events
import info.nightscout.androidaps.events.Event
import info.nightscout.androidaps.events.EventLoop
class EventAutosensCalculationFinished(val cause: Event?) : EventLoop() class EventAutosensCalculationFinished(val cause: Event?) : EventLoop()

View file

@ -1,5 +1,3 @@
package info.nightscout.androidaps.events package info.nightscout.androidaps.events
import info.nightscout.androidaps.events.Event
class EventDanaRSyncStatus(var message: String) : Event() class EventDanaRSyncStatus(var message: String) : Event()

View file

@ -5,6 +5,7 @@ import android.os.Looper
import android.view.View import android.view.View
fun Boolean.toVisibility() = if (this) View.VISIBLE else View.GONE fun Boolean.toVisibility() = if (this) View.VISIBLE else View.GONE
fun Boolean.toVisibilityKeepSpace() = if (this) View.VISIBLE else View.INVISIBLE
fun runOnUiThread(theRunnable: Runnable?) = theRunnable?.let { fun runOnUiThread(theRunnable: Runnable?) = theRunnable?.let {
Handler(Looper.getMainLooper()).post(it) Handler(Looper.getMainLooper()).post(it)

View file

@ -52,6 +52,12 @@ interface ActivePlugin {
*/ */
val activeSafety: Safety val activeSafety: Safety
/**
* Currently selected Safety plugin
* Always IobCobCalculatorPlugin
*/
val activeIobCobCalculator: IobCobCalculator
/** /**
* List of all registered plugins * List of all registered plugins
*/ */

View file

@ -28,6 +28,8 @@ interface Loop {
val isDisconnected: Boolean val isDisconnected: Boolean
var enabled: Boolean var enabled: Boolean
var lastBgTriggeredRun: Long
fun invoke(initiator: String, allowNotification: Boolean, tempBasalFallback: Boolean = false) fun invoke(initiator: String, allowNotification: Boolean, tempBasalFallback: Boolean = false)
fun acceptChangeRequest() fun acceptChangeRequest()

View file

@ -4,6 +4,5 @@ import info.nightscout.androidaps.plugins.bus.RxBus
interface Overview : ConfigExportImport { interface Overview : ConfigExportImport {
fun refreshLoop(from: String)
val overviewBus: RxBus val overviewBus: RxBus
} }

View file

@ -20,6 +20,7 @@ interface Sensitivity : ConfigExportImport {
val id: SensitivityType val id: SensitivityType
fun detectSensitivity(ads: AutosensDataStore, fromTime: Long, toTime: Long): AutosensResult fun detectSensitivity(ads: AutosensDataStore, fromTime: Long, toTime: Long): AutosensResult
fun maxAbsorptionHours(): Double
companion object { companion object {

View file

@ -234,6 +234,9 @@
<color name="mdtp_button_selected">#33969696</color> <color name="mdtp_button_selected">#33969696</color>
<color name="plastic">#EBEBEA</color> <color name="plastic">#EBEBEA</color>
<color name="textAppearancemediumLight">#000000</color>
<color name="textAppearancemediumDark">#B3FFFFFF</color>
<!-- dash and eros colors --> <!-- dash and eros colors -->
<color name="omni_yellow">#FFFF00</color> <color name="omni_yellow">#FFFF00</color>
<color name="omni_cyan">#00FFFF</color> <color name="omni_cyan">#00FFFF</color>

View file

@ -225,6 +225,7 @@
<item name="activityColor">@color/activity</item> <item name="activityColor">@color/activity</item>
<!-- CardView specific colors --> <!-- CardView specific colors -->
<item name="strokeColor">@color/plastic_grey</item> <item name="strokeColor">@color/plastic_grey</item>
<item name="textAppearancemediumColor">@color/textAppearancemediumDark</item>
</style> </style>
<style name="Theme.MaterialComponents.DayNight.DarkActionBar" parent="Theme.MaterialComponents.DayNight.Bridge"/> <style name="Theme.MaterialComponents.DayNight.DarkActionBar" parent="Theme.MaterialComponents.DayNight.Bridge"/>

View file

@ -205,4 +205,5 @@
<attr name="activityColor" format="reference|color" /> <attr name="activityColor" format="reference|color" />
<!-- CardView specific colors --> <!-- CardView specific colors -->
<attr name="strokeColor" format="reference|color" /> <attr name="strokeColor" format="reference|color" />
<attr name="textAppearancemediumColor" format="reference|color" />
</resources> </resources>

View file

@ -238,6 +238,9 @@
<color name="mdtp_button_selected">#33969696</color> <color name="mdtp_button_selected">#33969696</color>
<color name="plastic">#EBEBEA</color> <color name="plastic">#EBEBEA</color>
<color name="textAppearancemediumLight">#000000</color>
<color name="textAppearancemediumDark">#B3FFFFFF</color>
<!-- dash and eros colors --> <!-- dash and eros colors -->
<color name="omni_yellow">#FFFF00</color> <color name="omni_yellow">#FFFF00</color>
<color name="omni_cyan">#00FFFF</color> <color name="omni_cyan">#00FFFF</color>

View file

@ -53,7 +53,7 @@
<!-- BG source temp button --> <!-- BG source temp button -->
<item name="setTempButton">@color/colorSetTempButton</item> <item name="setTempButton">@color/colorSetTempButton</item>
<!-- Card Item--> <!-- Card Item-->
<item name="cardItemBackgroundColor">@color/cardColorBackground</item> <item name="cardItemBackgroundColor">?attr/colorSurface</item>
<!-- Exercise --> <!-- Exercise -->
<item name="exerciseColor">@color/exercise</item> <item name="exerciseColor">@color/exercise</item>
<!-- BG low --> <!-- BG low -->
@ -233,6 +233,7 @@
<item name="activityColor">@color/activity</item> <item name="activityColor">@color/activity</item>
<!-- CardView specific colors --> <!-- CardView specific colors -->
<item name="strokeColor">@color/plastic_grey</item> <item name="strokeColor">@color/plastic_grey</item>
<item name="textAppearancemediumColor">@color/textAppearancemediumLight</item>
</style> </style>
<style name="Theme.MaterialComponents.DayNight.DarkActionBar" parent="Theme.MaterialComponents.DayNight.Bridge"/> <style name="Theme.MaterialComponents.DayNight.DarkActionBar" parent="Theme.MaterialComponents.DayNight.Bridge"/>

View file

@ -27,7 +27,7 @@ android {
sourceSets { sourceSets {
main { main {
jniLibs.srcDirs = ['libs'] jniLibs.srcDirs = ['src/main/jniLibs']
} }
} }

View file

@ -111,8 +111,8 @@ dependencies {
implementation(files('libs/wearpreferenceactivity-0.5.0.aar')) implementation(files('libs/wearpreferenceactivity-0.5.0.aar'))
implementation('com.github.lecho:hellocharts-library:1.5.8@aar') implementation('com.github.lecho:hellocharts-library:1.5.8@aar')
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.0' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.1'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-guava:1.6.0' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-guava:1.6.1'
implementation "androidx.core:core-ktx:$core_version" implementation "androidx.core:core-ktx:$core_version"
implementation "androidx.wear.tiles:tiles:1.0.1" implementation "androidx.wear.tiles:tiles:1.0.1"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"