MVVM like for overview
This commit is contained in:
parent
3040be317c
commit
a1c1355ce6
28 changed files with 1455 additions and 1138 deletions
|
@ -75,11 +75,11 @@ class CompatDBHelper @Inject constructor(
|
||||||
rxBus.send(EventFoodDatabaseChanged())
|
rxBus.send(EventFoodDatabaseChanged())
|
||||||
}
|
}
|
||||||
it.filterIsInstance<ProfileSwitch>().firstOrNull()?.let {
|
it.filterIsInstance<ProfileSwitch>().firstOrNull()?.let {
|
||||||
aapsLogger.debug(LTag.DATABASE, "Firing EventProfileNeedsUpdate")
|
aapsLogger.debug(LTag.DATABASE, "Firing EventProfileSwitchChanged")
|
||||||
rxBus.send(EventProfileSwitchChanged())
|
rxBus.send(EventProfileSwitchChanged())
|
||||||
}
|
}
|
||||||
it.filterIsInstance<EffectiveProfileSwitch>().firstOrNull()?.let {
|
it.filterIsInstance<EffectiveProfileSwitch>().firstOrNull()?.let {
|
||||||
aapsLogger.debug(LTag.DATABASE, "Firing EventProfileNeedsUpdate")
|
aapsLogger.debug(LTag.DATABASE, "Firing EventProfileSwitchChanged")
|
||||||
rxBus.send(EventProfileSwitchChanged())
|
rxBus.send(EventProfileSwitchChanged())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -279,7 +279,7 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
|
||||||
if (destroyed) return@launch
|
if (destroyed) return@launch
|
||||||
binding.date.text = dateUtil.dateAndTimeString(start)
|
binding.date.text = dateUtil.dateAndTimeString(start)
|
||||||
binding.zoom.text = rangeToDisplay.toString()
|
binding.zoom.text = rangeToDisplay.toString()
|
||||||
val graphData = GraphData(injector, binding.bggraph, iobCobCalculatorPluginHistory)
|
val graphData = GraphData(injector, binding.bggraph)
|
||||||
val secondaryGraphsData: ArrayList<GraphData> = ArrayList()
|
val secondaryGraphsData: ArrayList<GraphData> = ArrayList()
|
||||||
|
|
||||||
// do preparation in different thread
|
// do preparation in different thread
|
||||||
|
@ -293,29 +293,29 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
|
||||||
graphData.addInRangeArea(fromTime, toTime, lowLine, highLine)
|
graphData.addInRangeArea(fromTime, toTime, lowLine, highLine)
|
||||||
|
|
||||||
// **** BG ****
|
// **** BG ****
|
||||||
graphData.addBgReadings(fromTime, toTime, highLine, null)
|
// graphData.addBgReadings(fromTime, toTime, highLine, null)
|
||||||
if (buildHelper.isDev()) graphData.addBucketedData(fromTime, toTime)
|
// if (buildHelper.isDev()) graphData.addBucketedData(fromTime, toTime)
|
||||||
|
|
||||||
// add target line
|
// add target line
|
||||||
graphData.addTargetLine(fromTime, toTime, profile, null)
|
// graphData.addTargetLine(fromTime, toTime, profile, null)
|
||||||
|
|
||||||
// **** NOW line ****
|
// **** NOW line ****
|
||||||
graphData.addNowLine(pointer)
|
graphData.addNowLine(pointer)
|
||||||
|
|
||||||
if (!bgOnly) {
|
if (!bgOnly) {
|
||||||
// Treatments
|
// Treatments
|
||||||
graphData.addTreatments(fromTime, toTime)
|
// graphData.addTreatments(fromTime, toTime)
|
||||||
if (menuChartSettings[0][OverviewMenus.CharType.ACT.ordinal])
|
if (menuChartSettings[0][OverviewMenus.CharType.ACT.ordinal])
|
||||||
graphData.addActivity(fromTime, toTime, false, 0.8)
|
// graphData.addActivity(fromTime, toTime, false, 0.8)
|
||||||
|
|
||||||
// add basal data
|
// add basal data
|
||||||
if (pump.pumpDescription.isTempBasalCapable && menuChartSettings[0][OverviewMenus.CharType.BAS.ordinal]) {
|
if (pump.pumpDescription.isTempBasalCapable && menuChartSettings[0][OverviewMenus.CharType.BAS.ordinal]) {
|
||||||
graphData.addBasals(fromTime, toTime, lowLine / graphData.maxY / 1.2)
|
// graphData.addBasals(fromTime, toTime, lowLine / graphData.maxY / 1.2)
|
||||||
}
|
}
|
||||||
// ------------------ 2nd graph
|
// ------------------ 2nd graph
|
||||||
synchronized(graphLock) {
|
synchronized(graphLock) {
|
||||||
for (g in 0 until secondaryGraphs.size) {
|
for (g in 0 until secondaryGraphs.size) {
|
||||||
val secondGraphData = GraphData(injector, secondaryGraphs[g], iobCobCalculatorPluginHistory)
|
val secondGraphData = GraphData(injector, secondaryGraphs[g])
|
||||||
var useIobForScale = false
|
var useIobForScale = false
|
||||||
var useCobForScale = false
|
var useCobForScale = false
|
||||||
var useDevForScale = false
|
var useDevForScale = false
|
||||||
|
@ -336,13 +336,13 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
|
||||||
val alignIobScale = menuChartSettings[g + 1][OverviewMenus.CharType.ABS.ordinal] && menuChartSettings[g + 1][OverviewMenus.CharType.IOB.ordinal]
|
val alignIobScale = menuChartSettings[g + 1][OverviewMenus.CharType.ABS.ordinal] && menuChartSettings[g + 1][OverviewMenus.CharType.IOB.ordinal]
|
||||||
val alignDevBgiScale = menuChartSettings[g + 1][OverviewMenus.CharType.DEV.ordinal] && menuChartSettings[g + 1][OverviewMenus.CharType.BGI.ordinal]
|
val alignDevBgiScale = menuChartSettings[g + 1][OverviewMenus.CharType.DEV.ordinal] && menuChartSettings[g + 1][OverviewMenus.CharType.BGI.ordinal]
|
||||||
|
|
||||||
if (menuChartSettings[g + 1][OverviewMenus.CharType.ABS.ordinal]) secondGraphData.addAbsIob(fromTime, toTime, useABSForScale, 1.0)
|
// if (menuChartSettings[g + 1][OverviewMenus.CharType.ABS.ordinal]) secondGraphData.addAbsIob(fromTime, toTime, useABSForScale, 1.0)
|
||||||
if (menuChartSettings[g + 1][OverviewMenus.CharType.IOB.ordinal]) secondGraphData.addIob(fromTime, toTime, useIobForScale, 1.0, menuChartSettings[g + 1][OverviewMenus.CharType.PRE.ordinal], alignIobScale)
|
// if (menuChartSettings[g + 1][OverviewMenus.CharType.IOB.ordinal]) secondGraphData.addIob(fromTime, toTime, useIobForScale, 1.0, menuChartSettings[g + 1][OverviewMenus.CharType.PRE.ordinal], alignIobScale)
|
||||||
if (menuChartSettings[g + 1][OverviewMenus.CharType.COB.ordinal]) secondGraphData.addCob(fromTime, toTime, useCobForScale, if (useCobForScale) 1.0 else 0.5)
|
// if (menuChartSettings[g + 1][OverviewMenus.CharType.COB.ordinal]) secondGraphData.addCob(fromTime, toTime, useCobForScale, if (useCobForScale) 1.0 else 0.5)
|
||||||
if (menuChartSettings[g + 1][OverviewMenus.CharType.DEV.ordinal]) secondGraphData.addDeviations(fromTime, toTime, useDevForScale, 1.0, alignDevBgiScale)
|
// if (menuChartSettings[g + 1][OverviewMenus.CharType.DEV.ordinal]) secondGraphData.addDeviations(fromTime, toTime, useDevForScale, 1.0, alignDevBgiScale)
|
||||||
if (menuChartSettings[g + 1][OverviewMenus.CharType.SEN.ordinal]) secondGraphData.addRatio(fromTime, toTime, useRatioForScale, 1.0)
|
// if (menuChartSettings[g + 1][OverviewMenus.CharType.SEN.ordinal]) secondGraphData.addRatio(fromTime, toTime, useRatioForScale, 1.0)
|
||||||
if (menuChartSettings[g + 1][OverviewMenus.CharType.BGI.ordinal]) secondGraphData.addMinusBGI(fromTime, toTime, useBGIForScale, if (alignDevBgiScale) 1.0 else 0.8, alignDevBgiScale)
|
// if (menuChartSettings[g + 1][OverviewMenus.CharType.BGI.ordinal]) secondGraphData.addMinusBGI(fromTime, toTime, useBGIForScale, if (alignDevBgiScale) 1.0 else 0.8, alignDevBgiScale)
|
||||||
if (menuChartSettings[g + 1][OverviewMenus.CharType.DEVSLOPE.ordinal] && buildHelper.isDev()) secondGraphData.addDeviationSlope(fromTime, toTime, useDSForScale, 1.0)
|
// if (menuChartSettings[g + 1][OverviewMenus.CharType.DEVSLOPE.ordinal] && buildHelper.isDev()) secondGraphData.addDeviationSlope(fromTime, toTime, useDSForScale, 1.0)
|
||||||
|
|
||||||
// set manual x bounds to have nice steps
|
// set manual x bounds to have nice steps
|
||||||
secondGraphData.formatAxis(fromTime, toTime)
|
secondGraphData.formatAxis(fromTime, toTime)
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
package info.nightscout.androidaps.plugins.aps.events
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.events.Event
|
||||||
|
|
||||||
|
class EventLoopInvoked : Event()
|
|
@ -54,6 +54,7 @@ import info.nightscout.androidaps.extensions.buildDeviceStatus
|
||||||
import info.nightscout.androidaps.extensions.convertedToAbsolute
|
import info.nightscout.androidaps.extensions.convertedToAbsolute
|
||||||
import info.nightscout.androidaps.extensions.convertedToPercent
|
import info.nightscout.androidaps.extensions.convertedToPercent
|
||||||
import info.nightscout.androidaps.extensions.plannedRemainingMinutes
|
import info.nightscout.androidaps.extensions.plannedRemainingMinutes
|
||||||
|
import info.nightscout.androidaps.plugins.aps.events.EventLoopInvoked
|
||||||
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
||||||
import info.nightscout.androidaps.utils.rx.AapsSchedulers
|
import info.nightscout.androidaps.utils.rx.AapsSchedulers
|
||||||
import info.nightscout.androidaps.utils.sharedPreferences.SP
|
import info.nightscout.androidaps.utils.sharedPreferences.SP
|
||||||
|
@ -286,7 +287,7 @@ open class LoopPlugin @Inject constructor(
|
||||||
if (apsResult == null) {
|
if (apsResult == null) {
|
||||||
rxBus.send(EventLoopSetLastRunGui(resourceHelper.gs(R.string.noapsselected)))
|
rxBus.send(EventLoopSetLastRunGui(resourceHelper.gs(R.string.noapsselected)))
|
||||||
return
|
return
|
||||||
}
|
} else rxBus.send(EventLoopInvoked())
|
||||||
|
|
||||||
// Prepare for pumps using % basals
|
// Prepare for pumps using % basals
|
||||||
if (pump.pumpDescription.tempBasalStyle == PumpDescription.PERCENT && allowPercentage()) {
|
if (pump.pumpDescription.tempBasalStyle == PumpDescription.PERCENT && allowPercentage()) {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package info.nightscout.androidaps.plugins.configBuilder
|
package info.nightscout.androidaps.plugins.configBuilder
|
||||||
|
|
||||||
|
import androidx.collection.LongSparseArray
|
||||||
import info.nightscout.androidaps.Constants
|
import info.nightscout.androidaps.Constants
|
||||||
import info.nightscout.androidaps.core.R
|
import info.nightscout.androidaps.core.R
|
||||||
import info.nightscout.androidaps.data.ProfileSealed
|
import info.nightscout.androidaps.data.ProfileSealed
|
||||||
|
@ -35,6 +36,8 @@ class ProfileFunctionImplementation @Inject constructor(
|
||||||
private val dateUtil: DateUtil
|
private val dateUtil: DateUtil
|
||||||
) : ProfileFunction {
|
) : ProfileFunction {
|
||||||
|
|
||||||
|
val cache = LongSparseArray<Profile>()
|
||||||
|
|
||||||
private val disposable = CompositeDisposable()
|
private val disposable = CompositeDisposable()
|
||||||
|
|
||||||
override fun getProfileName(): String =
|
override fun getProfileName(): String =
|
||||||
|
@ -65,10 +68,20 @@ class ProfileFunctionImplementation @Inject constructor(
|
||||||
getProfile(dateUtil.now())
|
getProfile(dateUtil.now())
|
||||||
|
|
||||||
override fun getProfile(time: Long): Profile? {
|
override fun getProfile(time: Long): Profile? {
|
||||||
|
val rounded = time - time % 1000
|
||||||
|
val cached = cache[rounded]
|
||||||
|
if (cached != null) {
|
||||||
|
// aapsLogger.debug("XXXXXXXXXXXXXXX HIT getProfile for $time $rounded")
|
||||||
|
return cached
|
||||||
|
}
|
||||||
// aapsLogger.debug("XXXXXXXXXXXXXXX getProfile called for $time")
|
// aapsLogger.debug("XXXXXXXXXXXXXXX getProfile called for $time")
|
||||||
val ps = repository.getEffectiveProfileSwitchActiveAt(time).blockingGet()
|
val ps = repository.getEffectiveProfileSwitchActiveAt(time).blockingGet()
|
||||||
return if (ps is ValueWrapper.Existing) ProfileSealed.EPS(ps.value)
|
if (ps is ValueWrapper.Existing) {
|
||||||
else null
|
val sealed = ProfileSealed.EPS(ps.value)
|
||||||
|
cache.put(rounded, sealed)
|
||||||
|
return sealed
|
||||||
|
}
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getRequestedProfile(): ProfileSwitch? = repository.getActiveProfileSwitch(dateUtil.now())
|
override fun getRequestedProfile(): ProfileSwitch? = repository.getActiveProfileSwitch(dateUtil.now())
|
||||||
|
|
|
@ -11,7 +11,6 @@ import android.widget.LinearLayout
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import dagger.android.support.DaggerFragment
|
import dagger.android.support.DaggerFragment
|
||||||
import info.nightscout.androidaps.interfaces.Config
|
|
||||||
import info.nightscout.androidaps.Constants
|
import info.nightscout.androidaps.Constants
|
||||||
import info.nightscout.androidaps.R
|
import info.nightscout.androidaps.R
|
||||||
import info.nightscout.androidaps.activities.ErrorHelperActivity
|
import info.nightscout.androidaps.activities.ErrorHelperActivity
|
||||||
|
@ -21,13 +20,18 @@ import info.nightscout.androidaps.database.ValueWrapper
|
||||||
import info.nightscout.androidaps.database.entities.UserEntry.Action
|
import info.nightscout.androidaps.database.entities.UserEntry.Action
|
||||||
import info.nightscout.androidaps.database.entities.UserEntry.Sources
|
import info.nightscout.androidaps.database.entities.UserEntry.Sources
|
||||||
import info.nightscout.androidaps.dialogs.*
|
import info.nightscout.androidaps.dialogs.*
|
||||||
import info.nightscout.androidaps.events.*
|
import info.nightscout.androidaps.events.EventCustomActionsChanged
|
||||||
|
import info.nightscout.androidaps.events.EventExtendedBolusChange
|
||||||
|
import info.nightscout.androidaps.events.EventInitializationChanged
|
||||||
|
import info.nightscout.androidaps.events.EventTempBasalChange
|
||||||
|
import info.nightscout.androidaps.events.EventTherapyEventChange
|
||||||
import info.nightscout.androidaps.extensions.toStringMedium
|
import info.nightscout.androidaps.extensions.toStringMedium
|
||||||
import info.nightscout.androidaps.extensions.toStringShort
|
import info.nightscout.androidaps.extensions.toStringShort
|
||||||
import info.nightscout.androidaps.extensions.toVisibility
|
import info.nightscout.androidaps.extensions.toVisibility
|
||||||
import info.nightscout.androidaps.historyBrowser.HistoryBrowseActivity
|
import info.nightscout.androidaps.historyBrowser.HistoryBrowseActivity
|
||||||
import info.nightscout.androidaps.interfaces.ActivePlugin
|
import info.nightscout.androidaps.interfaces.ActivePlugin
|
||||||
import info.nightscout.androidaps.interfaces.CommandQueueProvider
|
import info.nightscout.androidaps.interfaces.CommandQueueProvider
|
||||||
|
import info.nightscout.androidaps.interfaces.Config
|
||||||
import info.nightscout.androidaps.interfaces.IobCobCalculator
|
import info.nightscout.androidaps.interfaces.IobCobCalculator
|
||||||
import info.nightscout.androidaps.interfaces.ProfileFunction
|
import info.nightscout.androidaps.interfaces.ProfileFunction
|
||||||
import info.nightscout.androidaps.logging.AAPSLogger
|
import info.nightscout.androidaps.logging.AAPSLogger
|
||||||
|
@ -229,10 +233,6 @@ class ActionsFragment : DaggerFragment() {
|
||||||
.toObservable(EventInitializationChanged::class.java)
|
.toObservable(EventInitializationChanged::class.java)
|
||||||
.observeOn(aapsSchedulers.main)
|
.observeOn(aapsSchedulers.main)
|
||||||
.subscribe({ updateGui() }, fabricPrivacy::logException)
|
.subscribe({ updateGui() }, fabricPrivacy::logException)
|
||||||
disposable += rxBus
|
|
||||||
.toObservable(EventRefreshOverview::class.java)
|
|
||||||
.observeOn(aapsSchedulers.main)
|
|
||||||
.subscribe({ updateGui() }, fabricPrivacy::logException)
|
|
||||||
disposable += rxBus
|
disposable += rxBus
|
||||||
.toObservable(EventExtendedBolusChange::class.java)
|
.toObservable(EventExtendedBolusChange::class.java)
|
||||||
.observeOn(aapsSchedulers.main)
|
.observeOn(aapsSchedulers.main)
|
||||||
|
|
|
@ -1,32 +1,133 @@
|
||||||
package info.nightscout.androidaps.plugins.general.overview
|
package info.nightscout.androidaps.plugins.general.overview
|
||||||
|
|
||||||
|
import com.jjoe64.graphview.series.BarGraphSeries
|
||||||
|
import com.jjoe64.graphview.series.DataPoint
|
||||||
|
import com.jjoe64.graphview.series.LineGraphSeries
|
||||||
import info.nightscout.androidaps.R
|
import info.nightscout.androidaps.R
|
||||||
|
import info.nightscout.androidaps.data.IobTotal
|
||||||
|
import info.nightscout.androidaps.database.entities.ExtendedBolus
|
||||||
|
import info.nightscout.androidaps.database.entities.GlucoseValue
|
||||||
import info.nightscout.androidaps.database.entities.TemporaryBasal
|
import info.nightscout.androidaps.database.entities.TemporaryBasal
|
||||||
|
import info.nightscout.androidaps.database.entities.TemporaryTarget
|
||||||
import info.nightscout.androidaps.extensions.convertedToPercent
|
import info.nightscout.androidaps.extensions.convertedToPercent
|
||||||
import info.nightscout.androidaps.extensions.toStringFull
|
import info.nightscout.androidaps.extensions.toStringFull
|
||||||
import info.nightscout.androidaps.extensions.toStringShort
|
import info.nightscout.androidaps.extensions.toStringShort
|
||||||
|
import info.nightscout.androidaps.extensions.valueToUnits
|
||||||
|
import info.nightscout.androidaps.interfaces.ActivePlugin
|
||||||
import info.nightscout.androidaps.interfaces.Profile
|
import info.nightscout.androidaps.interfaces.Profile
|
||||||
|
import info.nightscout.androidaps.interfaces.ProfileFunction
|
||||||
|
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.DataPointWithLabelInterface
|
||||||
|
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.FixedLineGraphSeries
|
||||||
|
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.PointsWithLabelGraphSeries
|
||||||
|
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.Scale
|
||||||
|
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.ScaledDataPoint
|
||||||
|
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.CobInfo
|
||||||
|
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.data.AutosensData
|
||||||
import info.nightscout.androidaps.utils.DateUtil
|
import info.nightscout.androidaps.utils.DateUtil
|
||||||
|
import info.nightscout.androidaps.utils.DefaultValueHelper
|
||||||
|
import info.nightscout.androidaps.utils.T
|
||||||
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
||||||
|
import info.nightscout.androidaps.utils.sharedPreferences.SP
|
||||||
|
import java.util.*
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
import kotlin.collections.ArrayList
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
class OverviewData @Inject constructor(
|
class OverviewData @Inject constructor(
|
||||||
private val resourceHelper: ResourceHelper,
|
private val resourceHelper: ResourceHelper,
|
||||||
private val dateUtil: DateUtil
|
private val dateUtil: DateUtil,
|
||||||
|
private val sp: SP,
|
||||||
|
private val activePlugin: ActivePlugin,
|
||||||
|
private val defaultValueHelper: DefaultValueHelper,
|
||||||
|
private val profileFunction: ProfileFunction
|
||||||
) {
|
) {
|
||||||
|
|
||||||
enum class Property {
|
enum class Property {
|
||||||
|
TIME,
|
||||||
|
CALC_PROGRESS,
|
||||||
PROFILE,
|
PROFILE,
|
||||||
TEMPORARY_BASAL
|
TEMPORARY_BASAL,
|
||||||
|
EXTENDED_BOLUS,
|
||||||
|
TEMPORARY_TARGET,
|
||||||
|
BG,
|
||||||
|
IOB_COB,
|
||||||
|
SENSITIVITY,
|
||||||
|
GRAPH
|
||||||
}
|
}
|
||||||
|
|
||||||
@get:Synchronized @set:Synchronized
|
var rangeToDisplay = 6 // for graph
|
||||||
var profile: Profile? = null
|
var toTime: Long = 0
|
||||||
|
var fromTime: Long = 0
|
||||||
|
var endTime: Long = 0
|
||||||
|
|
||||||
@get:Synchronized @set:Synchronized
|
fun initRange() {
|
||||||
|
rangeToDisplay = sp.getInt(R.string.key_rangetodisplay, 6)
|
||||||
|
|
||||||
|
val calendar = Calendar.getInstance().also {
|
||||||
|
it.timeInMillis = System.currentTimeMillis()
|
||||||
|
it[Calendar.MILLISECOND] = 0
|
||||||
|
it[Calendar.SECOND] = 0
|
||||||
|
it[Calendar.MINUTE] = 0
|
||||||
|
it.add(Calendar.HOUR, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
toTime = calendar.timeInMillis + 100000 // little bit more to avoid wrong rounding - GraphView specific
|
||||||
|
fromTime = toTime - T.hours(rangeToDisplay.toLong()).msecs()
|
||||||
|
endTime = toTime
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* PROFILE
|
||||||
|
*/
|
||||||
|
var profile: Profile? = null
|
||||||
var profileName: String? = null
|
var profileName: String? = null
|
||||||
|
var profileNameWithRemainingTime: String? = null
|
||||||
|
|
||||||
|
val profileBackgroudColor: Int
|
||||||
|
get() =
|
||||||
|
profile?.let { profile ->
|
||||||
|
if (profile.percentage != 100 || profile.timeshift != 0) resourceHelper.gc(R.color.ribbonWarning)
|
||||||
|
else resourceHelper.gc(R.color.ribbonDefault)
|
||||||
|
} ?: resourceHelper.gc(R.color.ribbonTextDefault)
|
||||||
|
|
||||||
|
val profileTextColor: Int
|
||||||
|
get() =
|
||||||
|
profile?.let { profile ->
|
||||||
|
if (profile.percentage != 100 || profile.timeshift != 0) resourceHelper.gc(R.color.ribbonTextWarning)
|
||||||
|
else resourceHelper.gc(R.color.ribbonTextDefault)
|
||||||
|
} ?: resourceHelper.gc(R.color.ribbonTextDefault)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CALC PROGRESS
|
||||||
|
*/
|
||||||
|
|
||||||
|
var calcProgress: String = ""
|
||||||
|
|
||||||
|
/*
|
||||||
|
* BG
|
||||||
|
*/
|
||||||
|
|
||||||
|
var lastBg: GlucoseValue? = null
|
||||||
|
|
||||||
|
val lastBgColor: Int
|
||||||
|
get() = lastBg?.let { lastBg ->
|
||||||
|
when {
|
||||||
|
lastBg.valueToUnits(profileFunction.getUnits()) < defaultValueHelper.determineLowLine() -> resourceHelper.gc(R.color.low)
|
||||||
|
lastBg.valueToUnits(profileFunction.getUnits()) > defaultValueHelper.determineHighLine() -> resourceHelper.gc(R.color.high)
|
||||||
|
else -> resourceHelper.gc(R.color.inrange)
|
||||||
|
}
|
||||||
|
} ?: resourceHelper.gc(R.color.inrange)
|
||||||
|
|
||||||
|
val isActualBg: Boolean
|
||||||
|
get() =
|
||||||
|
lastBg?.let { lastBg ->
|
||||||
|
lastBg.timestamp > dateUtil.now() - T.mins(9).msecs()
|
||||||
|
} ?: false
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TEMPORARY BASAL
|
||||||
|
*/
|
||||||
|
|
||||||
var temporaryBasal: TemporaryBasal? = null
|
var temporaryBasal: TemporaryBasal? = null
|
||||||
|
|
||||||
|
@ -62,4 +163,119 @@ class OverviewData @Inject constructor(
|
||||||
val temporaryBasalColor: Int
|
val temporaryBasalColor: Int
|
||||||
get() = temporaryBasal?.let { resourceHelper.gc(R.color.basal) }
|
get() = temporaryBasal?.let { resourceHelper.gc(R.color.basal) }
|
||||||
?: resourceHelper.gc(R.color.defaulttextcolor)
|
?: resourceHelper.gc(R.color.defaulttextcolor)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* EXTENDED BOLUS
|
||||||
|
*/
|
||||||
|
|
||||||
|
var extendedBolus: ExtendedBolus? = null
|
||||||
|
|
||||||
|
val extendedBolusText: String
|
||||||
|
get() =
|
||||||
|
extendedBolus?.let { extendedBolus ->
|
||||||
|
if (activePlugin.activePump.isFakingTempsByExtendedBoluses) resourceHelper.gs(R.string.pump_basebasalrate, extendedBolus.rate)
|
||||||
|
else ""
|
||||||
|
} ?: ""
|
||||||
|
|
||||||
|
val extendedBolusDialogText: String
|
||||||
|
get() = extendedBolus?.toStringFull(dateUtil) ?: ""
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IOB, COB
|
||||||
|
*/
|
||||||
|
|
||||||
|
var bolusIob: IobTotal? = null
|
||||||
|
var basalIob: IobTotal? = null
|
||||||
|
var cobInfo: CobInfo? = null
|
||||||
|
var lastCarbsTime: Long = 0L
|
||||||
|
|
||||||
|
val iobText: String
|
||||||
|
get() =
|
||||||
|
bolusIob?.let { bolusIob ->
|
||||||
|
basalIob?.let { basalIob ->
|
||||||
|
resourceHelper.gs(R.string.formatinsulinunits, bolusIob.iob + basalIob.basaliob)
|
||||||
|
} ?: resourceHelper.gs(R.string.value_unavailable_short)
|
||||||
|
} ?: resourceHelper.gs(R.string.value_unavailable_short)
|
||||||
|
|
||||||
|
val iobDialogText: String
|
||||||
|
get() =
|
||||||
|
bolusIob?.let { bolusIob ->
|
||||||
|
basalIob?.let { basalIob ->
|
||||||
|
resourceHelper.gs(R.string.formatinsulinunits, bolusIob.iob + basalIob.basaliob) + "\n" +
|
||||||
|
resourceHelper.gs(R.string.bolus) + ": " + resourceHelper.gs(R.string.formatinsulinunits, bolusIob.iob) + "\n" +
|
||||||
|
resourceHelper.gs(R.string.basal) + ": " + resourceHelper.gs(R.string.formatinsulinunits, basalIob.basaliob)
|
||||||
|
} ?: resourceHelper.gs(R.string.value_unavailable_short)
|
||||||
|
} ?: resourceHelper.gs(R.string.value_unavailable_short)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TEMP TARGET
|
||||||
|
*/
|
||||||
|
|
||||||
|
var temporarytarget: TemporaryTarget? = null
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SENSITIVITY
|
||||||
|
*/
|
||||||
|
|
||||||
|
var lastAutosensData: AutosensData? = null
|
||||||
|
/*
|
||||||
|
* Graphs
|
||||||
|
*/
|
||||||
|
|
||||||
|
var bgReadingsArray: List<GlucoseValue> = ArrayList()
|
||||||
|
var maxBgValue = Double.MIN_VALUE
|
||||||
|
var bucketedGraphSeries: PointsWithLabelGraphSeries<DataPointWithLabelInterface> = PointsWithLabelGraphSeries()
|
||||||
|
var bgReadingGraphSeries: PointsWithLabelGraphSeries<DataPointWithLabelInterface> = PointsWithLabelGraphSeries()
|
||||||
|
var predictionsGraphSeries: PointsWithLabelGraphSeries<DataPointWithLabelInterface> = PointsWithLabelGraphSeries()
|
||||||
|
|
||||||
|
var maxBasalValueFound = 0.0
|
||||||
|
val basalScale = Scale()
|
||||||
|
var baseBasalGraphSeries: LineGraphSeries<ScaledDataPoint> = LineGraphSeries()
|
||||||
|
var tempBasalGraphSeries: LineGraphSeries<ScaledDataPoint> = LineGraphSeries()
|
||||||
|
var basalLineGraphSeries: LineGraphSeries<ScaledDataPoint> = LineGraphSeries()
|
||||||
|
var absoluteBasalGraphSeries: LineGraphSeries<ScaledDataPoint> = LineGraphSeries()
|
||||||
|
|
||||||
|
var temporaryTargetSeries: LineGraphSeries<DataPoint> = LineGraphSeries()
|
||||||
|
|
||||||
|
var maxIAValue = 0.0
|
||||||
|
val actScale = Scale()
|
||||||
|
var activitySeries: FixedLineGraphSeries<ScaledDataPoint> = FixedLineGraphSeries()
|
||||||
|
var activityPredictionSeries: FixedLineGraphSeries<ScaledDataPoint> = FixedLineGraphSeries()
|
||||||
|
|
||||||
|
var maxTreatmentsValue = 0.0
|
||||||
|
var treatmentsSeries: PointsWithLabelGraphSeries<DataPointWithLabelInterface> = PointsWithLabelGraphSeries()
|
||||||
|
|
||||||
|
var maxIobValueFound = Double.MIN_VALUE
|
||||||
|
val iobScale = Scale()
|
||||||
|
var iobSeries: FixedLineGraphSeries<ScaledDataPoint> = FixedLineGraphSeries()
|
||||||
|
var absIobSeries: FixedLineGraphSeries<ScaledDataPoint> = FixedLineGraphSeries()
|
||||||
|
var iobPredictions1Series: PointsWithLabelGraphSeries<DataPointWithLabelInterface> = PointsWithLabelGraphSeries()
|
||||||
|
var iobPredictions2Series: PointsWithLabelGraphSeries<DataPointWithLabelInterface> = PointsWithLabelGraphSeries()
|
||||||
|
|
||||||
|
var maxBGIValue = Double.MIN_VALUE
|
||||||
|
val bgiScale = Scale()
|
||||||
|
var minusBgiSeries: FixedLineGraphSeries<ScaledDataPoint> = FixedLineGraphSeries()
|
||||||
|
var minusBgiHistSeries: FixedLineGraphSeries<ScaledDataPoint> = FixedLineGraphSeries()
|
||||||
|
|
||||||
|
var maxCobValueFound = Double.MIN_VALUE
|
||||||
|
val cobScale = Scale()
|
||||||
|
var cobSeries: FixedLineGraphSeries<ScaledDataPoint> = FixedLineGraphSeries()
|
||||||
|
var cobMinFailOverSeries: PointsWithLabelGraphSeries<DataPointWithLabelInterface> = PointsWithLabelGraphSeries()
|
||||||
|
|
||||||
|
var maxDevValueFound = Double.MIN_VALUE
|
||||||
|
val devScale = Scale()
|
||||||
|
var deviationsSeries: BarGraphSeries<OverviewPlugin.DeviationDataPoint> = BarGraphSeries()
|
||||||
|
|
||||||
|
var maxRatioValueFound = 5.0 //even if sens data equals 0 for all the period, minimum scale is between 95% and 105%
|
||||||
|
var minRatioValueFound = -maxRatioValueFound
|
||||||
|
val ratioScale = Scale()
|
||||||
|
var ratioSeries: LineGraphSeries<ScaledDataPoint> = LineGraphSeries()
|
||||||
|
|
||||||
|
var maxFromMaxValueFound = Double.MIN_VALUE
|
||||||
|
var maxFromMinValueFound = Double.MIN_VALUE
|
||||||
|
val dsMaxScale = Scale()
|
||||||
|
val dsMinScale = Scale()
|
||||||
|
var dsMaxSeries: LineGraphSeries<ScaledDataPoint> = LineGraphSeries()
|
||||||
|
var dsMinSeries: LineGraphSeries<ScaledDataPoint> = LineGraphSeries()
|
||||||
|
|
||||||
}
|
}
|
|
@ -20,7 +20,6 @@ import android.widget.LinearLayout
|
||||||
import android.widget.RelativeLayout
|
import android.widget.RelativeLayout
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.core.text.toSpanned
|
import androidx.core.text.toSpanned
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import com.jjoe64.graphview.GraphView
|
import com.jjoe64.graphview.GraphView
|
||||||
import dagger.android.HasAndroidInjector
|
import dagger.android.HasAndroidInjector
|
||||||
|
@ -28,15 +27,19 @@ import dagger.android.support.DaggerFragment
|
||||||
import info.nightscout.androidaps.Constants
|
import info.nightscout.androidaps.Constants
|
||||||
import info.nightscout.androidaps.R
|
import info.nightscout.androidaps.R
|
||||||
import info.nightscout.androidaps.database.AppRepository
|
import info.nightscout.androidaps.database.AppRepository
|
||||||
import info.nightscout.androidaps.database.ValueWrapper
|
|
||||||
import info.nightscout.androidaps.database.entities.TemporaryTarget
|
|
||||||
import info.nightscout.androidaps.database.entities.UserEntry.Action
|
import info.nightscout.androidaps.database.entities.UserEntry.Action
|
||||||
import info.nightscout.androidaps.database.entities.UserEntry.Sources
|
import info.nightscout.androidaps.database.entities.UserEntry.Sources
|
||||||
import info.nightscout.androidaps.database.interfaces.end
|
import info.nightscout.androidaps.database.interfaces.end
|
||||||
import info.nightscout.androidaps.databinding.OverviewFragmentBinding
|
import info.nightscout.androidaps.databinding.OverviewFragmentBinding
|
||||||
import info.nightscout.androidaps.dialogs.*
|
import info.nightscout.androidaps.dialogs.*
|
||||||
import info.nightscout.androidaps.events.*
|
import info.nightscout.androidaps.events.EventAcceptOpenLoopChange
|
||||||
import info.nightscout.androidaps.extensions.*
|
import info.nightscout.androidaps.events.EventInitializationChanged
|
||||||
|
import info.nightscout.androidaps.events.EventPreferenceChange
|
||||||
|
import info.nightscout.androidaps.events.EventPumpStatusChanged
|
||||||
|
import info.nightscout.androidaps.events.EventRefreshOverview
|
||||||
|
import info.nightscout.androidaps.extensions.directionToIcon
|
||||||
|
import info.nightscout.androidaps.extensions.toVisibility
|
||||||
|
import info.nightscout.androidaps.extensions.valueToUnitsString
|
||||||
import info.nightscout.androidaps.interfaces.*
|
import info.nightscout.androidaps.interfaces.*
|
||||||
import info.nightscout.androidaps.logging.AAPSLogger
|
import info.nightscout.androidaps.logging.AAPSLogger
|
||||||
import info.nightscout.androidaps.logging.UserEntryLogger
|
import info.nightscout.androidaps.logging.UserEntryLogger
|
||||||
|
@ -48,11 +51,9 @@ import info.nightscout.androidaps.plugins.general.nsclient.data.NSDeviceStatus
|
||||||
import info.nightscout.androidaps.plugins.general.overview.activities.QuickWizardListActivity
|
import info.nightscout.androidaps.plugins.general.overview.activities.QuickWizardListActivity
|
||||||
import info.nightscout.androidaps.plugins.general.overview.events.EventUpdateOverview
|
import info.nightscout.androidaps.plugins.general.overview.events.EventUpdateOverview
|
||||||
import info.nightscout.androidaps.plugins.general.overview.graphData.GraphData
|
import info.nightscout.androidaps.plugins.general.overview.graphData.GraphData
|
||||||
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.GlucoseValueDataPoint
|
|
||||||
import info.nightscout.androidaps.plugins.general.overview.notifications.NotificationStore
|
import info.nightscout.androidaps.plugins.general.overview.notifications.NotificationStore
|
||||||
import info.nightscout.androidaps.plugins.general.wear.events.EventWearInitiateAction
|
import info.nightscout.androidaps.plugins.general.wear.events.EventWearInitiateAction
|
||||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatusProvider
|
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatusProvider
|
||||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventIobCalculationProgress
|
|
||||||
import info.nightscout.androidaps.plugins.pump.common.defs.PumpType
|
import info.nightscout.androidaps.plugins.pump.common.defs.PumpType
|
||||||
import info.nightscout.androidaps.plugins.source.DexcomPlugin
|
import info.nightscout.androidaps.plugins.source.DexcomPlugin
|
||||||
import info.nightscout.androidaps.plugins.source.XdripPlugin
|
import info.nightscout.androidaps.plugins.source.XdripPlugin
|
||||||
|
@ -62,7 +63,6 @@ import info.nightscout.androidaps.skins.SkinProvider
|
||||||
import info.nightscout.androidaps.utils.*
|
import info.nightscout.androidaps.utils.*
|
||||||
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
|
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
|
||||||
import info.nightscout.androidaps.utils.buildHelper.BuildHelper
|
import info.nightscout.androidaps.utils.buildHelper.BuildHelper
|
||||||
import info.nightscout.androidaps.utils.extensions.*
|
|
||||||
import info.nightscout.androidaps.utils.protection.ProtectionCheck
|
import info.nightscout.androidaps.utils.protection.ProtectionCheck
|
||||||
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
||||||
import info.nightscout.androidaps.utils.rx.AapsSchedulers
|
import info.nightscout.androidaps.utils.rx.AapsSchedulers
|
||||||
|
@ -70,15 +70,11 @@ import info.nightscout.androidaps.utils.sharedPreferences.SP
|
||||||
import info.nightscout.androidaps.utils.ui.UIRunnable
|
import info.nightscout.androidaps.utils.ui.UIRunnable
|
||||||
import info.nightscout.androidaps.utils.wizard.QuickWizard
|
import info.nightscout.androidaps.utils.wizard.QuickWizard
|
||||||
import io.reactivex.disposables.CompositeDisposable
|
import io.reactivex.disposables.CompositeDisposable
|
||||||
import kotlinx.coroutines.Dispatchers
|
import io.reactivex.rxkotlin.plusAssign
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlin.collections.ArrayList
|
import kotlin.collections.ArrayList
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
import kotlin.math.ceil
|
|
||||||
import kotlin.math.max
|
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
|
||||||
class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickListener {
|
class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickListener {
|
||||||
|
@ -117,6 +113,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
|
||||||
@Inject lateinit var repository: AppRepository
|
@Inject lateinit var repository: AppRepository
|
||||||
@Inject lateinit var glucoseStatusProvider: GlucoseStatusProvider
|
@Inject lateinit var glucoseStatusProvider: GlucoseStatusProvider
|
||||||
@Inject lateinit var overviewData: OverviewData
|
@Inject lateinit var overviewData: OverviewData
|
||||||
|
@Inject lateinit var overviewPlugin: OverviewPlugin
|
||||||
|
|
||||||
private val disposable = CompositeDisposable()
|
private val disposable = CompositeDisposable()
|
||||||
|
|
||||||
|
@ -124,17 +121,14 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
|
||||||
private var smallHeight = false
|
private var smallHeight = false
|
||||||
private lateinit var dm: DisplayMetrics
|
private lateinit var dm: DisplayMetrics
|
||||||
private var axisWidth: Int = 0
|
private var axisWidth: Int = 0
|
||||||
private var rangeToDisplay = 6 // for graph
|
|
||||||
private lateinit var loopHandler: Handler
|
|
||||||
private var refreshLoop: Runnable? = null
|
private var refreshLoop: Runnable? = null
|
||||||
|
private lateinit var handler: Handler
|
||||||
|
|
||||||
private val secondaryGraphs = ArrayList<GraphView>()
|
private val secondaryGraphs = ArrayList<GraphView>()
|
||||||
private val secondaryGraphsLabel = ArrayList<TextView>()
|
private val secondaryGraphsLabel = ArrayList<TextView>()
|
||||||
|
|
||||||
private var carbAnimation: AnimationDrawable? = null
|
private var carbAnimation: AnimationDrawable? = null
|
||||||
|
|
||||||
private val graphLock = Object()
|
|
||||||
|
|
||||||
private var _binding: OverviewFragmentBinding? = null
|
private var _binding: OverviewFragmentBinding? = null
|
||||||
|
|
||||||
// This property is only valid between onCreateView and
|
// This property is only valid between onCreateView and
|
||||||
|
@ -151,7 +145,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
loopHandler = Handler(HandlerThread(this::class.simpleName + "Handler").also { it.start() }.looper)
|
handler = Handler(HandlerThread(this::class.simpleName + "Handler").also { it.start() }.looper)
|
||||||
|
|
||||||
// pre-process landscape mode
|
// pre-process landscape mode
|
||||||
val screenWidth = dm.widthPixels
|
val screenWidth = dm.widthPixels
|
||||||
|
@ -177,13 +171,14 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
|
||||||
carbAnimation?.setEnterFadeDuration(1200)
|
carbAnimation?.setEnterFadeDuration(1200)
|
||||||
carbAnimation?.setExitFadeDuration(1200)
|
carbAnimation?.setExitFadeDuration(1200)
|
||||||
|
|
||||||
rangeToDisplay = sp.getInt(R.string.key_rangetodisplay, 6)
|
|
||||||
|
|
||||||
binding.graphsLayout.bgGraph.setOnLongClickListener {
|
binding.graphsLayout.bgGraph.setOnLongClickListener {
|
||||||
rangeToDisplay += 6
|
overviewData.rangeToDisplay += 6
|
||||||
rangeToDisplay = if (rangeToDisplay > 24) 6 else rangeToDisplay
|
overviewData.rangeToDisplay = if (overviewData.rangeToDisplay > 24) 6 else overviewData.rangeToDisplay
|
||||||
sp.putInt(R.string.key_rangetodisplay, rangeToDisplay)
|
sp.putInt(R.string.key_rangetodisplay, overviewData.rangeToDisplay)
|
||||||
updateGUI("rangeChange")
|
overviewData.initRange()
|
||||||
|
updateGUI("rangeChange", OverviewData.Property.GRAPH)
|
||||||
|
rxBus.send(EventPreferenceChange(resourceHelper, R.string.key_rangetodisplay))
|
||||||
sp.putBoolean(R.string.key_objectiveusescale, true)
|
sp.putBoolean(R.string.key_objectiveusescale, true)
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
@ -212,51 +207,32 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
|
||||||
override fun onPause() {
|
override fun onPause() {
|
||||||
super.onPause()
|
super.onPause()
|
||||||
disposable.clear()
|
disposable.clear()
|
||||||
loopHandler.removeCallbacksAndMessages(null)
|
handler.removeCallbacksAndMessages(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
disposable.add(rxBus
|
disposable += activePlugin.activeOverview.overviewBus
|
||||||
.toObservable(EventRefreshOverview::class.java)
|
|
||||||
.observeOn(aapsSchedulers.main)
|
|
||||||
.subscribe({
|
|
||||||
if (it.now) updateGUI(it.from)
|
|
||||||
else scheduleUpdateGUI(it.from)
|
|
||||||
}, fabricPrivacy::logException))
|
|
||||||
disposable.add(rxBus
|
|
||||||
.toObservable(EventUpdateOverview::class.java)
|
.toObservable(EventUpdateOverview::class.java)
|
||||||
.observeOn(aapsSchedulers.main)
|
.observeOn(aapsSchedulers.main)
|
||||||
.subscribe({ updateGUI(it.what) }, fabricPrivacy::logException))
|
.subscribe({ updateGUI(it.from, it.what) }, fabricPrivacy::logException)
|
||||||
|
|
||||||
disposable.add(rxBus
|
disposable.add(rxBus
|
||||||
.toObservable(EventExtendedBolusChange::class.java)
|
.toObservable(EventRefreshOverview::class.java)
|
||||||
.observeOn(aapsSchedulers.io)
|
.observeOn(aapsSchedulers.io)
|
||||||
.subscribe({ scheduleUpdateGUI("EventExtendedBolusChange") }, fabricPrivacy::logException))
|
.subscribe({
|
||||||
disposable.add(rxBus
|
if (it.now) overviewPlugin.refreshLoop(it.from)
|
||||||
.toObservable(EventTreatmentChange::class.java)
|
else scheduleUpdateGUI(it.from)
|
||||||
.observeOn(aapsSchedulers.io)
|
}, fabricPrivacy::logException))
|
||||||
.subscribe({ scheduleUpdateGUI("EventTreatmentChange") }, fabricPrivacy::logException))
|
|
||||||
disposable.add(rxBus
|
|
||||||
.toObservable(EventTempTargetChange::class.java)
|
|
||||||
.observeOn(aapsSchedulers.io)
|
|
||||||
.subscribe({ scheduleUpdateGUI("EventTempTargetChange") }, fabricPrivacy::logException))
|
|
||||||
disposable.add(rxBus
|
disposable.add(rxBus
|
||||||
.toObservable(EventAcceptOpenLoopChange::class.java)
|
.toObservable(EventAcceptOpenLoopChange::class.java)
|
||||||
.observeOn(aapsSchedulers.io)
|
.observeOn(aapsSchedulers.io)
|
||||||
.subscribe({ scheduleUpdateGUI("EventAcceptOpenLoopChange") }, fabricPrivacy::logException))
|
.subscribe({ scheduleUpdateGUI("EventAcceptOpenLoopChange") }, fabricPrivacy::logException))
|
||||||
disposable.add(rxBus
|
|
||||||
.toObservable(EventTherapyEventChange::class.java)
|
|
||||||
.observeOn(aapsSchedulers.io)
|
|
||||||
.subscribe({ scheduleUpdateGUI("EventCareportalEventChange") }, fabricPrivacy::logException))
|
|
||||||
disposable.add(rxBus
|
disposable.add(rxBus
|
||||||
.toObservable(EventInitializationChanged::class.java)
|
.toObservable(EventInitializationChanged::class.java)
|
||||||
.observeOn(aapsSchedulers.io)
|
.observeOn(aapsSchedulers.main)
|
||||||
.subscribe({ scheduleUpdateGUI("EventInitializationChanged") }, fabricPrivacy::logException))
|
.subscribe({ updateGUI("EventInitializationChanged", OverviewData.Property.TIME) }, fabricPrivacy::logException))
|
||||||
disposable.add(rxBus
|
|
||||||
.toObservable(EventAutosensCalculationFinished::class.java)
|
|
||||||
.observeOn(aapsSchedulers.io)
|
|
||||||
.subscribe({ scheduleUpdateGUI("EventAutosensCalculationFinished") }, fabricPrivacy::logException))
|
|
||||||
disposable.add(rxBus
|
disposable.add(rxBus
|
||||||
.toObservable(EventPreferenceChange::class.java)
|
.toObservable(EventPreferenceChange::class.java)
|
||||||
.observeOn(aapsSchedulers.io)
|
.observeOn(aapsSchedulers.io)
|
||||||
|
@ -269,18 +245,14 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
|
||||||
.toObservable(EventPumpStatusChanged::class.java)
|
.toObservable(EventPumpStatusChanged::class.java)
|
||||||
.observeOn(aapsSchedulers.main)
|
.observeOn(aapsSchedulers.main)
|
||||||
.subscribe({ updatePumpStatus(it) }, fabricPrivacy::logException))
|
.subscribe({ updatePumpStatus(it) }, fabricPrivacy::logException))
|
||||||
disposable.add(rxBus
|
|
||||||
.toObservable(EventIobCalculationProgress::class.java)
|
|
||||||
.observeOn(aapsSchedulers.main)
|
|
||||||
.subscribe({ binding.graphsLayout.iobCalculationProgress.text = it.progress }, fabricPrivacy::logException))
|
|
||||||
|
|
||||||
refreshLoop = Runnable {
|
refreshLoop = Runnable {
|
||||||
scheduleUpdateGUI("refreshLoop")
|
overviewPlugin.refreshLoop("refreshLoop")
|
||||||
loopHandler.postDelayed(refreshLoop, 60 * 1000L)
|
handler.postDelayed(refreshLoop, 60 * 1000L)
|
||||||
}
|
}
|
||||||
loopHandler.postDelayed(refreshLoop, 60 * 1000L)
|
handler.postDelayed(refreshLoop, 60 * 1000L)
|
||||||
|
|
||||||
updateGUI("onResume")
|
for (p in OverviewData.Property.values()) updateGUI("onResume", p)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
|
@ -340,7 +312,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
|
||||||
R.id.accept_temp_button -> {
|
R.id.accept_temp_button -> {
|
||||||
profileFunction.getProfile() ?: return
|
profileFunction.getProfile() ?: return
|
||||||
if (loopPlugin.isEnabled(PluginType.LOOP)) {
|
if (loopPlugin.isEnabled(PluginType.LOOP)) {
|
||||||
loopHandler.post {
|
handler.post {
|
||||||
val lastRun = loopPlugin.lastRun
|
val lastRun = loopPlugin.lastRun
|
||||||
loopPlugin.invoke("Accept temp button", false)
|
loopPlugin.invoke("Accept temp button", false)
|
||||||
if (lastRun?.lastAPSRun != null && lastRun.constraintsProcessed?.isChangeRequested == true) {
|
if (lastRun?.lastAPSRun != null && lastRun.constraintsProcessed?.isChangeRequested == true) {
|
||||||
|
@ -490,149 +462,11 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun prepareGraphsIfNeeded(numOfGraphs: Int) {
|
private fun processAps() {
|
||||||
synchronized(graphLock) {
|
|
||||||
if (numOfGraphs != secondaryGraphs.size - 1) {
|
|
||||||
//aapsLogger.debug("New secondary graph count ${numOfGraphs-1}")
|
|
||||||
// rebuild needed
|
|
||||||
secondaryGraphs.clear()
|
|
||||||
secondaryGraphsLabel.clear()
|
|
||||||
binding.graphsLayout.iobGraph.removeAllViews()
|
|
||||||
for (i in 1 until numOfGraphs) {
|
|
||||||
val relativeLayout = RelativeLayout(context)
|
|
||||||
relativeLayout.layoutParams = RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
|
|
||||||
|
|
||||||
val graph = GraphView(context)
|
|
||||||
graph.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, resourceHelper.dpToPx(skinProvider.activeSkin().secondaryGraphHeight)).also { it.setMargins(0, resourceHelper.dpToPx(15), 0, resourceHelper.dpToPx(10)) }
|
|
||||||
graph.gridLabelRenderer?.gridColor = resourceHelper.gc(R.color.graphgrid)
|
|
||||||
graph.gridLabelRenderer?.reloadStyles()
|
|
||||||
graph.gridLabelRenderer?.isHorizontalLabelsVisible = false
|
|
||||||
graph.gridLabelRenderer?.labelVerticalWidth = axisWidth
|
|
||||||
graph.gridLabelRenderer?.numVerticalLabels = 3
|
|
||||||
graph.viewport.backgroundColor = Color.argb(20, 255, 255, 255) // 8% of gray
|
|
||||||
relativeLayout.addView(graph)
|
|
||||||
|
|
||||||
val label = TextView(context)
|
|
||||||
val layoutParams = RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT).also { it.setMargins(resourceHelper.dpToPx(30), resourceHelper.dpToPx(25), 0, 0) }
|
|
||||||
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_TOP)
|
|
||||||
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT)
|
|
||||||
label.layoutParams = layoutParams
|
|
||||||
relativeLayout.addView(label)
|
|
||||||
secondaryGraphsLabel.add(label)
|
|
||||||
|
|
||||||
binding.graphsLayout.iobGraph.addView(relativeLayout)
|
|
||||||
secondaryGraphs.add(graph)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var task: Runnable? = null
|
|
||||||
|
|
||||||
private fun scheduleUpdateGUI(from: String) {
|
|
||||||
class UpdateRunnable : Runnable {
|
|
||||||
|
|
||||||
override fun run() {
|
|
||||||
updateGUI(from)
|
|
||||||
task = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
view?.removeCallbacks(task)
|
|
||||||
task = UpdateRunnable()
|
|
||||||
view?.postDelayed(task, 500)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun updateGUI(what: OverviewData.Property) {
|
|
||||||
when (what) {
|
|
||||||
OverviewData.Property.PROFILE -> TODO()
|
|
||||||
|
|
||||||
OverviewData.Property.TEMPORARY_BASAL -> {
|
|
||||||
// Basal, TBR
|
|
||||||
binding.infoLayout.baseBasal.text = overviewData.temporaryBasalText
|
|
||||||
binding.infoLayout.baseBasal.setTextColor(overviewData.temporaryBasalColor)
|
|
||||||
binding.infoLayout.baseBasalIcon.setImageResource(overviewData.temporaryBasalIcon)
|
|
||||||
binding.infoLayout.basalLayout.setOnClickListener {
|
|
||||||
activity?.let { OKDialog.show(it, resourceHelper.gs(R.string.basal), overviewData.temporaryBasalDialogText) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("SetTextI18n")
|
|
||||||
fun updateGUI(from: String) {
|
|
||||||
if (_binding == null) return
|
|
||||||
aapsLogger.debug("UpdateGUI from $from")
|
|
||||||
|
|
||||||
binding.infoLayout.time.text = dateUtil.timeString(dateUtil.now())
|
|
||||||
|
|
||||||
if (overviewData.profile == null) {
|
|
||||||
binding.loopPumpStatusLayout.pumpStatus.setText(R.string.noprofileset)
|
|
||||||
binding.loopPumpStatusLayout.pumpStatusLayout.visibility = View.VISIBLE
|
|
||||||
binding.loopPumpStatusLayout.loopLayout.visibility = View.GONE
|
|
||||||
return
|
|
||||||
}
|
|
||||||
binding.notifications.let { notificationStore.updateNotifications(it) }
|
|
||||||
binding.loopPumpStatusLayout.pumpStatusLayout.visibility = View.GONE
|
|
||||||
binding.loopPumpStatusLayout.loopLayout.visibility = View.VISIBLE
|
|
||||||
|
|
||||||
val profile = overviewData.profile ?: return
|
|
||||||
val actualBG = iobCobCalculator.ads.actualBg()
|
|
||||||
val lastBG = iobCobCalculator.ads.lastBg()
|
|
||||||
val pump = activePlugin.activePump
|
val pump = activePlugin.activePump
|
||||||
val units = profileFunction.getUnits()
|
|
||||||
val lowLine = defaultValueHelper.determineLowLine()
|
|
||||||
val highLine = defaultValueHelper.determineHighLine()
|
|
||||||
val lastRun = loopPlugin.lastRun
|
|
||||||
val predictionsAvailable = if (config.APS) lastRun?.request?.hasPredictions == true else config.NSCLIENT
|
|
||||||
|
|
||||||
try {
|
|
||||||
updateGraph(lastRun, predictionsAvailable, lowLine, highLine, pump, profile)
|
|
||||||
} catch (e: IllegalStateException) {
|
|
||||||
return // view no longer exists
|
|
||||||
}
|
|
||||||
|
|
||||||
//Start with updating the BG as it is unaffected by loop.
|
|
||||||
// **** BG value ****
|
|
||||||
if (lastBG != null) {
|
|
||||||
val color = when {
|
|
||||||
lastBG.valueToUnits(units) < lowLine -> resourceHelper.gc(R.color.low)
|
|
||||||
lastBG.valueToUnits(units) > highLine -> resourceHelper.gc(R.color.high)
|
|
||||||
else -> resourceHelper.gc(R.color.inrange)
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.infoLayout.bg.text = lastBG.valueToUnitsString(units)
|
|
||||||
binding.infoLayout.bg.setTextColor(color)
|
|
||||||
binding.infoLayout.arrow.setImageResource(trendCalculator.getTrendArrow(lastBG).directionToIcon())
|
|
||||||
binding.infoLayout.arrow.setColorFilter(color)
|
|
||||||
|
|
||||||
val glucoseStatus = glucoseStatusProvider.glucoseStatusData
|
|
||||||
if (glucoseStatus != null) {
|
|
||||||
binding.infoLayout.deltaLarge.text = Profile.toSignedUnitsString(glucoseStatus.delta, glucoseStatus.delta * Constants.MGDL_TO_MMOLL, units)
|
|
||||||
binding.infoLayout.deltaLarge.setTextColor(color)
|
|
||||||
binding.infoLayout.delta.text = Profile.toSignedUnitsString(glucoseStatus.delta, glucoseStatus.delta * Constants.MGDL_TO_MMOLL, units)
|
|
||||||
binding.infoLayout.avgDelta.text = Profile.toSignedUnitsString(glucoseStatus.shortAvgDelta, glucoseStatus.shortAvgDelta * Constants.MGDL_TO_MMOLL, units)
|
|
||||||
binding.infoLayout.longAvgDelta.text = Profile.toSignedUnitsString(glucoseStatus.longAvgDelta, glucoseStatus.longAvgDelta * Constants.MGDL_TO_MMOLL, units)
|
|
||||||
} else {
|
|
||||||
binding.infoLayout.delta.text = "Δ " + resourceHelper.gs(R.string.notavailable)
|
|
||||||
binding.infoLayout.avgDelta.text = ""
|
|
||||||
binding.infoLayout.longAvgDelta.text = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// strike through if BG is old
|
|
||||||
binding.infoLayout.bg.let { overview_bg ->
|
|
||||||
var flag = overview_bg.paintFlags
|
|
||||||
flag = if (actualBG == null) {
|
|
||||||
flag or Paint.STRIKE_THRU_TEXT_FLAG
|
|
||||||
} else flag and Paint.STRIKE_THRU_TEXT_FLAG.inv()
|
|
||||||
overview_bg.paintFlags = flag
|
|
||||||
}
|
|
||||||
binding.infoLayout.timeAgo.text = dateUtil.minAgo(resourceHelper, lastBG.timestamp)
|
|
||||||
binding.infoLayout.timeAgoShort.text = "(" + dateUtil.minAgoShort(lastBG.timestamp) + ")"
|
|
||||||
|
|
||||||
}
|
|
||||||
val closedLoopEnabled = constraintChecker.isClosedLoopAllowed()
|
|
||||||
|
|
||||||
// aps mode
|
// aps mode
|
||||||
|
val closedLoopEnabled = constraintChecker.isClosedLoopAllowed()
|
||||||
if (config.APS && pump.pumpDescription.isTempBasalCapable) {
|
if (config.APS && pump.pumpDescription.isTempBasalCapable) {
|
||||||
binding.infoLayout.apsMode.visibility = View.VISIBLE
|
binding.infoLayout.apsMode.visibility = View.VISIBLE
|
||||||
binding.infoLayout.timeLayout.visibility = View.GONE
|
binding.infoLayout.timeLayout.visibility = View.GONE
|
||||||
|
@ -693,15 +527,193 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
|
||||||
binding.infoLayout.timeLayout.visibility = View.VISIBLE
|
binding.infoLayout.timeLayout.visibility = View.VISIBLE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// pump status from ns
|
||||||
|
binding.pump.text = nsDeviceStatus.pumpStatus
|
||||||
|
binding.pump.setOnClickListener { activity?.let { OKDialog.show(it, resourceHelper.gs(R.string.pump), nsDeviceStatus.extendedPumpStatus) } }
|
||||||
|
|
||||||
|
// OpenAPS status from ns
|
||||||
|
binding.openaps.text = nsDeviceStatus.openApsStatus
|
||||||
|
binding.openaps.setOnClickListener { activity?.let { OKDialog.show(it, resourceHelper.gs(R.string.openaps), nsDeviceStatus.extendedOpenApsStatus) } }
|
||||||
|
|
||||||
|
// Uploader status from ns
|
||||||
|
binding.uploader.text = nsDeviceStatus.uploaderStatusSpanned
|
||||||
|
binding.uploader.setOnClickListener { activity?.let { OKDialog.show(it, resourceHelper.gs(R.string.uploader), nsDeviceStatus.extendedUploaderStatus) } }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun prepareGraphsIfNeeded(numOfGraphs: Int) {
|
||||||
|
if (numOfGraphs != secondaryGraphs.size - 1) {
|
||||||
|
//aapsLogger.debug("New secondary graph count ${numOfGraphs-1}")
|
||||||
|
// rebuild needed
|
||||||
|
secondaryGraphs.clear()
|
||||||
|
secondaryGraphsLabel.clear()
|
||||||
|
binding.graphsLayout.iobGraph.removeAllViews()
|
||||||
|
for (i in 1 until numOfGraphs) {
|
||||||
|
val relativeLayout = RelativeLayout(context)
|
||||||
|
relativeLayout.layoutParams = RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
|
||||||
|
|
||||||
|
val graph = GraphView(context)
|
||||||
|
graph.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, resourceHelper.dpToPx(skinProvider.activeSkin().secondaryGraphHeight)).also { it.setMargins(0, resourceHelper.dpToPx(15), 0, resourceHelper.dpToPx(10)) }
|
||||||
|
graph.gridLabelRenderer?.gridColor = resourceHelper.gc(R.color.graphgrid)
|
||||||
|
graph.gridLabelRenderer?.reloadStyles()
|
||||||
|
graph.gridLabelRenderer?.isHorizontalLabelsVisible = false
|
||||||
|
graph.gridLabelRenderer?.labelVerticalWidth = axisWidth
|
||||||
|
graph.gridLabelRenderer?.numVerticalLabels = 3
|
||||||
|
graph.viewport.backgroundColor = Color.argb(20, 255, 255, 255) // 8% of gray
|
||||||
|
relativeLayout.addView(graph)
|
||||||
|
|
||||||
|
val label = TextView(context)
|
||||||
|
val layoutParams = RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT).also { it.setMargins(resourceHelper.dpToPx(30), resourceHelper.dpToPx(25), 0, 0) }
|
||||||
|
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_TOP)
|
||||||
|
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT)
|
||||||
|
label.layoutParams = layoutParams
|
||||||
|
relativeLayout.addView(label)
|
||||||
|
secondaryGraphsLabel.add(label)
|
||||||
|
|
||||||
|
binding.graphsLayout.iobGraph.addView(relativeLayout)
|
||||||
|
secondaryGraphs.add(graph)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var task: Runnable? = null
|
||||||
|
|
||||||
|
private fun scheduleUpdateGUI(from: String) {
|
||||||
|
class UpdateRunnable : Runnable {
|
||||||
|
|
||||||
|
override fun run() {
|
||||||
|
overviewPlugin.refreshLoop(from)
|
||||||
|
task = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
handler.removeCallbacks(task)
|
||||||
|
task = UpdateRunnable()
|
||||||
|
handler.postDelayed(task, 500)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("UNUSED_PARAMETER")
|
||||||
|
@SuppressLint("SetTextI18n")
|
||||||
|
fun updateGUI(from: String, what: OverviewData.Property) {
|
||||||
|
// if (what != OverviewData.Property.CALC_PROGRESS)
|
||||||
|
// aapsLogger.debug(LTag.UI, "UpdateGui $from $what")
|
||||||
|
if (overviewData.profile == null) {
|
||||||
|
binding.loopPumpStatusLayout.pumpStatus.setText(R.string.noprofileset)
|
||||||
|
binding.loopPumpStatusLayout.pumpStatusLayout.visibility = View.VISIBLE
|
||||||
|
binding.loopPumpStatusLayout.loopLayout.visibility = View.GONE
|
||||||
|
return
|
||||||
|
}
|
||||||
|
binding.notifications.let { notificationStore.updateNotifications(it) }
|
||||||
|
binding.loopPumpStatusLayout.pumpStatusLayout.visibility = View.GONE
|
||||||
|
binding.loopPumpStatusLayout.loopLayout.visibility = View.VISIBLE
|
||||||
|
|
||||||
|
val units = profileFunction.getUnits()
|
||||||
|
val pump = activePlugin.activePump
|
||||||
|
when (what) {
|
||||||
|
OverviewData.Property.BG -> {
|
||||||
|
binding.infoLayout.bg.text = overviewData.lastBg?.valueToUnitsString(units)
|
||||||
|
?: resourceHelper.gs(R.string.notavailable)
|
||||||
|
binding.infoLayout.bg.setTextColor(overviewData.lastBgColor)
|
||||||
|
binding.infoLayout.arrow.setImageResource(trendCalculator.getTrendArrow(overviewData.lastBg).directionToIcon())
|
||||||
|
binding.infoLayout.arrow.setColorFilter(overviewData.lastBgColor)
|
||||||
|
|
||||||
|
val glucoseStatus = glucoseStatusProvider.glucoseStatusData
|
||||||
|
if (glucoseStatus != null) {
|
||||||
|
binding.infoLayout.deltaLarge.text = Profile.toSignedUnitsString(glucoseStatus.delta, glucoseStatus.delta * Constants.MGDL_TO_MMOLL, units)
|
||||||
|
binding.infoLayout.deltaLarge.setTextColor(overviewData.lastBgColor)
|
||||||
|
binding.infoLayout.delta.text = Profile.toSignedUnitsString(glucoseStatus.delta, glucoseStatus.delta * Constants.MGDL_TO_MMOLL, units)
|
||||||
|
binding.infoLayout.avgDelta.text = Profile.toSignedUnitsString(glucoseStatus.shortAvgDelta, glucoseStatus.shortAvgDelta * Constants.MGDL_TO_MMOLL, units)
|
||||||
|
binding.infoLayout.longAvgDelta.text = Profile.toSignedUnitsString(glucoseStatus.longAvgDelta, glucoseStatus.longAvgDelta * Constants.MGDL_TO_MMOLL, units)
|
||||||
|
} else {
|
||||||
|
binding.infoLayout.deltaLarge.text = "Δ " + resourceHelper.gs(R.string.notavailable)
|
||||||
|
binding.infoLayout.delta.text = "Δ " + resourceHelper.gs(R.string.notavailable)
|
||||||
|
binding.infoLayout.avgDelta.text = ""
|
||||||
|
binding.infoLayout.longAvgDelta.text = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// strike through if BG is old
|
||||||
|
binding.infoLayout.bg.paintFlags =
|
||||||
|
if (!overviewData.isActualBg) binding.infoLayout.bg.paintFlags or Paint.STRIKE_THRU_TEXT_FLAG
|
||||||
|
else binding.infoLayout.bg.paintFlags and Paint.STRIKE_THRU_TEXT_FLAG.inv()
|
||||||
|
binding.infoLayout.timeAgo.text = dateUtil.minAgo(resourceHelper, overviewData.lastBg?.timestamp)
|
||||||
|
binding.infoLayout.timeAgoShort.text = "(" + dateUtil.minAgoShort(overviewData.lastBg?.timestamp) + ")"
|
||||||
|
}
|
||||||
|
|
||||||
|
OverviewData.Property.PROFILE -> {
|
||||||
|
binding.loopPumpStatusLayout.activeProfile.text = overviewData.profileNameWithRemainingTime
|
||||||
|
?: ""
|
||||||
|
binding.loopPumpStatusLayout.activeProfile.setBackgroundColor(overviewData.profileBackgroudColor)
|
||||||
|
binding.loopPumpStatusLayout.activeProfile.setTextColor(overviewData.profileTextColor)
|
||||||
|
}
|
||||||
|
|
||||||
|
OverviewData.Property.TEMPORARY_BASAL -> {
|
||||||
|
binding.infoLayout.baseBasal.text = overviewData.temporaryBasalText
|
||||||
|
binding.infoLayout.baseBasal.setTextColor(overviewData.temporaryBasalColor)
|
||||||
|
binding.infoLayout.baseBasalIcon.setImageResource(overviewData.temporaryBasalIcon)
|
||||||
|
binding.infoLayout.basalLayout.setOnClickListener {
|
||||||
|
activity?.let { OKDialog.show(it, resourceHelper.gs(R.string.basal), overviewData.temporaryBasalDialogText) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OverviewData.Property.EXTENDED_BOLUS -> {
|
||||||
|
binding.infoLayout.extendedBolus.text = overviewData.extendedBolusText
|
||||||
|
binding.infoLayout.extendedBolus.setOnClickListener {
|
||||||
|
activity?.let { OKDialog.show(it, resourceHelper.gs(R.string.extended_bolus), overviewData.extendedBolusDialogText) }
|
||||||
|
}
|
||||||
|
binding.infoLayout.extendedLayout.visibility = (overviewData.extendedBolus != null && !pump.isFakingTempsByExtendedBoluses).toVisibility()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
OverviewData.Property.TIME -> {
|
||||||
|
binding.infoLayout.time.text = dateUtil.timeString(dateUtil.now())
|
||||||
|
// Status lights
|
||||||
|
binding.statusLightsLayout.statusLights.visibility = (sp.getBoolean(R.string.key_show_statuslights, true) || config.NSCLIENT).toVisibility()
|
||||||
|
statusLightHandler.updateStatusLights(binding.statusLightsLayout.cannulaAge, binding.statusLightsLayout.insulinAge, binding.statusLightsLayout.reservoirLevel, binding.statusLightsLayout.sensorAge, null, binding.statusLightsLayout.pbAge, binding.statusLightsLayout.batteryLevel)
|
||||||
|
processButtonsVisibility()
|
||||||
|
processAps()
|
||||||
|
}
|
||||||
|
|
||||||
|
OverviewData.Property.IOB_COB -> {
|
||||||
|
binding.infoLayout.iob.text = overviewData.iobText
|
||||||
|
binding.infoLayout.iobLayout.setOnClickListener {
|
||||||
|
activity?.let { OKDialog.show(it, resourceHelper.gs(R.string.iob), overviewData.iobDialogText) }
|
||||||
|
}
|
||||||
|
// cob
|
||||||
|
var cobText: String = resourceHelper.gs(R.string.value_unavailable_short)
|
||||||
|
overviewData.cobInfo?.let { cobInfo ->
|
||||||
|
if (cobInfo.displayCob != null) {
|
||||||
|
cobText = resourceHelper.gs(R.string.format_carbs, cobInfo.displayCob!!.toInt())
|
||||||
|
if (cobInfo.futureCarbs > 0) cobText += "(" + DecimalFormatter.to0Decimal(cobInfo.futureCarbs) + ")"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
binding.infoLayout.cob.text = cobText
|
||||||
|
|
||||||
|
val constraintsProcessed = loopPlugin.lastRun?.constraintsProcessed
|
||||||
|
val lastRun = loopPlugin.lastRun
|
||||||
|
if (config.APS && constraintsProcessed != null && lastRun != null) {
|
||||||
|
if (constraintsProcessed.carbsReq > 0) {
|
||||||
|
//only display carbsreq when carbs have not been entered recently
|
||||||
|
if (overviewData.lastCarbsTime < lastRun.lastAPSRun) {
|
||||||
|
cobText = cobText + " | " + constraintsProcessed.carbsReq + " " + resourceHelper.gs(R.string.required)
|
||||||
|
}
|
||||||
|
if (carbAnimation?.isRunning == false)
|
||||||
|
carbAnimation?.start()
|
||||||
|
} else {
|
||||||
|
carbAnimation?.stop()
|
||||||
|
carbAnimation?.selectDrawable(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OverviewData.Property.TEMPORARY_TARGET -> {
|
||||||
// temp target
|
// temp target
|
||||||
val tempTarget: ValueWrapper<TemporaryTarget> = repository.getTemporaryTargetActiveAt(dateUtil.now()).blockingGet()
|
val tempTarget = overviewData.temporarytarget
|
||||||
if (tempTarget is ValueWrapper.Existing) {
|
if (tempTarget != null) {
|
||||||
binding.loopPumpStatusLayout.tempTarget.setTextColor(resourceHelper.gc(R.color.ribbonTextWarning))
|
binding.loopPumpStatusLayout.tempTarget.setTextColor(resourceHelper.gc(R.color.ribbonTextWarning))
|
||||||
binding.loopPumpStatusLayout.tempTarget.setBackgroundColor(resourceHelper.gc(R.color.ribbonWarning))
|
binding.loopPumpStatusLayout.tempTarget.setBackgroundColor(resourceHelper.gc(R.color.ribbonWarning))
|
||||||
binding.loopPumpStatusLayout.tempTarget.text = Profile.toTargetRangeString(tempTarget.value.lowTarget, tempTarget.value.highTarget, GlucoseUnit.MGDL, units) + " " + dateUtil.untilString(tempTarget.value.end, resourceHelper)
|
binding.loopPumpStatusLayout.tempTarget.text = Profile.toTargetRangeString(tempTarget.lowTarget, tempTarget.highTarget, GlucoseUnit.MGDL, units) + " " + dateUtil.untilString(tempTarget.end, resourceHelper)
|
||||||
} else {
|
} else {
|
||||||
// If the target is not the same as set in the profile then oref has overridden it
|
// If the target is not the same as set in the profile then oref has overridden it
|
||||||
val targetUsed = lastRun?.constraintsProcessed?.targetBG ?: 0.0
|
overviewData.profile?.let { profile ->
|
||||||
|
val targetUsed = loopPlugin.lastRun?.constraintsProcessed?.targetBG ?: 0.0
|
||||||
|
|
||||||
if (targetUsed != 0.0 && abs(profile.getTargetMgdl() - targetUsed) > 0.01) {
|
if (targetUsed != 0.0 && abs(profile.getTargetMgdl() - targetUsed) > 0.01) {
|
||||||
aapsLogger.debug("Adjusted target. Profile: ${profile.getTargetMgdl()} APS: $targetUsed")
|
aapsLogger.debug("Adjusted target. Profile: ${profile.getTargetMgdl()} APS: $targetUsed")
|
||||||
|
@ -714,176 +726,36 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
|
||||||
binding.loopPumpStatusLayout.tempTarget.text = Profile.toTargetRangeString(profile.getTargetLowMgdl(), profile.getTargetHighMgdl(), GlucoseUnit.MGDL, units)
|
binding.loopPumpStatusLayout.tempTarget.text = Profile.toTargetRangeString(profile.getTargetLowMgdl(), profile.getTargetHighMgdl(), GlucoseUnit.MGDL, units)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extended bolus
|
|
||||||
val extendedBolus = repository.getExtendedBolusActiveAt(dateUtil.now()).blockingGet()
|
|
||||||
binding.infoLayout.extendedBolus.text =
|
|
||||||
if (extendedBolus is ValueWrapper.Existing && !pump.isFakingTempsByExtendedBoluses)
|
|
||||||
resourceHelper.gs(R.string.pump_basebasalrate, extendedBolus.value.rate)
|
|
||||||
else ""
|
|
||||||
binding.infoLayout.extendedBolus.setOnClickListener {
|
|
||||||
if (extendedBolus is ValueWrapper.Existing) activity?.let {
|
|
||||||
OKDialog.show(it, resourceHelper.gs(R.string.extended_bolus), extendedBolus.value.toStringFull(dateUtil))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
binding.infoLayout.extendedLayout.visibility = (extendedBolus is ValueWrapper.Existing && !pump.isFakingTempsByExtendedBoluses).toVisibility()
|
|
||||||
|
|
||||||
// Active profile
|
|
||||||
binding.loopPumpStatusLayout.activeProfile.text = profileFunction.getProfileNameWithRemainingTime()
|
|
||||||
if (profile.percentage != 100 || profile.timeshift != 0) {
|
|
||||||
binding.loopPumpStatusLayout.activeProfile.setBackgroundColor(resourceHelper.gc(R.color.ribbonWarning))
|
|
||||||
binding.loopPumpStatusLayout.activeProfile.setTextColor(resourceHelper.gc(R.color.ribbonTextWarning))
|
|
||||||
} else {
|
|
||||||
binding.loopPumpStatusLayout.activeProfile.setBackgroundColor(resourceHelper.gc(R.color.ribbonDefault))
|
|
||||||
binding.loopPumpStatusLayout.activeProfile.setTextColor(resourceHelper.gc(R.color.ribbonTextDefault))
|
|
||||||
}
|
|
||||||
|
|
||||||
processButtonsVisibility()
|
|
||||||
|
|
||||||
// iob
|
|
||||||
val bolusIob = iobCobCalculator.calculateIobFromBolus().round()
|
|
||||||
val basalIob = iobCobCalculator.calculateIobFromTempBasalsIncludingConvertedExtended().round()
|
|
||||||
binding.infoLayout.iob.text = resourceHelper.gs(R.string.formatinsulinunits, bolusIob.iob + basalIob.basaliob)
|
|
||||||
|
|
||||||
binding.infoLayout.iobLayout.setOnClickListener {
|
|
||||||
activity?.let {
|
|
||||||
OKDialog.show(it, resourceHelper.gs(R.string.iob),
|
|
||||||
resourceHelper.gs(R.string.formatinsulinunits, bolusIob.iob + basalIob.basaliob) + "\n" +
|
|
||||||
resourceHelper.gs(R.string.bolus) + ": " + resourceHelper.gs(R.string.formatinsulinunits, bolusIob.iob) + "\n" +
|
|
||||||
resourceHelper.gs(R.string.basal) + ": " + resourceHelper.gs(R.string.formatinsulinunits, basalIob.basaliob)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Status lights
|
OverviewData.Property.GRAPH -> {
|
||||||
binding.statusLightsLayout.statusLights.visibility = (sp.getBoolean(R.string.key_show_statuslights, true) || config.NSCLIENT).toVisibility()
|
val graphData = GraphData(injector, binding.graphsLayout.bgGraph)
|
||||||
statusLightHandler.updateStatusLights(binding.statusLightsLayout.cannulaAge, binding.statusLightsLayout.insulinAge, binding.statusLightsLayout.reservoirLevel, binding.statusLightsLayout.sensorAge, null, binding.statusLightsLayout.pbAge, binding.statusLightsLayout.batteryLevel)
|
|
||||||
|
|
||||||
// cob
|
|
||||||
var cobText: String = resourceHelper.gs(R.string.value_unavailable_short)
|
|
||||||
val cobInfo = iobCobCalculator.getCobInfo(false, "Overview COB")
|
|
||||||
if (cobInfo.displayCob != null) {
|
|
||||||
cobText = resourceHelper.gs(R.string.format_carbs, cobInfo.displayCob!!.toInt())
|
|
||||||
if (cobInfo.futureCarbs > 0) cobText += "(" + DecimalFormatter.to0Decimal(cobInfo.futureCarbs) + ")"
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.APS && lastRun?.constraintsProcessed != null) {
|
|
||||||
if (lastRun.constraintsProcessed!!.carbsReq > 0) {
|
|
||||||
//only display carbsreq when carbs have not been entered recently
|
|
||||||
val lastCarb = repository.getLastCarbsRecordWrapped().blockingGet()
|
|
||||||
val lastCarbsTime = if (lastCarb is ValueWrapper.Existing) lastCarb.value.timestamp else 0L
|
|
||||||
if (lastCarbsTime < lastRun.lastAPSRun) {
|
|
||||||
cobText = cobText + " | " + lastRun.constraintsProcessed!!.carbsReq + " " + resourceHelper.gs(R.string.required)
|
|
||||||
}
|
|
||||||
binding.infoLayout.cob.text = cobText
|
|
||||||
if (carbAnimation?.isRunning == false)
|
|
||||||
carbAnimation?.start()
|
|
||||||
} else {
|
|
||||||
binding.infoLayout.cob.text = cobText
|
|
||||||
carbAnimation?.stop()
|
|
||||||
carbAnimation?.selectDrawable(0)
|
|
||||||
}
|
|
||||||
} else binding.infoLayout.cob.text = cobText
|
|
||||||
|
|
||||||
// pump status from ns
|
|
||||||
binding.pump.text = nsDeviceStatus.pumpStatus
|
|
||||||
binding.pump.setOnClickListener { activity?.let { OKDialog.show(it, resourceHelper.gs(R.string.pump), nsDeviceStatus.extendedPumpStatus) } }
|
|
||||||
|
|
||||||
// OpenAPS status from ns
|
|
||||||
binding.openaps.text = nsDeviceStatus.openApsStatus
|
|
||||||
binding.openaps.setOnClickListener { activity?.let { OKDialog.show(it, resourceHelper.gs(R.string.openaps), nsDeviceStatus.extendedOpenApsStatus) } }
|
|
||||||
|
|
||||||
// Uploader status from ns
|
|
||||||
binding.uploader.text = nsDeviceStatus.uploaderStatusSpanned
|
|
||||||
binding.uploader.setOnClickListener { activity?.let { OKDialog.show(it, resourceHelper.gs(R.string.uploader), nsDeviceStatus.extendedUploaderStatus) } }
|
|
||||||
|
|
||||||
// Sensitivity
|
|
||||||
if (sp.getBoolean(R.string.key_openapsama_useautosens, false) && constraintChecker.isAutosensModeEnabled().value()) {
|
|
||||||
binding.infoLayout.sensitivityIcon.setImageResource(R.drawable.ic_swap_vert_black_48dp_green)
|
|
||||||
} else {
|
|
||||||
binding.infoLayout.sensitivityIcon.setImageResource(R.drawable.ic_x_swap_vert)
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.infoLayout.sensitivity.text =
|
|
||||||
iobCobCalculator.ads.getLastAutosensData("Overview", aapsLogger, dateUtil)?.let { autosensData ->
|
|
||||||
String.format(Locale.ENGLISH, "%.0f%%", autosensData.autosensResult.ratio * 100)
|
|
||||||
} ?: ""
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun updateGraph(lastRun: LoopInterface.LastRun?, predictionsAvailable: Boolean, lowLine: Double, highLine: Double, pump: Pump, profile: Profile) {
|
|
||||||
viewLifecycleOwner.lifecycleScope.launch(Dispatchers.Main) {
|
|
||||||
if (_binding == null) return@launch
|
|
||||||
val menuChartSettings = overviewMenus.setting
|
val menuChartSettings = overviewMenus.setting
|
||||||
prepareGraphsIfNeeded(menuChartSettings.size)
|
graphData.addInRangeArea(overviewData.fromTime, overviewData.endTime, defaultValueHelper.determineLowLine(), defaultValueHelper.determineHighLine())
|
||||||
val graphData = GraphData(injector, binding.graphsLayout.bgGraph, iobCobCalculator)
|
graphData.addBgReadings(menuChartSettings[0][OverviewMenus.CharType.PRE.ordinal])
|
||||||
val secondaryGraphsData: ArrayList<GraphData> = ArrayList()
|
if (buildHelper.isDev()) graphData.addBucketedData()
|
||||||
|
graphData.addTreatments()
|
||||||
// do preparation in different thread
|
if (menuChartSettings[0][OverviewMenus.CharType.ACT.ordinal])
|
||||||
withContext(Dispatchers.Default) {
|
graphData.addActivity(0.8)
|
||||||
// align to hours
|
if (pump.pumpDescription.isTempBasalCapable && menuChartSettings[0][OverviewMenus.CharType.BAS.ordinal])
|
||||||
val calendar = Calendar.getInstance()
|
graphData.addBasals()
|
||||||
calendar.timeInMillis = System.currentTimeMillis()
|
graphData.addTargetLine()
|
||||||
calendar[Calendar.MILLISECOND] = 0
|
graphData.addNowLine(dateUtil.now())
|
||||||
calendar[Calendar.SECOND] = 0
|
|
||||||
calendar[Calendar.MINUTE] = 0
|
|
||||||
calendar.add(Calendar.HOUR, 1)
|
|
||||||
val hoursToFetch: Int
|
|
||||||
val toTime: Long
|
|
||||||
val fromTime: Long
|
|
||||||
val endTime: Long
|
|
||||||
val apsResult = if (config.APS) lastRun?.constraintsProcessed else nsDeviceStatus.getAPSResult(injector)
|
|
||||||
if (predictionsAvailable && apsResult != null && menuChartSettings[0][OverviewMenus.CharType.PRE.ordinal]) {
|
|
||||||
var predictionHours = (ceil(apsResult.latestPredictionsTime - System.currentTimeMillis().toDouble()) / (60 * 60 * 1000)).toInt()
|
|
||||||
predictionHours = min(2, predictionHours)
|
|
||||||
predictionHours = max(0, predictionHours)
|
|
||||||
hoursToFetch = rangeToDisplay - predictionHours
|
|
||||||
toTime = calendar.timeInMillis + 100000 // little bit more to avoid wrong rounding - GraphView specific
|
|
||||||
fromTime = toTime - T.hours(hoursToFetch.toLong()).msecs()
|
|
||||||
endTime = toTime + T.hours(predictionHours.toLong()).msecs()
|
|
||||||
} else {
|
|
||||||
hoursToFetch = rangeToDisplay
|
|
||||||
toTime = calendar.timeInMillis + 100000 // little bit more to avoid wrong rounding - GraphView specific
|
|
||||||
fromTime = toTime - T.hours(hoursToFetch.toLong()).msecs()
|
|
||||||
endTime = toTime
|
|
||||||
}
|
|
||||||
val now = System.currentTimeMillis()
|
|
||||||
|
|
||||||
// ------------------ 1st graph
|
|
||||||
|
|
||||||
// **** In range Area ****
|
|
||||||
graphData.addInRangeArea(fromTime, endTime, lowLine, highLine)
|
|
||||||
|
|
||||||
// **** BG ****
|
|
||||||
if (predictionsAvailable && menuChartSettings[0][OverviewMenus.CharType.PRE.ordinal])
|
|
||||||
graphData.addBgReadings(fromTime, toTime, highLine, apsResult?.predictions?.map { bg -> GlucoseValueDataPoint(bg, defaultValueHelper, profileFunction, resourceHelper) }?.toMutableList())
|
|
||||||
else graphData.addBgReadings(fromTime, toTime, highLine, null)
|
|
||||||
if (buildHelper.isDev()) graphData.addBucketedData(fromTime, toTime)
|
|
||||||
|
|
||||||
// Treatments
|
|
||||||
graphData.addTreatments(fromTime, endTime)
|
|
||||||
|
|
||||||
// set manual x bounds to have nice steps
|
// set manual x bounds to have nice steps
|
||||||
graphData.setNumVerticalLabels()
|
graphData.setNumVerticalLabels()
|
||||||
graphData.formatAxis(fromTime, endTime)
|
graphData.formatAxis(overviewData.fromTime, overviewData.endTime)
|
||||||
|
|
||||||
|
graphData.performUpdate()
|
||||||
|
|
||||||
if (menuChartSettings[0][OverviewMenus.CharType.ACT.ordinal])
|
// 2nd graphs
|
||||||
graphData.addActivity(fromTime, endTime, false, 0.8)
|
prepareGraphsIfNeeded(menuChartSettings.size)
|
||||||
|
val secondaryGraphsData: ArrayList<GraphData> = ArrayList()
|
||||||
|
|
||||||
// add basal data
|
val now = System.currentTimeMillis()
|
||||||
if (pump.pumpDescription.isTempBasalCapable && menuChartSettings[0][OverviewMenus.CharType.BAS.ordinal])
|
|
||||||
graphData.addBasals(fromTime, now, lowLine / graphData.maxY / 1.2)
|
|
||||||
|
|
||||||
// add target line
|
|
||||||
graphData.addTargetLine(fromTime, toTime, profile, loopPlugin.lastRun)
|
|
||||||
|
|
||||||
// **** NOW line ****
|
|
||||||
graphData.addNowLine(now)
|
|
||||||
|
|
||||||
// ------------------ 2nd graph
|
|
||||||
synchronized(graphLock) {
|
|
||||||
for (g in 0 until min(secondaryGraphs.size, menuChartSettings.size + 1)) {
|
for (g in 0 until min(secondaryGraphs.size, menuChartSettings.size + 1)) {
|
||||||
val secondGraphData = GraphData(injector, secondaryGraphs[g], iobCobCalculator)
|
val secondGraphData = GraphData(injector, secondaryGraphs[g])
|
||||||
var useABSForScale = false
|
var useABSForScale = false
|
||||||
var useIobForScale = false
|
var useIobForScale = false
|
||||||
var useCobForScale = false
|
var useCobForScale = false
|
||||||
|
@ -900,27 +772,21 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
|
||||||
menuChartSettings[g + 1][OverviewMenus.CharType.SEN.ordinal] -> useRatioForScale = true
|
menuChartSettings[g + 1][OverviewMenus.CharType.SEN.ordinal] -> useRatioForScale = true
|
||||||
menuChartSettings[g + 1][OverviewMenus.CharType.DEVSLOPE.ordinal] -> useDSForScale = true
|
menuChartSettings[g + 1][OverviewMenus.CharType.DEVSLOPE.ordinal] -> useDSForScale = true
|
||||||
}
|
}
|
||||||
val alignIobScale = menuChartSettings[g + 1][OverviewMenus.CharType.ABS.ordinal] && menuChartSettings[g + 1][OverviewMenus.CharType.IOB.ordinal]
|
|
||||||
val alignDevBgiScale = menuChartSettings[g + 1][OverviewMenus.CharType.DEV.ordinal] && menuChartSettings[g + 1][OverviewMenus.CharType.BGI.ordinal]
|
val alignDevBgiScale = menuChartSettings[g + 1][OverviewMenus.CharType.DEV.ordinal] && menuChartSettings[g + 1][OverviewMenus.CharType.BGI.ordinal]
|
||||||
|
|
||||||
if (menuChartSettings[g + 1][OverviewMenus.CharType.ABS.ordinal]) secondGraphData.addAbsIob(fromTime, now, useABSForScale, 1.0)
|
if (menuChartSettings[g + 1][OverviewMenus.CharType.ABS.ordinal]) secondGraphData.addAbsIob(useABSForScale, 1.0)
|
||||||
if (menuChartSettings[g + 1][OverviewMenus.CharType.IOB.ordinal]) secondGraphData.addIob(fromTime, now, useIobForScale, 1.0, menuChartSettings[g + 1][OverviewMenus.CharType.PRE.ordinal], alignIobScale)
|
if (menuChartSettings[g + 1][OverviewMenus.CharType.IOB.ordinal]) secondGraphData.addIob(useIobForScale, 1.0)
|
||||||
if (menuChartSettings[g + 1][OverviewMenus.CharType.COB.ordinal]) secondGraphData.addCob(fromTime, now, useCobForScale, if (useCobForScale) 1.0 else 0.5)
|
if (menuChartSettings[g + 1][OverviewMenus.CharType.COB.ordinal]) secondGraphData.addCob(useCobForScale, if (useCobForScale) 1.0 else 0.5)
|
||||||
if (menuChartSettings[g + 1][OverviewMenus.CharType.DEV.ordinal]) secondGraphData.addDeviations(fromTime, now, useDevForScale, 1.0, alignDevBgiScale)
|
if (menuChartSettings[g + 1][OverviewMenus.CharType.DEV.ordinal]) secondGraphData.addDeviations(useDevForScale, 1.0)
|
||||||
if (menuChartSettings[g + 1][OverviewMenus.CharType.BGI.ordinal]) secondGraphData.addMinusBGI(fromTime, endTime, useBGIForScale, if (alignDevBgiScale) 1.0 else 0.8, alignDevBgiScale)
|
if (menuChartSettings[g + 1][OverviewMenus.CharType.BGI.ordinal]) secondGraphData.addMinusBGI(useBGIForScale, if (alignDevBgiScale) 1.0 else 0.8)
|
||||||
if (menuChartSettings[g + 1][OverviewMenus.CharType.SEN.ordinal]) secondGraphData.addRatio(fromTime, now, useRatioForScale, if (useRatioForScale) 1.0 else 0.8)
|
if (menuChartSettings[g + 1][OverviewMenus.CharType.SEN.ordinal]) secondGraphData.addRatio(useRatioForScale, if (useRatioForScale) 1.0 else 0.8)
|
||||||
if (menuChartSettings[g + 1][OverviewMenus.CharType.DEVSLOPE.ordinal] && buildHelper.isDev()) secondGraphData.addDeviationSlope(fromTime, now, useDSForScale, 1.0)
|
if (menuChartSettings[g + 1][OverviewMenus.CharType.DEVSLOPE.ordinal] && buildHelper.isDev()) secondGraphData.addDeviationSlope(useDSForScale, 1.0)
|
||||||
|
|
||||||
// set manual x bounds to have nice steps
|
// set manual x bounds to have nice steps
|
||||||
secondGraphData.formatAxis(fromTime, endTime)
|
secondGraphData.formatAxis(overviewData.fromTime, overviewData.endTime)
|
||||||
secondGraphData.addNowLine(now)
|
secondGraphData.addNowLine(now)
|
||||||
secondaryGraphsData.add(secondGraphData)
|
secondaryGraphsData.add(secondGraphData)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
// finally enforce drawing of graphs in UI thread
|
|
||||||
graphData.performUpdate()
|
|
||||||
synchronized(graphLock) {
|
|
||||||
for (g in 0 until min(secondaryGraphs.size, menuChartSettings.size + 1)) {
|
for (g in 0 until min(secondaryGraphs.size, menuChartSettings.size + 1)) {
|
||||||
secondaryGraphsLabel[g].text = overviewMenus.enabledTypes(g + 1)
|
secondaryGraphsLabel[g].text = overviewMenus.enabledTypes(g + 1)
|
||||||
secondaryGraphs[g].visibility = (
|
secondaryGraphs[g].visibility = (
|
||||||
|
@ -935,6 +801,23 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
|
||||||
secondaryGraphsData[g].performUpdate()
|
secondaryGraphsData[g].performUpdate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OverviewData.Property.CALC_PROGRESS -> {
|
||||||
|
binding.graphsLayout.iobCalculationProgress.text = overviewData.calcProgress
|
||||||
|
}
|
||||||
|
|
||||||
|
OverviewData.Property.SENSITIVITY -> {
|
||||||
|
if (sp.getBoolean(R.string.key_openapsama_useautosens, false) && constraintChecker.isAutosensModeEnabled().value()) {
|
||||||
|
binding.infoLayout.sensitivityIcon.setImageResource(R.drawable.ic_swap_vert_black_48dp_green)
|
||||||
|
} else {
|
||||||
|
binding.infoLayout.sensitivityIcon.setImageResource(R.drawable.ic_x_swap_vert)
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.infoLayout.sensitivity.text =
|
||||||
|
overviewData.lastAutosensData?.let { autosensData ->
|
||||||
|
String.format(Locale.ENGLISH, "%.0f%%", autosensData.autosensResult.ratio * 100)
|
||||||
|
} ?: ""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,6 +46,7 @@ class OverviewMenus @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
const val MAX_GRAPHS = 5 // including main
|
const val MAX_GRAPHS = 5 // including main
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,8 +59,6 @@ class OverviewMenus @Inject constructor(
|
||||||
return r.toString()
|
return r.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private var _setting: MutableList<Array<Boolean>> = ArrayList()
|
private var _setting: MutableList<Array<Boolean>> = ArrayList()
|
||||||
|
|
||||||
val setting: List<Array<Boolean>>
|
val setting: List<Array<Boolean>>
|
||||||
|
@ -71,7 +70,7 @@ class OverviewMenus @Inject constructor(
|
||||||
aapsLogger.debug(sts)
|
aapsLogger.debug(sts)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadGraphConfig() {
|
fun loadGraphConfig() {
|
||||||
val sts = sp.getString(R.string.key_graphconfig, "")
|
val sts = sp.getString(R.string.key_graphconfig, "")
|
||||||
if (sts.isNotEmpty()) {
|
if (sts.isNotEmpty()) {
|
||||||
_setting = Gson().fromJson(sts, Array<Array<Boolean>>::class.java).toMutableList()
|
_setting = Gson().fromJson(sts, Array<Array<Boolean>>::class.java).toMutableList()
|
||||||
|
@ -88,7 +87,6 @@ class OverviewMenus @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setupChartMenu(chartButton: ImageButton) {
|
fun setupChartMenu(chartButton: ImageButton) {
|
||||||
loadGraphConfig()
|
|
||||||
val settingsCopy = setting
|
val settingsCopy = setting
|
||||||
val numOfGraphs = settingsCopy.size // 1 main + x secondary
|
val numOfGraphs = settingsCopy.size // 1 main + x secondary
|
||||||
|
|
||||||
|
@ -100,6 +98,8 @@ class OverviewMenus @Inject constructor(
|
||||||
}
|
}
|
||||||
val popup = PopupMenu(v.context, v)
|
val popup = PopupMenu(v.context, v)
|
||||||
|
|
||||||
|
val used = arrayListOf<Int>()
|
||||||
|
|
||||||
for (g in 0 until numOfGraphs) {
|
for (g in 0 until numOfGraphs) {
|
||||||
if (g != 0 && g < numOfGraphs) {
|
if (g != 0 && g < numOfGraphs) {
|
||||||
val dividerItem = popup.menu.add(Menu.NONE, g, Menu.NONE, "------- ${resourceHelper.gs(R.string.graph_menu_divider_header)} $g -------")
|
val dividerItem = popup.menu.add(Menu.NONE, g, Menu.NONE, "------- ${resourceHelper.gs(R.string.graph_menu_divider_header)} $g -------")
|
||||||
|
@ -112,6 +112,7 @@ class OverviewMenus @Inject constructor(
|
||||||
var insert = true
|
var insert = true
|
||||||
if (m == CharType.PRE) insert = predictionsAvailable
|
if (m == CharType.PRE) insert = predictionsAvailable
|
||||||
if (m == CharType.DEVSLOPE) insert = buildHelper.isDev()
|
if (m == CharType.DEVSLOPE) insert = buildHelper.isDev()
|
||||||
|
if (used.contains(m.ordinal)) insert = false
|
||||||
if (insert) {
|
if (insert) {
|
||||||
val item = popup.menu.add(Menu.NONE, m.ordinal + 100 * (g + 1), Menu.NONE, resourceHelper.gs(m.nameId))
|
val item = popup.menu.add(Menu.NONE, m.ordinal + 100 * (g + 1), Menu.NONE, resourceHelper.gs(m.nameId))
|
||||||
val title = item.title
|
val title = item.title
|
||||||
|
@ -120,6 +121,7 @@ class OverviewMenus @Inject constructor(
|
||||||
item.title = s
|
item.title = s
|
||||||
item.isCheckable = true
|
item.isCheckable = true
|
||||||
item.isChecked = settingsCopy[g][m.ordinal]
|
item.isChecked = settingsCopy[g][m.ordinal]
|
||||||
|
if (settingsCopy[g][m.ordinal]) used.add(m.ordinal)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -131,17 +133,21 @@ class OverviewMenus @Inject constructor(
|
||||||
|
|
||||||
popup.setOnMenuItemClickListener {
|
popup.setOnMenuItemClickListener {
|
||||||
// id < 100 graph header - divider 1, 2, 3 .....
|
// id < 100 graph header - divider 1, 2, 3 .....
|
||||||
if (it.itemId == numOfGraphs) {
|
when {
|
||||||
|
it.itemId == numOfGraphs -> {
|
||||||
// add new empty
|
// add new empty
|
||||||
_setting.add(Array(CharType.values().size) { false })
|
_setting.add(Array(CharType.values().size) { false })
|
||||||
} else if (it.itemId < 100) {
|
}
|
||||||
|
it.itemId < 100 -> {
|
||||||
// remove graph
|
// remove graph
|
||||||
_setting.removeAt(it.itemId)
|
_setting.removeAt(it.itemId)
|
||||||
} else {
|
}
|
||||||
|
else -> {
|
||||||
val graphNumber = it.itemId / 100 - 1
|
val graphNumber = it.itemId / 100 - 1
|
||||||
val item = it.itemId % 100
|
val item = it.itemId % 100
|
||||||
_setting[graphNumber][item] = !it.isChecked
|
_setting[graphNumber][item] = !it.isChecked
|
||||||
}
|
}
|
||||||
|
}
|
||||||
storeGraphConfig()
|
storeGraphConfig()
|
||||||
setupChartMenu(chartButton)
|
setupChartMenu(chartButton)
|
||||||
rxBus.send(EventRefreshOverview("OnMenuItemClickListener", now = true))
|
rxBus.send(EventRefreshOverview("OnMenuItemClickListener", now = true))
|
||||||
|
@ -153,4 +159,11 @@ class OverviewMenus @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun isEnabledIn(type: CharType): Int {
|
||||||
|
val settingsCopy = setting
|
||||||
|
val numOfGraphs = settingsCopy.size // 1 main + x secondary
|
||||||
|
for (g in 0 until numOfGraphs) if (settingsCopy[g][type.ordinal]) return g
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,31 +1,50 @@
|
||||||
package info.nightscout.androidaps.plugins.general.overview
|
package info.nightscout.androidaps.plugins.general.overview
|
||||||
|
|
||||||
|
import android.graphics.DashPathEffect
|
||||||
|
import android.graphics.Paint
|
||||||
import androidx.preference.PreferenceFragmentCompat
|
import androidx.preference.PreferenceFragmentCompat
|
||||||
import androidx.preference.SwitchPreference
|
import androidx.preference.SwitchPreference
|
||||||
|
import com.jjoe64.graphview.series.BarGraphSeries
|
||||||
|
import com.jjoe64.graphview.series.DataPoint
|
||||||
|
import com.jjoe64.graphview.series.LineGraphSeries
|
||||||
import dagger.android.HasAndroidInjector
|
import dagger.android.HasAndroidInjector
|
||||||
import info.nightscout.androidaps.R
|
import info.nightscout.androidaps.R
|
||||||
import info.nightscout.androidaps.events.EventAppInitialized
|
import info.nightscout.androidaps.data.IobTotal
|
||||||
import info.nightscout.androidaps.events.EventProfileSwitchChanged
|
import info.nightscout.androidaps.database.AppRepository
|
||||||
import info.nightscout.androidaps.events.EventRefreshOverview
|
import info.nightscout.androidaps.database.ValueWrapper
|
||||||
import info.nightscout.androidaps.events.EventTempBasalChange
|
import info.nightscout.androidaps.database.entities.Bolus
|
||||||
|
import info.nightscout.androidaps.events.*
|
||||||
import info.nightscout.androidaps.extensions.*
|
import info.nightscout.androidaps.extensions.*
|
||||||
import info.nightscout.androidaps.interfaces.*
|
import info.nightscout.androidaps.interfaces.*
|
||||||
import info.nightscout.androidaps.logging.AAPSLogger
|
import info.nightscout.androidaps.logging.AAPSLogger
|
||||||
|
import info.nightscout.androidaps.logging.LTag
|
||||||
|
import info.nightscout.androidaps.plugins.aps.events.EventLoopInvoked
|
||||||
|
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin
|
||||||
|
import info.nightscout.androidaps.plugins.aps.openAPSSMB.SMBDefaults
|
||||||
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
|
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
|
||||||
|
import info.nightscout.androidaps.plugins.general.nsclient.data.NSDeviceStatus
|
||||||
import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification
|
import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification
|
||||||
import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification
|
import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification
|
||||||
import info.nightscout.androidaps.plugins.general.overview.events.EventUpdateOverview
|
import info.nightscout.androidaps.plugins.general.overview.events.EventUpdateOverview
|
||||||
|
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.*
|
||||||
import info.nightscout.androidaps.plugins.general.overview.notifications.NotificationStore
|
import info.nightscout.androidaps.plugins.general.overview.notifications.NotificationStore
|
||||||
import info.nightscout.androidaps.utils.DateUtil
|
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult
|
||||||
import info.nightscout.androidaps.utils.FabricPrivacy
|
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventBucketedDataCreated
|
||||||
|
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventIobCalculationProgress
|
||||||
|
import info.nightscout.androidaps.utils.*
|
||||||
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
||||||
import info.nightscout.androidaps.utils.rx.AapsSchedulers
|
import info.nightscout.androidaps.utils.rx.AapsSchedulers
|
||||||
import info.nightscout.androidaps.utils.sharedPreferences.SP
|
import info.nightscout.androidaps.utils.sharedPreferences.SP
|
||||||
import io.reactivex.disposables.CompositeDisposable
|
import io.reactivex.disposables.CompositeDisposable
|
||||||
import io.reactivex.rxkotlin.plusAssign
|
import io.reactivex.rxkotlin.plusAssign
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
|
import java.util.*
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
import kotlin.math.abs
|
||||||
|
import kotlin.math.ceil
|
||||||
|
import kotlin.math.max
|
||||||
|
import kotlin.math.min
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
class OverviewPlugin @Inject constructor(
|
class OverviewPlugin @Inject constructor(
|
||||||
|
@ -39,9 +58,17 @@ class OverviewPlugin @Inject constructor(
|
||||||
resourceHelper: ResourceHelper,
|
resourceHelper: ResourceHelper,
|
||||||
private val config: Config,
|
private val config: Config,
|
||||||
private val dateUtil: DateUtil,
|
private val dateUtil: DateUtil,
|
||||||
|
private val translator: Translator,
|
||||||
|
// private val profiler: Profiler,
|
||||||
private val profileFunction: ProfileFunction,
|
private val profileFunction: ProfileFunction,
|
||||||
private val iobCobCalculator: IobCobCalculator,
|
private val iobCobCalculator: IobCobCalculator,
|
||||||
private val overviewData: OverviewData
|
private val repository: AppRepository,
|
||||||
|
private val defaultValueHelper: DefaultValueHelper,
|
||||||
|
private val loopPlugin: LoopPlugin,
|
||||||
|
private val activePlugin: ActivePlugin,
|
||||||
|
private val nsDeviceStatus: NSDeviceStatus,
|
||||||
|
private val overviewData: OverviewData,
|
||||||
|
private val overviewMenus: OverviewMenus
|
||||||
) : PluginBase(PluginDescription()
|
) : PluginBase(PluginDescription()
|
||||||
.mainType(PluginType.GENERAL)
|
.mainType(PluginType.GENERAL)
|
||||||
.fragmentClass(OverviewFragment::class.qualifiedName)
|
.fragmentClass(OverviewFragment::class.qualifiedName)
|
||||||
|
@ -57,8 +84,15 @@ class OverviewPlugin @Inject constructor(
|
||||||
|
|
||||||
private var disposable: CompositeDisposable = CompositeDisposable()
|
private var disposable: CompositeDisposable = CompositeDisposable()
|
||||||
|
|
||||||
|
override val overviewBus = RxBusWrapper(aapsSchedulers)
|
||||||
|
|
||||||
|
class DeviationDataPoint(x: Double, y: Double, var color: Int, scale: Scale) : ScaledDataPoint(x, y, scale)
|
||||||
|
|
||||||
override fun onStart() {
|
override fun onStart() {
|
||||||
super.onStart()
|
super.onStart()
|
||||||
|
overviewMenus.loadGraphConfig()
|
||||||
|
overviewData.initRange()
|
||||||
|
|
||||||
notificationStore.createNotificationChannel()
|
notificationStore.createNotificationChannel()
|
||||||
disposable += rxBus
|
disposable += rxBus
|
||||||
.toObservable(EventNewNotification::class.java)
|
.toObservable(EventNewNotification::class.java)
|
||||||
|
@ -75,17 +109,62 @@ class OverviewPlugin @Inject constructor(
|
||||||
rxBus.send(EventRefreshOverview("EventDismissNotification"))
|
rxBus.send(EventRefreshOverview("EventDismissNotification"))
|
||||||
}, fabricPrivacy::logException)
|
}, fabricPrivacy::logException)
|
||||||
disposable += rxBus
|
disposable += rxBus
|
||||||
.toObservable(EventAppInitialized::class.java)
|
.toObservable(EventIobCalculationProgress::class.java)
|
||||||
.observeOn(aapsSchedulers.io)
|
.observeOn(aapsSchedulers.main)
|
||||||
.subscribe( { loadAll() }, fabricPrivacy::logException)
|
.subscribe({ overviewData.calcProgress = it.progress; overviewBus.send(EventUpdateOverview("EventIobCalculationProgress", OverviewData.Property.CALC_PROGRESS)) }, fabricPrivacy::logException)
|
||||||
disposable += rxBus
|
disposable += rxBus
|
||||||
.toObservable(EventTempBasalChange::class.java)
|
.toObservable(EventTempBasalChange::class.java)
|
||||||
.observeOn(aapsSchedulers.io)
|
.observeOn(aapsSchedulers.io)
|
||||||
.subscribe( { loadTemporaryBasal() }, fabricPrivacy::logException)
|
.subscribe({ loadTemporaryBasal("EventTempBasalChange") }, fabricPrivacy::logException)
|
||||||
|
disposable += rxBus
|
||||||
|
.toObservable(EventExtendedBolusChange::class.java)
|
||||||
|
.observeOn(aapsSchedulers.io)
|
||||||
|
.subscribe({ loadExtendedBolus("EventExtendedBolusChange") }, fabricPrivacy::logException)
|
||||||
|
disposable += rxBus
|
||||||
|
.toObservable(EventNewBG::class.java)
|
||||||
|
.observeOn(aapsSchedulers.io)
|
||||||
|
.subscribe({ loadBg("EventNewBG") }, fabricPrivacy::logException)
|
||||||
|
disposable += rxBus
|
||||||
|
.toObservable(EventTempTargetChange::class.java)
|
||||||
|
.observeOn(aapsSchedulers.io)
|
||||||
|
.subscribe({ loadTemporaryTarget("EventTempTargetChange") }, fabricPrivacy::logException)
|
||||||
|
disposable += rxBus
|
||||||
|
.toObservable(EventTreatmentChange::class.java)
|
||||||
|
.observeOn(aapsSchedulers.io)
|
||||||
|
.subscribe({
|
||||||
|
loadIobCobResults("EventTreatmentChange")
|
||||||
|
prepareTreatmentsData("EventTreatmentChange")
|
||||||
|
overviewBus.send(EventUpdateOverview("EventTreatmentChange", OverviewData.Property.GRAPH))
|
||||||
|
}, fabricPrivacy::logException)
|
||||||
|
disposable += rxBus
|
||||||
|
.toObservable(EventTherapyEventChange::class.java)
|
||||||
|
.observeOn(aapsSchedulers.io)
|
||||||
|
.subscribe({
|
||||||
|
prepareTreatmentsData("EventTherapyEventChange")
|
||||||
|
overviewBus.send(EventUpdateOverview("EventTherapyEventChange", OverviewData.Property.GRAPH))
|
||||||
|
}, fabricPrivacy::logException)
|
||||||
|
disposable += rxBus
|
||||||
|
.toObservable(EventBucketedDataCreated::class.java)
|
||||||
|
.observeOn(aapsSchedulers.io)
|
||||||
|
.subscribe({
|
||||||
|
prepareBucketedData("EventBucketedDataCreated")
|
||||||
|
prepareBgData("EventBucketedDataCreated")
|
||||||
|
overviewBus.send(EventUpdateOverview("EventBucketedDataCreated", OverviewData.Property.GRAPH))
|
||||||
|
}, fabricPrivacy::logException)
|
||||||
|
disposable += rxBus
|
||||||
|
.toObservable(EventLoopInvoked::class.java)
|
||||||
|
.observeOn(aapsSchedulers.io)
|
||||||
|
.subscribe({ preparePredictions("EventLoopInvoked") }, fabricPrivacy::logException)
|
||||||
disposable.add(rxBus
|
disposable.add(rxBus
|
||||||
.toObservable(EventProfileSwitchChanged::class.java)
|
.toObservable(EventProfileSwitchChanged::class.java)
|
||||||
.observeOn(aapsSchedulers.io)
|
.observeOn(aapsSchedulers.io)
|
||||||
.subscribe({ loadProfile() }, fabricPrivacy::logException))
|
.subscribe({ loadProfile("EventProfileSwitchChanged") }, fabricPrivacy::logException))
|
||||||
|
disposable.add(rxBus
|
||||||
|
.toObservable(EventAutosensCalculationFinished::class.java)
|
||||||
|
.observeOn(aapsSchedulers.io)
|
||||||
|
.subscribe({ refreshLoop("EventAutosensCalculationFinished") }, fabricPrivacy::logException))
|
||||||
|
|
||||||
|
Thread { loadAll("onResume") }.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStop() {
|
override fun onStop() {
|
||||||
|
@ -160,21 +239,600 @@ class OverviewPlugin @Inject constructor(
|
||||||
.storeDouble(R.string.key_statuslights_bat_critical, sp, resourceHelper)
|
.storeDouble(R.string.key_statuslights_bat_critical, sp, resourceHelper)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadAll() {
|
@Volatile var runningRefresh = false
|
||||||
loadProfile()
|
override fun refreshLoop(from: String) {
|
||||||
loadTemporaryBasal()
|
if (runningRefresh) return
|
||||||
|
runningRefresh = true
|
||||||
|
overviewBus.send(EventUpdateOverview(from, OverviewData.Property.BG))
|
||||||
|
overviewBus.send(EventUpdateOverview(from, OverviewData.Property.TIME))
|
||||||
|
overviewBus.send(EventUpdateOverview(from, OverviewData.Property.TEMPORARY_BASAL))
|
||||||
|
overviewBus.send(EventUpdateOverview(from, OverviewData.Property.EXTENDED_BOLUS))
|
||||||
|
overviewBus.send(EventUpdateOverview(from, OverviewData.Property.IOB_COB))
|
||||||
|
overviewBus.send(EventUpdateOverview(from, OverviewData.Property.TEMPORARY_TARGET))
|
||||||
|
overviewBus.send(EventUpdateOverview(from, OverviewData.Property.SENSITIVITY))
|
||||||
|
loadAsData(from)
|
||||||
|
preparePredictions(from)
|
||||||
|
prepareBasalData(from)
|
||||||
|
prepareTemporaryTargetData(from)
|
||||||
|
prepareTreatmentsData(from)
|
||||||
|
prepareIobAutosensData(from)
|
||||||
|
overviewBus.send(EventUpdateOverview(from, OverviewData.Property.GRAPH))
|
||||||
|
aapsLogger.debug(LTag.UI, "refreshLoop finished")
|
||||||
|
runningRefresh = false
|
||||||
}
|
}
|
||||||
|
|
||||||
@Synchronized
|
@Suppress("SameParameterValue")
|
||||||
private fun loadProfile() {
|
private fun loadAll(from: String) {
|
||||||
|
loadBg(from)
|
||||||
|
loadProfile(from)
|
||||||
|
loadTemporaryBasal(from)
|
||||||
|
loadExtendedBolus(from)
|
||||||
|
loadTemporaryTarget(from)
|
||||||
|
loadIobCobResults(from)
|
||||||
|
loadAsData(from)
|
||||||
|
prepareBasalData(from)
|
||||||
|
prepareTemporaryTargetData(from)
|
||||||
|
prepareTreatmentsData(from)
|
||||||
|
// prepareIobAutosensData(from)
|
||||||
|
// preparePredictions(from)
|
||||||
|
overviewBus.send(EventUpdateOverview(from, OverviewData.Property.GRAPH))
|
||||||
|
aapsLogger.debug(LTag.UI, "loadAll finished")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadProfile(from: String) {
|
||||||
overviewData.profile = profileFunction.getProfile()
|
overviewData.profile = profileFunction.getProfile()
|
||||||
overviewData.profileName = profileFunction.getProfileName()
|
overviewData.profileName = profileFunction.getProfileName()
|
||||||
rxBus.send(EventUpdateOverview(OverviewData.Property.PROFILE))
|
overviewData.profileNameWithRemainingTime = profileFunction.getProfileNameWithRemainingTime()
|
||||||
|
overviewBus.send(EventUpdateOverview(from, OverviewData.Property.PROFILE))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadTemporaryBasal(from: String) {
|
||||||
|
overviewData.temporaryBasal = iobCobCalculator.getTempBasalIncludingConvertedExtended(dateUtil.now())
|
||||||
|
overviewBus.send(EventUpdateOverview(from, OverviewData.Property.TEMPORARY_BASAL))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadExtendedBolus(from: String) {
|
||||||
|
overviewData.extendedBolus = iobCobCalculator.getExtendedBolus(dateUtil.now())
|
||||||
|
overviewBus.send(EventUpdateOverview(from, OverviewData.Property.EXTENDED_BOLUS))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadTemporaryTarget(from: String) {
|
||||||
|
val tempTarget = repository.getTemporaryTargetActiveAt(dateUtil.now()).blockingGet()
|
||||||
|
if (tempTarget is ValueWrapper.Existing) overviewData.temporarytarget = tempTarget.value
|
||||||
|
else overviewData.temporarytarget = null
|
||||||
|
overviewBus.send(EventUpdateOverview(from, OverviewData.Property.TEMPORARY_TARGET))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadAsData(from: String) {
|
||||||
|
overviewData.lastAutosensData = iobCobCalculator.ads.getLastAutosensData("Overview", aapsLogger, dateUtil)
|
||||||
|
overviewBus.send(EventUpdateOverview(from, OverviewData.Property.SENSITIVITY))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadBg(from: String) {
|
||||||
|
val gvWrapped = repository.getLastGlucoseValueWrapped().blockingGet()
|
||||||
|
if (gvWrapped is ValueWrapper.Existing) overviewData.lastBg = gvWrapped.value
|
||||||
|
else overviewData.lastBg = null
|
||||||
|
overviewBus.send(EventUpdateOverview(from, OverviewData.Property.BG))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadIobCobResults(from: String) {
|
||||||
|
overviewData.bolusIob = iobCobCalculator.calculateIobFromBolus().round()
|
||||||
|
overviewData.basalIob = iobCobCalculator.calculateIobFromTempBasalsIncludingConvertedExtended().round()
|
||||||
|
overviewData.cobInfo = iobCobCalculator.getCobInfo(false, "Overview COB")
|
||||||
|
val lastCarbs = repository.getLastCarbsRecordWrapped().blockingGet()
|
||||||
|
overviewData.lastCarbsTime = if (lastCarbs is ValueWrapper.Existing) lastCarbs.value.timestamp else 0L
|
||||||
|
|
||||||
|
overviewBus.send(EventUpdateOverview(from, OverviewData.Property.IOB_COB))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
private fun loadTemporaryBasal() {
|
@Suppress("SameParameterValue", "UNUSED_PARAMETER")
|
||||||
overviewData.temporaryBasal = iobCobCalculator.getTempBasalIncludingConvertedExtended(dateUtil.now())
|
private fun prepareBgData(from: String) {
|
||||||
rxBus.send(EventUpdateOverview(OverviewData.Property.TEMPORARY_BASAL))
|
// val start = dateUtil.now()
|
||||||
|
var maxBgValue = Double.MIN_VALUE
|
||||||
|
overviewData.bgReadingsArray = repository.compatGetBgReadingsDataFromTime(overviewData.fromTime, overviewData.toTime, false).blockingGet()
|
||||||
|
val bgListArray: MutableList<DataPointWithLabelInterface> = ArrayList()
|
||||||
|
for (bg in overviewData.bgReadingsArray) {
|
||||||
|
if (bg.timestamp < overviewData.fromTime || bg.timestamp > overviewData.toTime) continue
|
||||||
|
if (bg.value > maxBgValue) maxBgValue = bg.value
|
||||||
|
bgListArray.add(GlucoseValueDataPoint(bg, defaultValueHelper, profileFunction, resourceHelper))
|
||||||
|
}
|
||||||
|
overviewData.bgReadingGraphSeries = PointsWithLabelGraphSeries(Array(bgListArray.size) { i -> bgListArray[i] })
|
||||||
|
overviewData.maxBgValue = Profile.fromMgdlToUnits(maxBgValue, profileFunction.getUnits())
|
||||||
|
if (defaultValueHelper.determineHighLine() > maxBgValue) overviewData.maxBgValue = defaultValueHelper.determineHighLine()
|
||||||
|
overviewData.maxBgValue = addUpperChartMargin(overviewData.maxBgValue)
|
||||||
|
// profiler.log(LTag.UI, "prepareBgData() $from", start)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("UNUSED_PARAMETER")
|
||||||
|
@Synchronized
|
||||||
|
private fun preparePredictions(from: String) {
|
||||||
|
// val start = dateUtil.now()
|
||||||
|
val apsResult = if (config.APS) loopPlugin.lastRun?.constraintsProcessed else nsDeviceStatus.getAPSResult(injector)
|
||||||
|
val predictionsAvailable = if (config.APS) loopPlugin.lastRun?.request?.hasPredictions == true else config.NSCLIENT
|
||||||
|
val menuChartSettings = overviewMenus.setting
|
||||||
|
// align to hours
|
||||||
|
val calendar = Calendar.getInstance().also {
|
||||||
|
it.timeInMillis = System.currentTimeMillis()
|
||||||
|
it[Calendar.MILLISECOND] = 0
|
||||||
|
it[Calendar.SECOND] = 0
|
||||||
|
it[Calendar.MINUTE] = 0
|
||||||
|
it.add(Calendar.HOUR, 1)
|
||||||
|
}
|
||||||
|
if (predictionsAvailable && apsResult != null && menuChartSettings[0][OverviewMenus.CharType.PRE.ordinal]) {
|
||||||
|
var predictionHours = (ceil(apsResult.latestPredictionsTime - System.currentTimeMillis().toDouble()) / (60 * 60 * 1000)).toInt()
|
||||||
|
predictionHours = min(2, predictionHours)
|
||||||
|
predictionHours = max(0, predictionHours)
|
||||||
|
val hoursToFetch = overviewData.rangeToDisplay - predictionHours
|
||||||
|
overviewData.toTime = calendar.timeInMillis + 100000 // little bit more to avoid wrong rounding - GraphView specific
|
||||||
|
overviewData.fromTime = overviewData.toTime - T.hours(hoursToFetch.toLong()).msecs()
|
||||||
|
overviewData.endTime = overviewData.toTime + T.hours(predictionHours.toLong()).msecs()
|
||||||
|
} else {
|
||||||
|
overviewData.toTime = calendar.timeInMillis + 100000 // little bit more to avoid wrong rounding - GraphView specific
|
||||||
|
overviewData.fromTime = overviewData.toTime - T.hours(overviewData.rangeToDisplay.toLong()).msecs()
|
||||||
|
overviewData.endTime = overviewData.toTime
|
||||||
|
}
|
||||||
|
|
||||||
|
val bgListArray: MutableList<DataPointWithLabelInterface> = ArrayList()
|
||||||
|
val predictions: MutableList<GlucoseValueDataPoint>? = apsResult?.predictions
|
||||||
|
?.map { bg -> GlucoseValueDataPoint(bg, defaultValueHelper, profileFunction, resourceHelper) }
|
||||||
|
?.toMutableList()
|
||||||
|
if (predictions != null) {
|
||||||
|
predictions.sortWith { o1: GlucoseValueDataPoint, o2: GlucoseValueDataPoint -> o1.x.compareTo(o2.x) }
|
||||||
|
for (prediction in predictions) if (prediction.data.value >= 40) bgListArray.add(prediction)
|
||||||
|
}
|
||||||
|
overviewData.predictionsGraphSeries = PointsWithLabelGraphSeries(Array(bgListArray.size) { i -> bgListArray[i] })
|
||||||
|
// profiler.log(LTag.UI, "preparePredictions() $from", start)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
@Suppress("SameParameterValue", "UNUSED_PARAMETER")
|
||||||
|
private fun prepareBucketedData(from: String) {
|
||||||
|
// val start = dateUtil.now()
|
||||||
|
val bucketedData = iobCobCalculator.ads.getBucketedDataTableCopy() ?: return
|
||||||
|
if (bucketedData.isEmpty()) {
|
||||||
|
aapsLogger.debug("No bucketed data.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val bucketedListArray: MutableList<DataPointWithLabelInterface> = ArrayList()
|
||||||
|
for (inMemoryGlucoseValue in bucketedData) {
|
||||||
|
if (inMemoryGlucoseValue.timestamp < overviewData.fromTime || inMemoryGlucoseValue.timestamp > overviewData.toTime) continue
|
||||||
|
bucketedListArray.add(InMemoryGlucoseValueDataPoint(inMemoryGlucoseValue, profileFunction, resourceHelper))
|
||||||
|
}
|
||||||
|
overviewData.bucketedGraphSeries = PointsWithLabelGraphSeries(Array(bucketedListArray.size) { i -> bucketedListArray[i] })
|
||||||
|
// profiler.log(LTag.UI, "prepareBucketedData() $from", start)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("UNUSED_PARAMETER")
|
||||||
|
@Synchronized
|
||||||
|
private fun prepareBasalData(from: String) {
|
||||||
|
// val start = dateUtil.now()
|
||||||
|
overviewData.maxBasalValueFound = 0.0
|
||||||
|
val baseBasalArray: MutableList<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 = overviewData.fromTime
|
||||||
|
while (time < overviewData.toTime) {
|
||||||
|
val profile = profileFunction.getProfile(time)
|
||||||
|
if (profile == null) {
|
||||||
|
time += 60 * 1000L
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
val basalData = iobCobCalculator.getBasalData(profile, time)
|
||||||
|
val baseBasalValue = basalData.basal
|
||||||
|
var absoluteLineValue = baseBasalValue
|
||||||
|
var tempBasalValue = 0.0
|
||||||
|
var basal = 0.0
|
||||||
|
if (basalData.isTempBasalRunning) {
|
||||||
|
tempBasalValue = basalData.tempBasalAbsolute
|
||||||
|
absoluteLineValue = tempBasalValue
|
||||||
|
if (tempBasalValue != lastTempBasal) {
|
||||||
|
tempBasalArray.add(ScaledDataPoint(time, lastTempBasal, overviewData.basalScale))
|
||||||
|
tempBasalArray.add(ScaledDataPoint(time, tempBasalValue.also { basal = it }, overviewData.basalScale))
|
||||||
|
}
|
||||||
|
if (lastBaseBasal != 0.0) {
|
||||||
|
baseBasalArray.add(ScaledDataPoint(time, lastBaseBasal, overviewData.basalScale))
|
||||||
|
baseBasalArray.add(ScaledDataPoint(time, 0.0, overviewData.basalScale))
|
||||||
|
lastBaseBasal = 0.0
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (baseBasalValue != lastBaseBasal) {
|
||||||
|
baseBasalArray.add(ScaledDataPoint(time, lastBaseBasal, overviewData.basalScale))
|
||||||
|
baseBasalArray.add(ScaledDataPoint(time, baseBasalValue.also { basal = it }, overviewData.basalScale))
|
||||||
|
lastBaseBasal = baseBasalValue
|
||||||
|
}
|
||||||
|
if (lastTempBasal != 0.0) {
|
||||||
|
tempBasalArray.add(ScaledDataPoint(time, lastTempBasal, overviewData.basalScale))
|
||||||
|
tempBasalArray.add(ScaledDataPoint(time, 0.0, overviewData.basalScale))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (baseBasalValue != lastLineBasal) {
|
||||||
|
basalLineArray.add(ScaledDataPoint(time, lastLineBasal, overviewData.basalScale))
|
||||||
|
basalLineArray.add(ScaledDataPoint(time, baseBasalValue, overviewData.basalScale))
|
||||||
|
}
|
||||||
|
if (absoluteLineValue != lastAbsoluteLineBasal) {
|
||||||
|
absoluteBasalLineArray.add(ScaledDataPoint(time, lastAbsoluteLineBasal, overviewData.basalScale))
|
||||||
|
absoluteBasalLineArray.add(ScaledDataPoint(time, basal, overviewData.basalScale))
|
||||||
|
}
|
||||||
|
lastAbsoluteLineBasal = absoluteLineValue
|
||||||
|
lastLineBasal = baseBasalValue
|
||||||
|
lastTempBasal = tempBasalValue
|
||||||
|
overviewData.maxBasalValueFound = max(overviewData.maxBasalValueFound, max(tempBasalValue, baseBasalValue))
|
||||||
|
time += 60 * 1000L
|
||||||
|
}
|
||||||
|
|
||||||
|
// final points
|
||||||
|
basalLineArray.add(ScaledDataPoint(overviewData.toTime, lastLineBasal, overviewData.basalScale))
|
||||||
|
baseBasalArray.add(ScaledDataPoint(overviewData.toTime, lastBaseBasal, overviewData.basalScale))
|
||||||
|
tempBasalArray.add(ScaledDataPoint(overviewData.toTime, lastTempBasal, overviewData.basalScale))
|
||||||
|
absoluteBasalLineArray.add(ScaledDataPoint(overviewData.toTime, lastAbsoluteLineBasal, overviewData.basalScale))
|
||||||
|
|
||||||
|
// create series
|
||||||
|
overviewData.baseBasalGraphSeries = LineGraphSeries(Array(baseBasalArray.size) { i -> baseBasalArray[i] }).also {
|
||||||
|
it.isDrawBackground = true
|
||||||
|
it.backgroundColor = resourceHelper.gc(R.color.basebasal)
|
||||||
|
it.thickness = 0
|
||||||
|
}
|
||||||
|
overviewData.tempBasalGraphSeries = LineGraphSeries(Array(tempBasalArray.size) { i -> tempBasalArray[i] }).also {
|
||||||
|
it.isDrawBackground = true
|
||||||
|
it.backgroundColor = resourceHelper.gc(R.color.tempbasal)
|
||||||
|
it.thickness = 0
|
||||||
|
}
|
||||||
|
overviewData.basalLineGraphSeries = LineGraphSeries(Array(basalLineArray.size) { i -> basalLineArray[i] }).also {
|
||||||
|
it.setCustomPaint(Paint().also { paint ->
|
||||||
|
paint.style = Paint.Style.STROKE
|
||||||
|
paint.strokeWidth = resourceHelper.getDisplayMetrics().scaledDensity * 2
|
||||||
|
paint.pathEffect = DashPathEffect(floatArrayOf(2f, 4f), 0f)
|
||||||
|
paint.color = resourceHelper.gc(R.color.basal)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
overviewData.absoluteBasalGraphSeries = LineGraphSeries(Array(absoluteBasalLineArray.size) { i -> absoluteBasalLineArray[i] }).also {
|
||||||
|
it.setCustomPaint(Paint().also { absolutePaint ->
|
||||||
|
absolutePaint.style = Paint.Style.STROKE
|
||||||
|
absolutePaint.strokeWidth = resourceHelper.getDisplayMetrics().scaledDensity * 2
|
||||||
|
absolutePaint.color = resourceHelper.gc(R.color.basal)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// profiler.log(LTag.UI, "prepareBasalData() $from", start)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("UNUSED_PARAMETER")
|
||||||
|
@Synchronized
|
||||||
|
private fun prepareTemporaryTargetData(from: String) {
|
||||||
|
// val start = dateUtil.now()
|
||||||
|
val profile = overviewData.profile ?: return
|
||||||
|
val units = profileFunction.getUnits()
|
||||||
|
var toTime = overviewData.toTime
|
||||||
|
val targetsSeriesArray: MutableList<DataPoint> = ArrayList()
|
||||||
|
var lastTarget = -1.0
|
||||||
|
loopPlugin.lastRun?.constraintsProcessed?.let { toTime = max(it.latestPredictionsTime, toTime) }
|
||||||
|
var time = overviewData.fromTime
|
||||||
|
while (time < toTime) {
|
||||||
|
val tt = repository.getTemporaryTargetActiveAt(time).blockingGet()
|
||||||
|
val value: Double = if (tt is ValueWrapper.Existing) {
|
||||||
|
Profile.fromMgdlToUnits(tt.value.target(), units)
|
||||||
|
} else {
|
||||||
|
Profile.fromMgdlToUnits((profile.getTargetLowMgdl(time) + profile.getTargetHighMgdl(time)) / 2, units)
|
||||||
|
}
|
||||||
|
if (lastTarget != value) {
|
||||||
|
if (lastTarget != -1.0) targetsSeriesArray.add(DataPoint(time.toDouble(), lastTarget))
|
||||||
|
targetsSeriesArray.add(DataPoint(time.toDouble(), value))
|
||||||
|
}
|
||||||
|
lastTarget = value
|
||||||
|
time += 5 * 60 * 1000L
|
||||||
|
}
|
||||||
|
// final point
|
||||||
|
targetsSeriesArray.add(DataPoint(toTime.toDouble(), lastTarget))
|
||||||
|
// create series
|
||||||
|
overviewData.temporaryTargetSeries = LineGraphSeries(Array(targetsSeriesArray.size) { i -> targetsSeriesArray[i] }).also {
|
||||||
|
it.isDrawBackground = false
|
||||||
|
it.color = resourceHelper.gc(R.color.tempTargetBackground)
|
||||||
|
it.thickness = 2
|
||||||
|
}
|
||||||
|
// profiler.log(LTag.UI, "prepareTemporaryTargetData() $from", start)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("UNUSED_PARAMETER")
|
||||||
|
@Synchronized
|
||||||
|
private fun prepareTreatmentsData(from: String) {
|
||||||
|
// val start = dateUtil.now()
|
||||||
|
overviewData.maxTreatmentsValue = 0.0
|
||||||
|
val filteredTreatments: MutableList<DataPointWithLabelInterface> = ArrayList()
|
||||||
|
repository.getBolusesIncludingInvalidFromTimeToTime(overviewData.fromTime, overviewData.endTime, true).blockingGet()
|
||||||
|
.map { BolusDataPoint(it, resourceHelper, activePlugin, defaultValueHelper) }
|
||||||
|
.filter { it.data.type != Bolus.Type.SMB || it.data.isValid }
|
||||||
|
.forEach {
|
||||||
|
it.y = getNearestBg(it.x.toLong())
|
||||||
|
filteredTreatments.add(it)
|
||||||
|
}
|
||||||
|
repository.getCarbsIncludingInvalidFromTimeToTimeExpanded(overviewData.fromTime, overviewData.endTime, true).blockingGet()
|
||||||
|
.map { CarbsDataPoint(it, resourceHelper) }
|
||||||
|
.forEach {
|
||||||
|
it.y = getNearestBg(it.x.toLong())
|
||||||
|
filteredTreatments.add(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProfileSwitch
|
||||||
|
repository.getEffectiveProfileSwitchDataFromTimeToTime(overviewData.fromTime, overviewData.endTime, true).blockingGet()
|
||||||
|
.map { EffectiveProfileSwitchDataPoint(it) }
|
||||||
|
.forEach(filteredTreatments::add)
|
||||||
|
|
||||||
|
// Extended bolus
|
||||||
|
if (!activePlugin.activePump.isFakingTempsByExtendedBoluses) {
|
||||||
|
repository.getExtendedBolusDataFromTimeToTime(overviewData.fromTime, overviewData.endTime, true).blockingGet()
|
||||||
|
.map { ExtendedBolusDataPoint(it) }
|
||||||
|
.filter { it.duration != 0L }
|
||||||
|
.forEach {
|
||||||
|
it.y = getNearestBg(it.x.toLong())
|
||||||
|
filteredTreatments.add(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Careportal
|
||||||
|
repository.compatGetTherapyEventDataFromToTime(overviewData.fromTime - T.hours(6).msecs(), overviewData.endTime).blockingGet()
|
||||||
|
.map { TherapyEventDataPoint(it, resourceHelper, profileFunction, translator) }
|
||||||
|
.filterTimeframe(overviewData.fromTime, overviewData.endTime)
|
||||||
|
.forEach {
|
||||||
|
if (it.y == 0.0) it.y = getNearestBg(it.x.toLong())
|
||||||
|
filteredTreatments.add(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
// increase maxY if a treatment forces it's own height that's higher than a BG value
|
||||||
|
filteredTreatments.map { it.y }
|
||||||
|
.maxOrNull()
|
||||||
|
?.let(::addUpperChartMargin)
|
||||||
|
?.let { overviewData.maxTreatmentsValue = maxOf(overviewData.maxTreatmentsValue, it) }
|
||||||
|
|
||||||
|
overviewData.treatmentsSeries = PointsWithLabelGraphSeries(filteredTreatments.toTypedArray())
|
||||||
|
// profiler.log(LTag.UI, "prepareTreatmentsData() $from", start)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("UNUSED_PARAMETER")
|
||||||
|
@Synchronized
|
||||||
|
private fun prepareIobAutosensData(from: String) {
|
||||||
|
// val start = dateUtil.now()
|
||||||
|
val iobArray: MutableList<ScaledDataPoint> = ArrayList()
|
||||||
|
val absIobArray: MutableList<ScaledDataPoint> = ArrayList()
|
||||||
|
overviewData.maxIobValueFound = Double.MIN_VALUE
|
||||||
|
var lastIob = 0.0
|
||||||
|
var absLastIob = 0.0
|
||||||
|
var time = overviewData.fromTime
|
||||||
|
|
||||||
|
val minFailOverActiveList: MutableList<DataPointWithLabelInterface> = ArrayList()
|
||||||
|
val cobArray: MutableList<ScaledDataPoint> = ArrayList()
|
||||||
|
overviewData.maxCobValueFound = Double.MIN_VALUE
|
||||||
|
var lastCob = 0
|
||||||
|
|
||||||
|
val actArrayHist: MutableList<ScaledDataPoint> = ArrayList()
|
||||||
|
val actArrayPrediction: MutableList<ScaledDataPoint> = ArrayList()
|
||||||
|
val now = dateUtil.now().toDouble()
|
||||||
|
overviewData.maxIAValue = 0.0
|
||||||
|
|
||||||
|
val bgiArrayHist: MutableList<ScaledDataPoint> = ArrayList()
|
||||||
|
val bgiArrayPrediction: MutableList<ScaledDataPoint> = ArrayList()
|
||||||
|
overviewData.maxBGIValue = Double.MIN_VALUE
|
||||||
|
|
||||||
|
val devArray: MutableList<DeviationDataPoint> = ArrayList()
|
||||||
|
overviewData.maxDevValueFound = Double.MIN_VALUE
|
||||||
|
|
||||||
|
val ratioArray: MutableList<ScaledDataPoint> = ArrayList()
|
||||||
|
overviewData.maxRatioValueFound = 5.0 //even if sens data equals 0 for all the period, minimum scale is between 95% and 105%
|
||||||
|
overviewData.minRatioValueFound = -5.0
|
||||||
|
|
||||||
|
val dsMaxArray: MutableList<ScaledDataPoint> = ArrayList()
|
||||||
|
val dsMinArray: MutableList<ScaledDataPoint> = ArrayList()
|
||||||
|
overviewData.maxFromMaxValueFound = Double.MIN_VALUE
|
||||||
|
overviewData.maxFromMinValueFound = Double.MIN_VALUE
|
||||||
|
|
||||||
|
val adsData = iobCobCalculator.ads.clone()
|
||||||
|
|
||||||
|
while (time <= overviewData.toTime) {
|
||||||
|
val profile = profileFunction.getProfile(time)
|
||||||
|
if (profile == null) {
|
||||||
|
time += 5 * 60 * 1000L
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// IOB
|
||||||
|
val iob = iobCobCalculator.calculateFromTreatmentsAndTemps(time, profile)
|
||||||
|
val baseBasalIob = iobCobCalculator.calculateAbsoluteIobFromBaseBasals(time)
|
||||||
|
val absIob = IobTotal.combine(iob, baseBasalIob)
|
||||||
|
val autosensData = adsData.getAutosensDataAtTime(time)
|
||||||
|
if (abs(lastIob - iob.iob) > 0.02) {
|
||||||
|
if (abs(lastIob - iob.iob) > 0.2) iobArray.add(ScaledDataPoint(time, lastIob, overviewData.iobScale))
|
||||||
|
iobArray.add(ScaledDataPoint(time, iob.iob, overviewData.iobScale))
|
||||||
|
overviewData.maxIobValueFound = maxOf(overviewData.maxIobValueFound, abs(iob.iob))
|
||||||
|
lastIob = iob.iob
|
||||||
|
}
|
||||||
|
if (abs(absLastIob - absIob.iob) > 0.02) {
|
||||||
|
if (abs(absLastIob - absIob.iob) > 0.2) absIobArray.add(ScaledDataPoint(time, absLastIob, overviewData.iobScale))
|
||||||
|
absIobArray.add(ScaledDataPoint(time, absIob.iob, overviewData.iobScale))
|
||||||
|
overviewData.maxIobValueFound = maxOf(overviewData.maxIobValueFound, abs(absIob.iob))
|
||||||
|
absLastIob = absIob.iob
|
||||||
|
}
|
||||||
|
|
||||||
|
// COB
|
||||||
|
if (autosensData != null) {
|
||||||
|
val cob = autosensData.cob.toInt()
|
||||||
|
if (cob != lastCob) {
|
||||||
|
if (autosensData.carbsFromBolus > 0) cobArray.add(ScaledDataPoint(time, lastCob.toDouble(), overviewData.cobScale))
|
||||||
|
cobArray.add(ScaledDataPoint(time, cob.toDouble(), overviewData.cobScale))
|
||||||
|
overviewData.maxCobValueFound = max(overviewData.maxCobValueFound, cob.toDouble())
|
||||||
|
lastCob = cob
|
||||||
|
}
|
||||||
|
if (autosensData.failoverToMinAbsorbtionRate) {
|
||||||
|
autosensData.setScale(overviewData.cobScale)
|
||||||
|
autosensData.setChartTime(time)
|
||||||
|
minFailOverActiveList.add(autosensData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ACTIVITY
|
||||||
|
if (time <= now) actArrayHist.add(ScaledDataPoint(time, iob.activity, overviewData.actScale))
|
||||||
|
else actArrayPrediction.add(ScaledDataPoint(time, iob.activity, overviewData.actScale))
|
||||||
|
overviewData.maxIAValue = max(overviewData.maxIAValue, abs(iob.activity))
|
||||||
|
|
||||||
|
// BGI
|
||||||
|
val devBgiScale = overviewMenus.isEnabledIn(OverviewMenus.CharType.DEV) == overviewMenus.isEnabledIn(OverviewMenus.CharType.BGI)
|
||||||
|
val deviation = if (devBgiScale) autosensData?.deviation ?: 0.0 else 0.0
|
||||||
|
val bgi: Double = iob.activity * profile.getIsfMgdl(time) * 5.0
|
||||||
|
if (time <= now) bgiArrayHist.add(ScaledDataPoint(time, bgi, overviewData.bgiScale))
|
||||||
|
else bgiArrayPrediction.add(ScaledDataPoint(time, bgi, overviewData.bgiScale))
|
||||||
|
overviewData.maxBGIValue = max(overviewData.maxBGIValue, max(abs(bgi), deviation))
|
||||||
|
|
||||||
|
// DEVIATIONS
|
||||||
|
if (autosensData != null) {
|
||||||
|
var color = resourceHelper.gc(R.color.deviationblack) // "="
|
||||||
|
if (autosensData.type == "" || autosensData.type == "non-meal") {
|
||||||
|
if (autosensData.pastSensitivity == "C") color = resourceHelper.gc(R.color.deviationgrey)
|
||||||
|
if (autosensData.pastSensitivity == "+") color = resourceHelper.gc(R.color.deviationgreen)
|
||||||
|
if (autosensData.pastSensitivity == "-") color = resourceHelper.gc(R.color.deviationred)
|
||||||
|
} else if (autosensData.type == "uam") {
|
||||||
|
color = resourceHelper.gc(R.color.uam)
|
||||||
|
} else if (autosensData.type == "csf") {
|
||||||
|
color = resourceHelper.gc(R.color.deviationgrey)
|
||||||
|
}
|
||||||
|
devArray.add(DeviationDataPoint(time.toDouble(), autosensData.deviation, color, overviewData.devScale))
|
||||||
|
overviewData.maxDevValueFound = maxOf(overviewData.maxDevValueFound, abs(autosensData.deviation), abs(bgi))
|
||||||
|
}
|
||||||
|
|
||||||
|
// RATIO
|
||||||
|
if (autosensData != null) {
|
||||||
|
ratioArray.add(ScaledDataPoint(time, 100.0 * (autosensData.autosensResult.ratio - 1), overviewData.ratioScale))
|
||||||
|
overviewData.maxRatioValueFound = max(overviewData.maxRatioValueFound, 100.0 * (autosensData.autosensResult.ratio - 1))
|
||||||
|
overviewData.minRatioValueFound = min(overviewData.minRatioValueFound, 100.0 * (autosensData.autosensResult.ratio - 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DEV SLOPE
|
||||||
|
if (autosensData != null) {
|
||||||
|
dsMaxArray.add(ScaledDataPoint(time, autosensData.slopeFromMaxDeviation, overviewData.dsMaxScale))
|
||||||
|
dsMinArray.add(ScaledDataPoint(time, autosensData.slopeFromMinDeviation, overviewData.dsMinScale))
|
||||||
|
overviewData.maxFromMaxValueFound = max(overviewData.maxFromMaxValueFound, abs(autosensData.slopeFromMaxDeviation))
|
||||||
|
overviewData.maxFromMinValueFound = max(overviewData.maxFromMinValueFound, abs(autosensData.slopeFromMinDeviation))
|
||||||
|
}
|
||||||
|
|
||||||
|
time += 5 * 60 * 1000L
|
||||||
|
}
|
||||||
|
// IOB
|
||||||
|
overviewData.iobSeries = FixedLineGraphSeries(Array(iobArray.size) { i -> iobArray[i] }).also {
|
||||||
|
it.isDrawBackground = true
|
||||||
|
it.backgroundColor = -0x7f000001 and resourceHelper.gc(R.color.iob) //50%
|
||||||
|
it.color = resourceHelper.gc(R.color.iob)
|
||||||
|
it.thickness = 3
|
||||||
|
}
|
||||||
|
overviewData.absIobSeries = FixedLineGraphSeries(Array(absIobArray.size) { i -> absIobArray[i] }).also {
|
||||||
|
it.isDrawBackground = true
|
||||||
|
it.backgroundColor = -0x7f000001 and resourceHelper.gc(R.color.iob) //50%
|
||||||
|
it.color = resourceHelper.gc(R.color.iob)
|
||||||
|
it.thickness = 3
|
||||||
|
}
|
||||||
|
|
||||||
|
if (overviewMenus.setting[0][OverviewMenus.CharType.PRE.ordinal]) {
|
||||||
|
val autosensData = adsData.getLastAutosensData("GraphData", aapsLogger, dateUtil)
|
||||||
|
val lastAutosensResult = autosensData?.autosensResult ?: AutosensResult()
|
||||||
|
val isTempTarget = repository.getTemporaryTargetActiveAt(dateUtil.now()).blockingGet() is ValueWrapper.Existing
|
||||||
|
val iobPrediction: MutableList<DataPointWithLabelInterface> = ArrayList()
|
||||||
|
val iobPredictionArray = iobCobCalculator.calculateIobArrayForSMB(lastAutosensResult, SMBDefaults.exercise_mode, SMBDefaults.half_basal_exercise_target, isTempTarget)
|
||||||
|
for (i in iobPredictionArray) {
|
||||||
|
iobPrediction.add(i.setColor(resourceHelper.gc(R.color.iobPredAS)))
|
||||||
|
overviewData.maxIobValueFound = max(overviewData.maxIobValueFound, abs(i.iob))
|
||||||
|
}
|
||||||
|
overviewData.iobPredictions1Series = PointsWithLabelGraphSeries(Array(iobPrediction.size) { i -> iobPrediction[i] })
|
||||||
|
val iobPrediction2: MutableList<DataPointWithLabelInterface> = ArrayList()
|
||||||
|
val iobPredictionArray2 = iobCobCalculator.calculateIobArrayForSMB(AutosensResult(), SMBDefaults.exercise_mode, SMBDefaults.half_basal_exercise_target, isTempTarget)
|
||||||
|
for (i in iobPredictionArray2) {
|
||||||
|
iobPrediction2.add(i.setColor(resourceHelper.gc(R.color.iobPred)))
|
||||||
|
overviewData.maxIobValueFound = max(overviewData.maxIobValueFound, abs(i.iob))
|
||||||
|
}
|
||||||
|
overviewData.iobPredictions2Series = PointsWithLabelGraphSeries(Array(iobPrediction2.size) { i -> iobPrediction2[i] })
|
||||||
|
aapsLogger.debug(LTag.AUTOSENS, "IOB prediction for AS=" + DecimalFormatter.to2Decimal(lastAutosensResult.ratio) + ": " + iobCobCalculator.iobArrayToString(iobPredictionArray))
|
||||||
|
aapsLogger.debug(LTag.AUTOSENS, "IOB prediction for AS=" + DecimalFormatter.to2Decimal(1.0) + ": " + iobCobCalculator.iobArrayToString(iobPredictionArray2))
|
||||||
|
} else {
|
||||||
|
overviewData.iobPredictions1Series = PointsWithLabelGraphSeries()
|
||||||
|
overviewData.iobPredictions2Series = PointsWithLabelGraphSeries()
|
||||||
|
}
|
||||||
|
|
||||||
|
// COB
|
||||||
|
overviewData.cobSeries = FixedLineGraphSeries(Array(cobArray.size) { i -> cobArray[i] }).also {
|
||||||
|
it.isDrawBackground = true
|
||||||
|
it.backgroundColor = -0x7f000001 and resourceHelper.gc(R.color.cob) //50%
|
||||||
|
it.color = resourceHelper.gc(R.color.cob)
|
||||||
|
it.thickness = 3
|
||||||
|
}
|
||||||
|
overviewData.cobMinFailOverSeries = PointsWithLabelGraphSeries(Array(minFailOverActiveList.size) { i -> minFailOverActiveList[i] })
|
||||||
|
|
||||||
|
// ACTIVITY
|
||||||
|
overviewData.activitySeries = FixedLineGraphSeries(Array(actArrayHist.size) { i -> actArrayHist[i] }).also {
|
||||||
|
it.isDrawBackground = false
|
||||||
|
it.color = resourceHelper.gc(R.color.activity)
|
||||||
|
it.thickness = 3
|
||||||
|
}
|
||||||
|
overviewData.activityPredictionSeries = FixedLineGraphSeries(Array(actArrayPrediction.size) { i -> actArrayPrediction[i] }).also {
|
||||||
|
it.setCustomPaint(Paint().also { paint ->
|
||||||
|
paint.style = Paint.Style.STROKE
|
||||||
|
paint.strokeWidth = 3f
|
||||||
|
paint.pathEffect = DashPathEffect(floatArrayOf(4f, 4f), 0f)
|
||||||
|
paint.color = resourceHelper.gc(R.color.activity)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// BGI
|
||||||
|
overviewData.minusBgiSeries = FixedLineGraphSeries(Array(bgiArrayHist.size) { i -> bgiArrayHist[i] }).also {
|
||||||
|
it.isDrawBackground = false
|
||||||
|
it.color = resourceHelper.gc(R.color.bgi)
|
||||||
|
it.thickness = 3
|
||||||
|
}
|
||||||
|
overviewData.minusBgiHistSeries = FixedLineGraphSeries(Array(bgiArrayPrediction.size) { i -> bgiArrayPrediction[i] }).also {
|
||||||
|
it.setCustomPaint(Paint().also { paint ->
|
||||||
|
paint.style = Paint.Style.STROKE
|
||||||
|
paint.strokeWidth = 3f
|
||||||
|
paint.pathEffect = DashPathEffect(floatArrayOf(4f, 4f), 0f)
|
||||||
|
paint.color = resourceHelper.gc(R.color.bgi)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// DEVIATIONS
|
||||||
|
overviewData.deviationsSeries = BarGraphSeries(Array(devArray.size) { i -> devArray[i] }).also {
|
||||||
|
it.setValueDependentColor { data: DeviationDataPoint -> data.color }
|
||||||
|
}
|
||||||
|
|
||||||
|
// RATIO
|
||||||
|
overviewData.ratioSeries = LineGraphSeries(Array(ratioArray.size) { i -> ratioArray[i] }).also {
|
||||||
|
it.color = resourceHelper.gc(R.color.ratio)
|
||||||
|
it.thickness = 3
|
||||||
|
}
|
||||||
|
|
||||||
|
// DEV SLOPE
|
||||||
|
overviewData.dsMaxSeries = LineGraphSeries(Array(dsMaxArray.size) { i -> dsMaxArray[i] }).also {
|
||||||
|
it.color = resourceHelper.gc(R.color.devslopepos)
|
||||||
|
it.thickness = 3
|
||||||
|
}
|
||||||
|
overviewData.dsMinSeries = LineGraphSeries(Array(dsMinArray.size) { i -> dsMinArray[i] }).also {
|
||||||
|
it.color = resourceHelper.gc(R.color.devslopeneg)
|
||||||
|
it.thickness = 3
|
||||||
|
}
|
||||||
|
|
||||||
|
// profiler.log(LTag.UI, "prepareIobAutosensData() $from", start)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun addUpperChartMargin(maxBgValue: Double) =
|
||||||
|
if (profileFunction.getUnits() == GlucoseUnit.MGDL) Round.roundTo(maxBgValue, 40.0) + 80 else Round.roundTo(maxBgValue, 2.0) + 4
|
||||||
|
|
||||||
|
private fun getNearestBg(date: Long): Double {
|
||||||
|
overviewData.bgReadingsArray.let { bgReadingsArray ->
|
||||||
|
for (reading in bgReadingsArray) {
|
||||||
|
if (reading.timestamp > date) continue
|
||||||
|
return Profile.fromMgdlToUnits(reading.value, profileFunction.getUnits())
|
||||||
|
}
|
||||||
|
return if (bgReadingsArray.isNotEmpty()) Profile.fromMgdlToUnits(bgReadingsArray[0].value, profileFunction.getUnits())
|
||||||
|
else Profile.fromMgdlToUnits(100.0, profileFunction.getUnits())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <E : DataPointWithLabelInterface> List<E>.filterTimeframe(fromTime: Long, endTime: Long): List<E> =
|
||||||
|
filter { it.x + it.duration >= fromTime && it.x <= endTime }
|
||||||
|
}
|
||||||
|
|
|
@ -3,4 +3,4 @@ package info.nightscout.androidaps.plugins.general.overview.events
|
||||||
import info.nightscout.androidaps.events.Event
|
import info.nightscout.androidaps.events.Event
|
||||||
import info.nightscout.androidaps.plugins.general.overview.OverviewData
|
import info.nightscout.androidaps.plugins.general.overview.OverviewData
|
||||||
|
|
||||||
class EventUpdateOverview(val what: OverviewData.Property) : Event()
|
class EventUpdateOverview(val from: String, val what: OverviewData.Property) : Event()
|
|
@ -4,52 +4,39 @@ import android.graphics.Color
|
||||||
import android.graphics.DashPathEffect
|
import android.graphics.DashPathEffect
|
||||||
import android.graphics.Paint
|
import android.graphics.Paint
|
||||||
import com.jjoe64.graphview.GraphView
|
import com.jjoe64.graphview.GraphView
|
||||||
import com.jjoe64.graphview.series.BarGraphSeries
|
|
||||||
import com.jjoe64.graphview.series.DataPoint
|
import com.jjoe64.graphview.series.DataPoint
|
||||||
import com.jjoe64.graphview.series.LineGraphSeries
|
import com.jjoe64.graphview.series.LineGraphSeries
|
||||||
import com.jjoe64.graphview.series.Series
|
import com.jjoe64.graphview.series.Series
|
||||||
import dagger.android.HasAndroidInjector
|
import dagger.android.HasAndroidInjector
|
||||||
import info.nightscout.androidaps.R
|
import info.nightscout.androidaps.R
|
||||||
import info.nightscout.androidaps.data.IobTotal
|
import info.nightscout.androidaps.interfaces.GlucoseUnit
|
||||||
import info.nightscout.androidaps.database.AppRepository
|
import info.nightscout.androidaps.interfaces.ProfileFunction
|
||||||
import info.nightscout.androidaps.database.ValueWrapper
|
|
||||||
import info.nightscout.androidaps.database.entities.Bolus
|
|
||||||
import info.nightscout.androidaps.database.entities.GlucoseValue
|
|
||||||
import info.nightscout.androidaps.extensions.target
|
|
||||||
import info.nightscout.androidaps.interfaces.*
|
|
||||||
import info.nightscout.androidaps.logging.AAPSLogger
|
import info.nightscout.androidaps.logging.AAPSLogger
|
||||||
import info.nightscout.androidaps.logging.LTag
|
import info.nightscout.androidaps.plugins.general.overview.OverviewData
|
||||||
import info.nightscout.androidaps.plugins.aps.openAPSSMB.SMBDefaults
|
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.AreaGraphSeries
|
||||||
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.*
|
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.DoubleDataPoint
|
||||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult
|
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.TimeAsXAxisLabelFormatter
|
||||||
import info.nightscout.androidaps.utils.*
|
import info.nightscout.androidaps.utils.DefaultValueHelper
|
||||||
|
import info.nightscout.androidaps.utils.Round
|
||||||
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
import kotlin.math.min
|
|
||||||
|
|
||||||
class GraphData(
|
class GraphData(
|
||||||
injector: HasAndroidInjector,
|
injector: HasAndroidInjector,
|
||||||
private val graph: GraphView,
|
private val graph: GraphView
|
||||||
private val iobCobCalculator: IobCobCalculator
|
|
||||||
) {
|
) {
|
||||||
|
|
||||||
// IobCobCalculatorPlugin Cannot be injected: HistoryBrowser
|
|
||||||
@Inject lateinit var aapsLogger: AAPSLogger
|
@Inject lateinit var aapsLogger: AAPSLogger
|
||||||
@Inject lateinit var profileFunction: ProfileFunction
|
@Inject lateinit var profileFunction: ProfileFunction
|
||||||
@Inject lateinit var resourceHelper: ResourceHelper
|
@Inject lateinit var resourceHelper: ResourceHelper
|
||||||
@Inject lateinit var activePlugin: ActivePlugin
|
|
||||||
@Inject lateinit var databaseHelper: DatabaseHelperInterface
|
|
||||||
@Inject lateinit var repository: AppRepository
|
|
||||||
@Inject lateinit var dateUtil: DateUtil
|
|
||||||
@Inject lateinit var defaultValueHelper: DefaultValueHelper
|
@Inject lateinit var defaultValueHelper: DefaultValueHelper
|
||||||
@Inject lateinit var translator: Translator
|
@Inject lateinit var overviewData: OverviewData
|
||||||
|
|
||||||
var maxY = Double.MIN_VALUE
|
var maxY = Double.MIN_VALUE
|
||||||
private var minY = Double.MAX_VALUE
|
private var minY = Double.MAX_VALUE
|
||||||
private var bgReadingsArray: List<GlucoseValue>? = null
|
|
||||||
private val units: GlucoseUnit
|
private val units: GlucoseUnit
|
||||||
private val series: MutableList<Series<*>> = ArrayList()
|
private val series: MutableList<Series<*>> = ArrayList()
|
||||||
|
|
||||||
|
@ -58,585 +45,130 @@ class GraphData(
|
||||||
units = profileFunction.getUnits()
|
units = profileFunction.getUnits()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addBucketedData(fromTime: Long, toTime: Long) {
|
fun addBucketedData() {
|
||||||
val bucketedData = iobCobCalculator.ads.getBucketedDataTableCopy() ?: return
|
addSeries(overviewData.bucketedGraphSeries)
|
||||||
if (bucketedData.isEmpty()) {
|
|
||||||
aapsLogger.debug("No bucketed data.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
val bucketedListArray: MutableList<DataPointWithLabelInterface> = ArrayList()
|
|
||||||
for (inMemoryGlucoseValue in bucketedData) {
|
|
||||||
if (inMemoryGlucoseValue.timestamp < fromTime || inMemoryGlucoseValue.timestamp > toTime) continue
|
|
||||||
bucketedListArray.add(InMemoryGlucoseValueDataPoint(inMemoryGlucoseValue, profileFunction, resourceHelper))
|
|
||||||
}
|
|
||||||
addSeries(PointsWithLabelGraphSeries(Array(bucketedListArray.size) { i -> bucketedListArray[i] }))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addBgReadings(fromTime: Long, toTime: Long, highLine: Double, predictions: MutableList<GlucoseValueDataPoint>?) {
|
fun addBgReadings(addPredictions: Boolean) {
|
||||||
var maxBgValue = Double.MIN_VALUE
|
maxY = if (overviewData.bgReadingsArray.isEmpty()) {
|
||||||
bgReadingsArray = repository.compatGetBgReadingsDataFromTime(fromTime, toTime, false).blockingGet()
|
if (units == GlucoseUnit.MGDL) 180.0 else 10.0
|
||||||
if (bgReadingsArray?.isEmpty() != false) {
|
} else overviewData.maxBgValue
|
||||||
aapsLogger.debug("No BG data.")
|
|
||||||
maxY = if (units == GlucoseUnit.MGDL) 180.0 else 10.0
|
|
||||||
minY = 0.0
|
minY = 0.0
|
||||||
return
|
addSeries(overviewData.bgReadingGraphSeries)
|
||||||
|
if (addPredictions) addSeries(overviewData.predictionsGraphSeries)
|
||||||
}
|
}
|
||||||
val bgListArray: MutableList<DataPointWithLabelInterface> = 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, resourceHelper))
|
|
||||||
}
|
|
||||||
if (predictions != null) {
|
|
||||||
predictions.sortWith(Comparator { o1: GlucoseValueDataPoint, o2: GlucoseValueDataPoint -> o1.x.compareTo(o2.x) })
|
|
||||||
for (prediction in predictions) if (prediction.data.value >= 40) bgListArray.add(prediction)
|
|
||||||
}
|
|
||||||
maxBgValue = Profile.fromMgdlToUnits(maxBgValue, units)
|
|
||||||
maxBgValue = addUpperChartMargin(maxBgValue)
|
|
||||||
if (highLine > maxBgValue) maxBgValue = highLine
|
|
||||||
maxY = maxBgValue
|
|
||||||
minY = 0.0
|
|
||||||
addSeries(PointsWithLabelGraphSeries(Array(bgListArray.size) { i -> bgListArray[i] }))
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun addUpperChartMargin(maxBgValue: Double) =
|
|
||||||
if (units == GlucoseUnit.MGDL) Round.roundTo(maxBgValue, 40.0) + 80 else Round.roundTo(maxBgValue, 2.0) + 4
|
|
||||||
|
|
||||||
fun addInRangeArea(fromTime: Long, toTime: Long, lowLine: Double, highLine: Double) {
|
fun addInRangeArea(fromTime: Long, toTime: Long, lowLine: Double, highLine: Double) {
|
||||||
val inRangeAreaSeries: AreaGraphSeries<DoubleDataPoint>
|
|
||||||
val inRangeAreaDataPoints = arrayOf(
|
val inRangeAreaDataPoints = arrayOf(
|
||||||
DoubleDataPoint(fromTime.toDouble(), lowLine, highLine),
|
DoubleDataPoint(fromTime.toDouble(), lowLine, highLine),
|
||||||
DoubleDataPoint(toTime.toDouble(), lowLine, highLine)
|
DoubleDataPoint(toTime.toDouble(), lowLine, highLine)
|
||||||
)
|
)
|
||||||
inRangeAreaSeries = AreaGraphSeries(inRangeAreaDataPoints)
|
addSeries(AreaGraphSeries(inRangeAreaDataPoints).also {
|
||||||
inRangeAreaSeries.color = 0
|
it.color = 0
|
||||||
inRangeAreaSeries.isDrawBackground = true
|
|
||||||
inRangeAreaSeries.backgroundColor = resourceHelper.gc(R.color.inrangebackground)
|
|
||||||
addSeries(inRangeAreaSeries)
|
|
||||||
}
|
|
||||||
|
|
||||||
// scale in % of vertical size (like 0.3)
|
|
||||||
fun addBasals(fromTime: Long, toTime: Long, scale: Double) {
|
|
||||||
var maxBasalValueFound = 0.0
|
|
||||||
val basalScale = Scale()
|
|
||||||
val baseBasalArray: MutableList<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 = 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
|
|
||||||
addSeries(LineGraphSeries(Array(baseBasalArray.size) { i -> baseBasalArray[i] }).also {
|
|
||||||
it.isDrawBackground = true
|
it.isDrawBackground = true
|
||||||
it.backgroundColor = resourceHelper.gc(R.color.basebasal)
|
it.backgroundColor = resourceHelper.gc(R.color.inrangebackground)
|
||||||
it.thickness = 0
|
|
||||||
})
|
|
||||||
addSeries(LineGraphSeries(Array(tempBasalArray.size) { i -> tempBasalArray[i] }).also {
|
|
||||||
it.isDrawBackground = true
|
|
||||||
it.backgroundColor = resourceHelper.gc(R.color.tempbasal)
|
|
||||||
it.thickness = 0
|
|
||||||
})
|
|
||||||
addSeries(LineGraphSeries(Array(basalLineArray.size) { i -> basalLineArray[i] }).also {
|
|
||||||
it.setCustomPaint(Paint().also { paint ->
|
|
||||||
paint.style = Paint.Style.STROKE
|
|
||||||
paint.strokeWidth = resourceHelper.getDisplayMetrics().scaledDensity * 2
|
|
||||||
paint.pathEffect = DashPathEffect(floatArrayOf(2f, 4f), 0f)
|
|
||||||
paint.color = resourceHelper.gc(R.color.basal)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
addSeries(LineGraphSeries(Array(absoluteBasalLineArray.size) { i -> absoluteBasalLineArray[i] }).also {
|
|
||||||
it.setCustomPaint(Paint().also { absolutePaint ->
|
|
||||||
absolutePaint.style = Paint.Style.STROKE
|
|
||||||
absolutePaint.strokeWidth = resourceHelper.getDisplayMetrics().scaledDensity * 2
|
|
||||||
absolutePaint.color = resourceHelper.gc(R.color.basal)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
basalScale.setMultiplier(maxY * scale / maxBasalValueFound)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun addTargetLine(fromTime: Long, toTimeParam: Long, profile: Profile, lastRun: LoopInterface.LastRun?) {
|
|
||||||
var toTime = toTimeParam
|
|
||||||
val targetsSeriesArray: MutableList<DataPoint> = ArrayList()
|
|
||||||
var lastTarget = -1.0
|
|
||||||
lastRun?.constraintsProcessed?.let { toTime = max(it.latestPredictionsTime, toTime) }
|
|
||||||
var time = fromTime
|
|
||||||
while (time < toTime) {
|
|
||||||
val tt = repository.getTemporaryTargetActiveAt(time).blockingGet()
|
|
||||||
val value: Double = if (tt is ValueWrapper.Existing) {
|
|
||||||
Profile.fromMgdlToUnits(tt.value.target(), units)
|
|
||||||
} else {
|
|
||||||
Profile.fromMgdlToUnits((profile.getTargetLowMgdl(time) + profile.getTargetHighMgdl(time)) / 2, units)
|
|
||||||
}
|
|
||||||
if (lastTarget != value) {
|
|
||||||
if (lastTarget != -1.0) targetsSeriesArray.add(DataPoint(time.toDouble(), lastTarget))
|
|
||||||
targetsSeriesArray.add(DataPoint(time.toDouble(), value))
|
|
||||||
}
|
|
||||||
lastTarget = value
|
|
||||||
time += 5 * 60 * 1000L
|
|
||||||
}
|
|
||||||
// final point
|
|
||||||
targetsSeriesArray.add(DataPoint(toTime.toDouble(), lastTarget))
|
|
||||||
// create series
|
|
||||||
addSeries(LineGraphSeries(Array(targetsSeriesArray.size) { i -> targetsSeriesArray[i] }).also {
|
|
||||||
it.isDrawBackground = false
|
|
||||||
it.color = resourceHelper.gc(R.color.tempTargetBackground)
|
|
||||||
it.thickness = 2
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addTreatments(fromTime: Long, endTime: Long) {
|
fun addBasals() {
|
||||||
val filteredTreatments: MutableList<DataPointWithLabelInterface> = ArrayList()
|
val scale = defaultValueHelper.determineLowLine() / maxY / 1.2
|
||||||
repository.getBolusesIncludingInvalidFromTimeToTime(fromTime, endTime, true).blockingGet()
|
addSeries(overviewData.baseBasalGraphSeries)
|
||||||
.map { BolusDataPoint(it, resourceHelper, activePlugin, defaultValueHelper) }
|
addSeries(overviewData.tempBasalGraphSeries)
|
||||||
.filter { it.data.type != Bolus.Type.SMB || it.data.isValid }
|
addSeries(overviewData.basalLineGraphSeries)
|
||||||
.forEach {
|
addSeries(overviewData.absoluteBasalGraphSeries)
|
||||||
it.y = getNearestBg(it.x.toLong())
|
overviewData.basalScale.setMultiplier(maxY * scale / overviewData.maxBasalValueFound)
|
||||||
filteredTreatments.add(it)
|
|
||||||
}
|
|
||||||
repository.getCarbsIncludingInvalidFromTimeToTimeExpanded(fromTime, endTime, true).blockingGet()
|
|
||||||
.map { CarbsDataPoint(it, resourceHelper) }
|
|
||||||
.forEach {
|
|
||||||
it.y = getNearestBg(it.x.toLong())
|
|
||||||
filteredTreatments.add(it)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProfileSwitch
|
fun addTargetLine() {
|
||||||
repository.getEffectiveProfileSwitchDataFromTimeToTime(fromTime, endTime, true).blockingGet()
|
addSeries(overviewData.temporaryTargetSeries)
|
||||||
.map { EffectiveProfileSwitchDataPoint(it) }
|
|
||||||
.forEach(filteredTreatments::add)
|
|
||||||
|
|
||||||
// Extended bolus
|
|
||||||
if (!activePlugin.activePump.isFakingTempsByExtendedBoluses) {
|
|
||||||
repository.getExtendedBolusDataFromTimeToTime(fromTime, endTime, true).blockingGet()
|
|
||||||
.map { ExtendedBolusDataPoint(it) }
|
|
||||||
.filter { it.duration != 0L }
|
|
||||||
.forEach {
|
|
||||||
it.y = getNearestBg(it.x.toLong())
|
|
||||||
filteredTreatments.add(it)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Careportal
|
fun addTreatments() {
|
||||||
repository.compatGetTherapyEventDataFromToTime(fromTime - T.hours(6).msecs(), endTime).blockingGet()
|
maxY = maxOf(maxY, overviewData.maxTreatmentsValue)
|
||||||
.map { TherapyEventDataPoint(it, resourceHelper, profileFunction, translator) }
|
addSeries(overviewData.treatmentsSeries)
|
||||||
.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
|
fun addActivity(scale: Double) {
|
||||||
filteredTreatments.map { it.y }
|
addSeries(overviewData.activitySeries)
|
||||||
.maxOrNull()
|
addSeries(overviewData.activityPredictionSeries)
|
||||||
?.let(::addUpperChartMargin)
|
overviewData.actScale.setMultiplier(maxY * scale / overviewData.maxIAValue)
|
||||||
?.let { maxY = maxOf(maxY, it) }
|
|
||||||
|
|
||||||
addSeries(PointsWithLabelGraphSeries(filteredTreatments.toTypedArray()))
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getNearestBg(date: Long): Double {
|
|
||||||
bgReadingsArray?.let { bgReadingsArray ->
|
|
||||||
for (reading in bgReadingsArray) {
|
|
||||||
if (reading.timestamp > date) continue
|
|
||||||
return Profile.fromMgdlToUnits(reading.value, units)
|
|
||||||
}
|
|
||||||
return if (bgReadingsArray.isNotEmpty()) Profile.fromMgdlToUnits(bgReadingsArray[0].value, units) else Profile.fromMgdlToUnits(100.0, units)
|
|
||||||
} ?: return Profile.fromMgdlToUnits(100.0, units)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun addActivity(fromTime: Long, toTime: Long, useForScale: Boolean, scale: Double) {
|
|
||||||
val actArrayHist: MutableList<ScaledDataPoint> = ArrayList()
|
|
||||||
val actArrayPrediction: MutableList<ScaledDataPoint> = ArrayList()
|
|
||||||
val now = System.currentTimeMillis().toDouble()
|
|
||||||
val actScale = Scale()
|
|
||||||
var total: IobTotal
|
|
||||||
var maxIAValue = 0.0
|
|
||||||
var time = fromTime
|
|
||||||
while (time <= toTime) {
|
|
||||||
val profile = profileFunction.getProfile(time)
|
|
||||||
if (profile == null) {
|
|
||||||
time += 5 * 60 * 1000L
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
total = iobCobCalculator.calculateFromTreatmentsAndTemps(time, profile)
|
|
||||||
val act: Double = total.activity
|
|
||||||
if (time <= now) actArrayHist.add(ScaledDataPoint(time, act, actScale)) else actArrayPrediction.add(ScaledDataPoint(time, act, actScale))
|
|
||||||
maxIAValue = max(maxIAValue, abs(act))
|
|
||||||
time += 5 * 60 * 1000L
|
|
||||||
}
|
|
||||||
addSeries(FixedLineGraphSeries(Array(actArrayHist.size) { i -> actArrayHist[i] }).also {
|
|
||||||
it.isDrawBackground = false
|
|
||||||
it.color = resourceHelper.gc(R.color.activity)
|
|
||||||
it.thickness = 3
|
|
||||||
})
|
|
||||||
addSeries(FixedLineGraphSeries(Array(actArrayPrediction.size) { i -> actArrayPrediction[i] }).also {
|
|
||||||
it.setCustomPaint(Paint().also { paint ->
|
|
||||||
paint.style = Paint.Style.STROKE
|
|
||||||
paint.strokeWidth = 3f
|
|
||||||
paint.pathEffect = DashPathEffect(floatArrayOf(4f, 4f), 0f)
|
|
||||||
paint.color = resourceHelper.gc(R.color.activity)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
if (useForScale) {
|
|
||||||
maxY = maxIAValue
|
|
||||||
minY = -maxIAValue
|
|
||||||
}
|
|
||||||
actScale.setMultiplier(maxY * scale / maxIAValue)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Function below show -BGI to be able to compare curves with deviations
|
//Function below show -BGI to be able to compare curves with deviations
|
||||||
fun addMinusBGI(fromTime: Long, toTime: Long, useForScale: Boolean, scale: Double, devBgiScale: Boolean) {
|
fun addMinusBGI(useForScale: Boolean, scale: Double) {
|
||||||
val bgiArrayHist: MutableList<ScaledDataPoint> = ArrayList()
|
|
||||||
val bgiArrayPrediction: MutableList<ScaledDataPoint> = ArrayList()
|
|
||||||
val now = System.currentTimeMillis().toDouble()
|
|
||||||
val bgiScale = Scale()
|
|
||||||
var total: IobTotal
|
|
||||||
var maxBGIValue = 0.0
|
|
||||||
var time = fromTime
|
|
||||||
while (time <= toTime) {
|
|
||||||
val profile = profileFunction.getProfile(time)
|
|
||||||
if (profile == null) {
|
|
||||||
time += 5 * 60 * 1000L
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
val deviation = if (devBgiScale) iobCobCalculator.ads.getAutosensDataAtTime(time)?.deviation
|
|
||||||
?: 0.0 else 0.0
|
|
||||||
|
|
||||||
total = iobCobCalculator.calculateFromTreatmentsAndTemps(time, profile)
|
|
||||||
val bgi: Double = total.activity * profile.getIsfMgdl(time) * 5.0
|
|
||||||
if (time <= now) bgiArrayHist.add(ScaledDataPoint(time, bgi, bgiScale)) else bgiArrayPrediction.add(ScaledDataPoint(time, bgi, bgiScale))
|
|
||||||
maxBGIValue = max(maxBGIValue, max(abs(bgi), deviation))
|
|
||||||
time += 5 * 60 * 1000L
|
|
||||||
}
|
|
||||||
addSeries(FixedLineGraphSeries(Array(bgiArrayHist.size) { i -> bgiArrayHist[i] }).also {
|
|
||||||
it.isDrawBackground = false
|
|
||||||
it.color = resourceHelper.gc(R.color.bgi)
|
|
||||||
it.thickness = 3
|
|
||||||
})
|
|
||||||
addSeries(FixedLineGraphSeries(Array(bgiArrayPrediction.size) { i -> bgiArrayPrediction[i] }).also {
|
|
||||||
it.setCustomPaint(Paint().also { paint ->
|
|
||||||
paint.style = Paint.Style.STROKE
|
|
||||||
paint.strokeWidth = 3f
|
|
||||||
paint.pathEffect = DashPathEffect(floatArrayOf(4f, 4f), 0f)
|
|
||||||
paint.color = resourceHelper.gc(R.color.bgi)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
if (useForScale) {
|
if (useForScale) {
|
||||||
maxY = maxBGIValue
|
maxY = overviewData.maxBGIValue
|
||||||
minY = -maxBGIValue
|
minY = -overviewData.maxBGIValue
|
||||||
}
|
}
|
||||||
bgiScale.setMultiplier(maxY * scale / maxBGIValue)
|
overviewData.bgiScale.setMultiplier(maxY * scale / overviewData.maxBGIValue)
|
||||||
|
addSeries(overviewData.minusBgiSeries)
|
||||||
|
addSeries(overviewData.minusBgiHistSeries)
|
||||||
}
|
}
|
||||||
|
|
||||||
// scale in % of vertical size (like 0.3)
|
// scale in % of vertical size (like 0.3)
|
||||||
fun addIob(fromTime: Long, toTime: Long, useForScale: Boolean, scale: Double, showPrediction: Boolean, absScale: Boolean) {
|
fun addIob(useForScale: Boolean, scale: Double) {
|
||||||
val iobSeries: FixedLineGraphSeries<ScaledDataPoint?>
|
|
||||||
val iobArray: MutableList<ScaledDataPoint> = ArrayList()
|
|
||||||
var maxIobValueFound = Double.MIN_VALUE
|
|
||||||
var lastIob = 0.0
|
|
||||||
val iobScale = Scale()
|
|
||||||
var time = fromTime
|
|
||||||
while (time <= toTime) {
|
|
||||||
val profile = profileFunction.getProfile(time)
|
|
||||||
if (profile == null) {
|
|
||||||
time += 5 * 60 * 1000L
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
var absIob = 0.0
|
|
||||||
val iob: Double = iobCobCalculator.calculateFromTreatmentsAndTemps(time, profile).iob
|
|
||||||
if (absScale) absIob = iobCobCalculator.calculateAbsInsulinFromTreatmentsAndTemps(time).iob
|
|
||||||
if (abs(lastIob - iob) > 0.02) {
|
|
||||||
if (abs(lastIob - iob) > 0.2) iobArray.add(ScaledDataPoint(time, lastIob, iobScale))
|
|
||||||
iobArray.add(ScaledDataPoint(time, iob, iobScale))
|
|
||||||
maxIobValueFound = if (absScale) max(maxIobValueFound, abs(absIob)) else max(maxIobValueFound, abs(iob))
|
|
||||||
lastIob = iob
|
|
||||||
}
|
|
||||||
time += 5 * 60 * 1000L
|
|
||||||
}
|
|
||||||
iobSeries = FixedLineGraphSeries(Array(iobArray.size) { i -> iobArray[i] }).also {
|
|
||||||
it.isDrawBackground = true
|
|
||||||
it.backgroundColor = -0x7f000001 and resourceHelper.gc(R.color.iob) //50%
|
|
||||||
it.color = resourceHelper.gc(R.color.iob)
|
|
||||||
it.thickness = 3
|
|
||||||
}
|
|
||||||
if (showPrediction) {
|
|
||||||
val autosensData = iobCobCalculator.getLastAutosensDataWithWaitForCalculationFinish("GraphData")
|
|
||||||
val lastAutosensResult = autosensData?.autosensResult ?: AutosensResult()
|
|
||||||
val isTempTarget = repository.getTemporaryTargetActiveAt(dateUtil.now()).blockingGet() is ValueWrapper.Existing
|
|
||||||
val iobPrediction: MutableList<DataPointWithLabelInterface> = ArrayList()
|
|
||||||
val iobPredictionArray = iobCobCalculator.calculateIobArrayForSMB(lastAutosensResult, SMBDefaults.exercise_mode, SMBDefaults.half_basal_exercise_target, isTempTarget)
|
|
||||||
for (i in iobPredictionArray) {
|
|
||||||
iobPrediction.add(i.setColor(resourceHelper.gc(R.color.iobPredAS)))
|
|
||||||
maxIobValueFound = max(maxIobValueFound, abs(i.iob))
|
|
||||||
}
|
|
||||||
addSeries(PointsWithLabelGraphSeries(Array(iobPrediction.size) { i -> iobPrediction[i] }))
|
|
||||||
val iobPrediction2: MutableList<DataPointWithLabelInterface> = ArrayList()
|
|
||||||
val iobPredictionArray2 = iobCobCalculator.calculateIobArrayForSMB(AutosensResult(), SMBDefaults.exercise_mode, SMBDefaults.half_basal_exercise_target, isTempTarget)
|
|
||||||
for (i in iobPredictionArray2) {
|
|
||||||
iobPrediction2.add(i.setColor(resourceHelper.gc(R.color.iobPred)))
|
|
||||||
maxIobValueFound = max(maxIobValueFound, abs(i.iob))
|
|
||||||
}
|
|
||||||
addSeries(PointsWithLabelGraphSeries(Array(iobPrediction2.size) { i -> iobPrediction2[i] }))
|
|
||||||
aapsLogger.debug(LTag.AUTOSENS, "IOB prediction for AS=" + DecimalFormatter.to2Decimal(lastAutosensResult.ratio) + ": " + iobCobCalculator.iobArrayToString(iobPredictionArray))
|
|
||||||
aapsLogger.debug(LTag.AUTOSENS, "IOB prediction for AS=" + DecimalFormatter.to2Decimal(1.0) + ": " + iobCobCalculator.iobArrayToString(iobPredictionArray2))
|
|
||||||
}
|
|
||||||
if (useForScale) {
|
if (useForScale) {
|
||||||
maxY = maxIobValueFound
|
maxY = overviewData.maxIobValueFound
|
||||||
minY = -maxIobValueFound
|
minY = -overviewData.maxIobValueFound
|
||||||
}
|
}
|
||||||
iobScale.setMultiplier(maxY * scale / maxIobValueFound)
|
overviewData.iobScale.setMultiplier(maxY * scale / overviewData.maxIobValueFound)
|
||||||
addSeries(iobSeries)
|
addSeries(overviewData.iobSeries)
|
||||||
|
addSeries(overviewData.iobPredictions1Series)
|
||||||
|
addSeries(overviewData.iobPredictions2Series)
|
||||||
}
|
}
|
||||||
|
|
||||||
// scale in % of vertical size (like 0.3)
|
// scale in % of vertical size (like 0.3)
|
||||||
fun addAbsIob(fromTime: Long, toTime: Long, useForScale: Boolean, scale: Double) {
|
fun addAbsIob(useForScale: Boolean, scale: Double) {
|
||||||
val iobSeries: FixedLineGraphSeries<ScaledDataPoint?>
|
|
||||||
val iobArray: MutableList<ScaledDataPoint> = ArrayList()
|
|
||||||
var maxIobValueFound = Double.MIN_VALUE
|
|
||||||
var lastIob = 0.0
|
|
||||||
val iobScale = Scale()
|
|
||||||
var time = fromTime
|
|
||||||
while (time <= toTime) {
|
|
||||||
val profile = profileFunction.getProfile(time)
|
|
||||||
if (profile == null) {
|
|
||||||
time += 5 * 60 * 1000L
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
val iob: Double = iobCobCalculator.calculateAbsInsulinFromTreatmentsAndTemps(time).iob
|
|
||||||
if (abs(lastIob - iob) > 0.02) {
|
|
||||||
if (abs(lastIob - iob) > 0.2) iobArray.add(ScaledDataPoint(time, lastIob, iobScale))
|
|
||||||
iobArray.add(ScaledDataPoint(time, iob, iobScale))
|
|
||||||
maxIobValueFound = max(maxIobValueFound, abs(iob))
|
|
||||||
lastIob = iob
|
|
||||||
}
|
|
||||||
time += 5 * 60 * 1000L
|
|
||||||
}
|
|
||||||
iobSeries = FixedLineGraphSeries(Array(iobArray.size) { i -> iobArray[i] }).also {
|
|
||||||
it.isDrawBackground = true
|
|
||||||
it.backgroundColor = -0x7f000001 and resourceHelper.gc(R.color.iob) //50%
|
|
||||||
it.color = resourceHelper.gc(R.color.iob)
|
|
||||||
it.thickness = 3
|
|
||||||
}
|
|
||||||
if (useForScale) {
|
if (useForScale) {
|
||||||
maxY = maxIobValueFound
|
maxY = overviewData.maxIobValueFound
|
||||||
minY = -maxIobValueFound
|
minY = -overviewData.maxIobValueFound
|
||||||
}
|
}
|
||||||
iobScale.setMultiplier(maxY * scale / maxIobValueFound)
|
overviewData.iobScale.setMultiplier(maxY * scale / overviewData.maxIobValueFound)
|
||||||
addSeries(iobSeries)
|
addSeries(overviewData.absIobSeries)
|
||||||
}
|
}
|
||||||
|
|
||||||
// scale in % of vertical size (like 0.3)
|
// scale in % of vertical size (like 0.3)
|
||||||
fun addCob(fromTime: Long, toTime: Long, useForScale: Boolean, scale: Double) {
|
fun addCob(useForScale: Boolean, scale: Double) {
|
||||||
val minFailOverActiveList: MutableList<DataPointWithLabelInterface> = ArrayList()
|
|
||||||
val cobArray: MutableList<ScaledDataPoint> = ArrayList()
|
|
||||||
var maxCobValueFound = 0.0
|
|
||||||
var lastCob = 0
|
|
||||||
val cobScale = Scale()
|
|
||||||
var time = fromTime
|
|
||||||
while (time <= toTime) {
|
|
||||||
iobCobCalculator.ads.getAutosensDataAtTime(time)?.let { autosensData ->
|
|
||||||
val cob = autosensData.cob.toInt()
|
|
||||||
if (cob != lastCob) {
|
|
||||||
if (autosensData.carbsFromBolus > 0) cobArray.add(ScaledDataPoint(time, lastCob.toDouble(), cobScale))
|
|
||||||
cobArray.add(ScaledDataPoint(time, cob.toDouble(), cobScale))
|
|
||||||
maxCobValueFound = max(maxCobValueFound, cob.toDouble())
|
|
||||||
lastCob = cob
|
|
||||||
}
|
|
||||||
if (autosensData.failoverToMinAbsorbtionRate) {
|
|
||||||
autosensData.setScale(cobScale)
|
|
||||||
autosensData.setChartTime(time)
|
|
||||||
minFailOverActiveList.add(autosensData)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
time += 5 * 60 * 1000L
|
|
||||||
}
|
|
||||||
|
|
||||||
// COB
|
|
||||||
addSeries(FixedLineGraphSeries(Array(cobArray.size) { i -> cobArray[i] }).also {
|
|
||||||
it.isDrawBackground = true
|
|
||||||
it.backgroundColor = -0x7f000001 and resourceHelper.gc(R.color.cob) //50%
|
|
||||||
it.color = resourceHelper.gc(R.color.cob)
|
|
||||||
it.thickness = 3
|
|
||||||
})
|
|
||||||
if (useForScale) {
|
if (useForScale) {
|
||||||
maxY = maxCobValueFound
|
maxY = overviewData.maxCobValueFound
|
||||||
minY = 0.0
|
minY = 0.0
|
||||||
}
|
}
|
||||||
cobScale.setMultiplier(maxY * scale / maxCobValueFound)
|
overviewData.cobScale.setMultiplier(maxY * scale / overviewData.maxCobValueFound)
|
||||||
addSeries(PointsWithLabelGraphSeries(Array(minFailOverActiveList.size) { i -> minFailOverActiveList[i] }))
|
addSeries(overviewData.cobSeries)
|
||||||
|
addSeries(overviewData.cobMinFailOverSeries)
|
||||||
}
|
}
|
||||||
|
|
||||||
// scale in % of vertical size (like 0.3)
|
// scale in % of vertical size (like 0.3)
|
||||||
fun addDeviations(fromTime: Long, toTime: Long, useForScale: Boolean, scale: Double, devBgiScale: Boolean) {
|
fun addDeviations(useForScale: Boolean, scale: Double) {
|
||||||
class DeviationDataPoint(x: Double, y: Double, var color: Int, scale: Scale) : ScaledDataPoint(x, y, scale)
|
|
||||||
|
|
||||||
val devArray: MutableList<DeviationDataPoint> = ArrayList()
|
|
||||||
var maxDevValueFound = 0.0
|
|
||||||
val devScale = Scale()
|
|
||||||
var time = fromTime
|
|
||||||
var total: IobTotal
|
|
||||||
|
|
||||||
while (time <= toTime) {
|
|
||||||
// if align Dev Scale with BGI scale, then calculate BGI value, else bgi = 0.0
|
|
||||||
val bgi: Double = if (devBgiScale) {
|
|
||||||
val profile = profileFunction.getProfile(time)
|
|
||||||
if (profile == null) {
|
|
||||||
time += 5 * 60 * 1000L
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
total = iobCobCalculator.calculateFromTreatmentsAndTemps(time, profile)
|
|
||||||
total.activity * profile.getIsfMgdl(time) * 5.0
|
|
||||||
} else 0.0
|
|
||||||
|
|
||||||
iobCobCalculator.ads.getAutosensDataAtTime(time)?.let { autosensData ->
|
|
||||||
var color = resourceHelper.gc(R.color.deviationblack) // "="
|
|
||||||
if (autosensData.type == "" || autosensData.type == "non-meal") {
|
|
||||||
if (autosensData.pastSensitivity == "C") color = resourceHelper.gc(R.color.deviationgrey)
|
|
||||||
if (autosensData.pastSensitivity == "+") color = resourceHelper.gc(R.color.deviationgreen)
|
|
||||||
if (autosensData.pastSensitivity == "-") color = resourceHelper.gc(R.color.deviationred)
|
|
||||||
} else if (autosensData.type == "uam") {
|
|
||||||
color = resourceHelper.gc(R.color.uam)
|
|
||||||
} else if (autosensData.type == "csf") {
|
|
||||||
color = resourceHelper.gc(R.color.deviationgrey)
|
|
||||||
}
|
|
||||||
devArray.add(DeviationDataPoint(time.toDouble(), autosensData.deviation, color, devScale))
|
|
||||||
maxDevValueFound = max(maxDevValueFound, max(abs(autosensData.deviation), abs(bgi)))
|
|
||||||
}
|
|
||||||
time += 5 * 60 * 1000L
|
|
||||||
}
|
|
||||||
|
|
||||||
// DEVIATIONS
|
|
||||||
addSeries(BarGraphSeries(Array(devArray.size) { i -> devArray[i] }).also {
|
|
||||||
it.setValueDependentColor { data: DeviationDataPoint -> data.color }
|
|
||||||
})
|
|
||||||
if (useForScale) {
|
if (useForScale) {
|
||||||
maxY = maxDevValueFound
|
maxY = overviewData.maxDevValueFound
|
||||||
minY = -maxY
|
minY = -maxY
|
||||||
}
|
}
|
||||||
devScale.setMultiplier(maxY * scale / maxDevValueFound)
|
overviewData.devScale.setMultiplier(maxY * scale / overviewData.maxDevValueFound)
|
||||||
|
addSeries(overviewData.deviationsSeries)
|
||||||
}
|
}
|
||||||
|
|
||||||
// scale in % of vertical size (like 0.3)
|
// scale in % of vertical size (like 0.3)
|
||||||
fun addRatio(fromTime: Long, toTime: Long, useForScale: Boolean, scale: Double) {
|
fun addRatio(useForScale: Boolean, scale: Double) {
|
||||||
val ratioArray: MutableList<ScaledDataPoint> = ArrayList()
|
|
||||||
var maxRatioValueFound = 5.0 //even if sens data equals 0 for all the period, minimum scale is between 95% and 105%
|
|
||||||
var minRatioValueFound = -maxRatioValueFound
|
|
||||||
val ratioScale = if (useForScale) Scale(100.0) else Scale()
|
|
||||||
var time = fromTime
|
|
||||||
while (time <= toTime) {
|
|
||||||
iobCobCalculator.ads.getAutosensDataAtTime(time)?.let { autosensData ->
|
|
||||||
ratioArray.add(ScaledDataPoint(time, 100.0 * (autosensData.autosensResult.ratio - 1), ratioScale))
|
|
||||||
maxRatioValueFound = max(maxRatioValueFound, 100.0 * (autosensData.autosensResult.ratio - 1))
|
|
||||||
minRatioValueFound = min(minRatioValueFound, 100.0 * (autosensData.autosensResult.ratio - 1))
|
|
||||||
}
|
|
||||||
time += 5 * 60 * 1000L
|
|
||||||
}
|
|
||||||
|
|
||||||
// RATIOS
|
|
||||||
addSeries(LineGraphSeries(Array(ratioArray.size) { i -> ratioArray[i] }).also {
|
|
||||||
it.color = resourceHelper.gc(R.color.ratio)
|
|
||||||
it.thickness = 3
|
|
||||||
})
|
|
||||||
if (useForScale) {
|
if (useForScale) {
|
||||||
maxY = 100.0 + max(maxRatioValueFound, abs(minRatioValueFound))
|
maxY = 100.0 + max(overviewData.maxRatioValueFound, abs(overviewData.minRatioValueFound))
|
||||||
minY = 100.0 - max(maxRatioValueFound, abs(minRatioValueFound))
|
minY = 100.0 - max(overviewData.maxRatioValueFound, abs(overviewData.minRatioValueFound))
|
||||||
ratioScale.setMultiplier(1.0)
|
overviewData.ratioScale.setMultiplier(1.0)
|
||||||
} else
|
} else
|
||||||
ratioScale.setMultiplier(maxY * scale / max(maxRatioValueFound, abs(minRatioValueFound)))
|
overviewData.ratioScale.setMultiplier(maxY * scale / max(overviewData.maxRatioValueFound, abs(overviewData.minRatioValueFound)))
|
||||||
|
addSeries(overviewData.ratioSeries)
|
||||||
}
|
}
|
||||||
|
|
||||||
// scale in % of vertical size (like 0.3)
|
// scale in % of vertical size (like 0.3)
|
||||||
fun addDeviationSlope(fromTime: Long, toTime: Long, useForScale: Boolean, scale: Double) {
|
fun addDeviationSlope(useForScale: Boolean, scale: Double) {
|
||||||
val dsMaxArray: MutableList<ScaledDataPoint> = ArrayList()
|
|
||||||
val dsMinArray: MutableList<ScaledDataPoint> = ArrayList()
|
|
||||||
var maxFromMaxValueFound = 0.0
|
|
||||||
var maxFromMinValueFound = 0.0
|
|
||||||
val dsMaxScale = Scale()
|
|
||||||
val dsMinScale = Scale()
|
|
||||||
var time = fromTime
|
|
||||||
while (time <= toTime) {
|
|
||||||
iobCobCalculator.ads.getAutosensDataAtTime(time)?.let { autosensData ->
|
|
||||||
dsMaxArray.add(ScaledDataPoint(time, autosensData.slopeFromMaxDeviation, dsMaxScale))
|
|
||||||
dsMinArray.add(ScaledDataPoint(time, autosensData.slopeFromMinDeviation, dsMinScale))
|
|
||||||
maxFromMaxValueFound = max(maxFromMaxValueFound, abs(autosensData.slopeFromMaxDeviation))
|
|
||||||
maxFromMinValueFound = max(maxFromMinValueFound, abs(autosensData.slopeFromMinDeviation))
|
|
||||||
}
|
|
||||||
time += 5 * 60 * 1000L
|
|
||||||
}
|
|
||||||
|
|
||||||
// Slopes
|
|
||||||
addSeries(LineGraphSeries(Array(dsMaxArray.size) { i -> dsMaxArray[i] }).also {
|
|
||||||
it.color = resourceHelper.gc(R.color.devslopepos)
|
|
||||||
it.thickness = 3
|
|
||||||
})
|
|
||||||
addSeries(LineGraphSeries(Array(dsMinArray.size) { i -> dsMinArray[i] }).also {
|
|
||||||
it.color = resourceHelper.gc(R.color.devslopeneg)
|
|
||||||
it.thickness = 3
|
|
||||||
})
|
|
||||||
if (useForScale) {
|
if (useForScale) {
|
||||||
maxY = max(maxFromMaxValueFound, maxFromMinValueFound)
|
maxY = max(overviewData.maxFromMaxValueFound, overviewData.maxFromMinValueFound)
|
||||||
minY = -maxY
|
minY = -maxY
|
||||||
}
|
}
|
||||||
dsMaxScale.setMultiplier(maxY * scale / maxFromMaxValueFound)
|
overviewData.dsMaxScale.setMultiplier(maxY * scale / overviewData.maxFromMaxValueFound)
|
||||||
dsMinScale.setMultiplier(maxY * scale / maxFromMinValueFound)
|
overviewData.dsMinScale.setMultiplier(maxY * scale / overviewData.maxFromMinValueFound)
|
||||||
|
addSeries(overviewData.dsMaxSeries)
|
||||||
|
addSeries(overviewData.dsMinSeries)
|
||||||
}
|
}
|
||||||
|
|
||||||
// scale in % of vertical size (like 0.3)
|
// scale in % of vertical size (like 0.3)
|
||||||
|
@ -669,7 +201,7 @@ class GraphData(
|
||||||
graph.gridLabelRenderer.numHorizontalLabels = 7 // only 7 because of the space
|
graph.gridLabelRenderer.numHorizontalLabels = 7 // only 7 because of the space
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addSeries(s: Series<*>) = series.add(s)
|
internal fun addSeries(s: Series<*>) = series.add(s)
|
||||||
|
|
||||||
fun performUpdate() {
|
fun performUpdate() {
|
||||||
// clear old data
|
// clear old data
|
||||||
|
@ -693,5 +225,3 @@ class GraphData(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun <E : DataPointWithLabelInterface> List<E>.filterTimeframe(fromTime: Long, endTime: Long): List<E> =
|
|
||||||
filter { it.x + it.duration >= fromTime && it.x <= endTime }
|
|
||||||
|
|
|
@ -7,7 +7,6 @@ import info.nightscout.androidaps.Constants
|
||||||
import info.nightscout.androidaps.R
|
import info.nightscout.androidaps.R
|
||||||
import info.nightscout.androidaps.data.IobTotal
|
import info.nightscout.androidaps.data.IobTotal
|
||||||
import info.nightscout.androidaps.data.MealData
|
import info.nightscout.androidaps.data.MealData
|
||||||
import info.nightscout.androidaps.interfaces.Profile
|
|
||||||
import info.nightscout.androidaps.database.AppRepository
|
import info.nightscout.androidaps.database.AppRepository
|
||||||
import info.nightscout.androidaps.database.ValueWrapper
|
import info.nightscout.androidaps.database.ValueWrapper
|
||||||
import info.nightscout.androidaps.database.entities.Bolus
|
import info.nightscout.androidaps.database.entities.Bolus
|
||||||
|
@ -71,7 +70,6 @@ open class IobCobCalculatorPlugin @Inject constructor(
|
||||||
private val disposable = CompositeDisposable()
|
private val disposable = CompositeDisposable()
|
||||||
|
|
||||||
private var iobTable = LongSparseArray<IobTotal>() // oldest at index 0
|
private var iobTable = LongSparseArray<IobTotal>() // oldest at index 0
|
||||||
private var absIobTable = LongSparseArray<IobTotal>() // oldest at index 0, absolute insulin in the body
|
|
||||||
private var basalDataTable = LongSparseArray<BasalData>() // oldest at index 0
|
private var basalDataTable = LongSparseArray<BasalData>() // oldest at index 0
|
||||||
|
|
||||||
override var ads: AutosensDataStore = AutosensDataStore()
|
override var ads: AutosensDataStore = AutosensDataStore()
|
||||||
|
@ -169,10 +167,9 @@ open class IobCobCalculatorPlugin @Inject constructor(
|
||||||
return getBGDataFrom
|
return getBGDataFrom
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun calculateFromTreatmentsAndTemps(fromTime: Long, profile: Profile): IobTotal {
|
override fun calculateFromTreatmentsAndTemps(toTime: Long, profile: Profile): IobTotal {
|
||||||
synchronized(dataLock) {
|
|
||||||
val now = System.currentTimeMillis()
|
val now = System.currentTimeMillis()
|
||||||
val time = ads.roundUpTime(fromTime)
|
val time = ads.roundUpTime(toTime)
|
||||||
val cacheHit = iobTable[time]
|
val cacheHit = iobTable[time]
|
||||||
if (time < now && cacheHit != null) {
|
if (time < now && cacheHit != null) {
|
||||||
//og.debug(">>> calculateFromTreatmentsAndTemps Cache hit " + new Date(time).toLocaleString());
|
//og.debug(">>> calculateFromTreatmentsAndTemps Cache hit " + new Date(time).toLocaleString());
|
||||||
|
@ -196,30 +193,12 @@ open class IobCobCalculatorPlugin @Inject constructor(
|
||||||
basalIob.iobWithZeroTemp = IobTotal.combine(bolusIob, basalIobWithZeroTemp).round()
|
basalIob.iobWithZeroTemp = IobTotal.combine(bolusIob, basalIobWithZeroTemp).round()
|
||||||
val iobTotal = IobTotal.combine(bolusIob, basalIob).round()
|
val iobTotal = IobTotal.combine(bolusIob, basalIob).round()
|
||||||
if (time < System.currentTimeMillis()) {
|
if (time < System.currentTimeMillis()) {
|
||||||
|
synchronized(dataLock) {
|
||||||
iobTable.put(time, iobTotal)
|
iobTable.put(time, iobTotal)
|
||||||
}
|
}
|
||||||
return iobTotal
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun calculateAbsInsulinFromTreatmentsAndTemps(fromTime: Long): IobTotal {
|
|
||||||
synchronized(dataLock) {
|
|
||||||
val now = System.currentTimeMillis()
|
|
||||||
val time = ads.roundUpTime(fromTime)
|
|
||||||
val cacheHit = absIobTable[time]
|
|
||||||
if (time < now && cacheHit != null) {
|
|
||||||
//log.debug(">>> calculateFromTreatmentsAndTemps Cache hit " + new Date(time).toLocaleString());
|
|
||||||
return cacheHit
|
|
||||||
} // else log.debug(">>> calculateFromTreatmentsAndTemps Cache miss " + new Date(time).toLocaleString());
|
|
||||||
val bolusIob = calculateIobFromBolusToTime(time).round()
|
|
||||||
val basalIob = calculateAbsoluteIobTempBasals(time).round()
|
|
||||||
val iobTotal = IobTotal.combine(bolusIob, basalIob).round()
|
|
||||||
if (time < System.currentTimeMillis()) {
|
|
||||||
absIobTable.put(time, iobTotal)
|
|
||||||
}
|
}
|
||||||
return iobTotal
|
return iobTotal
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private fun calculateFromTreatmentsAndTemps(time: Long, lastAutosensResult: AutosensResult, exercise_mode: Boolean, half_basal_exercise_target: Int, isTempTarget: Boolean): IobTotal {
|
private fun calculateFromTreatmentsAndTemps(time: Long, lastAutosensResult: AutosensResult, exercise_mode: Boolean, half_basal_exercise_target: Int, isTempTarget: Boolean): IobTotal {
|
||||||
val now = dateUtil.now()
|
val now = dateUtil.now()
|
||||||
|
@ -246,7 +225,6 @@ open class IobCobCalculatorPlugin @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getBasalData(profile: Profile, fromTime: Long): BasalData {
|
override fun getBasalData(profile: Profile, fromTime: Long): BasalData {
|
||||||
synchronized(dataLock) {
|
|
||||||
val now = System.currentTimeMillis()
|
val now = System.currentTimeMillis()
|
||||||
val time = ads.roundUpTime(fromTime)
|
val time = ads.roundUpTime(fromTime)
|
||||||
var retVal = basalDataTable[time]
|
var retVal = basalDataTable[time]
|
||||||
|
@ -263,12 +241,13 @@ open class IobCobCalculatorPlugin @Inject constructor(
|
||||||
retVal.tempBasalAbsolute = retVal.basal
|
retVal.tempBasalAbsolute = retVal.basal
|
||||||
}
|
}
|
||||||
if (time < now) {
|
if (time < now) {
|
||||||
|
synchronized(dataLock) {
|
||||||
basalDataTable.append(time, retVal)
|
basalDataTable.append(time, retVal)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} //else log.debug(">>> getBasalData Cache hit " + new Date(time).toLocaleString());
|
} //else log.debug(">>> getBasalData Cache hit " + new Date(time).toLocaleString());
|
||||||
return retVal
|
return retVal
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
override fun getLastAutosensDataWithWaitForCalculationFinish(reason: String): AutosensData? {
|
override fun getLastAutosensDataWithWaitForCalculationFinish(reason: String): AutosensData? {
|
||||||
if (thread?.isAlive == true) {
|
if (thread?.isAlive == true) {
|
||||||
|
@ -408,14 +387,6 @@ open class IobCobCalculatorPlugin @Inject constructor(
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (index in absIobTable.size() - 1 downTo 0) {
|
|
||||||
if (absIobTable.keyAt(index) > time) {
|
|
||||||
aapsLogger.debug(LTag.AUTOSENS, "Removing from absIobTable: " + dateUtil.dateAndTimeAndSecondsString(absIobTable.keyAt(index)))
|
|
||||||
absIobTable.removeAt(index)
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (index in basalDataTable.size() - 1 downTo 0) {
|
for (index in basalDataTable.size() - 1 downTo 0) {
|
||||||
if (basalDataTable.keyAt(index) > time) {
|
if (basalDataTable.keyAt(index) > time) {
|
||||||
aapsLogger.debug(LTag.AUTOSENS, "Removing from basalDataTable: " + dateUtil.dateAndTimeAndSecondsString(basalDataTable.keyAt(index)))
|
aapsLogger.debug(LTag.AUTOSENS, "Removing from basalDataTable: " + dateUtil.dateAndTimeAndSecondsString(basalDataTable.keyAt(index)))
|
||||||
|
@ -459,8 +430,8 @@ open class IobCobCalculatorPlugin @Inject constructor(
|
||||||
* Time range to the past for IOB calculation
|
* Time range to the past for IOB calculation
|
||||||
* @return milliseconds
|
* @return milliseconds
|
||||||
*/
|
*/
|
||||||
fun range(): Long = ((profileFunction.getProfile()?.dia
|
fun range(): Long = ((/*overviewData.rangeToDisplay + */(profileFunction.getProfile()?.dia
|
||||||
?: Constants.defaultDIA) * 60 * 60 * 1000).toLong()
|
?: Constants.defaultDIA)) * 60 * 60 * 1000).toLong()
|
||||||
|
|
||||||
override fun calculateIobFromBolus(): IobTotal = calculateIobFromBolusToTime(dateUtil.now())
|
override fun calculateIobFromBolus(): IobTotal = calculateIobFromBolusToTime(dateUtil.now())
|
||||||
|
|
||||||
|
@ -533,6 +504,7 @@ open class IobCobCalculatorPlugin @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getTempBasalIncludingConvertedExtended(timestamp: Long): TemporaryBasal? {
|
override fun getTempBasalIncludingConvertedExtended(timestamp: Long): TemporaryBasal? {
|
||||||
|
|
||||||
val tb = repository.getTemporaryBasalActiveAt(timestamp).blockingGet()
|
val tb = repository.getTemporaryBasalActiveAt(timestamp).blockingGet()
|
||||||
if (tb is ValueWrapper.Existing) return tb.value
|
if (tb is ValueWrapper.Existing) return tb.value
|
||||||
val eb = repository.getExtendedBolusActiveAt(timestamp).blockingGet()
|
val eb = repository.getExtendedBolusActiveAt(timestamp).blockingGet()
|
||||||
|
@ -542,7 +514,7 @@ open class IobCobCalculatorPlugin @Inject constructor(
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun calculateAbsoluteIobTempBasals(toTime: Long): IobTotal {
|
override fun calculateAbsoluteIobFromBaseBasals(toTime: Long): IobTotal {
|
||||||
val total = IobTotal(toTime)
|
val total = IobTotal(toTime)
|
||||||
var i = toTime - range()
|
var i = toTime - range()
|
||||||
while (i < toTime) {
|
while (i < toTime) {
|
||||||
|
@ -551,8 +523,7 @@ open class IobCobCalculatorPlugin @Inject constructor(
|
||||||
i += T.mins(5).msecs()
|
i += T.mins(5).msecs()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
val runningTBR = getTempBasalIncludingConvertedExtended(i)
|
val running = profile.getBasal(i)
|
||||||
val running = runningTBR?.convertedToAbsolute(i, profile) ?: profile.getBasal(i)
|
|
||||||
val bolus = Bolus(
|
val bolus = Bolus(
|
||||||
timestamp = i,
|
timestamp = i,
|
||||||
amount = running * 5.0 / 60.0,
|
amount = running * 5.0 / 60.0,
|
||||||
|
|
|
@ -82,7 +82,7 @@ class IobCobOref1Thread internal constructor(
|
||||||
//log.debug("Locking calculateSensitivityData");
|
//log.debug("Locking calculateSensitivityData");
|
||||||
val oldestTimeWithData = iobCobCalculatorPlugin.calculateDetectionStart(end, limitDataToOldestAvailable)
|
val oldestTimeWithData = iobCobCalculatorPlugin.calculateDetectionStart(end, limitDataToOldestAvailable)
|
||||||
if (bgDataReload) {
|
if (bgDataReload) {
|
||||||
iobCobCalculatorPlugin.ads.loadBgData(end, repository, aapsLogger, dateUtil)
|
iobCobCalculatorPlugin.ads.loadBgData(end, repository, aapsLogger, dateUtil, rxBus)
|
||||||
iobCobCalculatorPlugin.clearCache()
|
iobCobCalculatorPlugin.clearCache()
|
||||||
}
|
}
|
||||||
// work on local copy and set back when finished
|
// work on local copy and set back when finished
|
||||||
|
|
|
@ -81,7 +81,7 @@ class IobCobThread @Inject internal constructor(
|
||||||
//log.debug("Locking calculateSensitivityData");
|
//log.debug("Locking calculateSensitivityData");
|
||||||
val oldestTimeWithData = iobCobCalculatorPlugin.calculateDetectionStart(end, limitDataToOldestAvailable)
|
val oldestTimeWithData = iobCobCalculatorPlugin.calculateDetectionStart(end, limitDataToOldestAvailable)
|
||||||
if (bgDataReload) {
|
if (bgDataReload) {
|
||||||
iobCobCalculatorPlugin.ads.loadBgData(end, repository, aapsLogger, dateUtil)
|
iobCobCalculatorPlugin.ads.loadBgData(end, repository, aapsLogger, dateUtil, rxBus)
|
||||||
iobCobCalculatorPlugin.clearCache()
|
iobCobCalculatorPlugin.clearCache()
|
||||||
}
|
}
|
||||||
// work on local copy and set back when finished
|
// work on local copy and set back when finished
|
||||||
|
|
|
@ -27,6 +27,7 @@ import info.nightscout.androidaps.utils.sharedPreferences.SP
|
||||||
import org.json.JSONArray
|
import org.json.JSONArray
|
||||||
import org.json.JSONException
|
import org.json.JSONException
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
|
import java.lang.Integer.min
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
@ -413,7 +414,7 @@ class LocalProfilePlugin @Inject constructor(
|
||||||
if (sp.getBoolean(R.string.key_ns_receive_profile_store, false) || config.NSCLIENT) {
|
if (sp.getBoolean(R.string.key_ns_receive_profile_store, false) || config.NSCLIENT) {
|
||||||
localProfilePlugin.loadFromStore(ProfileStore(injector, profileJson, dateUtil))
|
localProfilePlugin.loadFromStore(ProfileStore(injector, profileJson, dateUtil))
|
||||||
aapsLogger.debug(LTag.PROFILE, "Received profileStore: $profileJson")
|
aapsLogger.debug(LTag.PROFILE, "Received profileStore: $profileJson")
|
||||||
return Result.success(workDataOf("Data" to profileJson.toString().substring(1..5000)))
|
return Result.success(workDataOf("Data" to profileJson.toString().substring(0..min(5000, profileJson.length()))))
|
||||||
}
|
}
|
||||||
return Result.success()
|
return Result.success()
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,9 +10,12 @@ class TrendCalculator @Inject constructor(
|
||||||
private val repository: AppRepository
|
private val repository: AppRepository
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun getTrendArrow(glucoseValue: GlucoseValue): GlucoseValue.TrendArrow =
|
fun getTrendArrow(glucoseValue: GlucoseValue?): GlucoseValue.TrendArrow =
|
||||||
if (glucoseValue.trendArrow != GlucoseValue.TrendArrow.NONE) glucoseValue.trendArrow
|
when {
|
||||||
else calculateDirection(glucoseValue)
|
glucoseValue?.trendArrow == null -> GlucoseValue.TrendArrow.NONE
|
||||||
|
glucoseValue.trendArrow != GlucoseValue.TrendArrow.NONE -> glucoseValue.trendArrow
|
||||||
|
else -> calculateDirection(glucoseValue)
|
||||||
|
}
|
||||||
|
|
||||||
private fun calculateDirection(glucoseValue: GlucoseValue): GlucoseValue.TrendArrow {
|
private fun calculateDirection(glucoseValue: GlucoseValue): GlucoseValue.TrendArrow {
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,6 @@ import info.nightscout.androidaps.utils.DateUtil
|
||||||
import info.nightscout.androidaps.utils.Round
|
import info.nightscout.androidaps.utils.Round
|
||||||
import org.json.JSONException
|
import org.json.JSONException
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
@Suppress("SpellCheckingInspection")
|
@Suppress("SpellCheckingInspection")
|
||||||
class IobTotal(var time: Long) : DataPointWithLabelInterface {
|
class IobTotal(var time: Long) : DataPointWithLabelInterface {
|
||||||
|
@ -22,7 +21,6 @@ class IobTotal(var time: Long) : DataPointWithLabelInterface {
|
||||||
@JvmField var lastBolusTime: Long = 0
|
@JvmField var lastBolusTime: Long = 0
|
||||||
var iobWithZeroTemp: IobTotal? = null
|
var iobWithZeroTemp: IobTotal? = null
|
||||||
@JvmField var netInsulin = 0.0 // for calculations from temp basals only
|
@JvmField var netInsulin = 0.0 // for calculations from temp basals only
|
||||||
@JvmField var netRatio = 0.0 // net ratio at start of temp basal
|
|
||||||
@JvmField var extendedBolusInsulin = 0.0 // total insulin for extended bolus
|
@JvmField var extendedBolusInsulin = 0.0 // total insulin for extended bolus
|
||||||
fun copy(): IobTotal {
|
fun copy(): IobTotal {
|
||||||
val i = IobTotal(time)
|
val i = IobTotal(time)
|
||||||
|
@ -33,9 +31,8 @@ class IobTotal(var time: Long) : DataPointWithLabelInterface {
|
||||||
i.netbasalinsulin = netbasalinsulin
|
i.netbasalinsulin = netbasalinsulin
|
||||||
i.hightempinsulin = hightempinsulin
|
i.hightempinsulin = hightempinsulin
|
||||||
i.lastBolusTime = lastBolusTime
|
i.lastBolusTime = lastBolusTime
|
||||||
if (iobWithZeroTemp != null) i.iobWithZeroTemp = iobWithZeroTemp!!.copy()
|
i.iobWithZeroTemp = iobWithZeroTemp?.copy()
|
||||||
i.netInsulin = netInsulin
|
i.netInsulin = netInsulin
|
||||||
i.netRatio = netRatio
|
|
||||||
i.extendedBolusInsulin = extendedBolusInsulin
|
i.extendedBolusInsulin = extendedBolusInsulin
|
||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
|
|
|
@ -165,7 +165,6 @@ fun TemporaryBasal.iobCalc(time: Long, profile: Profile, insulinInterface: Insul
|
||||||
result.hightempinsulin += tempBolusPart.amount
|
result.hightempinsulin += tempBolusPart.amount
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result.netRatio = netBasalRate // ratio at the end of interval
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result.netInsulin = netBasalAmount
|
result.netInsulin = netBasalAmount
|
||||||
|
@ -217,7 +216,6 @@ fun TemporaryBasal.iobCalc(time: Long, profile: Profile, lastAutosensResult: Aut
|
||||||
result.hightempinsulin += tempBolusPart.amount
|
result.hightempinsulin += tempBolusPart.amount
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result.netRatio = netBasalRate // ratio at the end of interval
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result.netInsulin = netBasalAmount
|
result.netInsulin = netBasalAmount
|
||||||
|
|
|
@ -2,7 +2,6 @@ package info.nightscout.androidaps.interfaces
|
||||||
|
|
||||||
import info.nightscout.androidaps.data.IobTotal
|
import info.nightscout.androidaps.data.IobTotal
|
||||||
import info.nightscout.androidaps.data.MealData
|
import info.nightscout.androidaps.data.MealData
|
||||||
import info.nightscout.androidaps.interfaces.Profile
|
|
||||||
import info.nightscout.androidaps.database.entities.ExtendedBolus
|
import info.nightscout.androidaps.database.entities.ExtendedBolus
|
||||||
import info.nightscout.androidaps.database.entities.TemporaryBasal
|
import info.nightscout.androidaps.database.entities.TemporaryBasal
|
||||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensDataStore
|
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensDataStore
|
||||||
|
@ -19,8 +18,7 @@ interface IobCobCalculator {
|
||||||
fun getMealDataWithWaitingForCalculationFinish(): MealData
|
fun getMealDataWithWaitingForCalculationFinish(): MealData
|
||||||
fun getLastAutosensDataWithWaitForCalculationFinish(reason: String): AutosensData?
|
fun getLastAutosensDataWithWaitForCalculationFinish(reason: String): AutosensData?
|
||||||
|
|
||||||
fun calculateAbsInsulinFromTreatmentsAndTemps(fromTime: Long): IobTotal
|
fun calculateFromTreatmentsAndTemps(toTime: Long, profile: Profile): IobTotal
|
||||||
fun calculateFromTreatmentsAndTemps(fromTime: Long, profile: Profile): IobTotal
|
|
||||||
|
|
||||||
fun getBasalData(profile: Profile, fromTime: Long): BasalData
|
fun getBasalData(profile: Profile, fromTime: Long): BasalData
|
||||||
|
|
||||||
|
@ -72,14 +70,12 @@ interface IobCobCalculator {
|
||||||
fun getExtendedBolus(timestamp: Long): ExtendedBolus?
|
fun getExtendedBolus(timestamp: Long): ExtendedBolus?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculate IOB of all insulin in the body to the time
|
* Calculate IOB of base basal insulin (usualy not accounted towards IOB)
|
||||||
*
|
|
||||||
* Running basal is added to the IOB !!!
|
|
||||||
*
|
*
|
||||||
* @param toTime
|
* @param toTime
|
||||||
* @return IobTotal
|
* @return IobTotal
|
||||||
*/
|
*/
|
||||||
fun calculateAbsoluteIobTempBasals(toTime: Long): IobTotal
|
fun calculateAbsoluteIobFromBaseBasals(toTime: Long): IobTotal
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculate IOB from Temporary basals and Extended boluses (if emulation is enabled) to the the time specified
|
* Calculate IOB from Temporary basals and Extended boluses (if emulation is enabled) to the the time specified
|
||||||
|
|
|
@ -1,3 +1,9 @@
|
||||||
package info.nightscout.androidaps.interfaces
|
package info.nightscout.androidaps.interfaces
|
||||||
|
|
||||||
interface Overview : ConfigExportImport
|
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
|
||||||
|
|
||||||
|
interface Overview : ConfigExportImport {
|
||||||
|
|
||||||
|
fun refreshLoop(from: String)
|
||||||
|
val overviewBus: RxBusWrapper
|
||||||
|
}
|
|
@ -6,7 +6,9 @@ import info.nightscout.androidaps.database.AppRepository
|
||||||
import info.nightscout.androidaps.database.entities.GlucoseValue
|
import info.nightscout.androidaps.database.entities.GlucoseValue
|
||||||
import info.nightscout.androidaps.logging.AAPSLogger
|
import info.nightscout.androidaps.logging.AAPSLogger
|
||||||
import info.nightscout.androidaps.logging.LTag
|
import info.nightscout.androidaps.logging.LTag
|
||||||
|
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
|
||||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.data.AutosensData
|
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.data.AutosensData
|
||||||
|
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventBucketedDataCreated
|
||||||
import info.nightscout.androidaps.utils.DateUtil
|
import info.nightscout.androidaps.utils.DateUtil
|
||||||
import info.nightscout.androidaps.utils.T
|
import info.nightscout.androidaps.utils.T
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
|
@ -151,7 +153,7 @@ class AutosensDataStore {
|
||||||
return someTime + diff
|
return someTime + diff
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadBgData(to: Long, repository: AppRepository, aapsLogger: AAPSLogger, dateUtil: DateUtil) {
|
fun loadBgData(to: Long, repository: AppRepository, aapsLogger: AAPSLogger, dateUtil: DateUtil, rxBus: RxBusWrapper) {
|
||||||
synchronized(dataLock) {
|
synchronized(dataLock) {
|
||||||
val start = to - T.hours((24 + 10 /* max dia */).toLong()).msecs()
|
val start = to - T.hours((24 + 10 /* max dia */).toLong()).msecs()
|
||||||
// there can be some readings with time in close future (caused by wrong time setting on sensor)
|
// there can be some readings with time in close future (caused by wrong time setting on sensor)
|
||||||
|
@ -162,6 +164,7 @@ class AutosensDataStore {
|
||||||
.filter { it.value >= 39 }
|
.filter { it.value >= 39 }
|
||||||
aapsLogger.debug(LTag.AUTOSENS, "BG data loaded. Size: " + bgReadings.size + " Start date: " + dateUtil.dateAndTimeString(start) + " End date: " + dateUtil.dateAndTimeString(to))
|
aapsLogger.debug(LTag.AUTOSENS, "BG data loaded. Size: " + bgReadings.size + " Start date: " + dateUtil.dateAndTimeString(start) + " End date: " + dateUtil.dateAndTimeString(to))
|
||||||
createBucketedData(aapsLogger, dateUtil)
|
createBucketedData(aapsLogger, dateUtil)
|
||||||
|
rxBus.send(EventBucketedDataCreated())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
package info.nightscout.androidaps.plugins.iob.iobCobCalculator.events
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.events.Event
|
||||||
|
|
||||||
|
class EventBucketedDataCreated : Event()
|
|
@ -137,12 +137,14 @@ open class DateUtil @Inject constructor(private val context: Context) {
|
||||||
return if (mills == 0L) "" else dateString(mills) + " " + timeStringWithSeconds(mills)
|
return if (mills == 0L) "" else dateString(mills) + " " + timeStringWithSeconds(mills)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun minAgo(resourceHelper: ResourceHelper, time: Long): String {
|
fun minAgo(resourceHelper: ResourceHelper, time: Long?): String {
|
||||||
|
if (time == null) return ""
|
||||||
val mins = ((now() - time) / 1000 / 60).toInt()
|
val mins = ((now() - time) / 1000 / 60).toInt()
|
||||||
return resourceHelper.gs(R.string.minago, mins)
|
return resourceHelper.gs(R.string.minago, mins)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun minAgoShort(time: Long): String {
|
fun minAgoShort(time: Long?): String {
|
||||||
|
if (time == null) return ""
|
||||||
val mins = ((time - now()) / 1000 / 60).toInt()
|
val mins = ((time - now()) / 1000 / 60).toInt()
|
||||||
return (if (mins > 0) "+" else "") + mins
|
return (if (mins > 0) "+" else "") + mins
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,8 +7,6 @@ import io.reactivex.Completable
|
||||||
import io.reactivex.Maybe
|
import io.reactivex.Maybe
|
||||||
import io.reactivex.Observable
|
import io.reactivex.Observable
|
||||||
import io.reactivex.Single
|
import io.reactivex.Single
|
||||||
import io.reactivex.internal.operators.maybe.MaybeJust
|
|
||||||
import io.reactivex.rxkotlin.subscribeBy
|
|
||||||
import io.reactivex.schedulers.Schedulers
|
import io.reactivex.schedulers.Schedulers
|
||||||
import io.reactivex.subjects.PublishSubject
|
import io.reactivex.subjects.PublishSubject
|
||||||
import java.util.concurrent.Callable
|
import java.util.concurrent.Callable
|
||||||
|
@ -83,6 +81,11 @@ open class AppRepository @Inject internal constructor(
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
.toWrappedSingle()
|
.toWrappedSingle()
|
||||||
|
|
||||||
|
fun getLastGlucoseValueWrapped(): Single<ValueWrapper<GlucoseValue>> =
|
||||||
|
database.glucoseValueDao.getLast()
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.toWrappedSingle()
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* returns a Pair of the next entity to sync and the ID of the "update".
|
* returns a Pair of the next entity to sync and the ID of the "update".
|
||||||
* The update id might either be the entry id itself if it is a new entry - or the id
|
* The update id might either be the entry id itself if it is a new entry - or the id
|
||||||
|
|
|
@ -2,6 +2,7 @@ package info.nightscout.androidaps.database
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.room.Room
|
import androidx.room.Room
|
||||||
|
import androidx.room.RoomDatabase.Callback
|
||||||
import androidx.room.migration.Migration
|
import androidx.room.migration.Migration
|
||||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
|
@ -25,6 +26,15 @@ open class DatabaseModule {
|
||||||
// .addMigrations(migration6to7)
|
// .addMigrations(migration6to7)
|
||||||
// .addMigrations(migration7to8)
|
// .addMigrations(migration7to8)
|
||||||
// .addMigrations(migration11to12)
|
// .addMigrations(migration11to12)
|
||||||
|
.addCallback(object : Callback() {
|
||||||
|
override fun onOpen(db: SupportSQLiteDatabase) {
|
||||||
|
super.onOpen(db)
|
||||||
|
db.execSQL("CREATE INDEX IF NOT EXISTS `index_temporaryBasals_end` ON `temporaryBasals` (`timestamp` + `duration`)")
|
||||||
|
db.execSQL("CREATE INDEX IF NOT EXISTS `index_extendedBoluses_end` ON `extendedBoluses` (`timestamp` + `duration`)")
|
||||||
|
db.execSQL("CREATE INDEX IF NOT EXISTS `index_temporaryTargets_end` ON `temporaryTargets` (`timestamp` + `duration`)")
|
||||||
|
db.execSQL("CREATE INDEX IF NOT EXISTS `index_carbs_end` ON `carbs` (`timestamp` + `duration`)")
|
||||||
|
}
|
||||||
|
})
|
||||||
.fallbackToDestructiveMigration()
|
.fallbackToDestructiveMigration()
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,9 @@ internal interface GlucoseValueDao : TraceableDao<GlucoseValue> {
|
||||||
@Query("DELETE FROM $TABLE_GLUCOSE_VALUES")
|
@Query("DELETE FROM $TABLE_GLUCOSE_VALUES")
|
||||||
override fun deleteAllEntries()
|
override fun deleteAllEntries()
|
||||||
|
|
||||||
|
@Query("SELECT * FROM $TABLE_GLUCOSE_VALUES ORDER BY id DESC limit 1")
|
||||||
|
fun getLast(): Maybe<GlucoseValue>
|
||||||
|
|
||||||
@Query("SELECT id FROM $TABLE_GLUCOSE_VALUES ORDER BY id DESC limit 1")
|
@Query("SELECT id FROM $TABLE_GLUCOSE_VALUES ORDER BY id DESC limit 1")
|
||||||
fun getLastId(): Maybe<Long>
|
fun getLastId(): Maybe<Long>
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ class InvalidateAAPSStartedTherapyEventTransaction(private val note: String) : T
|
||||||
val result = TransactionResult()
|
val result = TransactionResult()
|
||||||
val therapyEvents = database.therapyEventDao.getValidByType(TherapyEvent.Type.NOTE)
|
val therapyEvents = database.therapyEventDao.getValidByType(TherapyEvent.Type.NOTE)
|
||||||
for (event in therapyEvents) {
|
for (event in therapyEvents) {
|
||||||
if (event.note?.contains(note) == true) {
|
if (event.note?.contains(note) == true && event.isValid) {
|
||||||
event.isValid = false
|
event.isValid = false
|
||||||
database.therapyEventDao.updateExistingEntry(event)
|
database.therapyEventDao.updateExistingEntry(event)
|
||||||
result.invalidated.add(event)
|
result.invalidated.add(event)
|
||||||
|
|
Loading…
Reference in a new issue