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 {
multiDexEnabled true
versionCode 1500
version "3.0.0.1-dev-h"
version "3.0.0.1-dev-i"
buildConfigField "String", "VERSION", '"' + version + '"'
buildConfigField "String", "BUILDVERSION", '"' + generateGitBuild() + '-' + generateDate() + '"'
buildConfigField "String", "REMOTE", '"' + generateGitRemote() + '"'

View file

@ -13,7 +13,6 @@ import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.data.ProfileSealed
import info.nightscout.androidaps.database.interfaces.end
import info.nightscout.androidaps.extensions.directionToIcon
import info.nightscout.androidaps.extensions.isInProgress
import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.extensions.valueToUnitsString
import info.nightscout.androidaps.interfaces.*
@ -78,7 +77,7 @@ class Widget : AppWidgetProvider() {
// Create an Intent to launch MainActivity when clicked
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
views.setOnClickPendingIntent(R.id.widget_layout, pendingIntent)
@ -105,7 +104,11 @@ class Widget : AppWidgetProvider() {
}
)
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
if (glucoseStatus != null) {
@ -127,21 +130,21 @@ class Widget : AppWidgetProvider() {
}
private fun updateTemporaryBasal(views: RemoteViews) {
views.setTextViewText(R.id.base_basal, overviewData.temporaryBasalText)
views.setTextColor(R.id.base_basal, overviewData.temporaryBasalColor)
views.setImageViewResource(R.id.base_basal_icon, overviewData.temporaryBasalIcon)
views.setTextViewText(R.id.base_basal, overviewData.temporaryBasalText(iobCobCalculator))
views.setTextColor(R.id.base_basal, overviewData.temporaryBasalColor(iobCobCalculator))
views.setImageViewResource(R.id.base_basal_icon, overviewData.temporaryBasalIcon(iobCobCalculator))
}
private fun updateExtendedBolus(views: RemoteViews) {
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())
}
private fun updateIobCob(views: RemoteViews) {
views.setTextViewText(R.id.iob, overviewData.iobText)
views.setTextViewText(R.id.iob, overviewData.iobText(iobCobCalculator))
// 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 lastRun = loop.lastRun
@ -158,7 +161,6 @@ class Widget : AppWidgetProvider() {
private fun updateTemporaryTarget(views: RemoteViews) {
val units = profileFunction.getUnits()
if (overviewData.temporaryTarget?.isInProgress(dateUtil) == false) overviewData.temporaryTarget = null
val tempTarget = overviewData.temporaryTarget
if (tempTarget != null) {
// this is crashing, use background as text for now
@ -210,12 +212,12 @@ class Widget : AppWidgetProvider() {
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())
views.setImageViewResource(R.id.sensitivity_icon, R.drawable.ic_swap_vert_black_48dp_green)
else
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)
} ?: "")

View file

@ -3,7 +3,6 @@ package info.nightscout.androidaps.activities
import android.annotation.SuppressLint
import android.app.DatePickerDialog
import android.content.Context
import android.graphics.Color
import android.os.Bundle
import android.util.DisplayMetrics
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.EventRefreshOverview
import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.extensions.toVisibilityKeepSpace
import info.nightscout.androidaps.interfaces.ActivePlugin
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.events.EventUpdateOverviewGraph
import info.nightscout.androidaps.plugins.general.overview.graphData.GraphData
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventBucketedDataCreated
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.utils.DateUtil
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.buildHelper.BuildHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.androidaps.workflow.CalculationWorkflow
import info.nightscout.shared.logging.LTag
import info.nightscout.shared.sharedPreferences.SP
import io.reactivex.rxjava3.disposables.CompositeDisposable
@ -59,9 +56,6 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
@Inject lateinit var defaultValueHelper: DefaultValueHelper
@Inject lateinit var activePlugin: ActivePlugin
@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 fabricPrivacy: FabricPrivacy
@Inject lateinit var overviewMenus: OverviewMenus
@ -72,6 +66,7 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
@Inject lateinit var translator: Translator
@Inject lateinit var context: Context
@Inject lateinit var dataWorker: DataWorker
@Inject lateinit var calculationWorkflow: CalculationWorkflow
private val disposable = CompositeDisposable()
@ -94,6 +89,17 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
setContentView(binding.root)
// 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 =
IobCobCalculatorPlugin(
injector,
@ -104,32 +110,11 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
rh,
profileFunction,
activePlugin,
sensitivityOref1Plugin,
sensitivityAAPSPlugin,
sensitivityWeightedAveragePlugin,
fabricPrivacy,
dateUtil,
repository,
context,
dataWorker
)
overviewData =
OverviewData(
injector,
aapsLogger,
rh,
dateUtil,
sp,
activePlugin,
defaultValueHelper,
profileFunction,
config,
loop,
nsDeviceStatus,
repository,
overviewMenus,
iobCobCalculator,
translator
overviewData,
calculationWorkflow
)
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
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?.labelVerticalWidth = axisWidth
@ -217,7 +202,7 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
override fun onPause() {
super.onPause()
disposable.clear()
iobCobCalculator.stopCalculation("onPause")
calculationWorkflow.stopCalculation(CalculationWorkflow.HISTORY_CALCULATION, "onPause")
}
@Synchronized
@ -239,22 +224,11 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
disposable += rxBus
.toObservable(EventIobCalculationProgress::class.java)
.observeOn(aapsSchedulers.main)
.subscribe({
if (it.cause is EventCustomCalculationFinished)
binding.overviewIobcalculationprogess.text = it.progressPct.toString() + "%"
}, fabricPrivacy::logException)
.subscribe({ updateCalcProgress(it.pass.finalPercent(it.progressPct)) }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventRefreshOverview::class.java)
.toObservable(EventUpdateOverviewGraph::class.java)
.observeOn(aapsSchedulers.main)
.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) {
// set start of current day
@ -285,12 +259,12 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
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.gridLabelRenderer?.gridColor = rh.gac( R.attr.graphgrid)
graph.gridLabelRenderer?.gridColor = rh.gac(R.attr.graphgrid)
graph.gridLabelRenderer?.reloadStyles()
graph.gridLabelRenderer?.isHorizontalLabelsVisible = false
graph.gridLabelRenderer?.labelVerticalWidth = axisWidth
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)
val label = TextView(this)
@ -310,17 +284,7 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
@Suppress("SameParameterValue")
private fun loadAll(from: String) {
updateDate()
Thread {
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()
runCalculation(from)
}
private fun setTime(start: Long) {
@ -341,22 +305,32 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
}
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
var runningRefresh = false
@Suppress("SameParameterValue")
private fun refreshLoop(from: String) {
if (runningRefresh) return
runningRefresh = true
overviewData.prepareIobAutosensData(from)
rxBus.send(EventRefreshOverview(from))
aapsLogger.debug(LTag.UI, "refreshLoop finished")
runningRefresh = false
}
fun updateDate() {
private fun updateDate() {
binding.date.text = dateUtil.dateAndTimeString(overviewData.fromTime)
binding.zoom.text = rangeToDisplay.toString()
}
@ -403,12 +377,12 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
var useDSForScale = false
var useBGIForScale = false
when {
menuChartSettings[g + 1][OverviewMenus.CharType.ABS.ordinal] -> useABSForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.IOB.ordinal] -> useIobForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.COB.ordinal] -> useCobForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.DEV.ordinal] -> useDevForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.BGI.ordinal] -> useBGIForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.SEN.ordinal] -> useRatioForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.ABS.ordinal] -> useABSForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.IOB.ordinal] -> useIobForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.COB.ordinal] -> useCobForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.DEV.ordinal] -> useDevForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.BGI.ordinal] -> useBGIForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.SEN.ordinal] -> useRatioForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.DEVSLOPE.ordinal] -> useDSForScale = true
}
val alignDevBgiScale = menuChartSettings[g + 1][OverviewMenus.CharType.DEV.ordinal] && menuChartSettings[g + 1][OverviewMenus.CharType.BGI.ordinal]
@ -440,4 +414,9 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
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.TreatmentsBolusCarbsItemBinding
import info.nightscout.androidaps.dialogs.WizardInfoDialog
import info.nightscout.androidaps.events.EventAutosensCalculationFinished
import info.nightscout.androidaps.events.EventTreatmentChange
import info.nightscout.androidaps.events.EventTreatmentUpdateGui
import info.nightscout.androidaps.extensions.iobCalc
import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.interfaces.ActivePlugin
@ -170,16 +168,6 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() {
.observeOn(aapsSchedulers.main)
.debounce(1L, TimeUnit.SECONDS)
.subscribe({ swapAdapter() }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventTreatmentUpdateGui::class.java) // TODO join with above
.observeOn(aapsSchedulers.io)
.debounce(1L, TimeUnit.SECONDS)
.subscribe({ swapAdapter() }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventAutosensCalculationFinished::class.java)
.observeOn(aapsSchedulers.main)
.debounce(1L, TimeUnit.SECONDS)
.subscribe({ swapAdapter() }, fabricPrivacy::logException)
}
@Synchronized
@ -334,7 +322,7 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() {
showInvalidated = true
updateMenuVisibility()
ToastUtils.showToastInUiThread(context, rh.gs(R.string.show_invalidated_records))
rxBus.send(EventTreatmentUpdateGui())
swapAdapter()
true
}
@ -342,7 +330,7 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() {
showInvalidated = false
updateMenuVisibility()
ToastUtils.showToastInUiThread(context, rh.gs(R.string.hide_invalidated_records))
rxBus.send(EventTreatmentUpdateGui())
swapAdapter()
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.TreatmentsCareportalItemBinding
import info.nightscout.androidaps.events.EventTherapyEventChange
import info.nightscout.androidaps.events.EventTreatmentUpdateGui
import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.bus.RxBus
@ -128,11 +127,6 @@ class TreatmentsCareportalFragment : DaggerFragment() {
.observeOn(aapsSchedulers.main)
.debounce(1L, TimeUnit.SECONDS)
.subscribe({ swapAdapter() }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventTreatmentUpdateGui::class.java) // TODO join with above
.observeOn(aapsSchedulers.io)
.debounce(1L, TimeUnit.SECONDS)
.subscribe({ swapAdapter() }, fabricPrivacy::logException)
}
@Synchronized
@ -216,7 +210,7 @@ class TreatmentsCareportalFragment : DaggerFragment() {
showInvalidated = true
updateMenuVisibility()
ToastUtils.showToastInUiThread(context, rh.gs(R.string.show_invalidated_records))
rxBus.send(EventTreatmentUpdateGui())
swapAdapter()
true
}
@ -224,7 +218,7 @@ class TreatmentsCareportalFragment : DaggerFragment() {
showInvalidated = false
updateMenuVisibility()
ToastUtils.showToastInUiThread(context, rh.gs(R.string.hide_invalidated_records))
rxBus.send(EventTreatmentUpdateGui())
swapAdapter()
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.TreatmentsExtendedbolusItemBinding
import info.nightscout.androidaps.events.EventExtendedBolusChange
import info.nightscout.androidaps.events.EventTreatmentUpdateGui
import info.nightscout.androidaps.extensions.iobCalc
import info.nightscout.androidaps.extensions.isInProgress
import info.nightscout.androidaps.extensions.toVisibility
@ -105,11 +104,6 @@ class TreatmentsExtendedBolusesFragment : DaggerFragment() {
.observeOn(aapsSchedulers.io)
.debounce(1L, TimeUnit.SECONDS)
.subscribe({ swapAdapter() }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventTreatmentUpdateGui::class.java) // TODO join with above
.observeOn(aapsSchedulers.io)
.debounce(1L, TimeUnit.SECONDS)
.subscribe({ swapAdapter() }, fabricPrivacy::logException)
}
@Synchronized
@ -203,7 +197,7 @@ class TreatmentsExtendedBolusesFragment : DaggerFragment() {
showInvalidated = true
updateMenuVisibility()
ToastUtils.showToastInUiThread(context, rh.gs(R.string.show_invalidated_records))
rxBus.send(EventTreatmentUpdateGui())
swapAdapter()
true
}
@ -211,7 +205,7 @@ class TreatmentsExtendedBolusesFragment : DaggerFragment() {
showInvalidated = false
updateMenuVisibility()
ToastUtils.showToastInUiThread(context, rh.gs(R.string.hide_invalidated_records))
rxBus.send(EventTreatmentUpdateGui())
swapAdapter()
true
}

View file

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

View file

@ -23,7 +23,6 @@ import info.nightscout.androidaps.databinding.TreatmentsTemptargetItemBinding
import info.nightscout.androidaps.events.EventEffectiveProfileSwitchChanged
import info.nightscout.androidaps.events.EventProfileSwitchChanged
import info.nightscout.androidaps.events.EventTempTargetChange
import info.nightscout.androidaps.events.EventTreatmentUpdateGui
import info.nightscout.androidaps.extensions.friendlyDescription
import info.nightscout.androidaps.extensions.highValueToUnitsToString
import info.nightscout.androidaps.extensions.lowValueToUnitsToString
@ -134,11 +133,6 @@ class TreatmentsTempTargetFragment : DaggerFragment() {
.observeOn(aapsSchedulers.io)
.debounce(1L, TimeUnit.SECONDS)
.subscribe({ swapAdapter() }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventTreatmentUpdateGui::class.java) // TODO join with above
.observeOn(aapsSchedulers.io)
.debounce(1L, TimeUnit.SECONDS)
.subscribe({ swapAdapter() }, fabricPrivacy::logException)
}
@Synchronized
@ -235,7 +229,7 @@ class TreatmentsTempTargetFragment : DaggerFragment() {
showInvalidated = true
updateMenuVisibility()
ToastUtils.showToastInUiThread(context, rh.gs(R.string.show_invalidated_records))
rxBus.send(EventTreatmentUpdateGui())
swapAdapter()
true
}
@ -243,7 +237,7 @@ class TreatmentsTempTargetFragment : DaggerFragment() {
showInvalidated = false
updateMenuVisibility()
ToastUtils.showToastInUiThread(context, rh.gs(R.string.show_invalidated_records))
rxBus.send(EventTreatmentUpdateGui())
swapAdapter()
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.databinding.TreatmentsTempbasalsFragmentBinding
import info.nightscout.androidaps.databinding.TreatmentsTempbasalsItemBinding
import info.nightscout.androidaps.events.EventAutosensCalculationFinished
import info.nightscout.androidaps.events.EventTempBasalChange
import info.nightscout.androidaps.events.EventTreatmentUpdateGui
import info.nightscout.androidaps.extensions.iobCalc
import info.nightscout.androidaps.extensions.toStringFull
import info.nightscout.androidaps.extensions.toTemporaryBasal
@ -141,15 +139,6 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() {
.toObservable(EventTempBasalChange::class.java)
.observeOn(aapsSchedulers.main)
.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
@ -183,7 +172,7 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() {
holder.binding.date.text = if (newDay) dateUtil.dateStringRelative(tempBasal.timestamp, rh) else ""
if (tempBasal.isInProgress) {
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 {
holder.binding.time.text = dateUtil.timeRangeString(tempBasal.timestamp, tempBasal.end)
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.emulatedSuspendFlag.visibility = (tempBasal.type == TemporaryBasal.Type.EMULATED_PUMP_SUSPEND).toVisibility()
holder.binding.superBolusFlag.visibility = (tempBasal.type == TemporaryBasal.Type.SUPERBOLUS).toVisibility()
if (abs(iob.basaliob) > 0.01) holder.binding.iob.setTextColor(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()
if (actionHelper.isRemoving) {
holder.binding.cbRemove.setOnCheckedChangeListener { _, value ->
@ -251,7 +240,7 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() {
showInvalidated = true
updateMenuVisibility()
ToastUtils.showToastInUiThread(context, rh.gs(R.string.show_invalidated_records))
rxBus.send(EventTreatmentUpdateGui())
swapAdapter()
true
}
@ -259,7 +248,7 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() {
showInvalidated = false
updateMenuVisibility()
ToastUtils.showToastInUiThread(context, rh.gs(R.string.hide_invalidated_records))
rxBus.send(EventTreatmentUpdateGui())
swapAdapter()
true
}
@ -281,41 +270,41 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() {
private fun removeSelected(selectedItems: SparseArray<TemporaryBasal>) {
if (selectedItems.size() > 0)
activity?.let { activity ->
OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), getConfirmationText(selectedItems), Runnable {
selectedItems.forEach {_, tempBasal ->
var extendedBolus: ExtendedBolus? = null
val isFakeExtended = tempBasal.type == TemporaryBasal.Type.FAKE_EXTENDED
if (isFakeExtended) {
val eb = repository.getExtendedBolusActiveAt(tempBasal.timestamp).blockingGet()
extendedBolus = if (eb is ValueWrapper.Existing) eb.value else null
OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), getConfirmationText(selectedItems), Runnable {
selectedItems.forEach { _, tempBasal ->
var extendedBolus: ExtendedBolus? = null
val isFakeExtended = tempBasal.type == TemporaryBasal.Type.FAKE_EXTENDED
if (isFakeExtended) {
val eb = repository.getExtendedBolusActiveAt(tempBasal.timestamp).blockingGet()
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) {
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()
})
}
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.TreatmentsUserEntryItemBinding
import info.nightscout.androidaps.events.EventPreferenceChange
import info.nightscout.androidaps.events.EventTreatmentUpdateGui
import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.interfaces.ImportExportPrefs
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 io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign
import java.util.concurrent.TimeUnit
import javax.inject.Inject
class TreatmentsUserEntryFragment : DaggerFragment() {
@ -99,11 +97,6 @@ class TreatmentsUserEntryFragment : DaggerFragment() {
.toObservable(EventPreferenceChange::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ swapAdapter() }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventTreatmentUpdateGui::class.java)
.observeOn(aapsSchedulers.io)
.debounce(1L, TimeUnit.SECONDS)
.subscribe({ swapAdapter() }, fabricPrivacy::logException)
}
@Synchronized
@ -172,7 +165,7 @@ class TreatmentsUserEntryFragment : DaggerFragment() {
showLoop = true
updateMenuVisibility()
ToastUtils.showToastInUiThread(context, rh.gs(R.string.show_loop_records))
rxBus.send(EventTreatmentUpdateGui())
swapAdapter()
true
}
@ -180,7 +173,7 @@ class TreatmentsUserEntryFragment : DaggerFragment() {
showLoop = false
updateMenuVisibility()
ToastUtils.showToastInUiThread(context, rh.gs(R.string.show_hide_records))
rxBus.send(EventTreatmentUpdateGui())
swapAdapter()
true
}

View file

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

View file

@ -169,6 +169,9 @@ class PluginStore @Inject constructor(
override val activeSafety: 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)
}

View file

@ -6,12 +6,11 @@ import android.content.pm.ResolveInfo
import android.os.Bundle
import dagger.android.HasAndroidInjector
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.toStringFull
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.bus.RxBus
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.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 javax.inject.Inject
import javax.inject.Singleton
@ -68,26 +69,6 @@ class DataBroadcastPlugin @Inject constructor(
.observeOn(aapsSchedulers.io)
.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
.toObservable(EventAutosensCalculationFinished::class.java)
.observeOn(aapsSchedulers.io)

View file

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

View file

@ -1,46 +1,42 @@
package info.nightscout.androidaps.plugins.general.overview
import android.content.Context
import android.graphics.DashPathEffect
import android.graphics.Paint
import androidx.annotation.ColorInt
import com.jjoe64.graphview.series.BarGraphSeries
import com.jjoe64.graphview.series.DataPoint
import com.jjoe64.graphview.series.LineGraphSeries
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.IobTotal
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.ValueWrapper
import info.nightscout.androidaps.database.entities.Bolus
import info.nightscout.androidaps.database.entities.GlucoseValue
import info.nightscout.androidaps.database.entities.TemporaryTarget
import info.nightscout.androidaps.database.entities.TherapyEvent
import info.nightscout.androidaps.extensions.*
import info.nightscout.androidaps.interfaces.*
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import info.nightscout.androidaps.plugins.aps.openAPSSMB.SMBDefaults
import info.nightscout.androidaps.plugins.general.nsclient.data.NSDeviceStatus
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.*
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult
import info.nightscout.androidaps.extensions.convertedToPercent
import info.nightscout.androidaps.extensions.isInProgress
import info.nightscout.androidaps.extensions.toStringFull
import info.nightscout.androidaps.extensions.toStringShort
import info.nightscout.androidaps.extensions.valueToUnits
import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.IobCobCalculator
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.DataPointWithLabelInterface
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.FixedLineGraphSeries
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.PointsWithLabelGraphSeries
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.Scale
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.ScaledDataPoint
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.CobInfo
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.data.AutosensData
import info.nightscout.androidaps.utils.*
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.DefaultValueHelper
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.sharedPreferences.SP
import java.util.*
import javax.inject.Inject
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
class OverviewData @Inject constructor(
private val injector: HasAndroidInjector,
private val aapsLogger: AAPSLogger,
private val rh: ResourceHelper,
private val dateUtil: DateUtil,
@ -48,13 +44,7 @@ class OverviewData @Inject constructor(
private val activePlugin: ActivePlugin,
private val defaultValueHelper: DefaultValueHelper,
private val profileFunction: ProfileFunction,
private val config: Config,
private val loop: Loop,
private val nsDeviceStatus: NSDeviceStatus,
private val repository: AppRepository,
private val overviewMenus: OverviewMenus,
private val iobCobCalculator: IobCobCalculator,
private val translator: Translator
private val repository: AppRepository
) {
var rangeToDisplay = 6 // for graph
@ -65,13 +55,6 @@ class OverviewData @Inject constructor(
fun reset() {
pumpStatus = ""
calcProgressPct = 100
lastBg = null
bolusIob = null
basalIob = null
cobInfo = null
lastCarbsTime = 0L
temporaryTarget = null
lastAutosensData = null
bgReadingsArray = ArrayList()
bucketedGraphSeries = PointsWithLabelGraphSeries()
bgReadingGraphSeries = PointsWithLabelGraphSeries()
@ -128,7 +111,12 @@ class OverviewData @Inject constructor(
* 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
get() = lastBg?.let { lastBg ->
@ -165,17 +153,16 @@ class OverviewData @Inject constructor(
* TEMPORARY BASAL
*/
val temporaryBasalText: String
get() =
profileFunction.getProfile()?.let { profile ->
var temporaryBasal = iobCobCalculator.getTempBasalIncludingConvertedExtended(dateUtil.now())
if (temporaryBasal?.isInProgress == false) temporaryBasal = null
temporaryBasal?.let { "T:" + it.toStringShort() }
?: rh.gs(R.string.pump_basebasalrate, profile.getBasal())
} ?: rh.gs(R.string.notavailable)
fun temporaryBasalText(iobCobCalculator: IobCobCalculator): String =
profileFunction.getProfile()?.let { profile ->
var temporaryBasal = iobCobCalculator.getTempBasalIncludingConvertedExtended(dateUtil.now())
if (temporaryBasal?.isInProgress == false) temporaryBasal = null
temporaryBasal?.let { "T:" + it.toStringShort() }
?: rh.gs(R.string.pump_basebasalrate, profile.getBasal())
} ?: rh.gs(R.string.notavailable)
val temporaryBasalDialogText: String
get() = profileFunction.getProfile()?.let { profile ->
fun temporaryBasalDialogText(iobCobCalculator: IobCobCalculator): String =
profileFunction.getProfile()?.let { profile ->
iobCobCalculator.getTempBasalIncludingConvertedExtended(dateUtil.now())?.let { temporaryBasal ->
"${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)
@ -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.notavailable)
val temporaryBasalIcon: Int
get() =
profileFunction.getProfile()?.let { profile ->
iobCobCalculator.getTempBasalIncludingConvertedExtended(dateUtil.now())?.let { temporaryBasal ->
val percentRate = temporaryBasal.convertedToPercent(dateUtil.now(), profile)
when {
percentRate > 100 -> R.drawable.ic_cp_basal_tbr_high
percentRate < 100 -> R.drawable.ic_cp_basal_tbr_low
else -> R.drawable.ic_cp_basal_no_tbr
}
fun temporaryBasalIcon(iobCobCalculator: IobCobCalculator): Int =
profileFunction.getProfile()?.let { profile ->
iobCobCalculator.getTempBasalIncludingConvertedExtended(dateUtil.now())?.let { temporaryBasal ->
val percentRate = temporaryBasal.convertedToPercent(dateUtil.now(), profile)
when {
percentRate > 100 -> R.drawable.ic_cp_basal_tbr_high
percentRate < 100 -> R.drawable.ic_cp_basal_tbr_low
else -> R.drawable.ic_cp_basal_no_tbr
}
} ?: R.drawable.ic_cp_basal_no_tbr
}
} ?: R.drawable.ic_cp_basal_no_tbr
val temporaryBasalColor: Int
get() = iobCobCalculator.getTempBasalIncludingConvertedExtended(dateUtil.now())?.let { rh.gc(R.color.basal) }
?: rh.gc(R.color.defaulttextcolor)
// will be removed if a solution of getting the right color for widget is solved
fun temporaryBasalColor(iobCobCalculator: IobCobCalculator): Int =
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
*/
val extendedBolusText: String
get() =
iobCobCalculator.getExtendedBolus(dateUtil.now())?.let { extendedBolus ->
if (!extendedBolus.isInProgress(dateUtil)) ""
else if (!activePlugin.activePump.isFakingTempsByExtendedBoluses) rh.gs(R.string.pump_basebasalrate, extendedBolus.rate)
else ""
} ?: ""
fun extendedBolusText(iobCobCalculator: IobCobCalculator): String =
iobCobCalculator.getExtendedBolus(dateUtil.now())?.let { extendedBolus ->
if (!extendedBolus.isInProgress(dateUtil)) ""
else if (!activePlugin.activePump.isFakingTempsByExtendedBoluses) rh.gs(R.string.pump_basebasalrate, extendedBolus.rate)
else ""
} ?: ""
val extendedBolusDialogText: String
get() = iobCobCalculator.getExtendedBolus(dateUtil.now())?.toStringFull(dateUtil) ?: ""
fun extendedBolusDialogText(iobCobCalculator: IobCobCalculator): String =
iobCobCalculator.getExtendedBolus(dateUtil.now())?.toStringFull(dateUtil) ?: ""
/*
* IOB, COB
*/
var bolusIob: IobTotal? = null
var basalIob: IobTotal? = null
var cobInfo: CobInfo? = null
var lastCarbsTime: Long = 0L
fun bolusIob(iobCobCalculator: IobCobCalculator): IobTotal = iobCobCalculator.calculateIobFromBolus().round()
fun basalIob(iobCobCalculator: IobCobCalculator): IobTotal = iobCobCalculator.calculateIobFromTempBasalsIncludingConvertedExtended().round()
fun cobInfo(iobCobCalculator: IobCobCalculator): CobInfo = iobCobCalculator.getCobInfo(true, "Overview COB")
val iobText: String
get() =
bolusIob?.let { bolusIob ->
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 lastCarbsTime: Long
get() = repository.getLastCarbsRecordWrapped().blockingGet().let { lastCarbs ->
if (lastCarbs is ValueWrapper.Existing) lastCarbs.value.timestamp else 0L
}
val iobDialogText: String
get() =
bolusIob?.let { bolusIob ->
basalIob?.let { basalIob ->
rh.gs(R.string.formatinsulinunits, bolusIob.iob + basalIob.basaliob) + "\n" +
rh.gs(R.string.bolus) + ": " + rh.gs(R.string.formatinsulinunits, bolusIob.iob) + "\n" +
rh.gs(R.string.basal) + ": " + rh.gs(R.string.formatinsulinunits, basalIob.basaliob)
} ?: rh.gs(R.string.value_unavailable_short)
} ?: rh.gs(R.string.value_unavailable_short)
fun iobText(iobCobCalculator: IobCobCalculator): String =
rh.gs(R.string.formatinsulinunits, bolusIob(iobCobCalculator).iob + basalIob(iobCobCalculator).basaliob)
fun iobDialogText(iobCobCalculator: IobCobCalculator): String =
rh.gs(R.string.formatinsulinunits, bolusIob(iobCobCalculator).iob + basalIob(iobCobCalculator).basaliob) + "\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(iobCobCalculator).basaliob)
/*
* 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
*/
var lastAutosensData: AutosensData? = null
fun lastAutosensData(iobCobCalculator: IobCobCalculator) = iobCobCalculator.ads.getLastAutosensData("Overview", aapsLogger, dateUtil)
/*
* Graphs
*/
@ -312,532 +301,4 @@ class OverviewData @Inject constructor(
val dsMinScale = Scale()
var dsMaxSeries: 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.databinding.OverviewFragmentBinding
import info.nightscout.androidaps.dialogs.*
import info.nightscout.androidaps.events.EventAcceptOpenLoopChange
import info.nightscout.androidaps.events.EventInitializationChanged
import info.nightscout.androidaps.events.EventPreferenceChange
import info.nightscout.androidaps.events.EventPumpStatusChanged
import info.nightscout.androidaps.events.EventRefreshOverview
import info.nightscout.androidaps.events.*
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.valueToUnitsString
import info.nightscout.androidaps.interfaces.*
@ -188,10 +184,6 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
overviewData.rangeToDisplay += 6
overviewData.rangeToDisplay = if (overviewData.rangeToDisplay > 24) 6 else 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))
sp.putBoolean(R.string.key_objectiveusescale, true)
false
@ -227,113 +219,106 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
@Synchronized
override fun 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
.toObservable(EventUpdateOverviewCalcProgress::class.java)
.observeOn(aapsSchedulers.main)
.subscribe({ updateCalcProgress(it.from) }, 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)
.subscribe({ updateCalcProgress() }, fabricPrivacy::logException)
disposable += activePlugin.activeOverview.overviewBus
.toObservable(EventUpdateOverviewIobCob::class.java)
.debounce(1L, TimeUnit.SECONDS)
.observeOn(aapsSchedulers.main)
.subscribe({ updateIobCob(it.from) }, fabricPrivacy::logException)
.subscribe({ updateIobCob() }, fabricPrivacy::logException)
disposable += activePlugin.activeOverview.overviewBus
.toObservable(EventUpdateOverviewSensitivity::class.java)
.debounce(1L, TimeUnit.SECONDS)
.observeOn(aapsSchedulers.main)
.subscribe({ updateSensitivity(it.from) }, fabricPrivacy::logException)
.subscribe({ updateSensitivity() }, fabricPrivacy::logException)
disposable += activePlugin.activeOverview.overviewBus
.toObservable(EventUpdateOverviewGraph::class.java)
.debounce(1L, TimeUnit.SECONDS)
.observeOn(aapsSchedulers.main)
.subscribe({ updateGraph(it.from) }, fabricPrivacy::logException)
.subscribe({ updateGraph() }, fabricPrivacy::logException)
disposable += activePlugin.activeOverview.overviewBus
.toObservable(EventUpdateOverviewPumpStatus::class.java)
.observeOn(aapsSchedulers.main)
.subscribe({ updatePumpStatus(it.from) }, fabricPrivacy::logException)
.subscribe({ updatePumpStatus() }, fabricPrivacy::logException)
disposable += activePlugin.activeOverview.overviewBus
.toObservable(EventUpdateOverviewNotification::class.java)
.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
.toObservable(EventRefreshOverview::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({
if (it.now) overviewPlugin.refreshLoop(it.from)
else scheduleUpdateGUI(it.from)
if (it.now) refreshAll()
else scheduleUpdateGUI()
}, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventAcceptOpenLoopChange::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ scheduleUpdateGUI("EventAcceptOpenLoopChange") }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventInitializationChanged::class.java)
.observeOn(aapsSchedulers.main)
.subscribe({ updateTime("EventInitializationChanged") }, fabricPrivacy::logException)
.subscribe({ scheduleUpdateGUI() }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventPreferenceChange::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ scheduleUpdateGUI("EventPreferenceChange") }, fabricPrivacy::logException)
.subscribe({ scheduleUpdateGUI() }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventNewOpenLoopNotification::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ scheduleUpdateGUI("EventNewOpenLoopNotification") }, fabricPrivacy::logException)
.subscribe({ scheduleUpdateGUI() }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventPumpStatusChanged::class.java)
.observeOn(aapsSchedulers.main)
.delay(30, TimeUnit.MILLISECONDS, aapsSchedulers.main)
.subscribe({
overviewData.pumpStatus = it.getStatus(rh)
updatePumpStatus("EventPumpStatusChanged")
updatePumpStatus()
}, 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 {
overviewPlugin.refreshLoop("refreshLoop")
refreshAll()
handler.postDelayed(refreshLoop, 60 * 1000L)
}
handler.postDelayed(refreshLoop, 60 * 1000L)
updateTime("onResume")
updateCalcProgress("onResume")
updateProfile("onResume")
updateTemporaryBasal("onResume")
updateExtendedBolus("onResume")
updateTemporaryTarget("onResume")
updateBg("onResume")
updateIobCob("onResume")
updateSensitivity("onResume")
updateGraph("onResume")
updatePumpStatus("onResume")
updateNotification("onResume")
refreshAll()
updatePumpStatus()
updateCalcProgress()
}
fun refreshAll() {
runOnUiThread {
updateBg()
updateTime()
updateProfile()
updateTemporaryBasal()
updateExtendedBolus()
updateTemporaryTarget()
updateIobCob()
updateSensitivity()
updateGraph()
updateNotification()
}
}
@Synchronized
@ -563,14 +548,14 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
binding.buttonsLayout.cgmButton.setCompoundDrawablesWithIntrinsicBounds(null, rh.gd(R.drawable.ic_byoda), null, null)
for (drawable in binding.buttonsLayout.cgmButton.compoundDrawables) {
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))
} else if (xDripIsBgSource) {
binding.buttonsLayout.cgmButton.setCompoundDrawablesWithIntrinsicBounds(null, rh.gd(R.drawable.ic_xdrip), null, null)
for (drawable in binding.buttonsLayout.cgmButton.compoundDrawables) {
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))
}
@ -752,11 +737,11 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
var task: Runnable? = null
private fun scheduleUpdateGUI(from: String) {
private fun scheduleUpdateGUI() {
class UpdateRunnable : Runnable {
override fun run() {
overviewPlugin.refreshLoop(from)
refreshAll()
task = null
}
}
@ -766,8 +751,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
}
@SuppressLint("SetTextI18n")
@Suppress("UNUSED_PARAMETER")
fun updateBg(from: String) {
fun updateBg() {
val units = profileFunction.getUnits()
binding.infoLayout.bg.text = overviewData.lastBg?.valueToUnitsString(units)
?: rh.gs(R.string.notavailable)
@ -816,8 +800,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
}
}
@Suppress("UNUSED_PARAMETER")
fun updateProfile(from: String) {
fun updateProfile() {
val profileBackgroundColor =
profileFunction.getProfile()?.let {
if (it is ProfileSealed.EPS) {
@ -849,28 +832,25 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
binding.activeProfile.setTextColor(profileTextColor)
}
@Suppress("UNUSED_PARAMETER")
fun updateTemporaryBasal(from: String) {
binding.infoLayout.baseBasal.text = overviewData.temporaryBasalText
binding.infoLayout.baseBasal.setTextColor(overviewData.temporaryBasalColor)
binding.infoLayout.baseBasalIcon.setImageResource(overviewData.temporaryBasalIcon)
private fun updateTemporaryBasal() {
binding.infoLayout.baseBasal.text = overviewData.temporaryBasalText(iobCobCalculator)
binding.infoLayout.baseBasal.setTextColor(overviewData.temporaryBasalColor(context, iobCobCalculator))
binding.infoLayout.baseBasalIcon.setImageResource(overviewData.temporaryBasalIcon(iobCobCalculator))
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")
fun updateExtendedBolus(from: String) {
private fun updateExtendedBolus() {
val pump = activePlugin.activePump
binding.infoLayout.extendedBolus.text = overviewData.extendedBolusText
binding.infoLayout.extendedBolus.text = overviewData.extendedBolusText(iobCobCalculator)
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()
}
@Suppress("UNUSED_PARAMETER")
fun updateTime(from: String) {
fun updateTime() {
binding.infoLayout.time.text = dateUtil.timeString(dateUtil.now())
// Status lights
val pump = activePlugin.activePump
@ -901,14 +881,13 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
processAps()
}
@Suppress("UNUSED_PARAMETER")
fun updateIobCob(from: String) {
binding.infoLayout.iob.text = overviewData.iobText
fun updateIobCob() {
binding.infoLayout.iob.text = overviewData.iobText(iobCobCalculator)
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
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 lastRun = loop.lastRun
@ -929,10 +908,8 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
}
@SuppressLint("SetTextI18n")
@Suppress("UNUSED_PARAMETER")
fun updateTemporaryTarget(from: String) {
fun updateTemporaryTarget() {
val units = profileFunction.getUnits()
if (overviewData.temporaryTarget?.isInProgress(dateUtil) == false) overviewData.temporaryTarget = null
val tempTarget = overviewData.temporaryTarget
if (tempTarget != null) {
binding.tempTarget.setTextColor(rh.gac(context, R.attr.ribbonTextWarningColor))
@ -957,8 +934,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
}
}
@Suppress("UNUSED_PARAMETER")
fun updateGraph(from: String) {
private fun updateGraph() {
val pump = activePlugin.activePump
val graphData = GraphData(injector, binding.graphsLayout.bgGraph, overviewData)
val menuChartSettings = overviewMenus.setting
@ -1036,14 +1012,12 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
}
}
@Suppress("UNUSED_PARAMETER")
fun updateCalcProgress(from: String) {
private fun updateCalcProgress() {
binding.progressBar.progress = overviewData.calcProgressPct
binding.progressBar.visibility = (overviewData.calcProgressPct != 100).toVisibility()
}
@Suppress("UNUSED_PARAMETER")
fun updateSensitivity(from: String) {
private fun updateSensitivity() {
if (sp.getBoolean(R.string.key_openapsama_useautosens, false) && constraintChecker.isAutosensModeEnabled().value()) {
binding.infoLayout.sensitivityIcon.setImageResource(R.drawable.ic_swap_vert_black_48dp_green)
} else {
@ -1051,20 +1025,18 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
}
binding.infoLayout.sensitivity.text =
overviewData.lastAutosensData?.let { autosensData ->
overviewData.lastAutosensData(iobCobCalculator)?.let { autosensData ->
String.format(Locale.ENGLISH, "%.0f%%", autosensData.autosensResult.ratio * 100)
} ?: ""
}
@Suppress("UNUSED_PARAMETER")
fun updatePumpStatus(from: String) {
private fun updatePumpStatus() {
val status = overviewData.pumpStatus
binding.pumpStatus.text = status
binding.pumpStatusLayout.visibility = (status != "").toVisibility()
}
@Suppress("UNUSED_PARAMETER")
fun updateNotification(from: String) {
private fun updateNotification() {
binding.notifications.let { notificationStore.updateNotifications(it) }
}
}

View file

@ -4,25 +4,26 @@ import androidx.preference.PreferenceFragmentCompat
import androidx.preference.SwitchPreference
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.events.*
import info.nightscout.androidaps.events.EventPumpStatusChanged
import info.nightscout.androidaps.extensions.*
import info.nightscout.androidaps.interfaces.*
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import info.nightscout.androidaps.plugins.aps.events.EventLoopInvoked
import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.interfaces.Overview
import info.nightscout.androidaps.interfaces.PluginBase
import info.nightscout.androidaps.interfaces.PluginDescription
import info.nightscout.androidaps.interfaces.PluginType
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.ScaledDataPoint
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.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.sharedPreferences.SP
import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign
@ -41,9 +42,6 @@ class OverviewPlugin @Inject constructor(
private val aapsSchedulers: AapsSchedulers,
rh: ResourceHelper,
private val config: Config,
private val dateUtil: DateUtil,
private val iobCobCalculator: IobCobCalculator,
private val repository: AppRepository,
private val overviewData: OverviewData,
private val overviewMenus: OverviewMenus
) : PluginBase(
@ -89,62 +87,9 @@ class OverviewPlugin @Inject constructor(
disposable += rxBus
.toObservable(EventIobCalculationProgress::class.java)
.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({
loadIobCobResults("EventTreatmentChange")
overviewData.prepareTreatmentsData("EventTreatmentChange")
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")
overviewData.calcProgressPct = it.pass.finalPercent(it.progressPct)
overviewBus.send(EventUpdateOverviewCalcProgress("EventIobCalculationProgress"))
}, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventPumpStatusChanged::class.java)
@ -152,20 +97,7 @@ class OverviewPlugin @Inject constructor(
.subscribe({
overviewData.pumpStatus = it.getStatus(rh)
}, 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() {
@ -243,7 +175,7 @@ class OverviewPlugin @Inject constructor(
.storeDouble(R.string.key_statuslights_bat_critical, sp, rh)
.storeInt(R.string.key_boluswizard_percentage, sp, rh)
}
/*
@Volatile
var runningRefresh = false
override fun refreshLoop(from: String) {
@ -284,38 +216,5 @@ class OverviewPlugin @Inject constructor(
overviewBus.send(EventUpdateOverviewGraph(from))
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 info.nightscout.androidaps.Constants
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.valueToUnitsString
import info.nightscout.androidaps.interfaces.*
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatusProvider
import info.nightscout.androidaps.utils.DecimalFormatter
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 io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign
import javax.inject.Inject
@ -72,26 +75,10 @@ class PersistentNotificationPlugin @Inject constructor(
.toObservable(EventRefreshOverview::class.java)
.observeOn(aapsSchedulers.io)
.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
.toObservable(EventInitializationChanged::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ triggerNotificationUpdate() }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventEffectiveProfileSwitchChanged::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ triggerNotificationUpdate() }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventAutosensCalculationFinished::class.java)
.observeOn(aapsSchedulers.io)

View file

@ -1,12 +1,6 @@
package info.nightscout.androidaps.plugins.iob.iobCobCalculator
import android.content.Context
import android.os.SystemClock
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 info.nightscout.androidaps.Constants
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.toTemporaryBasal
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.general.overview.OverviewData
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.data.AutosensData
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.DecimalFormatter
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.androidaps.workflow.CalculationWorkflow
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import info.nightscout.shared.sharedPreferences.SP
import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign
@ -63,14 +55,11 @@ class IobCobCalculatorPlugin @Inject constructor(
rh: ResourceHelper,
private val profileFunction: ProfileFunction,
private val activePlugin: ActivePlugin,
private val sensitivityOref1Plugin: SensitivityOref1Plugin,
private val sensitivityAAPSPlugin: SensitivityAAPSPlugin,
private val sensitivityWeightedAveragePlugin: SensitivityWeightedAveragePlugin,
private val fabricPrivacy: FabricPrivacy,
private val dateUtil: DateUtil,
private val repository: AppRepository,
private val context: Context,
private val dataWorker: DataWorker
val overviewData: OverviewData,
private val calculationWorkflow: CalculationWorkflow
) : PluginBase(
PluginDescription()
.mainType(PluginType.GENERAL)
@ -91,8 +80,6 @@ class IobCobCalculatorPlugin @Inject constructor(
private val dataLock = Any()
private var thread: Thread? = null
private val jobGroupName = "calculation"
override fun onStart() {
super.onStart()
// EventConfigBuilderChange
@ -126,14 +113,6 @@ class IobCobCalculatorPlugin @Inject constructor(
resetDataAndRunCalculation("onEventPreferenceChange", event)
}
}, 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
disposable += rxBus
.toObservable(EventNewHistoryData::class.java)
@ -147,10 +126,10 @@ class IobCobCalculatorPlugin @Inject constructor(
}
private fun resetDataAndRunCalculation(reason: String, event: Event?) {
stopCalculation(reason)
calculationWorkflow.stopCalculation(CalculationWorkflow.MAIN_CALCULATION,reason)
clearCache()
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() {
@ -310,11 +289,7 @@ class IobCobCalculatorPlugin @Inject constructor(
override fun getMealDataWithWaitingForCalculationFinish(): MealData {
val result = MealData()
val now = System.currentTimeMillis()
val maxAbsorptionHours: Double = if (sensitivityAAPSPlugin.isEnabled() || sensitivityWeightedAveragePlugin.isEnabled()) {
sp.getDouble(R.string.key_absorption_maxtime, Constants.DEFAULT_MAX_ABSORPTION_TIME)
} else {
sp.getDouble(R.string.key_absorption_cutoff, Constants.DEFAULT_MAX_ABSORPTION_TIME)
}
val maxAbsorptionHours: Double = activePlugin.activeSensitivity.maxAbsorptionHours()
val absorptionTimeAgo = now - (maxAbsorptionHours * T.hours(1).msecs()).toLong()
repository.getCarbsDataFromTimeToTimeExpanded(absorptionTimeAgo + 1, now, true)
.blockingGet()
@ -374,34 +349,6 @@ class IobCobCalculatorPlugin @Inject constructor(
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
private val historyWorker = Executors.newSingleThreadScheduledExecutor()
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
private fun newHistoryData(oldDataTimestamp: Long, bgDataReload: Boolean, event: Event) {
//log.debug("Locking onNewHistoryData");
stopCalculation("onEventNewHistoryData")
calculationWorkflow.stopCalculation(CalculationWorkflow.MAIN_CALCULATION,"onEventNewHistoryData")
synchronized(dataLock) {
// clear up 5 min back for proper COB calculation
@ -467,7 +414,7 @@ class IobCobCalculatorPlugin @Inject constructor(
}
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");
}

View file

@ -32,6 +32,7 @@ import info.nightscout.androidaps.utils.Profiler
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.buildHelper.BuildHelper
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.workflow.CalculationWorkflow
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import info.nightscout.shared.sharedPreferences.SP
@ -62,6 +63,7 @@ class IobCobOref1Worker(
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var repository: AppRepository
@Inject lateinit var dataWorker: DataWorker
@Inject lateinit var calculationWorkflow: CalculationWorkflow
init {
(context.applicationContext as HasAndroidInjector).androidInjector().inject(this)
@ -69,10 +71,9 @@ class IobCobOref1Worker(
class IobCobOref1WorkerData(
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 end: Long,
val bgDataReload: Boolean,
val limitDataToOldestAvailable: Boolean,
val cause: Event?
)
@ -90,13 +91,9 @@ class IobCobOref1Worker(
return Result.failure(workDataOf("Error" to "app still initializing"))
}
//log.debug("Locking calculateSensitivityData");
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()
}
val oldestTimeWithData = data.iobCobCalculator.calculateDetectionStart(data.end, data.limitDataToOldestAvailable)
// 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 autosensDataTable = ads.autosensDataTable
if (bucketedData == null || bucketedData.size < 3) {
@ -108,8 +105,7 @@ class IobCobOref1Worker(
var previous = autosensDataTable[prevDataTime]
// start from oldest to be able sub cob
for (i in bucketedData.size - 4 downTo 0) {
val progress = i.toString() + if (buildHelper.isDev()) " (${data.from})" else ""
rxBus.send(EventIobCalculationProgress(100 - (100.0 * i / bucketedData.size).toInt(), data.cause))
rxBus.send(EventIobCalculationProgress(CalculationWorkflow.ProgressData.IOB_COB_OREF, 100 - (100.0 * i / bucketedData.size).toInt(), data.cause))
if (isStopped) {
aapsLogger.debug(LTag.AUTOSENS, "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
delta = bg - bucketedData[i + 1].value
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 deviation = delta - bgi
val avgDeviation = ((avgDelta - bgi) * 1000).roundToLong() / 1000.0
@ -332,13 +328,13 @@ class IobCobOref1Worker(
autosensData.autosensResult = sensitivity
aapsLogger.debug(LTag.AUTOSENS, autosensData.toString())
}
data.iobCobCalculatorPlugin.ads = ads
data.iobCobCalculator.ads = ads
Thread {
SystemClock.sleep(1000)
rxBus.send(EventAutosensCalculationFinished(data.cause))
}.start()
} 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}")
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.buildHelper.BuildHelper
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.workflow.CalculationWorkflow
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
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 from: String,
val end: Long,
val bgDataReload: Boolean,
val limitDataToOldestAvailable: Boolean,
val cause: Event?
)
@ -87,10 +87,6 @@ class IobCobOrefWorker @Inject internal constructor(
}
//log.debug("Locking calculateSensitivityData");
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
val ads = data.iobCobCalculatorPlugin.ads.clone()
val bucketedData = ads.bucketedData
@ -104,8 +100,7 @@ class IobCobOrefWorker @Inject internal constructor(
var previous = autosensDataTable[prevDataTime]
// start from oldest to be able sub cob
for (i in bucketedData.size - 4 downTo 0) {
val progress = i.toString() + if (buildHelper.isDev()) " (${data.from})" else ""
rxBus.send(EventIobCalculationProgress(100 - (100.0 * i / bucketedData.size).toInt(), data.cause))
rxBus.send(EventIobCalculationProgress(CalculationWorkflow.ProgressData.IOB_COB_OREF, 100 - (100.0 * i / bucketedData.size).toInt(), data.cause))
if (isStopped) {
aapsLogger.debug(LTag.AUTOSENS, "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))
}.start()
} 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}")
profiler.log(LTag.AUTOSENS, "IobCobThread", start)
}

View file

@ -1,5 +1,6 @@
package info.nightscout.androidaps.plugins.iob.iobCobCalculator.events
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
}
override fun maxAbsorptionHours(): Double = sp.getDouble(R.string.key_absorption_maxtime, Constants.DEFAULT_MAX_ABSORPTION_TIME)
override val id: SensitivityType
get() = SensitivityType.SENSITIVITY_AAPS

View file

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

View file

@ -157,6 +157,8 @@ class SensitivityWeightedAveragePlugin @Inject constructor(
return output
}
override fun maxAbsorptionHours(): Double = sp.getDouble(R.string.key_absorption_maxtime, Constants.DEFAULT_MAX_ABSORPTION_TIME)
override val id: SensitivityType
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>
<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
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -88,13 +99,6 @@
android:paddingTop="5dp"
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>
<LinearLayout

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -5,6 +5,7 @@ import android.os.Looper
import android.view.View
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 {
Handler(Looper.getMainLooper()).post(it)

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -27,7 +27,7 @@ android {
sourceSets {
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('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-guava:1.6.0'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.1'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-guava:1.6.1'
implementation "androidx.core:core-ktx:$core_version"
implementation "androidx.wear.tiles:tiles:1.0.1"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"