OfflineEvent -> room

This commit is contained in:
Milos Kozak 2021-05-28 16:06:44 +02:00
parent 82cce81d0b
commit 6735d22934
63 changed files with 4653 additions and 409 deletions

View file

@ -82,5 +82,9 @@ class CompatDBHelper @Inject constructor(
aapsLogger.debug(LTag.DATABASE, "Firing EventProfileSwitchChanged") aapsLogger.debug(LTag.DATABASE, "Firing EventProfileSwitchChanged")
rxBus.send(EventProfileSwitchChanged()) rxBus.send(EventProfileSwitchChanged())
} }
it.filterIsInstance<OfflineEvent>().firstOrNull()?.let {
aapsLogger.debug(LTag.DATABASE, "Firing EventOfflineChange")
rxBus.send(EventOfflineChange())
}
} }
} }

View file

@ -83,7 +83,7 @@ open class AppModule {
@Binds fun bindNotificationHolderInterface(notificationHolder: NotificationHolderImpl): NotificationHolder @Binds fun bindNotificationHolderInterface(notificationHolder: NotificationHolderImpl): NotificationHolder
@Binds fun bindImportExportPrefsInterface(importExportPrefs: ImportExportPrefsImpl): ImportExportPrefs @Binds fun bindImportExportPrefsInterface(importExportPrefs: ImportExportPrefsImpl): ImportExportPrefs
@Binds fun bindIconsProviderInterface(iconsProvider: IconsProviderImplementation): IconsProvider @Binds fun bindIconsProviderInterface(iconsProvider: IconsProviderImplementation): IconsProvider
@Binds fun bindLoopInterface(loopPlugin: LoopPlugin): LoopInterface @Binds fun bindLoopInterface(loopPlugin: LoopPlugin): Loop
@Binds fun bindIobCobCalculatorInterface(iobCobCalculatorPlugin: IobCobCalculatorPlugin): IobCobCalculator @Binds fun bindIobCobCalculatorInterface(iobCobCalculatorPlugin: IobCobCalculatorPlugin): IobCobCalculator
@Binds fun bindSmsCommunicatorInterface(smsCommunicatorPlugin: SmsCommunicatorPlugin): SmsCommunicator @Binds fun bindSmsCommunicatorInterface(smsCommunicatorPlugin: SmsCommunicatorPlugin): SmsCommunicator
@Binds fun bindDataSyncSelector(dataSyncSelectorImplementation: DataSyncSelectorImplementation): DataSyncSelector @Binds fun bindDataSyncSelector(dataSyncSelectorImplementation: DataSyncSelectorImplementation): DataSyncSelector

View file

@ -17,7 +17,7 @@ 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.entities.ValueWithUnit import info.nightscout.androidaps.database.entities.ValueWithUnit
import info.nightscout.androidaps.database.transactions.InsertTemporaryTargetAndCancelCurrentTransaction import info.nightscout.androidaps.database.transactions.InsertAndCancelCurrentTemporaryTargetTransaction
import info.nightscout.androidaps.databinding.DialogCarbsBinding import info.nightscout.androidaps.databinding.DialogCarbsBinding
import info.nightscout.androidaps.extensions.formatColor import info.nightscout.androidaps.extensions.formatColor
import info.nightscout.androidaps.interfaces.Constraint import info.nightscout.androidaps.interfaces.Constraint
@ -226,7 +226,7 @@ class CarbsDialog : DialogFragmentWithDate() {
ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.ACTIVITY), ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.ACTIVITY),
ValueWithUnit.fromGlucoseUnit(activityTT, units.asText), ValueWithUnit.fromGlucoseUnit(activityTT, units.asText),
ValueWithUnit.Minute(activityTTDuration)) ValueWithUnit.Minute(activityTTDuration))
disposable += repository.runTransactionForResult(InsertTemporaryTargetAndCancelCurrentTransaction( disposable += repository.runTransactionForResult(InsertAndCancelCurrentTemporaryTargetTransaction(
timestamp = System.currentTimeMillis(), timestamp = System.currentTimeMillis(),
duration = TimeUnit.MINUTES.toMillis(activityTTDuration.toLong()), duration = TimeUnit.MINUTES.toMillis(activityTTDuration.toLong()),
reason = TemporaryTarget.Reason.ACTIVITY, reason = TemporaryTarget.Reason.ACTIVITY,
@ -245,7 +245,7 @@ class CarbsDialog : DialogFragmentWithDate() {
ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.EATING_SOON), ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.EATING_SOON),
ValueWithUnit.fromGlucoseUnit(eatingSoonTT, units.asText), ValueWithUnit.fromGlucoseUnit(eatingSoonTT, units.asText),
ValueWithUnit.Minute(eatingSoonTTDuration)) ValueWithUnit.Minute(eatingSoonTTDuration))
disposable += repository.runTransactionForResult(InsertTemporaryTargetAndCancelCurrentTransaction( disposable += repository.runTransactionForResult(InsertAndCancelCurrentTemporaryTargetTransaction(
timestamp = System.currentTimeMillis(), timestamp = System.currentTimeMillis(),
duration = TimeUnit.MINUTES.toMillis(eatingSoonTTDuration.toLong()), duration = TimeUnit.MINUTES.toMillis(eatingSoonTTDuration.toLong()),
reason = TemporaryTarget.Reason.EATING_SOON, reason = TemporaryTarget.Reason.EATING_SOON,
@ -264,7 +264,7 @@ class CarbsDialog : DialogFragmentWithDate() {
ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.HYPOGLYCEMIA), ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.HYPOGLYCEMIA),
ValueWithUnit.fromGlucoseUnit(hypoTT, units.asText), ValueWithUnit.fromGlucoseUnit(hypoTT, units.asText),
ValueWithUnit.Minute(hypoTTDuration)) ValueWithUnit.Minute(hypoTTDuration))
disposable += repository.runTransactionForResult(InsertTemporaryTargetAndCancelCurrentTransaction( disposable += repository.runTransactionForResult(InsertAndCancelCurrentTemporaryTargetTransaction(
timestamp = System.currentTimeMillis(), timestamp = System.currentTimeMillis(),
duration = TimeUnit.MINUTES.toMillis(hypoTTDuration.toLong()), duration = TimeUnit.MINUTES.toMillis(hypoTTDuration.toLong()),
reason = TemporaryTarget.Reason.HYPOGLYCEMIA, reason = TemporaryTarget.Reason.HYPOGLYCEMIA,

View file

@ -16,7 +16,7 @@ import info.nightscout.androidaps.database.entities.ValueWithUnit
import info.nightscout.androidaps.database.entities.TemporaryTarget 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.transactions.InsertTemporaryTargetAndCancelCurrentTransaction import info.nightscout.androidaps.database.transactions.InsertAndCancelCurrentTemporaryTargetTransaction
import info.nightscout.androidaps.databinding.DialogInsulinBinding import info.nightscout.androidaps.databinding.DialogInsulinBinding
import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.logging.UserEntryLogger
@ -186,7 +186,7 @@ class InsulinDialog : DialogFragmentWithDate() {
ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.EATING_SOON), ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.EATING_SOON),
ValueWithUnit.fromGlucoseUnit(eatingSoonTT, units.asText), ValueWithUnit.fromGlucoseUnit(eatingSoonTT, units.asText),
ValueWithUnit.Minute(eatingSoonTTDuration)) ValueWithUnit.Minute(eatingSoonTTDuration))
disposable += repository.runTransactionForResult(InsertTemporaryTargetAndCancelCurrentTransaction( disposable += repository.runTransactionForResult(InsertAndCancelCurrentTemporaryTargetTransaction(
timestamp = System.currentTimeMillis(), timestamp = System.currentTimeMillis(),
duration = TimeUnit.MINUTES.toMillis(eatingSoonTTDuration.toLong()), duration = TimeUnit.MINUTES.toMillis(eatingSoonTTDuration.toLong()),
reason = TemporaryTarget.Reason.EATING_SOON, reason = TemporaryTarget.Reason.EATING_SOON,

View file

@ -12,9 +12,13 @@ import androidx.fragment.app.FragmentManager
import dagger.android.support.DaggerDialogFragment import dagger.android.support.DaggerDialogFragment
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.activities.ErrorHelperActivity import info.nightscout.androidaps.activities.ErrorHelperActivity
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.OfflineEvent
import info.nightscout.androidaps.database.entities.ValueWithUnit import info.nightscout.androidaps.database.entities.ValueWithUnit
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.transactions.CancelCurrentOfflineEventIfAnyTransaction
import info.nightscout.androidaps.database.transactions.InsertAndCancelCurrentOfflineEventTransaction
import info.nightscout.androidaps.databinding.DialogLoopBinding import info.nightscout.androidaps.databinding.DialogLoopBinding
import info.nightscout.androidaps.events.EventPreferenceChange import info.nightscout.androidaps.events.EventPreferenceChange
import info.nightscout.androidaps.events.EventRefreshOverview import info.nightscout.androidaps.events.EventRefreshOverview
@ -29,8 +33,13 @@ import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.ToastUtils import info.nightscout.androidaps.utils.ToastUtils
import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.extensions.toVisibility import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP import info.nightscout.androidaps.utils.sharedPreferences.SP
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign
import javax.inject.Inject import javax.inject.Inject
class LoopDialog : DaggerDialogFragment() { class LoopDialog : DaggerDialogFragment() {
@ -48,6 +57,8 @@ class LoopDialog : DaggerDialogFragment() {
@Inject lateinit var commandQueue: CommandQueueProvider @Inject lateinit var commandQueue: CommandQueueProvider
@Inject lateinit var configBuilder: ConfigBuilder @Inject lateinit var configBuilder: ConfigBuilder
@Inject lateinit var uel: UserEntryLogger @Inject lateinit var uel: UserEntryLogger
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var repository: AppRepository
private var showOkCancel: Boolean = true private var showOkCancel: Boolean = true
private var _binding: DialogLoopBinding? = null private var _binding: DialogLoopBinding? = null
@ -58,6 +69,8 @@ class LoopDialog : DaggerDialogFragment() {
// onDestroyView. // onDestroyView.
private val binding get() = _binding!! private val binding get() = _binding!!
val disposable = CompositeDisposable()
override fun onStart() { override fun onStart() {
super.onStart() super.onStart()
dialog?.window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) dialog?.window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
@ -118,6 +131,7 @@ class LoopDialog : DaggerDialogFragment() {
super.onDestroyView() super.onDestroyView()
_binding = null _binding = null
loopHandler.removeCallbacksAndMessages(null) loopHandler.removeCallbacksAndMessages(null)
disposable.clear()
} }
var task: Runnable? = null var task: Runnable? = null
@ -238,7 +252,6 @@ class LoopDialog : DaggerDialogFragment() {
} }
fun onClick(v: View): Boolean { fun onClick(v: View): Boolean {
val profile = profileFunction.getProfile() ?: return true
when (v.id) { when (v.id) {
R.id.overview_closeloop -> { R.id.overview_closeloop -> {
uel.log(Action.CLOSED_LOOP_MODE, Sources.LoopDialog) uel.log(Action.CLOSED_LOOP_MODE, Sources.LoopDialog)
@ -274,7 +287,13 @@ class LoopDialog : DaggerDialogFragment() {
} }
} }
}) })
loopPlugin.createOfflineEvent(24 * 60) // upload 24h, we don't know real duration disposable += repository.runTransactionForResult(InsertAndCancelCurrentOfflineEventTransaction(dateUtil.now(), T.days(365).msecs(), OfflineEvent.Reason.DISABLE_LOOP))
.subscribe({ result ->
result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") }
result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted OfflineEvent $it") }
}, {
aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it)
})
return true return true
} }
@ -284,13 +303,23 @@ class LoopDialog : DaggerDialogFragment() {
loopPlugin.setFragmentVisible(PluginType.LOOP, true) loopPlugin.setFragmentVisible(PluginType.LOOP, true)
configBuilder.storeSettings("EnablingLoop") configBuilder.storeSettings("EnablingLoop")
rxBus.send(EventRefreshOverview("suspendmenu")) rxBus.send(EventRefreshOverview("suspendmenu"))
loopPlugin.createOfflineEvent(0) disposable += repository.runTransactionForResult(CancelCurrentOfflineEventIfAnyTransaction(dateUtil.now()))
.subscribe({ result ->
result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") }
}, {
aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it)
})
return true return true
} }
R.id.overview_resume, R.id.overview_reconnect -> { R.id.overview_resume, R.id.overview_reconnect -> {
uel.log(if (v.id==R.id.overview_resume) Action.RESUME else Action.RECONNECT, Sources.LoopDialog) uel.log(if (v.id == R.id.overview_resume) Action.RESUME else Action.RECONNECT, Sources.LoopDialog)
loopPlugin.suspendTo(0L) disposable += repository.runTransactionForResult(CancelCurrentOfflineEventIfAnyTransaction(dateUtil.now()))
.subscribe({ result ->
result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") }
}, {
aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it)
})
rxBus.send(EventRefreshOverview("suspendmenu")) rxBus.send(EventRefreshOverview("suspendmenu"))
commandQueue.cancelTempBasal(true, object : Callback() { commandQueue.cancelTempBasal(true, object : Callback() {
override fun run() { override fun run() {
@ -300,55 +329,96 @@ class LoopDialog : DaggerDialogFragment() {
} }
}) })
sp.putBoolean(R.string.key_objectiveusereconnect, true) sp.putBoolean(R.string.key_objectiveusereconnect, true)
loopPlugin.createOfflineEvent(0)
return true return true
} }
R.id.overview_suspend_1h -> { R.id.overview_suspend_1h -> {
uel.log(Action.SUSPEND, Sources.LoopDialog, ValueWithUnit.Hour(1)) uel.log(Action.SUSPEND, Sources.LoopDialog, ValueWithUnit.Hour(1))
loopPlugin.suspendLoop(60) disposable += repository.runTransactionForResult(InsertAndCancelCurrentOfflineEventTransaction(dateUtil.now(), T.hours(1).msecs(), OfflineEvent.Reason.SUSPEND))
.subscribe({ result ->
result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") }
result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted OfflineEvent $it") }
}, {
aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it)
})
rxBus.send(EventRefreshOverview("suspendmenu")) rxBus.send(EventRefreshOverview("suspendmenu"))
return true return true
} }
R.id.overview_suspend_2h -> { R.id.overview_suspend_2h -> {
uel.log(Action.SUSPEND, Sources.LoopDialog, ValueWithUnit.Hour(2)) uel.log(Action.SUSPEND, Sources.LoopDialog, ValueWithUnit.Hour(2))
loopPlugin.suspendLoop(120) disposable += repository.runTransactionForResult(InsertAndCancelCurrentOfflineEventTransaction(dateUtil.now(), T.hours(2).msecs(), OfflineEvent.Reason.SUSPEND))
.subscribe({ result ->
result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") }
result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted OfflineEvent $it") }
}, {
aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it)
})
rxBus.send(EventRefreshOverview("suspendmenu")) rxBus.send(EventRefreshOverview("suspendmenu"))
return true return true
} }
R.id.overview_suspend_3h -> { R.id.overview_suspend_3h -> {
uel.log(Action.SUSPEND, Sources.LoopDialog, ValueWithUnit.Hour(3)) uel.log(Action.SUSPEND, Sources.LoopDialog, ValueWithUnit.Hour(3))
loopPlugin.suspendLoop(180) disposable += repository.runTransactionForResult(InsertAndCancelCurrentOfflineEventTransaction(dateUtil.now(), T.hours(3).msecs(), OfflineEvent.Reason.SUSPEND))
.subscribe({ result ->
result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") }
result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted OfflineEvent $it") }
}, {
aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it)
})
rxBus.send(EventRefreshOverview("suspendmenu")) rxBus.send(EventRefreshOverview("suspendmenu"))
return true return true
} }
R.id.overview_suspend_10h -> { R.id.overview_suspend_10h -> {
uel.log(Action.SUSPEND, Sources.LoopDialog, ValueWithUnit.Hour(10)) uel.log(Action.SUSPEND, Sources.LoopDialog, ValueWithUnit.Hour(10))
loopPlugin.suspendLoop(600) disposable += repository.runTransactionForResult(InsertAndCancelCurrentOfflineEventTransaction(dateUtil.now(), T.hours(10).msecs(), OfflineEvent.Reason.SUSPEND))
.subscribe({ result ->
result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") }
result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted OfflineEvent $it") }
}, {
aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it)
})
rxBus.send(EventRefreshOverview("suspendmenu")) rxBus.send(EventRefreshOverview("suspendmenu"))
return true return true
} }
R.id.overview_disconnect_15m -> { R.id.overview_disconnect_15m -> {
uel.log(Action.DISCONNECT, Sources.LoopDialog, ValueWithUnit.Minute(15)) uel.log(Action.DISCONNECT, Sources.LoopDialog, ValueWithUnit.Minute(15))
loopPlugin.disconnectPump(15, profile) disposable += repository.runTransactionForResult(InsertAndCancelCurrentOfflineEventTransaction(dateUtil.now(), T.mins(15).msecs(), OfflineEvent.Reason.DISCONNECT_PUMP))
.subscribe({ result ->
result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") }
result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted OfflineEvent $it") }
}, {
aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it)
})
rxBus.send(EventRefreshOverview("suspendmenu")) rxBus.send(EventRefreshOverview("suspendmenu"))
return true return true
} }
R.id.overview_disconnect_30m -> { R.id.overview_disconnect_30m -> {
uel.log(Action.DISCONNECT, Sources.LoopDialog, ValueWithUnit.Minute(30)) uel.log(Action.DISCONNECT, Sources.LoopDialog, ValueWithUnit.Minute(30))
loopPlugin.disconnectPump(30, profile) disposable += repository.runTransactionForResult(InsertAndCancelCurrentOfflineEventTransaction(dateUtil.now(), T.mins(30).msecs(), OfflineEvent.Reason.DISCONNECT_PUMP))
.subscribe({ result ->
result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") }
result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted OfflineEvent $it") }
}, {
aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it)
})
rxBus.send(EventRefreshOverview("suspendmenu")) rxBus.send(EventRefreshOverview("suspendmenu"))
return true return true
} }
R.id.overview_disconnect_1h -> { R.id.overview_disconnect_1h -> {
uel.log(Action.DISCONNECT, Sources.LoopDialog, ValueWithUnit.Hour(1)) uel.log(Action.DISCONNECT, Sources.LoopDialog, ValueWithUnit.Hour(1))
loopPlugin.disconnectPump(60, profile) disposable += repository.runTransactionForResult(InsertAndCancelCurrentOfflineEventTransaction(dateUtil.now(), T.mins(60).msecs(), OfflineEvent.Reason.DISCONNECT_PUMP))
.subscribe({ result ->
result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") }
result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted OfflineEvent $it") }
}, {
aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it)
})
sp.putBoolean(R.string.key_objectiveusedisconnect, true) sp.putBoolean(R.string.key_objectiveusedisconnect, true)
rxBus.send(EventRefreshOverview("suspendmenu")) rxBus.send(EventRefreshOverview("suspendmenu"))
return true return true
@ -356,14 +426,26 @@ class LoopDialog : DaggerDialogFragment() {
R.id.overview_disconnect_2h -> { R.id.overview_disconnect_2h -> {
uel.log(Action.DISCONNECT, Sources.LoopDialog, ValueWithUnit.Hour(2)) uel.log(Action.DISCONNECT, Sources.LoopDialog, ValueWithUnit.Hour(2))
loopPlugin.disconnectPump(120, profile) disposable += repository.runTransactionForResult(InsertAndCancelCurrentOfflineEventTransaction(dateUtil.now(), T.mins(120).msecs(), OfflineEvent.Reason.DISCONNECT_PUMP))
.subscribe({ result ->
result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") }
result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted OfflineEvent $it") }
}, {
aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it)
})
rxBus.send(EventRefreshOverview("suspendmenu")) rxBus.send(EventRefreshOverview("suspendmenu"))
return true return true
} }
R.id.overview_disconnect_3h -> { R.id.overview_disconnect_3h -> {
uel.log(Action.DISCONNECT, Sources.LoopDialog, ValueWithUnit.Hour(3)) uel.log(Action.DISCONNECT, Sources.LoopDialog, ValueWithUnit.Hour(3))
loopPlugin.disconnectPump(180, profile) disposable += repository.runTransactionForResult(InsertAndCancelCurrentOfflineEventTransaction(dateUtil.now(), T.mins(180).msecs(), OfflineEvent.Reason.DISCONNECT_PUMP))
.subscribe({ result ->
result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") }
result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted OfflineEvent $it") }
}, {
aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it)
})
rxBus.send(EventRefreshOverview("suspendmenu")) rxBus.send(EventRefreshOverview("suspendmenu"))
return true return true
} }

View file

@ -17,7 +17,7 @@ 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.transactions.CancelCurrentTemporaryTargetIfAnyTransaction import info.nightscout.androidaps.database.transactions.CancelCurrentTemporaryTargetIfAnyTransaction
import info.nightscout.androidaps.database.transactions.InsertTemporaryTargetAndCancelCurrentTransaction import info.nightscout.androidaps.database.transactions.InsertAndCancelCurrentTemporaryTargetTransaction
import info.nightscout.androidaps.databinding.DialogTemptargetBinding import info.nightscout.androidaps.databinding.DialogTemptargetBinding
import info.nightscout.androidaps.interfaces.GlucoseUnit import info.nightscout.androidaps.interfaces.GlucoseUnit
import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.interfaces.ProfileFunction
@ -196,7 +196,7 @@ class TempTargetDialog : DialogFragmentWithDate() {
aapsLogger.error(LTag.DATABASE, "Error while saving temporary target", it) aapsLogger.error(LTag.DATABASE, "Error while saving temporary target", it)
}) })
} else { } else {
disposable += repository.runTransactionForResult(InsertTemporaryTargetAndCancelCurrentTransaction( disposable += repository.runTransactionForResult(InsertAndCancelCurrentTemporaryTargetTransaction(
timestamp = eventTime, timestamp = eventTime,
duration = TimeUnit.MINUTES.toMillis(duration.toLong()), duration = TimeUnit.MINUTES.toMillis(duration.toLong()),
reason = when (reason) { reason = when (reason) {

View file

@ -16,18 +16,19 @@ import info.nightscout.androidaps.data.DetailedBolusInfo
import info.nightscout.androidaps.interfaces.Profile import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.data.PumpEnactResult import info.nightscout.androidaps.data.PumpEnactResult
import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.TherapyEvent import info.nightscout.androidaps.database.ValueWrapper
import info.nightscout.androidaps.database.entities.OfflineEvent
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.entities.ValueWithUnit import info.nightscout.androidaps.database.entities.ValueWithUnit
import info.nightscout.androidaps.database.transactions.InsertIfNewByTimestampTherapyEventTransaction import info.nightscout.androidaps.database.transactions.InsertAndCancelCurrentOfflineEventTransaction
import info.nightscout.androidaps.database.transactions.InsertTherapyEventAnnouncementTransaction import info.nightscout.androidaps.database.transactions.InsertTherapyEventAnnouncementTransaction
import info.nightscout.androidaps.events.EventAcceptOpenLoopChange import info.nightscout.androidaps.events.EventAcceptOpenLoopChange
import info.nightscout.androidaps.events.EventAutosensCalculationFinished import info.nightscout.androidaps.events.EventAutosensCalculationFinished
import info.nightscout.androidaps.events.EventNewBG import info.nightscout.androidaps.events.EventNewBG
import info.nightscout.androidaps.events.EventTempTargetChange import info.nightscout.androidaps.events.EventTempTargetChange
import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.interfaces.LoopInterface.LastRun import info.nightscout.androidaps.interfaces.Loop.LastRun
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.logging.UserEntryLogger import info.nightscout.androidaps.logging.UserEntryLogger
@ -96,7 +97,7 @@ open class LoopPlugin @Inject constructor(
.enableByDefault(config.APS) .enableByDefault(config.APS)
.description(R.string.description_loop), .description(R.string.description_loop),
aapsLogger, resourceHelper, injector aapsLogger, resourceHelper, injector
), LoopInterface { ), Loop {
private val disposable = CompositeDisposable() private val disposable = CompositeDisposable()
private var lastBgTriggeredRun: Long = 0 private var lastBgTriggeredRun: Long = 0
@ -158,48 +159,19 @@ open class LoopPlugin @Inject constructor(
} }
} }
override fun suspendTo(endTime: Long) { override fun minutesToEndOfSuspend(): Int {
sp.putLong("loopSuspendedTill", endTime) val offlineEventWrapped = repository.getOfflineEventActiveAt(dateUtil.now()).blockingGet()
sp.putBoolean("isSuperBolus", false) return if (offlineEventWrapped is ValueWrapper.Existing) T.msecs(offlineEventWrapped.value.timestamp + offlineEventWrapped.value.duration - dateUtil.now()).mins().toInt()
sp.putBoolean("isDisconnected", false) else 0
} }
fun superBolusTo(endTime: Long) {
sp.putLong("loopSuspendedTill", endTime)
sp.putBoolean("isSuperBolus", true)
sp.putBoolean("isDisconnected", false)
}
private fun disconnectTo(endTime: Long) {
sp.putLong("loopSuspendedTill", endTime)
sp.putBoolean("isSuperBolus", false)
sp.putBoolean("isDisconnected", true)
}
fun minutesToEndOfSuspend(): Int {
val loopSuspendedTill = sp.getLong("loopSuspendedTill", 0L)
if (loopSuspendedTill == 0L) return 0
val now = System.currentTimeMillis()
val millisDiff = loopSuspendedTill - now
if (loopSuspendedTill <= now) { // time exceeded
suspendTo(0L)
return 0
}
return (millisDiff / 60.0 / 1000.0).toInt()
}
// time exceeded
override val isSuspended: Boolean override val isSuspended: Boolean
get() { get() = repository.getOfflineEventActiveAt(dateUtil.now()).blockingGet() is ValueWrapper.Existing
val loopSuspendedTill = sp.getLong("loopSuspendedTill", 0L)
if (loopSuspendedTill == 0L) return false override var enabled: Boolean
val now = System.currentTimeMillis() get() = isEnabled()
if (loopSuspendedTill <= now) { // time exceeded set(value) { setPluginEnabled(PluginType.LOOP, value)}
suspendTo(0L)
return false
}
return true
}
val isLGS: Boolean val isLGS: Boolean
get() { get() {
val closedLoopEnabled = constraintChecker.isClosedLoopAllowed() val closedLoopEnabled = constraintChecker.isClosedLoopAllowed()
@ -211,30 +183,16 @@ open class LoopPlugin @Inject constructor(
return isLGS return isLGS
} }
// time exceeded
val isSuperBolus: Boolean val isSuperBolus: Boolean
get() { get() {
val loopSuspendedTill = sp.getLong("loopSuspendedTill", 0L) val offlineEventWrapped = repository.getOfflineEventActiveAt(dateUtil.now()).blockingGet()
if (loopSuspendedTill == 0L) return false return offlineEventWrapped is ValueWrapper.Existing && offlineEventWrapped.value.reason == OfflineEvent.Reason.SUPER_BOLUS
val now = System.currentTimeMillis()
if (loopSuspendedTill <= now) { // time exceeded
suspendTo(0L)
return false
}
return sp.getBoolean("isSuperBolus", false)
} }
// time exceeded
val isDisconnected: Boolean val isDisconnected: Boolean
get() { get() {
val loopSuspendedTill = sp.getLong("loopSuspendedTill", 0L) val offlineEventWrapped = repository.getOfflineEventActiveAt(dateUtil.now()).blockingGet()
if (loopSuspendedTill == 0L) return false return offlineEventWrapped is ValueWrapper.Existing && offlineEventWrapped.value.reason == OfflineEvent.Reason.DISCONNECT_PUMP
val now = System.currentTimeMillis()
if (loopSuspendedTill <= now) { // time exceeded
suspendTo(0L)
return false
}
return sp.getBoolean("isDisconnected", false)
} }
@Suppress("SameParameterValue") @Suppress("SameParameterValue")
@ -642,11 +600,17 @@ open class LoopPlugin @Inject constructor(
return virtualPumpPlugin.isEnabled(PluginType.PUMP) return virtualPumpPlugin.isEnabled(PluginType.PUMP)
} }
fun disconnectPump(durationInMinutes: Int, profile: Profile?) { override fun goToZeroTemp(durationInMinutes: Int, profile: Profile, reason: OfflineEvent.Reason) {
val pump = activePlugin.activePump val pump = activePlugin.activePump
disconnectTo(System.currentTimeMillis() + durationInMinutes * 60 * 1000L) disposable += repository.runTransactionForResult(InsertAndCancelCurrentOfflineEventTransaction(dateUtil.now(), T.mins(durationInMinutes.toLong()).msecs(), reason))
.subscribe({ result ->
result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") }
result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted OfflineEvent $it") }
}, {
aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it)
})
if (pump.pumpDescription.tempBasalStyle == PumpDescription.ABSOLUTE) { if (pump.pumpDescription.tempBasalStyle == PumpDescription.ABSOLUTE) {
commandQueue.tempBasalAbsolute(0.0, durationInMinutes, true, profile!!, PumpSync.TemporaryBasalType.EMULATED_PUMP_SUSPEND, object : Callback() { commandQueue.tempBasalAbsolute(0.0, durationInMinutes, true, profile, PumpSync.TemporaryBasalType.EMULATED_PUMP_SUSPEND, object : Callback() {
override fun run() { override fun run() {
if (!result.success) { if (!result.success) {
ErrorHelperActivity.runAlarm(context, result.comment, resourceHelper.gs(R.string.tempbasaldeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror) ErrorHelperActivity.runAlarm(context, result.comment, resourceHelper.gs(R.string.tempbasaldeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror)
@ -654,7 +618,7 @@ open class LoopPlugin @Inject constructor(
} }
}) })
} else { } else {
commandQueue.tempBasalPercent(0, durationInMinutes, true, profile!!, PumpSync.TemporaryBasalType.EMULATED_PUMP_SUSPEND, object : Callback() { commandQueue.tempBasalPercent(0, durationInMinutes, true, profile, PumpSync.TemporaryBasalType.EMULATED_PUMP_SUSPEND, object : Callback() {
override fun run() { override fun run() {
if (!result.success) { if (!result.success) {
ErrorHelperActivity.runAlarm(context, result.comment, resourceHelper.gs(R.string.tempbasaldeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror) ErrorHelperActivity.runAlarm(context, result.comment, resourceHelper.gs(R.string.tempbasaldeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror)
@ -671,11 +635,16 @@ open class LoopPlugin @Inject constructor(
} }
}) })
} }
createOfflineEvent(durationInMinutes)
} }
override fun suspendLoop(durationInMinutes: Int) { override fun suspendLoop(durationInMinutes: Int) {
suspendTo(System.currentTimeMillis() + durationInMinutes * 60 * 1000) disposable += repository.runTransactionForResult(InsertAndCancelCurrentOfflineEventTransaction(dateUtil.now(), T.mins(durationInMinutes.toLong()).msecs(), OfflineEvent.Reason.SUSPEND))
.subscribe({ result ->
result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") }
result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted OfflineEvent $it") }
}, {
aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it)
})
commandQueue.cancelTempBasal(true, object : Callback() { commandQueue.cancelTempBasal(true, object : Callback() {
override fun run() { override fun run() {
if (!result.success) { if (!result.success) {
@ -683,20 +652,6 @@ open class LoopPlugin @Inject constructor(
} }
} }
}) })
createOfflineEvent(durationInMinutes)
}
override fun createOfflineEvent(durationInMinutes: Int) {
disposable += repository.runTransactionForResult(InsertIfNewByTimestampTherapyEventTransaction(
timestamp = dateUtil.now(),
type = TherapyEvent.Type.APS_OFFLINE,
duration = T.mins(durationInMinutes.toLong()).msecs(),
enteredBy = "openaps://" + "AndroidAPS",
glucoseUnit = TherapyEvent.GlucoseUnit.MGDL
)).subscribe(
{ result -> result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted therapy event $it") } },
{ aapsLogger.error(LTag.DATABASE, "Error while saving therapy event", it) }
)
} }
companion object { companion object {

View file

@ -39,6 +39,7 @@ class DataSyncSelectorImplementation @Inject constructor(
processChangedFoodsCompat() processChangedFoodsCompat()
processChangedTherapyEventsCompat() processChangedTherapyEventsCompat()
processChangedDeviceStatusesCompat() processChangedDeviceStatusesCompat()
processChangedOfflineEventsCompat()
processChangedProfileStore() processChangedProfileStore()
} }
} }
@ -55,6 +56,7 @@ class DataSyncSelectorImplementation @Inject constructor(
sp.remove(R.string.key_ns_extended_bolus_last_synced_id) sp.remove(R.string.key_ns_extended_bolus_last_synced_id)
sp.remove(R.string.key_ns_therapy_event_last_synced_id) sp.remove(R.string.key_ns_therapy_event_last_synced_id)
sp.remove(R.string.key_ns_profile_switch_last_synced_id) sp.remove(R.string.key_ns_profile_switch_last_synced_id)
sp.remove(R.string.key_ns_offline_event_last_synced_id)
sp.remove(R.string.key_ns_profile_store_last_synced_timestamp) sp.remove(R.string.key_ns_profile_store_last_synced_timestamp)
} }
@ -553,6 +555,49 @@ class DataSyncSelectorImplementation @Inject constructor(
return false return false
} }
override fun confirmLastOfflineEventIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_offline_event_last_synced_id, 0)) {
aapsLogger.debug(LTag.NSCLIENT, "Setting OfflineEvent data sync from $lastSynced")
sp.putLong(R.string.key_ns_offline_event_last_synced_id, lastSynced)
}
}
// Prepared for v3 (returns all modified after)
override fun changedOfflineEvents(): List<OfflineEvent> {
val startId = sp.getLong(R.string.key_ns_offline_event_last_synced_id, 0)
return appRepository.getModifiedOfflineEventsDataFromId(startId).blockingGet().also {
aapsLogger.debug(LTag.NSCLIENT, "Loading OfflineEvent data for sync from $startId. Records ${it.size}")
}
}
private var lastOeId = -1L
private var lastOeTime = -1L
override fun processChangedOfflineEventsCompat(): Boolean {
val lastDbIdWrapped = appRepository.getLastOfflineEventIdWrapped().blockingGet()
val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L
var startId = sp.getLong(R.string.key_ns_offline_event_last_synced_id, 0)
if (startId > lastDbId) {
sp.putLong(R.string.key_ns_offline_event_last_synced_id, 0)
startId = 0
}
if (startId == lastOeId && dateUtil.now() - lastOeTime < 5000) return false
lastOeId = startId
lastOeTime = dateUtil.now()
appRepository.getNextSyncElementOfflineEvent(startId).blockingGet()?.let { oe ->
aapsLogger.info(LTag.DATABASE, "Loading OfflineEvent data Start: $startId ID: ${oe.first.id} HistoryID: ${oe.second} ")
when {
// without nsId = create new
oe.first.interfaceIDs.nightscoutId == null ->
nsClientPlugin.nsClientService?.dbAdd("treatments", oe.first.toJson(true, dateUtil), DataSyncSelector.PairOfflineEvent(oe.first, oe.second), "$startId/$lastDbId")
// existing with nsId = update
oe.first.interfaceIDs.nightscoutId != null ->
nsClientPlugin.nsClientService?.dbUpdate("treatments", oe.first.interfaceIDs.nightscoutId, oe.first.toJson(false, dateUtil), DataSyncSelector.PairOfflineEvent(oe.first, oe.second), "$startId/$lastDbId")
}
return true
}
return false
}
override fun confirmLastProfileStore(lastSynced: Long) { override fun confirmLastProfileStore(lastSynced: Long) {
sp.putLong(R.string.key_ns_profile_store_last_synced_timestamp, lastSynced) sp.putLong(R.string.key_ns_profile_store_last_synced_timestamp, lastSynced)
} }

View file

@ -251,6 +251,26 @@ class NSClientAddAckWorker(
dataSyncSelector.confirmLastProfileStore(ack.originalObject.timestampSync) dataSyncSelector.confirmLastProfileStore(ack.originalObject.timestampSync)
rxBus.send(EventNSClientNewLog("DBADD", "Acked ProfileStore " + ack.id)) rxBus.send(EventNSClientNewLog("DBADD", "Acked ProfileStore " + ack.id))
} }
is PairOfflineEvent -> {
val pair = ack.originalObject
pair.value.interfaceIDs.nightscoutId = ack.id
repository.runTransactionForResult(UpdateNsIdOfflineEventTransaction(pair.value))
.doOnError { error ->
aapsLogger.error(LTag.DATABASE, "Updated ns id of OfflineEvent failed", error)
ret = Result.failure((workDataOf("Error" to error.toString())))
}
.doOnSuccess {
ret = Result.success(workDataOf("ProcessedData" to pair.toString()))
aapsLogger.debug(LTag.DATABASE, "Updated ns id of OfflineEvent " + pair.value)
dataSyncSelector.confirmLastOfflineEventIdIfGreater(pair.updateRecordId)
}
.blockingGet()
rxBus.send(EventNSClientNewLog("DBADD", "Acked OfflineEvent " + pair.value.interfaceIDs.nightscoutId))
// Send new if waiting
dataSyncSelector.processChangedOfflineEventsCompat()
}
} }
return ret return ret
} }

View file

@ -26,7 +26,6 @@ import info.nightscout.androidaps.receivers.DataWorker
import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.JsonHelper import info.nightscout.androidaps.utils.JsonHelper
import info.nightscout.androidaps.utils.JsonHelper.safeGetLong import info.nightscout.androidaps.utils.JsonHelper.safeGetLong
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.buildHelper.BuildHelper import info.nightscout.androidaps.utils.buildHelper.BuildHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP import info.nightscout.androidaps.utils.sharedPreferences.SP
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
@ -194,7 +193,6 @@ class NSClientAddUpdateWorker(
eventType == TherapyEvent.Type.ANNOUNCEMENT.text || eventType == TherapyEvent.Type.ANNOUNCEMENT.text ||
eventType == TherapyEvent.Type.QUESTION.text || eventType == TherapyEvent.Type.QUESTION.text ||
eventType == TherapyEvent.Type.EXERCISE.text || eventType == TherapyEvent.Type.EXERCISE.text ||
eventType == TherapyEvent.Type.APS_OFFLINE.text ||
eventType == TherapyEvent.Type.PUMP_BATTERY_CHANGE.text -> eventType == TherapyEvent.Type.PUMP_BATTERY_CHANGE.text ->
if (sp.getBoolean(R.string.key_ns_receive_therapy_events, false) || config.NSCLIENT) { if (sp.getBoolean(R.string.key_ns_receive_therapy_events, false) || config.NSCLIENT) {
therapyEventFromJson(json)?.let { therapyEvent -> therapyEventFromJson(json)?.let { therapyEvent ->
@ -341,6 +339,43 @@ class NSClientAddUpdateWorker(
} }
} ?: aapsLogger.error("Error parsing ProfileSwitch json $json") } ?: aapsLogger.error("Error parsing ProfileSwitch json $json")
} }
eventType == TherapyEvent.Type.APS_OFFLINE.text ->
if (sp.getBoolean(R.string.key_ns_receive_offline_event, false) && buildHelper.isEngineeringMode() || config.NSCLIENT) {
offlineEventFromJson(json)?.let { offlineEvent ->
repository.runTransactionForResult(SyncNsOfflineEventTransaction(offlineEvent, invalidateByNsOnly = false))
.doOnError {
aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it)
ret = Result.failure(workDataOf("Error" to it.toString()))
}
.blockingGet()
.also { result ->
result.inserted.forEach { oe ->
uel.log(Action.LOOP_CHANGE, Sources.NSClient,
ValueWithUnit.OfflineEventReason(oe.reason),
ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(oe.duration).toInt())
)
aapsLogger.debug(LTag.DATABASE, "Inserted OfflineEvent $oe")
}
result.invalidated.forEach { oe ->
uel.log(Action.LOOP_REMOVED, Sources.NSClient,
ValueWithUnit.OfflineEventReason(oe.reason),
ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(oe.duration).toInt())
)
aapsLogger.debug(LTag.DATABASE, "Invalidated OfflineEvent $oe")
}
result.ended.forEach { oe ->
uel.log(Action.LOOP_CHANGE, Sources.NSClient,
ValueWithUnit.OfflineEventReason(oe.reason),
ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(oe.duration).toInt())
)
aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $oe")
}
result.updatedNsId.forEach {
aapsLogger.debug(LTag.DATABASE, "Updated nsId OfflineEvent $it")
}
}
} ?: aapsLogger.error("Error parsing OfflineEvent json $json")
}
} }
if (sp.getBoolean(R.string.key_ns_receive_therapy_events, false) || config.NSCLIENT) if (sp.getBoolean(R.string.key_ns_receive_therapy_events, false) || config.NSCLIENT)
if (eventType == TherapyEvent.Type.ANNOUNCEMENT.text) { if (eventType == TherapyEvent.Type.ANNOUNCEMENT.text) {

View file

@ -125,6 +125,15 @@ class NSClientUpdateRemoveAckWorker(
dataSyncSelector.processChangedProfileSwitchesCompat() dataSyncSelector.processChangedProfileSwitchesCompat()
ret = Result.success(workDataOf("ProcessedData" to pair.toString())) ret = Result.success(workDataOf("ProcessedData" to pair.toString()))
} }
is PairOfflineEvent -> {
val pair = ack.originalObject
dataSyncSelector.confirmLastOfflineEventIdIfGreater(pair.updateRecordId)
rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked OfflineEvent" + ack._id))
// Send new if waiting
dataSyncSelector.processChangedOfflineEventsCompat()
ret = Result.success(workDataOf("ProcessedData" to pair.toString()))
}
} }
return ret return ret
} }

View file

@ -10,11 +10,7 @@ import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.IobTotal import info.nightscout.androidaps.data.IobTotal
import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.ValueWrapper import info.nightscout.androidaps.database.ValueWrapper
import info.nightscout.androidaps.database.entities.Bolus import info.nightscout.androidaps.database.entities.*
import info.nightscout.androidaps.database.entities.ExtendedBolus
import info.nightscout.androidaps.database.entities.GlucoseValue
import info.nightscout.androidaps.database.entities.TemporaryBasal
import info.nightscout.androidaps.database.entities.TemporaryTarget
import info.nightscout.androidaps.extensions.* 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
@ -533,6 +529,11 @@ class OverviewData @Inject constructor(
.map { EffectiveProfileSwitchDataPoint(it) } .map { EffectiveProfileSwitchDataPoint(it) }
.forEach(filteredTreatments::add) .forEach(filteredTreatments::add)
// OfflineEvent
repository.getOfflineEventDataFromTimeToTime(fromTime, endTime, true).blockingGet()
.map { TherapyEventDataPoint(TherapyEvent(timestamp = it.timestamp, duration = it.duration, type = TherapyEvent.Type.APS_OFFLINE, glucoseUnit = TherapyEvent.GlucoseUnit.MMOL), resourceHelper, profileFunction, translator) }
.forEach(filteredTreatments::add)
// Extended bolus // Extended bolus
if (!activePlugin.activePump.isFakingTempsByExtendedBoluses) { if (!activePlugin.activePump.isFakingTempsByExtendedBoluses) {
repository.getExtendedBolusDataFromTimeToTime(fromTime, endTime, true).blockingGet() repository.getExtendedBolusDataFromTimeToTime(fromTime, endTime, true).blockingGet()

View file

@ -139,9 +139,9 @@ class OverviewPlugin @Inject constructor(
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
.subscribe({ overviewData.preparePredictions("EventLoopInvoked") }, fabricPrivacy::logException) .subscribe({ overviewData.preparePredictions("EventLoopInvoked") }, fabricPrivacy::logException)
disposable.add(rxBus disposable.add(rxBus
.toObservable(EventProfileSwitchChanged::class.java) .toObservable(EventNewBasalProfile::class.java)
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
.subscribe({ loadProfile("EventProfileSwitchChanged") }, fabricPrivacy::logException)) .subscribe({ loadProfile("EventNewBasalProfile") }, fabricPrivacy::logException))
disposable.add(rxBus disposable.add(rxBus
.toObservable(EventAutosensCalculationFinished::class.java) .toObservable(EventAutosensCalculationFinished::class.java)
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)

View file

@ -11,25 +11,26 @@ import androidx.work.Worker
import androidx.work.WorkerParameters import androidx.work.WorkerParameters
import androidx.work.workDataOf import androidx.work.workDataOf
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
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.data.DetailedBolusInfo import info.nightscout.androidaps.data.DetailedBolusInfo
import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.ValueWithUnit import info.nightscout.androidaps.database.entities.OfflineEvent
import info.nightscout.androidaps.database.entities.TemporaryTarget 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.entities.ValueWithUnit
import info.nightscout.androidaps.database.transactions.CancelCurrentOfflineEventIfAnyTransaction
import info.nightscout.androidaps.database.transactions.CancelCurrentTemporaryTargetIfAnyTransaction import info.nightscout.androidaps.database.transactions.CancelCurrentTemporaryTargetIfAnyTransaction
import info.nightscout.androidaps.database.transactions.InsertTemporaryTargetAndCancelCurrentTransaction import info.nightscout.androidaps.database.transactions.InsertAndCancelCurrentOfflineEventTransaction
import info.nightscout.androidaps.database.transactions.InsertAndCancelCurrentTemporaryTargetTransaction
import info.nightscout.androidaps.events.EventPreferenceChange import info.nightscout.androidaps.events.EventPreferenceChange
import info.nightscout.androidaps.events.EventRefreshOverview import info.nightscout.androidaps.events.EventRefreshOverview
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.LTag import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin
import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart
@ -41,7 +42,6 @@ import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatusProv
import info.nightscout.androidaps.queue.Callback import info.nightscout.androidaps.queue.Callback
import info.nightscout.androidaps.receivers.DataWorker import info.nightscout.androidaps.receivers.DataWorker
import info.nightscout.androidaps.utils.* import info.nightscout.androidaps.utils.*
import info.nightscout.androidaps.extensions.valueToUnitsString
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
@ -72,7 +72,7 @@ class SmsCommunicatorPlugin @Inject constructor(
private val fabricPrivacy: FabricPrivacy, private val fabricPrivacy: FabricPrivacy,
private val activePlugin: ActivePlugin, private val activePlugin: ActivePlugin,
private val commandQueue: CommandQueueProvider, private val commandQueue: CommandQueueProvider,
private val loopPlugin: LoopPlugin, private val loop: Loop,
private val iobCobCalculator: IobCobCalculator, private val iobCobCalculator: IobCobCalculator,
private val xdripCalibrations: XdripCalibrations, private val xdripCalibrations: XdripCalibrations,
private var otp: OneTimePassword, private var otp: OneTimePassword,
@ -340,14 +340,14 @@ class SmsCommunicatorPlugin @Inject constructor(
private fun processLOOP(divided: Array<String>, receivedSms: Sms) { private fun processLOOP(divided: Array<String>, receivedSms: Sms) {
when (divided[1].uppercase(Locale.getDefault())) { when (divided[1].uppercase(Locale.getDefault())) {
"DISABLE", "STOP" -> { "DISABLE", "STOP" -> {
if (loopPlugin.isEnabled(PluginType.LOOP)) { if (loop.enabled) {
val passCode = generatePassCode() val passCode = generatePassCode()
val reply = String.format(resourceHelper.gs(R.string.smscommunicator_loopdisablereplywithcode), passCode) val reply = String.format(resourceHelper.gs(R.string.smscommunicator_loopdisablereplywithcode), passCode)
receivedSms.processed = true receivedSms.processed = true
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction() { messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction() {
override fun run() { override fun run() {
uel.log(Action.LOOP_DISABLED, Sources.SMS) uel.log(Action.LOOP_DISABLED, Sources.SMS)
loopPlugin.setPluginEnabled(PluginType.LOOP, false) loop.enabled = false
commandQueue.cancelTempBasal(true, object : Callback() { commandQueue.cancelTempBasal(true, object : Callback() {
override fun run() { override fun run() {
rxBus.send(EventRefreshOverview("SMS_LOOP_STOP")) rxBus.send(EventRefreshOverview("SMS_LOOP_STOP"))
@ -364,14 +364,14 @@ class SmsCommunicatorPlugin @Inject constructor(
} }
"ENABLE", "START" -> { "ENABLE", "START" -> {
if (!loopPlugin.isEnabled(PluginType.LOOP)) { if (!loop.enabled) {
val passCode = generatePassCode() val passCode = generatePassCode()
val reply = String.format(resourceHelper.gs(R.string.smscommunicator_loopenablereplywithcode), passCode) val reply = String.format(resourceHelper.gs(R.string.smscommunicator_loopenablereplywithcode), passCode)
receivedSms.processed = true receivedSms.processed = true
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction() { messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction() {
override fun run() { override fun run() {
uel.log(Action.LOOP_ENABLED, Sources.SMS) uel.log(Action.LOOP_ENABLED, Sources.SMS)
loopPlugin.setPluginEnabled(PluginType.LOOP, true) loop.enabled= true
sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.smscommunicator_loophasbeenenabled))) sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.smscommunicator_loophasbeenenabled)))
rxBus.send(EventRefreshOverview("SMS_LOOP_START")) rxBus.send(EventRefreshOverview("SMS_LOOP_START"))
} }
@ -382,8 +382,8 @@ class SmsCommunicatorPlugin @Inject constructor(
} }
"STATUS" -> { "STATUS" -> {
val reply = if (loopPlugin.isEnabled(PluginType.LOOP)) { val reply = if (loop.enabled) {
if (loopPlugin.isSuspended) String.format(resourceHelper.gs(R.string.loopsuspendedfor), loopPlugin.minutesToEndOfSuspend()) if (loop.isSuspended) String.format(resourceHelper.gs(R.string.loopsuspendedfor), loop.minutesToEndOfSuspend())
else resourceHelper.gs(R.string.smscommunicator_loopisenabled) else resourceHelper.gs(R.string.smscommunicator_loopisenabled)
} else } else
resourceHelper.gs(R.string.loopisdisabled) resourceHelper.gs(R.string.loopisdisabled)
@ -398,7 +398,12 @@ class SmsCommunicatorPlugin @Inject constructor(
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction() { messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction() {
override fun run() { override fun run() {
uel.log(Action.RESUME, Sources.SMS) uel.log(Action.RESUME, Sources.SMS)
loopPlugin.suspendTo(0L) disposable += repository.runTransactionForResult(CancelCurrentOfflineEventIfAnyTransaction(dateUtil.now()))
.subscribe({ result ->
result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") }
}, {
aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it)
})
rxBus.send(EventRefreshOverview("SMS_LOOP_RESUME")) rxBus.send(EventRefreshOverview("SMS_LOOP_RESUME"))
commandQueue.cancelTempBasal(true, object : Callback() { commandQueue.cancelTempBasal(true, object : Callback() {
override fun run() { override fun run() {
@ -409,7 +414,6 @@ class SmsCommunicatorPlugin @Inject constructor(
} }
} }
}) })
loopPlugin.createOfflineEvent(0)
sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.smscommunicator_loopresumed))) sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.smscommunicator_loopresumed)))
} }
}) })
@ -434,8 +438,13 @@ class SmsCommunicatorPlugin @Inject constructor(
commandQueue.cancelTempBasal(true, object : Callback() { commandQueue.cancelTempBasal(true, object : Callback() {
override fun run() { override fun run() {
if (result.success) { if (result.success) {
loopPlugin.suspendTo(dateUtil.now() + anInteger() * 60L * 1000) disposable += repository.runTransactionForResult(InsertAndCancelCurrentOfflineEventTransaction(dateUtil.now(), T.mins(anInteger().toLong()).msecs(), OfflineEvent.Reason.SUSPEND))
loopPlugin.createOfflineEvent(anInteger() * 60) .subscribe({ result ->
result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") }
result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted OfflineEvent $it") }
}, {
aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it)
})
rxBus.send(EventRefreshOverview("SMS_LOOP_SUSPENDED")) rxBus.send(EventRefreshOverview("SMS_LOOP_SUSPENDED"))
val replyText = resourceHelper.gs(R.string.smscommunicator_loopsuspended) + " " + val replyText = resourceHelper.gs(R.string.smscommunicator_loopsuspended) + " " +
resourceHelper.gs(if (result.success) R.string.smscommunicator_tempbasalcanceled else R.string.smscommunicator_tempbasalcancelfailed) resourceHelper.gs(if (result.success) R.string.smscommunicator_tempbasalcanceled else R.string.smscommunicator_tempbasalcancelfailed)
@ -512,10 +521,14 @@ class SmsCommunicatorPlugin @Inject constructor(
if (!result.success) { if (!result.success) {
sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.smscommunicator_pumpconnectfail))) sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.smscommunicator_pumpconnectfail)))
} else { } else {
loopPlugin.suspendTo(0L) disposable += repository.runTransactionForResult(CancelCurrentOfflineEventIfAnyTransaction(dateUtil.now()))
.subscribe({ result ->
result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") }
}, {
aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it)
})
sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.smscommunicator_reconnect))) sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.smscommunicator_reconnect)))
rxBus.send(EventRefreshOverview("SMS_PUMP_START")) rxBus.send(EventRefreshOverview("SMS_PUMP_START"))
loopPlugin.createOfflineEvent(0)
} }
} }
}) })
@ -536,8 +549,8 @@ class SmsCommunicatorPlugin @Inject constructor(
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction() { messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction() {
override fun run() { override fun run() {
uel.log(Action.DISCONNECT, Sources.SMS) uel.log(Action.DISCONNECT, Sources.SMS)
val profile = profileFunction.getProfile() val profile = profileFunction.getProfile() ?: return
loopPlugin.disconnectPump(duration, profile) loop.goToZeroTemp(duration, profile, OfflineEvent.Reason.DISCONNECT_PUMP)
rxBus.send(EventRefreshOverview("SMS_PUMP_DISCONNECT")) rxBus.send(EventRefreshOverview("SMS_PUMP_DISCONNECT"))
sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.smscommunicator_pumpdisconnected))) sendSMS(Sms(receivedSms.phoneNumber, resourceHelper.gs(R.string.smscommunicator_pumpdisconnected)))
} }
@ -832,7 +845,7 @@ class SmsCommunicatorPlugin @Inject constructor(
currentProfile.units == GlucoseUnit.MMOL -> Constants.defaultEatingSoonTTmmol currentProfile.units == GlucoseUnit.MMOL -> Constants.defaultEatingSoonTTmmol
else -> Constants.defaultEatingSoonTTmgdl else -> Constants.defaultEatingSoonTTmgdl
} }
disposable += repository.runTransactionForResult(InsertTemporaryTargetAndCancelCurrentTransaction( disposable += repository.runTransactionForResult(InsertAndCancelCurrentTemporaryTargetTransaction(
timestamp = dateUtil.now(), timestamp = dateUtil.now(),
duration = TimeUnit.MINUTES.toMillis(eatingSoonTTDuration.toLong()), duration = TimeUnit.MINUTES.toMillis(eatingSoonTTDuration.toLong()),
reason = TemporaryTarget.Reason.EATING_SOON, reason = TemporaryTarget.Reason.EATING_SOON,
@ -979,7 +992,7 @@ class SmsCommunicatorPlugin @Inject constructor(
var tt = sp.getDouble(keyTarget, if (units == GlucoseUnit.MMOL) defaultTargetMMOL else defaultTargetMGDL) var tt = sp.getDouble(keyTarget, if (units == GlucoseUnit.MMOL) defaultTargetMMOL else defaultTargetMGDL)
tt = Profile.toCurrentUnits(profileFunction, tt) tt = Profile.toCurrentUnits(profileFunction, tt)
tt = if (tt > 0) tt else if (units == GlucoseUnit.MMOL) defaultTargetMMOL else defaultTargetMGDL tt = if (tt > 0) tt else if (units == GlucoseUnit.MMOL) defaultTargetMMOL else defaultTargetMGDL
disposable += repository.runTransactionForResult(InsertTemporaryTargetAndCancelCurrentTransaction( disposable += repository.runTransactionForResult(InsertAndCancelCurrentTemporaryTargetTransaction(
timestamp = dateUtil.now(), timestamp = dateUtil.now(),
duration = TimeUnit.MINUTES.toMillis(ttDuration.toLong()), duration = TimeUnit.MINUTES.toMillis(ttDuration.toLong()),
reason = TemporaryTarget.Reason.EATING_SOON, reason = TemporaryTarget.Reason.EATING_SOON,

View file

@ -20,7 +20,7 @@ import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.database.entities.ValueWithUnit import info.nightscout.androidaps.database.entities.ValueWithUnit
import info.nightscout.androidaps.database.interfaces.end import info.nightscout.androidaps.database.interfaces.end
import info.nightscout.androidaps.database.transactions.CancelCurrentTemporaryTargetIfAnyTransaction import info.nightscout.androidaps.database.transactions.CancelCurrentTemporaryTargetIfAnyTransaction
import info.nightscout.androidaps.database.transactions.InsertTemporaryTargetAndCancelCurrentTransaction import info.nightscout.androidaps.database.transactions.InsertAndCancelCurrentTemporaryTargetTransaction
import info.nightscout.androidaps.extensions.total import info.nightscout.androidaps.extensions.total
import info.nightscout.androidaps.extensions.valueToUnits import info.nightscout.androidaps.extensions.valueToUnits
import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.interfaces.*
@ -572,7 +572,7 @@ class ActionStringHandler @Inject constructor(
private fun generateTempTarget(duration: Int, low: Double, high: Double) { private fun generateTempTarget(duration: Int, low: Double, high: Double) {
if (duration != 0) { if (duration != 0) {
disposable += repository.runTransactionForResult(InsertTemporaryTargetAndCancelCurrentTransaction( disposable += repository.runTransactionForResult(InsertAndCancelCurrentTemporaryTargetTransaction(
timestamp = System.currentTimeMillis(), timestamp = System.currentTimeMillis(),
duration = TimeUnit.MINUTES.toMillis(duration.toLong()), duration = TimeUnit.MINUTES.toMillis(duration.toLong()),
reason = TemporaryTarget.Reason.WEAR, reason = TemporaryTarget.Reason.WEAR,

View file

@ -86,7 +86,6 @@ open class CommandQueue @Inject constructor(
ErrorHelperActivity.runAlarm(context, result.comment, resourceHelper.gs(R.string.failedupdatebasalprofile), R.raw.boluserror) ErrorHelperActivity.runAlarm(context, result.comment, resourceHelper.gs(R.string.failedupdatebasalprofile), R.raw.boluserror)
} }
if (result.enacted) { if (result.enacted) {
rxBus.send(EventNewBasalProfile())
repository.createEffectiveProfileSwitch( repository.createEffectiveProfileSwitch(
EffectiveProfileSwitch( EffectiveProfileSwitch(
timestamp = dateUtil.now(), timestamp = dateUtil.now(),
@ -104,6 +103,7 @@ open class CommandQueue @Inject constructor(
insulinConfiguration = it.insulinConfiguration insulinConfiguration = it.insulinConfiguration
) )
) )
rxBus.send(EventNewBasalProfile())
} }
} }
}) })

View file

@ -12,6 +12,7 @@ import info.nightscout.androidaps.data.DetailedBolusInfo
import info.nightscout.androidaps.interfaces.Profile import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.BolusCalculatorResult import info.nightscout.androidaps.database.entities.BolusCalculatorResult
import info.nightscout.androidaps.database.entities.OfflineEvent
import info.nightscout.androidaps.database.entities.ValueWithUnit import info.nightscout.androidaps.database.entities.ValueWithUnit
import info.nightscout.androidaps.database.entities.TemporaryTarget import info.nightscout.androidaps.database.entities.TemporaryTarget
import info.nightscout.androidaps.database.entities.UserEntry.Action import info.nightscout.androidaps.database.entities.UserEntry.Action
@ -374,7 +375,7 @@ class BolusWizard @Inject constructor(
if (useSuperBolus) { if (useSuperBolus) {
uel.log(Action.SUPERBOLUS_TBR, Sources.WizardDialog) uel.log(Action.SUPERBOLUS_TBR, Sources.WizardDialog)
if (loopPlugin.isEnabled(PluginType.LOOP)) { if (loopPlugin.isEnabled(PluginType.LOOP)) {
loopPlugin.superBolusTo(System.currentTimeMillis() + 2 * 60L * 60 * 1000) loopPlugin.goToZeroTemp(2 * 60, profile, OfflineEvent.Reason.SUPER_BOLUS)
rxBus.send(EventRefreshOverview("WizardDialog")) rxBus.send(EventRefreshOverview("WizardDialog"))
} }
@ -408,7 +409,7 @@ class BolusWizard @Inject constructor(
context = ctx context = ctx
mgdlGlucose = Profile.toMgdl(bg, profile.units) mgdlGlucose = Profile.toMgdl(bg, profile.units)
glucoseType = DetailedBolusInfo.MeterType.MANUAL glucoseType = DetailedBolusInfo.MeterType.MANUAL
carbTime = this@BolusWizard.carbTime carbsTimestamp = dateUtil.now() + T.mins(this@BolusWizard.carbTime.toLong()).msecs()
bolusCalculatorResult = createBolusCalculatorResult() bolusCalculatorResult = createBolusCalculatorResult()
notes = this@BolusWizard.notes notes = this@BolusWizard.notes
if (insulin > 0 || carbs > 0) { if (insulin > 0 || carbs > 0) {

View file

@ -47,6 +47,7 @@
<string name="key_ns_temporary_basal_last_synced_id" translatable="false">ns_temporary_basal_last_synced_id</string> <string name="key_ns_temporary_basal_last_synced_id" translatable="false">ns_temporary_basal_last_synced_id</string>
<string name="key_ns_extended_bolus_last_synced_id" translatable="false">ns_extended_bolus_last_synced_id</string> <string name="key_ns_extended_bolus_last_synced_id" translatable="false">ns_extended_bolus_last_synced_id</string>
<string name="key_ns_profile_switch_last_synced_id" translatable="false">profile_switch_last_synced_id</string> <string name="key_ns_profile_switch_last_synced_id" translatable="false">profile_switch_last_synced_id</string>
<string name="key_ns_offline_event_last_synced_id" translatable="false">ns_offline_event_last_synced_id</string>
<string name="key_ns_profile_store_last_synced_timestamp" translatable="false">ns_profile_store_last_synced_timestamp</string> <string name="key_ns_profile_store_last_synced_timestamp" translatable="false">ns_profile_store_last_synced_timestamp</string>
<string name="key_local_profile_last_change" translatable="false">local_profile_last_change</string> <string name="key_local_profile_last_change" translatable="false">local_profile_last_change</string>
@ -1137,8 +1138,11 @@
<string name="ns_receive_temp_target">Receive temporary targets</string> <string name="ns_receive_temp_target">Receive temporary targets</string>
<string name="ns_receive_temp_target_summary">Accept temporary targets entered through NS or NSClient</string> <string name="ns_receive_temp_target_summary">Accept temporary targets entered through NS or NSClient</string>
<string name="key_ns_receive_profile_switch" translatable="false">ns_receive_profile_switch</string> <string name="key_ns_receive_profile_switch" translatable="false">ns_receive_profile_switch</string>
<string name="key_ns_receive_offline_event" translatable="false">ns_receive_offline_event</string>
<string name="ns_receive_profile_switch">Receive profile switches</string> <string name="ns_receive_profile_switch">Receive profile switches</string>
<string name="ns_receive_profile_switch_summary">Accept profile switches entered through NS or NSClient</string> <string name="ns_receive_profile_switch_summary">Accept profile switches entered through NS or NSClient</string>
<string name="ns_receive_offline_event">Receive APS offline events</string>
<string name="ns_receive_offline_event_summary">Accept APS Offline events entered through NS or NSClient</string>
<string name="key_ns_receive_insulin" translatable="false">ns_receive_insulin</string> <string name="key_ns_receive_insulin" translatable="false">ns_receive_insulin</string>
<string name="ns_receive_insulin">Receive insulin</string> <string name="ns_receive_insulin">Receive insulin</string>
<string name="ns_receive_insulin_summary">Accept insulin entered through NS or NSClient (it\'s not delivered, only calculated towards IOB)</string> <string name="ns_receive_insulin_summary">Accept insulin entered through NS or NSClient (it\'s not delivered, only calculated towards IOB)</string>

View file

@ -78,6 +78,12 @@
android:summary="@string/ns_receive_therapy_events_summary" android:summary="@string/ns_receive_therapy_events_summary"
android:title="@string/ns_receive_therapy_events" /> android:title="@string/ns_receive_therapy_events" />
<SwitchPreference
android:defaultValue="false"
android:key="@string/key_ns_receive_offline_event"
android:summary="@string/ns_receive_offline_event_summary"
android:title="@string/ns_receive_offline_event" />
</androidx.preference.PreferenceScreen>> </androidx.preference.PreferenceScreen>>
<androidx.preference.PreferenceScreen <androidx.preference.PreferenceScreen

View file

@ -5,7 +5,6 @@ package info.nightscout.androidaps.plugins.general.smsCommunicator
import android.telephony.SmsManager import android.telephony.SmsManager
import dagger.android.AndroidInjector import dagger.android.AndroidInjector
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.utils.buildHelper.ConfigImpl
import info.nightscout.androidaps.Constants import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.TestBaseWithProfile import info.nightscout.androidaps.TestBaseWithProfile
@ -13,7 +12,10 @@ import info.nightscout.androidaps.data.IobTotal
import info.nightscout.androidaps.data.PumpEnactResult import info.nightscout.androidaps.data.PumpEnactResult
import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.GlucoseValue import info.nightscout.androidaps.database.entities.GlucoseValue
import info.nightscout.androidaps.database.transactions.InsertTemporaryTargetAndCancelCurrentTransaction import info.nightscout.androidaps.database.transactions.CancelCurrentOfflineEventIfAnyTransaction
import info.nightscout.androidaps.database.transactions.InsertAndCancelCurrentOfflineEventTransaction
import info.nightscout.androidaps.database.transactions.InsertAndCancelCurrentTemporaryTargetTransaction
import info.nightscout.androidaps.database.transactions.Transaction
import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin
@ -26,13 +28,13 @@ import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatusProv
import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin
import info.nightscout.androidaps.plugins.pump.common.defs.PumpType import info.nightscout.androidaps.plugins.pump.common.defs.PumpType
import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin
import info.nightscout.androidaps.plugins.treatments.TreatmentService
import info.nightscout.androidaps.queue.Callback import info.nightscout.androidaps.queue.Callback
import info.nightscout.androidaps.queue.CommandQueue import info.nightscout.androidaps.queue.CommandQueue
import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.XdripCalibrations import info.nightscout.androidaps.utils.XdripCalibrations
import info.nightscout.androidaps.utils.buildHelper.ConfigImpl
import info.nightscout.androidaps.utils.sharedPreferences.SP import info.nightscout.androidaps.utils.sharedPreferences.SP
import io.reactivex.Single import io.reactivex.Single
import org.junit.Assert import org.junit.Assert
@ -45,7 +47,6 @@ import org.mockito.Mockito
import org.mockito.Mockito.`when` import org.mockito.Mockito.`when`
import org.mockito.Mockito.anyLong import org.mockito.Mockito.anyLong
import org.mockito.invocation.InvocationOnMock import org.mockito.invocation.InvocationOnMock
import org.mockito.stubbing.Answer
import org.powermock.api.mockito.PowerMockito import org.powermock.api.mockito.PowerMockito
import org.powermock.core.classloader.annotations.PrepareForTest import org.powermock.core.classloader.annotations.PrepareForTest
import org.powermock.modules.junit4.PowerMockRunner import org.powermock.modules.junit4.PowerMockRunner
@ -107,8 +108,8 @@ class SmsCommunicatorPluginTest : TestBaseWithProfile() {
`when`(sp.getString(R.string.key_smscommunicator_allowednumbers, "")).thenReturn("1234;5678") `when`(sp.getString(R.string.key_smscommunicator_allowednumbers, "")).thenReturn("1234;5678")
`when`( `when`(
repository.runTransactionForResult(anyObject<InsertTemporaryTargetAndCancelCurrentTransaction>()) repository.runTransactionForResult(anyObject<InsertAndCancelCurrentTemporaryTargetTransaction>())
).thenReturn(Single.just(InsertTemporaryTargetAndCancelCurrentTransaction.TransactionResult().apply { ).thenReturn(Single.just(InsertAndCancelCurrentTemporaryTargetTransaction.TransactionResult().apply {
})) }))
val glucoseStatusProvider = GlucoseStatusProvider(aapsLogger = aapsLogger, iobCobCalculator = iobCobCalculator, dateUtil = dateUtilMocked) val glucoseStatusProvider = GlucoseStatusProvider(aapsLogger = aapsLogger, iobCobCalculator = iobCobCalculator, dateUtil = dateUtilMocked)
@ -309,7 +310,7 @@ class SmsCommunicatorPluginTest : TestBaseWithProfile() {
`when`(sp.getBoolean(R.string.key_smscommunicator_remotecommandsallowed, false)).thenReturn(true) `when`(sp.getBoolean(R.string.key_smscommunicator_remotecommandsallowed, false)).thenReturn(true)
//LOOP STATUS : disabled //LOOP STATUS : disabled
PowerMockito.`when`(loopPlugin.isEnabled(PluginType.LOOP)).thenReturn(false) PowerMockito.`when`(loopPlugin.enabled).thenReturn(false)
smsCommunicatorPlugin.messages = ArrayList() smsCommunicatorPlugin.messages = ArrayList()
sms = Sms("1234", "LOOP STATUS") sms = Sms("1234", "LOOP STATUS")
smsCommunicatorPlugin.processSms(sms) smsCommunicatorPlugin.processSms(sms)
@ -318,7 +319,7 @@ class SmsCommunicatorPluginTest : TestBaseWithProfile() {
//LOOP STATUS : suspended //LOOP STATUS : suspended
PowerMockito.`when`(loopPlugin.minutesToEndOfSuspend()).thenReturn(10) PowerMockito.`when`(loopPlugin.minutesToEndOfSuspend()).thenReturn(10)
PowerMockito.`when`(loopPlugin.isEnabled(PluginType.LOOP)).thenReturn(true) PowerMockito.`when`(loopPlugin.enabled).thenReturn(true)
PowerMockito.`when`(loopPlugin.isSuspended).thenReturn(true) PowerMockito.`when`(loopPlugin.isSuspended).thenReturn(true)
smsCommunicatorPlugin.messages = ArrayList() smsCommunicatorPlugin.messages = ArrayList()
sms = Sms("1234", "LOOP STATUS") sms = Sms("1234", "LOOP STATUS")
@ -327,7 +328,7 @@ class SmsCommunicatorPluginTest : TestBaseWithProfile() {
Assert.assertEquals("Suspended (10 m)", smsCommunicatorPlugin.messages[1].text) Assert.assertEquals("Suspended (10 m)", smsCommunicatorPlugin.messages[1].text)
//LOOP STATUS : enabled //LOOP STATUS : enabled
PowerMockito.`when`(loopPlugin.isEnabled(PluginType.LOOP)).thenReturn(true) PowerMockito.`when`(loopPlugin.enabled).thenReturn(true)
PowerMockito.`when`(loopPlugin.isSuspended).thenReturn(false) PowerMockito.`when`(loopPlugin.isSuspended).thenReturn(false)
smsCommunicatorPlugin.messages = ArrayList() smsCommunicatorPlugin.messages = ArrayList()
sms = Sms("1234", "LOOP STATUS") sms = Sms("1234", "LOOP STATUS")
@ -337,7 +338,7 @@ class SmsCommunicatorPluginTest : TestBaseWithProfile() {
Assert.assertEquals("Loop is enabled", smsCommunicatorPlugin.messages[1].text) Assert.assertEquals("Loop is enabled", smsCommunicatorPlugin.messages[1].text)
//LOOP : wrong format //LOOP : wrong format
PowerMockito.`when`(loopPlugin.isEnabled(PluginType.LOOP)).thenReturn(true) PowerMockito.`when`(loopPlugin.enabled).thenReturn(true)
smsCommunicatorPlugin.messages = ArrayList() smsCommunicatorPlugin.messages = ArrayList()
sms = Sms("1234", "LOOP") sms = Sms("1234", "LOOP")
smsCommunicatorPlugin.processSms(sms) smsCommunicatorPlugin.processSms(sms)
@ -346,7 +347,7 @@ class SmsCommunicatorPluginTest : TestBaseWithProfile() {
Assert.assertEquals("Wrong format", smsCommunicatorPlugin.messages[1].text) Assert.assertEquals("Wrong format", smsCommunicatorPlugin.messages[1].text)
//LOOP DISABLE : already disabled //LOOP DISABLE : already disabled
PowerMockito.`when`(loopPlugin.isEnabled(PluginType.LOOP)).thenReturn(false) PowerMockito.`when`(loopPlugin.enabled).thenReturn(false)
smsCommunicatorPlugin.messages = ArrayList() smsCommunicatorPlugin.messages = ArrayList()
sms = Sms("1234", "LOOP DISABLE") sms = Sms("1234", "LOOP DISABLE")
smsCommunicatorPlugin.processSms(sms) smsCommunicatorPlugin.processSms(sms)
@ -356,11 +357,11 @@ class SmsCommunicatorPluginTest : TestBaseWithProfile() {
//LOOP DISABLE : from enabled //LOOP DISABLE : from enabled
hasBeenRun = false hasBeenRun = false
PowerMockito.`when`(loopPlugin.isEnabled(PluginType.LOOP)).thenReturn(true) PowerMockito.`when`(loopPlugin.enabled).thenReturn(true)
PowerMockito.doAnswer(Answer { // PowerMockito.doAnswer(Answer {
hasBeenRun = true // hasBeenRun = true
null // null
} as Answer<*>).`when`(loopPlugin).setPluginEnabled(PluginType.LOOP, false) // } as Answer<*>).`when`(loopPlugin).setPluginEnabled(PluginType.LOOP, false)
smsCommunicatorPlugin.messages = ArrayList() smsCommunicatorPlugin.messages = ArrayList()
sms = Sms("1234", "LOOP DISABLE") sms = Sms("1234", "LOOP DISABLE")
smsCommunicatorPlugin.processSms(sms) smsCommunicatorPlugin.processSms(sms)
@ -371,10 +372,10 @@ class SmsCommunicatorPluginTest : TestBaseWithProfile() {
smsCommunicatorPlugin.processSms(Sms("1234", passCode)) smsCommunicatorPlugin.processSms(Sms("1234", passCode))
Assert.assertEquals(passCode, smsCommunicatorPlugin.messages[2].text) Assert.assertEquals(passCode, smsCommunicatorPlugin.messages[2].text)
Assert.assertEquals("Loop has been disabled Temp basal canceled", smsCommunicatorPlugin.messages[3].text) Assert.assertEquals("Loop has been disabled Temp basal canceled", smsCommunicatorPlugin.messages[3].text)
Assert.assertTrue(hasBeenRun) //Assert.assertTrue(hasBeenRun)
//LOOP ENABLE : already enabled //LOOP ENABLE : already enabled
PowerMockito.`when`(loopPlugin.isEnabled(PluginType.LOOP)).thenReturn(true) PowerMockito.`when`(loopPlugin.enabled).thenReturn(true)
smsCommunicatorPlugin.messages = ArrayList() smsCommunicatorPlugin.messages = ArrayList()
sms = Sms("1234", "LOOP ENABLE") sms = Sms("1234", "LOOP ENABLE")
smsCommunicatorPlugin.processSms(sms) smsCommunicatorPlugin.processSms(sms)
@ -384,11 +385,11 @@ class SmsCommunicatorPluginTest : TestBaseWithProfile() {
//LOOP ENABLE : from disabled //LOOP ENABLE : from disabled
hasBeenRun = false hasBeenRun = false
PowerMockito.`when`(loopPlugin.isEnabled(PluginType.LOOP)).thenReturn(false) PowerMockito.`when`(loopPlugin.enabled).thenReturn(false)
PowerMockito.doAnswer(Answer { // PowerMockito.doAnswer(Answer {
hasBeenRun = true // hasBeenRun = true
null // null
} as Answer<*>).`when`(loopPlugin).setPluginEnabled(PluginType.LOOP, true) // } as Answer<*>).`when`(loopPlugin).setPluginEnabled(PluginType.LOOP, true)
smsCommunicatorPlugin.messages = ArrayList() smsCommunicatorPlugin.messages = ArrayList()
sms = Sms("1234", "LOOP ENABLE") sms = Sms("1234", "LOOP ENABLE")
smsCommunicatorPlugin.processSms(sms) smsCommunicatorPlugin.processSms(sms)
@ -399,9 +400,13 @@ class SmsCommunicatorPluginTest : TestBaseWithProfile() {
smsCommunicatorPlugin.processSms(Sms("1234", passCode)) smsCommunicatorPlugin.processSms(Sms("1234", passCode))
Assert.assertEquals(passCode, smsCommunicatorPlugin.messages[2].text) Assert.assertEquals(passCode, smsCommunicatorPlugin.messages[2].text)
Assert.assertEquals("Loop has been enabled", smsCommunicatorPlugin.messages[3].text) Assert.assertEquals("Loop has been enabled", smsCommunicatorPlugin.messages[3].text)
Assert.assertTrue(hasBeenRun) //Assert.assertTrue(hasBeenRun)
//LOOP RESUME : already enabled //LOOP RESUME : already enabled
`when`(
repository.runTransactionForResult(anyObject<Transaction<CancelCurrentOfflineEventIfAnyTransaction.TransactionResult>>())
).thenReturn(Single.just(CancelCurrentOfflineEventIfAnyTransaction.TransactionResult().apply {
}))
smsCommunicatorPlugin.messages = ArrayList() smsCommunicatorPlugin.messages = ArrayList()
sms = Sms("1234", "LOOP RESUME") sms = Sms("1234", "LOOP RESUME")
smsCommunicatorPlugin.processSms(sms) smsCommunicatorPlugin.processSms(sms)
@ -430,6 +435,10 @@ class SmsCommunicatorPluginTest : TestBaseWithProfile() {
Assert.assertEquals("Wrong duration", smsCommunicatorPlugin.messages[1].text) Assert.assertEquals("Wrong duration", smsCommunicatorPlugin.messages[1].text)
//LOOP SUSPEND 100 : suspend for 100 min + correct answer //LOOP SUSPEND 100 : suspend for 100 min + correct answer
`when`(
repository.runTransactionForResult(anyObject<Transaction<InsertAndCancelCurrentOfflineEventTransaction.TransactionResult>>())
).thenReturn(Single.just(InsertAndCancelCurrentOfflineEventTransaction.TransactionResult().apply {
}))
smsCommunicatorPlugin.messages = ArrayList() smsCommunicatorPlugin.messages = ArrayList()
sms = Sms("1234", "LOOP SUSPEND 100") sms = Sms("1234", "LOOP SUSPEND 100")
smsCommunicatorPlugin.processSms(sms) smsCommunicatorPlugin.processSms(sms)
@ -523,7 +532,11 @@ class SmsCommunicatorPluginTest : TestBaseWithProfile() {
Assert.assertEquals("Wrong format", smsCommunicatorPlugin.messages[1].text) Assert.assertEquals("Wrong format", smsCommunicatorPlugin.messages[1].text)
//PUMP CONNECT //PUMP CONNECT
PowerMockito.`when`(loopPlugin.isEnabled(PluginType.LOOP)).thenReturn(true) `when`(
repository.runTransactionForResult(anyObject<Transaction<CancelCurrentOfflineEventIfAnyTransaction.TransactionResult>>())
).thenReturn(Single.just(CancelCurrentOfflineEventIfAnyTransaction.TransactionResult().apply {
}))
PowerMockito.`when`(loopPlugin.enabled).thenReturn(true)
smsCommunicatorPlugin.messages = ArrayList() smsCommunicatorPlugin.messages = ArrayList()
sms = Sms("1234", "PUMP CONNECT") sms = Sms("1234", "PUMP CONNECT")
smsCommunicatorPlugin.processSms(sms) smsCommunicatorPlugin.processSms(sms)
@ -551,6 +564,7 @@ class SmsCommunicatorPluginTest : TestBaseWithProfile() {
Assert.assertEquals("Wrong duration", smsCommunicatorPlugin.messages[1].text) Assert.assertEquals("Wrong duration", smsCommunicatorPlugin.messages[1].text)
//PUMP DISCONNECT 30 //PUMP DISCONNECT 30
`when`(profileFunction.getProfile()).thenReturn(validProfile)
smsCommunicatorPlugin.messages = ArrayList() smsCommunicatorPlugin.messages = ArrayList()
sms = Sms("1234", "PUMP DISCONNECT 30") sms = Sms("1234", "PUMP DISCONNECT 30")
smsCommunicatorPlugin.processSms(sms) smsCommunicatorPlugin.processSms(sms)

View file

@ -8,7 +8,7 @@ import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.automation.R import info.nightscout.androidaps.automation.R
import info.nightscout.androidaps.events.* import info.nightscout.androidaps.events.*
import info.nightscout.androidaps.interfaces.Config import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.interfaces.LoopInterface import info.nightscout.androidaps.interfaces.Loop
import info.nightscout.androidaps.interfaces.PluginBase import info.nightscout.androidaps.interfaces.PluginBase
import info.nightscout.androidaps.interfaces.PluginDescription import info.nightscout.androidaps.interfaces.PluginDescription
import info.nightscout.androidaps.interfaces.PluginType import info.nightscout.androidaps.interfaces.PluginType
@ -45,7 +45,7 @@ class AutomationPlugin @Inject constructor(
private val context: Context, private val context: Context,
private val sp: SP, private val sp: SP,
private val fabricPrivacy: FabricPrivacy, private val fabricPrivacy: FabricPrivacy,
private val loopPlugin: LoopInterface, private val loopPlugin: Loop,
private val rxBus: RxBusWrapper, private val rxBus: RxBusWrapper,
private val constraintChecker: ConstraintChecker, private val constraintChecker: ConstraintChecker,
aapsLogger: AAPSLogger, aapsLogger: AAPSLogger,

View file

@ -9,7 +9,7 @@ import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.events.EventRefreshOverview import info.nightscout.androidaps.events.EventRefreshOverview
import info.nightscout.androidaps.interfaces.CommandQueueProvider import info.nightscout.androidaps.interfaces.CommandQueueProvider
import info.nightscout.androidaps.interfaces.ConfigBuilder import info.nightscout.androidaps.interfaces.ConfigBuilder
import info.nightscout.androidaps.interfaces.LoopInterface import info.nightscout.androidaps.interfaces.Loop
import info.nightscout.androidaps.interfaces.PluginBase import info.nightscout.androidaps.interfaces.PluginBase
import info.nightscout.androidaps.interfaces.PluginType import info.nightscout.androidaps.interfaces.PluginType
import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.logging.UserEntryLogger
@ -19,7 +19,7 @@ import info.nightscout.androidaps.utils.resources.ResourceHelper
import javax.inject.Inject import javax.inject.Inject
class ActionLoopDisable(injector: HasAndroidInjector) : Action(injector) { class ActionLoopDisable(injector: HasAndroidInjector) : Action(injector) {
@Inject lateinit var loopPlugin: LoopInterface @Inject lateinit var loopPlugin: Loop
@Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var configBuilder: ConfigBuilder @Inject lateinit var configBuilder: ConfigBuilder
@Inject lateinit var commandQueue: CommandQueueProvider @Inject lateinit var commandQueue: CommandQueueProvider

View file

@ -8,7 +8,7 @@ import info.nightscout.androidaps.database.entities.UserEntry
import info.nightscout.androidaps.database.entities.UserEntry.Sources import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.events.EventRefreshOverview import info.nightscout.androidaps.events.EventRefreshOverview
import info.nightscout.androidaps.interfaces.ConfigBuilder import info.nightscout.androidaps.interfaces.ConfigBuilder
import info.nightscout.androidaps.interfaces.LoopInterface import info.nightscout.androidaps.interfaces.Loop
import info.nightscout.androidaps.interfaces.PluginBase import info.nightscout.androidaps.interfaces.PluginBase
import info.nightscout.androidaps.interfaces.PluginType import info.nightscout.androidaps.interfaces.PluginType
import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.logging.UserEntryLogger
@ -20,7 +20,7 @@ import javax.inject.Inject
class ActionLoopEnable(injector: HasAndroidInjector) : Action(injector) { class ActionLoopEnable(injector: HasAndroidInjector) : Action(injector) {
@Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var loopPlugin: LoopInterface @Inject lateinit var loopPlugin: Loop
@Inject lateinit var configBuilder: ConfigBuilder @Inject lateinit var configBuilder: ConfigBuilder
@Inject lateinit var rxBus: RxBusWrapper @Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var uel: UserEntryLogger @Inject lateinit var uel: UserEntryLogger

View file

@ -4,33 +4,46 @@ import androidx.annotation.DrawableRes
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.automation.R import info.nightscout.androidaps.automation.R
import info.nightscout.androidaps.data.PumpEnactResult import info.nightscout.androidaps.data.PumpEnactResult
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.UserEntry import info.nightscout.androidaps.database.entities.UserEntry
import info.nightscout.androidaps.database.entities.UserEntry.Sources import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.database.transactions.CancelCurrentOfflineEventIfAnyTransaction
import info.nightscout.androidaps.events.EventRefreshOverview import info.nightscout.androidaps.events.EventRefreshOverview
import info.nightscout.androidaps.interfaces.ConfigBuilder import info.nightscout.androidaps.interfaces.ConfigBuilder
import info.nightscout.androidaps.interfaces.LoopInterface import info.nightscout.androidaps.interfaces.Loop
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.queue.Callback import info.nightscout.androidaps.queue.Callback
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign
import javax.inject.Inject import javax.inject.Inject
class ActionLoopResume(injector: HasAndroidInjector) : Action(injector) { class ActionLoopResume(injector: HasAndroidInjector) : Action(injector) {
@Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var loopPlugin: LoopInterface @Inject lateinit var loopPlugin: Loop
@Inject lateinit var configBuilder: ConfigBuilder @Inject lateinit var configBuilder: ConfigBuilder
@Inject lateinit var rxBus: RxBusWrapper @Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var uel: UserEntryLogger @Inject lateinit var uel: UserEntryLogger
@Inject lateinit var repository: AppRepository
@Inject lateinit var dateUtil: DateUtil
override fun friendlyName(): Int = R.string.resumeloop override fun friendlyName(): Int = R.string.resumeloop
override fun shortDescription(): String = resourceHelper.gs(R.string.resumeloop) override fun shortDescription(): String = resourceHelper.gs(R.string.resumeloop)
@DrawableRes override fun icon(): Int = R.drawable.ic_replay_24dp @DrawableRes override fun icon(): Int = R.drawable.ic_replay_24dp
val disposable = CompositeDisposable()
override fun doAction(callback: Callback) { override fun doAction(callback: Callback) {
if (loopPlugin.isSuspended) { if (loopPlugin.isSuspended) {
loopPlugin.suspendTo(0) disposable += repository.runTransactionForResult(CancelCurrentOfflineEventIfAnyTransaction(dateUtil.now()))
configBuilder.storeSettings("ActionLoopResume") .subscribe({ result ->
loopPlugin.createOfflineEvent(0) result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") }
}, {
aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it)
})
rxBus.send(EventRefreshOverview("ActionLoopResume")) rxBus.send(EventRefreshOverview("ActionLoopResume"))
uel.log(UserEntry.Action.RESUME, Sources.Automation, title) uel.log(UserEntry.Action.RESUME, Sources.Automation, title)
callback.result(PumpEnactResult(injector).success(true).comment(R.string.ok))?.run() callback.result(PumpEnactResult(injector).success(true).comment(R.string.ok))?.run()

View file

@ -9,7 +9,7 @@ import info.nightscout.androidaps.database.entities.UserEntry
import info.nightscout.androidaps.database.entities.UserEntry.Sources import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.database.entities.ValueWithUnit import info.nightscout.androidaps.database.entities.ValueWithUnit
import info.nightscout.androidaps.events.EventRefreshOverview import info.nightscout.androidaps.events.EventRefreshOverview
import info.nightscout.androidaps.interfaces.LoopInterface import info.nightscout.androidaps.interfaces.Loop
import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.general.automation.elements.InputDuration import info.nightscout.androidaps.plugins.general.automation.elements.InputDuration
@ -23,7 +23,7 @@ import javax.inject.Inject
class ActionLoopSuspend(injector: HasAndroidInjector) : Action(injector) { class ActionLoopSuspend(injector: HasAndroidInjector) : Action(injector) {
@Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var loopPlugin: LoopInterface @Inject lateinit var loopPlugin: Loop
@Inject lateinit var rxBus: RxBusWrapper @Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var uel: UserEntryLogger @Inject lateinit var uel: UserEntryLogger

View file

@ -12,7 +12,7 @@ import info.nightscout.androidaps.database.entities.TemporaryTarget
import info.nightscout.androidaps.database.entities.UserEntry import info.nightscout.androidaps.database.entities.UserEntry
import info.nightscout.androidaps.database.entities.UserEntry.Sources import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.database.entities.ValueWithUnit import info.nightscout.androidaps.database.entities.ValueWithUnit
import info.nightscout.androidaps.database.transactions.InsertTemporaryTargetAndCancelCurrentTransaction import info.nightscout.androidaps.database.transactions.InsertAndCancelCurrentTemporaryTargetTransaction
import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.logging.LTag
@ -59,7 +59,7 @@ class ActionStartTempTarget(injector: HasAndroidInjector) : Action(injector) {
@DrawableRes override fun icon(): Int = R.drawable.ic_temptarget_high @DrawableRes override fun icon(): Int = R.drawable.ic_temptarget_high
override fun doAction(callback: Callback) { override fun doAction(callback: Callback) {
disposable += repository.runTransactionForResult(InsertTemporaryTargetAndCancelCurrentTransaction(tt())) disposable += repository.runTransactionForResult(InsertAndCancelCurrentTemporaryTargetTransaction(tt()))
.subscribe({ result -> .subscribe({ result ->
result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted temp target $it") } result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted temp target $it") }
result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated temp target $it") } result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated temp target $it") }

View file

@ -4,7 +4,7 @@ import dagger.android.AndroidInjector
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.TestBase import info.nightscout.androidaps.TestBase
import info.nightscout.androidaps.interfaces.ConfigBuilder import info.nightscout.androidaps.interfaces.ConfigBuilder
import info.nightscout.androidaps.interfaces.LoopInterface import info.nightscout.androidaps.interfaces.Loop
import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.general.automation.actions.ActionLoopEnable import info.nightscout.androidaps.plugins.general.automation.actions.ActionLoopEnable
import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerConnectorTest import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerConnectorTest
@ -20,7 +20,7 @@ import org.powermock.modules.junit4.PowerMockRunner
@RunWith(PowerMockRunner::class) @RunWith(PowerMockRunner::class)
class AutomationEventTest : TestBase() { class AutomationEventTest : TestBase() {
@Mock lateinit var loopPlugin: LoopInterface @Mock lateinit var loopPlugin: Loop
@Mock lateinit var resourceHelper: ResourceHelper @Mock lateinit var resourceHelper: ResourceHelper
@Mock lateinit var configBuilder: ConfigBuilder @Mock lateinit var configBuilder: ConfigBuilder

View file

@ -1,12 +1,15 @@
package info.nightscout.androidaps.plugins.general.automation.actions package info.nightscout.androidaps.plugins.general.automation.actions
import info.nightscout.androidaps.automation.R import info.nightscout.androidaps.automation.R
import info.nightscout.androidaps.database.entities.TemporaryTarget
import info.nightscout.androidaps.database.transactions.CancelCurrentOfflineEventIfAnyTransaction
import info.nightscout.androidaps.database.transactions.Transaction
import info.nightscout.androidaps.queue.Callback import info.nightscout.androidaps.queue.Callback
import io.reactivex.Single
import org.junit.Assert import org.junit.Assert
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.mockito.Mockito
import org.mockito.Mockito.`when` import org.mockito.Mockito.`when`
import org.powermock.modules.junit4.PowerMockRunner import org.powermock.modules.junit4.PowerMockRunner
@ -38,20 +41,30 @@ class ActionLoopResumeTest : ActionsTestBase() {
@Test fun doActionTest() { @Test fun doActionTest() {
`when`(loopPlugin.isSuspended).thenReturn(true) `when`(loopPlugin.isSuspended).thenReturn(true)
val inserted = mutableListOf<TemporaryTarget>().apply {
// insert all inserted TTs
}
val updated = mutableListOf<TemporaryTarget>().apply {
// add(TemporaryTarget(id = 0, version = 0, dateCreated = 0, isValid = false, referenceId = null, interfaceIDs_backing = null, timestamp = 0, utcOffset = 0, reason =, highTarget = 0.0, lowTarget = 0.0, duration = 0))
// insert all updated TTs
}
`when`(
repository.runTransactionForResult(anyObject<Transaction<CancelCurrentOfflineEventIfAnyTransaction.TransactionResult>>())
).thenReturn(Single.just(CancelCurrentOfflineEventIfAnyTransaction.TransactionResult().apply {
inserted.addAll(inserted)
updated.addAll(updated)
}))
sut.doAction(object : Callback() { sut.doAction(object : Callback() {
override fun run() {} override fun run() {}
}) })
Mockito.verify(loopPlugin, Mockito.times(1)).suspendTo(0) //Mockito.verify(loopPlugin, Mockito.times(1)).suspendTo(0)
Mockito.verify(configBuilder, Mockito.times(1)).storeSettings("ActionLoopResume")
Mockito.verify(loopPlugin, Mockito.times(1)).createOfflineEvent(0)
// another call should keep it resumed, , no new invocation // another call should keep it resumed, , no new invocation
`when`(loopPlugin.isSuspended).thenReturn(false) `when`(loopPlugin.isSuspended).thenReturn(false)
sut.doAction(object : Callback() { sut.doAction(object : Callback() {
override fun run() {} override fun run() {}
}) })
Mockito.verify(loopPlugin, Mockito.times(1)).suspendTo(0) //Mockito.verify(loopPlugin, Mockito.times(1)).suspendTo(0)
Mockito.verify(configBuilder, Mockito.times(1)).storeSettings("ActionLoopResume")
Mockito.verify(loopPlugin, Mockito.times(1)).createOfflineEvent(0)
} }
} }

View file

@ -1,9 +1,8 @@
package info.nightscout.androidaps.plugins.general.automation.actions package info.nightscout.androidaps.plugins.general.automation.actions
import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.automation.R import info.nightscout.androidaps.automation.R
import info.nightscout.androidaps.database.entities.TemporaryTarget import info.nightscout.androidaps.database.entities.TemporaryTarget
import info.nightscout.androidaps.database.transactions.InsertTemporaryTargetAndCancelCurrentTransaction import info.nightscout.androidaps.database.transactions.InsertAndCancelCurrentTemporaryTargetTransaction
import info.nightscout.androidaps.database.transactions.Transaction import info.nightscout.androidaps.database.transactions.Transaction
import info.nightscout.androidaps.interfaces.GlucoseUnit import info.nightscout.androidaps.interfaces.GlucoseUnit
import info.nightscout.androidaps.plugins.general.automation.elements.InputDuration import info.nightscout.androidaps.plugins.general.automation.elements.InputDuration
@ -14,7 +13,6 @@ import org.junit.Assert
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.mockito.ArgumentMatcher
import org.mockito.Mockito import org.mockito.Mockito
import org.mockito.Mockito.`when` import org.mockito.Mockito.`when`
import org.powermock.modules.junit4.PowerMockRunner import org.powermock.modules.junit4.PowerMockRunner
@ -72,12 +70,12 @@ class ActionStartTempTargetTest : ActionsTestBase() {
} }
`when`( `when`(
repository.runTransactionForResult(argThatKotlin<InsertTemporaryTargetAndCancelCurrentTransaction> { repository.runTransactionForResult(argThatKotlin<InsertAndCancelCurrentTemporaryTargetTransaction> {
it.temporaryTarget it.temporaryTarget
.copy(timestamp = expectedTarget.timestamp, utcOffset = expectedTarget.utcOffset) // those can be different .copy(timestamp = expectedTarget.timestamp, utcOffset = expectedTarget.utcOffset) // those can be different
.contentEqualsTo(expectedTarget) .contentEqualsTo(expectedTarget)
}) })
).thenReturn(Single.just(InsertTemporaryTargetAndCancelCurrentTransaction.TransactionResult().apply { ).thenReturn(Single.just(InsertAndCancelCurrentTemporaryTargetTransaction.TransactionResult().apply {
inserted.addAll(inserted) inserted.addAll(inserted)
updated.addAll(updated) updated.addAll(updated)
})) }))
@ -87,7 +85,7 @@ class ActionStartTempTargetTest : ActionsTestBase() {
Assert.assertTrue(result.success) Assert.assertTrue(result.success)
} }
}) })
Mockito.verify(repository, Mockito.times(1)).runTransactionForResult(anyObject<Transaction<InsertTemporaryTargetAndCancelCurrentTransaction.TransactionResult>>()) Mockito.verify(repository, Mockito.times(1)).runTransactionForResult(anyObject<Transaction<InsertAndCancelCurrentTemporaryTargetTransaction.TransactionResult>>())
} }
@Test fun hasDialogTest() { @Test fun hasDialogTest() {

View file

@ -2,15 +2,15 @@ package info.nightscout.androidaps.plugins.general.automation.actions
import dagger.android.AndroidInjector import dagger.android.AndroidInjector
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.plugins.general.automation.TestBaseWithProfile
import info.nightscout.androidaps.TestPumpPlugin import info.nightscout.androidaps.TestPumpPlugin
import info.nightscout.androidaps.automation.R import info.nightscout.androidaps.automation.R
import info.nightscout.androidaps.data.PumpEnactResult import info.nightscout.androidaps.data.PumpEnactResult
import info.nightscout.androidaps.database.entities.OfflineEvent
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
import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.general.automation.TestBaseWithProfile
import info.nightscout.androidaps.plugins.general.automation.triggers.Trigger import info.nightscout.androidaps.plugins.general.automation.triggers.Trigger
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP import info.nightscout.androidaps.utils.sharedPreferences.SP
@ -29,13 +29,20 @@ open class ActionsTestBase : TestBaseWithProfile() {
pluginDescription: PluginDescription pluginDescription: PluginDescription
) : PluginBase( ) : PluginBase(
pluginDescription, aapsLogger, resourceHelper, injector pluginDescription, aapsLogger, resourceHelper, injector
), LoopInterface { ), Loop {
private var suspended = false private var suspended = false
override var lastRun: LoopInterface.LastRun? = LoopInterface.LastRun() override var lastRun: Loop.LastRun? = Loop.LastRun()
override val isSuspended: Boolean = suspended override val isSuspended: Boolean = suspended
override fun suspendTo(endTime: Long) {} override var enabled: Boolean
override fun createOfflineEvent(durationInMinutes: Int) {} get() = true
set(value) {}
override fun minutesToEndOfSuspend(): Int = 0
override fun goToZeroTemp(durationInMinutes: Int, profile: Profile, reason: OfflineEvent.Reason) {
}
override fun suspendLoop(durationInMinutes: Int) {} override fun suspendLoop(durationInMinutes: Int) {}
} }
@ -102,6 +109,8 @@ open class ActionsTestBase : TestBaseWithProfile() {
it.resourceHelper = resourceHelper it.resourceHelper = resourceHelper
it.configBuilder = configBuilder it.configBuilder = configBuilder
it.rxBus = rxBus it.rxBus = rxBus
it.repository = repository
it.dateUtil = dateUtil
it.uel = uel it.uel = uel
} }
if (it is ActionLoopEnable) { if (it is ActionLoopEnable) {

View file

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

View file

@ -11,6 +11,7 @@ fun BolusCalculatorResult.toJson(isAdd: Boolean, dateUtil: DateUtil): JSONObject
JSONObject() JSONObject()
.put("eventType", TherapyEvent.Type.BOLUS_WIZARD.text) .put("eventType", TherapyEvent.Type.BOLUS_WIZARD.text)
.put("created_at", dateUtil.toISOString(timestamp)) .put("created_at", dateUtil.toISOString(timestamp))
.put("isValid", isValid)
.put("bolusCalculatorResult", Gson().toJson(this)) .put("bolusCalculatorResult", Gson().toJson(this))
.put("date", timestamp) .put("date", timestamp)
.put("glucose", glucoseValue) .put("glucose", glucoseValue)

View file

@ -12,6 +12,7 @@ fun Carbs.toJson(isAdd: Boolean, dateUtil: DateUtil): JSONObject =
.put("eventType", if (amount < 12) TherapyEvent.Type.CARBS_CORRECTION.text else TherapyEvent.Type.MEAL_BOLUS.text) .put("eventType", if (amount < 12) TherapyEvent.Type.CARBS_CORRECTION.text else TherapyEvent.Type.MEAL_BOLUS.text)
.put("carbs", amount) .put("carbs", amount)
.put("created_at", dateUtil.toISOString(timestamp)) .put("created_at", dateUtil.toISOString(timestamp))
.put("isValid", isValid)
.put("date", timestamp).also { .put("date", timestamp).also {
if (duration != 0L) it.put("duration", duration) if (duration != 0L) it.put("duration", duration)
if (interfaceIDs.pumpId != null) it.put("pumpId", interfaceIDs.pumpId) if (interfaceIDs.pumpId != null) it.put("pumpId", interfaceIDs.pumpId)

View file

@ -3,7 +3,7 @@ package info.nightscout.androidaps.extensions
import android.os.Build import android.os.Build
import info.nightscout.androidaps.database.entities.DeviceStatus import info.nightscout.androidaps.database.entities.DeviceStatus
import info.nightscout.androidaps.interfaces.IobCobCalculator import info.nightscout.androidaps.interfaces.IobCobCalculator
import info.nightscout.androidaps.interfaces.LoopInterface import info.nightscout.androidaps.interfaces.Loop
import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.interfaces.Pump import info.nightscout.androidaps.interfaces.Pump
import info.nightscout.androidaps.plugins.configBuilder.RunningConfiguration import info.nightscout.androidaps.plugins.configBuilder.RunningConfiguration
@ -28,7 +28,7 @@ fun DeviceStatus.toJson(dateUtil: DateUtil): JSONObject =
fun buildDeviceStatus( fun buildDeviceStatus(
dateUtil: DateUtil, dateUtil: DateUtil,
loopPlugin: LoopInterface, loopPlugin: Loop,
iobCobCalculatorPlugin: IobCobCalculator, iobCobCalculatorPlugin: IobCobCalculator,
profileFunction: ProfileFunction, profileFunction: ProfileFunction,
pump: Pump, pump: Pump,

View file

@ -20,6 +20,7 @@ fun GlucoseValue.toJson(isAdd : Boolean, dateUtil: DateUtil): JSONObject =
.put("device", sourceSensor.text) .put("device", sourceSensor.text)
.put("date", timestamp) .put("date", timestamp)
.put("dateString", dateUtil.toISOString(timestamp)) .put("dateString", dateUtil.toISOString(timestamp))
.put("isValid", isValid)
.put("sgv", value) .put("sgv", value)
.put("direction", trendArrow.text) .put("direction", trendArrow.text)
.put("type", "sgv") .put("type", "sgv")

View file

@ -0,0 +1,64 @@
package info.nightscout.androidaps.extensions
import info.nightscout.androidaps.database.embedments.InterfaceIDs
import info.nightscout.androidaps.database.entities.OfflineEvent
import info.nightscout.androidaps.database.entities.TherapyEvent
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.JsonHelper
import info.nightscout.androidaps.utils.T
import org.json.JSONObject
fun OfflineEvent.toJson(isAdd: Boolean, dateUtil: DateUtil): JSONObject =
JSONObject()
.put("created_at", dateUtil.toISOString(timestamp))
.put("enteredBy", "openaps://" + "AndroidAPS")
.put("eventType", TherapyEvent.Type.APS_OFFLINE.text)
.put("isValid", isValid)
.put("duration", T.msecs(duration).mins())
.put("reason", reason.name)
.also {
if (interfaceIDs.pumpId != null) it.put("pumpId", interfaceIDs.pumpId)
if (interfaceIDs.pumpType != null) it.put("pumpType", interfaceIDs.pumpType!!.name)
if (interfaceIDs.pumpSerial != null) it.put("pumpSerial", interfaceIDs.pumpSerial)
if (isAdd && interfaceIDs.nightscoutId != null) it.put("_id", interfaceIDs.nightscoutId)
}
/* NS PS
{
"enteredBy": "undefined",
"eventType": "OpenAPS Offline",
"duration": 60,
"created_at": "2021-05-27T15:11:52.230Z",
"utcOffset": 0,
"_id": "60afb6ba3c0d77e3e720f2fe",
"mills": 1622128312230,
"carbs": null,
"insulin": null
}
*/
fun offlineEventFromJson(jsonObject: JSONObject): OfflineEvent? {
val timestamp = JsonHelper.safeGetLongAllowNull(jsonObject, "mills", null) ?: return null
val duration = JsonHelper.safeGetLong(jsonObject, "duration")
val isValid = JsonHelper.safeGetBoolean(jsonObject, "isValid", true)
val id = JsonHelper.safeGetStringAllowNull(jsonObject, "_id", null)
val pumpId = JsonHelper.safeGetLongAllowNull(jsonObject, "pumpId", null)
val pumpType = InterfaceIDs.PumpType.fromString(JsonHelper.safeGetStringAllowNull(jsonObject, "pumpType", null))
val pumpSerial = JsonHelper.safeGetStringAllowNull(jsonObject, "pumpSerial", null)
val reason = OfflineEvent.Reason.fromString(JsonHelper.safeGetString(jsonObject, "reason", OfflineEvent.Reason.OTHER.name))
return OfflineEvent(
timestamp = timestamp,
duration = T.mins(duration).msecs(),
isValid = isValid,
reason = reason
).also {
it.interfaceIDs.nightscoutId = id
it.interfaceIDs.pumpId = pumpId
it.interfaceIDs.pumpType = pumpType
it.interfaceIDs.pumpSerial = pumpSerial
}
}

View file

@ -21,6 +21,7 @@ fun ProfileSwitch.toJson(isAdd: Boolean, dateUtil: DateUtil): JSONObject =
JSONObject() JSONObject()
.put("created_at", dateUtil.toISOString(timestamp)) .put("created_at", dateUtil.toISOString(timestamp))
.put("enteredBy", "openaps://" + "AndroidAPS") .put("enteredBy", "openaps://" + "AndroidAPS")
.put("isValid", isValid)
.put("eventType", TherapyEvent.Type.PROFILE_SWITCH.text) .put("eventType", TherapyEvent.Type.PROFILE_SWITCH.text)
.put("duration", T.msecs(duration).mins()) .put("duration", T.msecs(duration).mins())
.put("profile", getCustomizedName()) .put("profile", getCustomizedName())

View file

@ -67,6 +67,7 @@ fun TemporaryBasal.toJson(isAdd: Boolean, profile: Profile, dateUtil: DateUtil,
.put("created_at", dateUtil.toISOString(timestamp)) .put("created_at", dateUtil.toISOString(timestamp))
.put("enteredBy", "openaps://" + "AndroidAPS") .put("enteredBy", "openaps://" + "AndroidAPS")
.put("eventType", TherapyEvent.Type.TEMPORARY_BASAL.text) .put("eventType", TherapyEvent.Type.TEMPORARY_BASAL.text)
.put("isValid", isValid)
.put("duration", T.msecs(duration).mins()) .put("duration", T.msecs(duration).mins())
.put("rate", rate) .put("rate", rate)
.put("type", type.name) .put("type", type.name)

View file

@ -100,6 +100,7 @@ fun therapyEventFromJson(jsonObject: JSONObject): TherapyEvent? {
fun TherapyEvent.toJson(isAdd: Boolean): JSONObject = fun TherapyEvent.toJson(isAdd: Boolean): JSONObject =
JSONObject() JSONObject()
.put("eventType", type.text) .put("eventType", type.text)
.put("isValid", isValid)
.put("created_at", timestamp) .put("created_at", timestamp)
.put("enteredBy", enteredBy) .put("enteredBy", enteredBy)
.put("units", if (glucoseUnit == TherapyEvent.GlucoseUnit.MGDL) Constants.MGDL else Constants.MMOL) .put("units", if (glucoseUnit == TherapyEvent.GlucoseUnit.MGDL) Constants.MGDL else Constants.MMOL)

View file

@ -16,6 +16,7 @@ interface DataSyncSelector {
data class PairTemporaryBasal(val value: TemporaryBasal, val updateRecordId: Long) data class PairTemporaryBasal(val value: TemporaryBasal, val updateRecordId: Long)
data class PairExtendedBolus(val value: ExtendedBolus, val updateRecordId: Long) data class PairExtendedBolus(val value: ExtendedBolus, val updateRecordId: Long)
data class PairProfileSwitch(val value: ProfileSwitch, val updateRecordId: Long) data class PairProfileSwitch(val value: ProfileSwitch, val updateRecordId: Long)
data class PairOfflineEvent(val value: OfflineEvent, val updateRecordId: Long)
data class PairProfileStore(val value: JSONObject, val timestampSync: Long) data class PairProfileStore(val value: JSONObject, val timestampSync: Long)
fun doUpload() fun doUpload()
@ -77,6 +78,11 @@ interface DataSyncSelector {
// Until NS v3 // Until NS v3
fun processChangedProfileSwitchesCompat(): Boolean fun processChangedProfileSwitchesCompat(): Boolean
fun confirmLastOfflineEventIdIfGreater(lastSynced: Long)
fun changedOfflineEvents() : List<OfflineEvent>
// Until NS v3
fun processChangedOfflineEventsCompat(): Boolean
fun confirmLastProfileStore(lastSynced: Long) fun confirmLastProfileStore(lastSynced: Long)
fun processChangedProfileStore() fun processChangedProfileStore()
} }

View file

@ -1,9 +1,10 @@
package info.nightscout.androidaps.interfaces package info.nightscout.androidaps.interfaces
import info.nightscout.androidaps.data.PumpEnactResult import info.nightscout.androidaps.data.PumpEnactResult
import info.nightscout.androidaps.database.entities.OfflineEvent
import info.nightscout.androidaps.plugins.aps.loop.APSResult import info.nightscout.androidaps.plugins.aps.loop.APSResult
interface LoopInterface { interface Loop {
class LastRun { class LastRun {
@ -22,8 +23,9 @@ interface LoopInterface {
var lastRun: LastRun? var lastRun: LastRun?
val isSuspended: Boolean val isSuspended: Boolean
var enabled: Boolean
fun suspendTo(endTime: Long) fun minutesToEndOfSuspend(): Int
fun createOfflineEvent(durationInMinutes: Int) fun goToZeroTemp(durationInMinutes: Int, profile: Profile, reason: OfflineEvent.Reason)
fun suspendLoop(durationInMinutes: Int) fun suspendLoop(durationInMinutes: Int)
} }

View file

@ -1,6 +1,7 @@
package info.nightscout.androidaps.utils package info.nightscout.androidaps.utils
import info.nightscout.androidaps.core.R import info.nightscout.androidaps.core.R
import info.nightscout.androidaps.database.entities.OfflineEvent
import info.nightscout.androidaps.database.entities.TemporaryTarget import info.nightscout.androidaps.database.entities.TemporaryTarget
import info.nightscout.androidaps.database.entities.TherapyEvent import info.nightscout.androidaps.database.entities.TherapyEvent
import info.nightscout.androidaps.database.entities.UserEntry.Action import info.nightscout.androidaps.database.entities.UserEntry.Action
@ -15,10 +16,7 @@ class Translator @Inject internal constructor(
private val resourceHelper: ResourceHelper private val resourceHelper: ResourceHelper
) { ) {
@Deprecated("use type instead of string") fun translate(action: Action): String = when (action) {
fun translate(text: String): String = text
fun translate(action: Action): String = when(action) {
Action.BOLUS -> resourceHelper.gs(R.string.uel_bolus) Action.BOLUS -> resourceHelper.gs(R.string.uel_bolus)
Action.SMB -> resourceHelper.gs(R.string.smb_shortname) Action.SMB -> resourceHelper.gs(R.string.smb_shortname)
Action.BOLUS_ADVISOR -> resourceHelper.gs(R.string.uel_bolus_advisor) Action.BOLUS_ADVISOR -> resourceHelper.gs(R.string.uel_bolus_advisor)
@ -99,10 +97,12 @@ class Translator @Inject internal constructor(
Action.EXIT_AAPS -> resourceHelper.gs(R.string.uel_exit_aaps) Action.EXIT_AAPS -> resourceHelper.gs(R.string.uel_exit_aaps)
Action.PLUGIN_ENABLED -> resourceHelper.gs(R.string.uel_plugin_enabled) Action.PLUGIN_ENABLED -> resourceHelper.gs(R.string.uel_plugin_enabled)
Action.PLUGIN_DISABLED -> resourceHelper.gs(R.string.uel_plugin_disabled) Action.PLUGIN_DISABLED -> resourceHelper.gs(R.string.uel_plugin_disabled)
Action.LOOP_CHANGE -> resourceHelper.gs(R.string.uel_loop_change)
Action.LOOP_REMOVED -> resourceHelper.gs(R.string.uel_loop_removed)
Action.UNKNOWN -> resourceHelper.gs(R.string.unknown) Action.UNKNOWN -> resourceHelper.gs(R.string.unknown)
} }
fun translate(units: ValueWithUnit?): String = when(units) { fun translate(units: ValueWithUnit?): String = when (units) {
is ValueWithUnit.Gram -> resourceHelper.gs(R.string.shortgram) is ValueWithUnit.Gram -> resourceHelper.gs(R.string.shortgram)
is ValueWithUnit.Hour -> resourceHelper.gs(R.string.shorthour) is ValueWithUnit.Hour -> resourceHelper.gs(R.string.shorthour)
is ValueWithUnit.Insulin -> resourceHelper.gs(R.string.insulin_unit_shortname) is ValueWithUnit.Insulin -> resourceHelper.gs(R.string.insulin_unit_shortname)
@ -114,7 +114,7 @@ class Translator @Inject internal constructor(
else -> "" else -> ""
} }
fun translate(meterType: TherapyEvent.MeterType): String = when(meterType) { fun translate(meterType: TherapyEvent.MeterType): String = when (meterType) {
TherapyEvent.MeterType.FINGER -> resourceHelper.gs(R.string.glucosetype_finger) TherapyEvent.MeterType.FINGER -> resourceHelper.gs(R.string.glucosetype_finger)
TherapyEvent.MeterType.SENSOR -> resourceHelper.gs(R.string.glucosetype_sensor) TherapyEvent.MeterType.SENSOR -> resourceHelper.gs(R.string.glucosetype_sensor)
TherapyEvent.MeterType.MANUAL -> resourceHelper.gs(R.string.manual) TherapyEvent.MeterType.MANUAL -> resourceHelper.gs(R.string.manual)
@ -122,7 +122,7 @@ class Translator @Inject internal constructor(
else -> resourceHelper.gs(R.string.unknown) else -> resourceHelper.gs(R.string.unknown)
} }
fun translate(type: TherapyEvent.Type): String = when(type) { fun translate(type: TherapyEvent.Type): String = when (type) {
TherapyEvent.Type.FINGER_STICK_BG_VALUE -> resourceHelper.gs(R.string.careportal_bgcheck) TherapyEvent.Type.FINGER_STICK_BG_VALUE -> resourceHelper.gs(R.string.careportal_bgcheck)
TherapyEvent.Type.SNACK_BOLUS -> resourceHelper.gs(R.string.careportal_snackbolus) TherapyEvent.Type.SNACK_BOLUS -> resourceHelper.gs(R.string.careportal_snackbolus)
TherapyEvent.Type.MEAL_BOLUS -> resourceHelper.gs(R.string.careportal_mealbolus) TherapyEvent.Type.MEAL_BOLUS -> resourceHelper.gs(R.string.careportal_mealbolus)
@ -173,7 +173,7 @@ class Translator @Inject internal constructor(
else -> resourceHelper.gs(R.string.unknown) else -> resourceHelper.gs(R.string.unknown)
} }
fun translate(reason: TemporaryTarget.Reason): String = when(reason) { fun translate(reason: TemporaryTarget.Reason): String = when (reason) {
TemporaryTarget.Reason.CUSTOM -> resourceHelper.gs(R.string.custom) TemporaryTarget.Reason.CUSTOM -> resourceHelper.gs(R.string.custom)
TemporaryTarget.Reason.HYPOGLYCEMIA -> resourceHelper.gs(R.string.hypo) TemporaryTarget.Reason.HYPOGLYCEMIA -> resourceHelper.gs(R.string.hypo)
TemporaryTarget.Reason.EATING_SOON -> resourceHelper.gs(R.string.eatingsoon) TemporaryTarget.Reason.EATING_SOON -> resourceHelper.gs(R.string.eatingsoon)
@ -184,7 +184,16 @@ class Translator @Inject internal constructor(
else -> resourceHelper.gs(R.string.unknown) else -> resourceHelper.gs(R.string.unknown)
} }
fun translate(source: Sources): String = when(source) { fun translate(reason: OfflineEvent.Reason): String = when (reason) {
OfflineEvent.Reason.SUSPEND -> resourceHelper.gs(R.string.uel_suspend)
OfflineEvent.Reason.DISABLE_LOOP -> resourceHelper.gs(R.string.disableloop)
OfflineEvent.Reason.DISCONNECT_PUMP -> resourceHelper.gs(R.string.uel_disconnect)
OfflineEvent.Reason.OTHER -> resourceHelper.gs(R.string.uel_other)
else -> resourceHelper.gs(R.string.unknown)
}
fun translate(source: Sources): String = when (source) {
/* /*
Sources.TreatmentDialog -> TODO() Sources.TreatmentDialog -> TODO()
Sources.InsulinDialog -> TODO() Sources.InsulinDialog -> TODO()

View file

@ -123,6 +123,7 @@ class UserEntryPresentationHelper @Inject constructor(
is ValueWithUnit.SimpleString -> valueWithUnit.value is ValueWithUnit.SimpleString -> valueWithUnit.value
is ValueWithUnit.TherapyEventMeterType -> translator.translate(valueWithUnit.value) is ValueWithUnit.TherapyEventMeterType -> translator.translate(valueWithUnit.value)
is ValueWithUnit.TherapyEventTTReason -> translator.translate(valueWithUnit.value) is ValueWithUnit.TherapyEventTTReason -> translator.translate(valueWithUnit.value)
is ValueWithUnit.OfflineEventReason -> translator.translate(valueWithUnit.value)
is ValueWithUnit.TherapyEventType -> translator.translate(valueWithUnit.value) is ValueWithUnit.TherapyEventType -> translator.translate(valueWithUnit.value)
is ValueWithUnit.Timestamp -> dateUtil.dateAndTimeAndSecondsString(valueWithUnit.value) is ValueWithUnit.Timestamp -> dateUtil.dateAndTimeAndSecondsString(valueWithUnit.value)
@ -165,10 +166,10 @@ class UserEntryPresentationHelper @Inject constructor(
) + "\n" ) + "\n"
private fun getCsvEntry(entry: UserEntry): String { private fun getCsvEntry(entry: UserEntry): String {
val fullvalueWithUnitList = ArrayList(entry.values) val fullValueWithUnitList = ArrayList(entry.values)
val timestampRec = entry.timestamp.toString() val timestampRec = entry.timestamp.toString()
val dateTimestampRev = dateUtil.dateAndTimeAndSecondsString(entry.timestamp) val dateTimestampRev = dateUtil.dateAndTimeAndSecondsString(entry.timestamp)
val utcOffset = dateUtil.timeStringFromSeconds((entry.utcOffset/1000).toInt()) val utcOffset = dateUtil.timeStringFromSeconds((entry.utcOffset / 1000).toInt())
val action = csvString(entry.action) val action = csvString(entry.action)
var therapyEvent = "" var therapyEvent = ""
val source = translator.translate(entry.source) val source = translator.translate(entry.source)
@ -184,7 +185,7 @@ class UserEntryPresentationHelper @Inject constructor(
var minute = "" var minute = ""
var noUnit = "" var noUnit = ""
for (valueWithUnit in fullvalueWithUnitList.filterNotNull()) { for (valueWithUnit in fullValueWithUnitList.filterNotNull()) {
when (valueWithUnit) { when (valueWithUnit) {
is ValueWithUnit.Gram -> gram = valueWithUnit.value.toString() is ValueWithUnit.Gram -> gram = valueWithUnit.value.toString()
is ValueWithUnit.Hour -> hour = valueWithUnit.value.toString() is ValueWithUnit.Hour -> hour = valueWithUnit.value.toString()
@ -196,6 +197,7 @@ class UserEntryPresentationHelper @Inject constructor(
is ValueWithUnit.SimpleString -> simpleString = simpleString.addWithSeparator(valueWithUnit.value) is ValueWithUnit.SimpleString -> simpleString = simpleString.addWithSeparator(valueWithUnit.value)
is ValueWithUnit.TherapyEventMeterType -> therapyEvent = therapyEvent.addWithSeparator(translator.translate(valueWithUnit.value)) is ValueWithUnit.TherapyEventMeterType -> therapyEvent = therapyEvent.addWithSeparator(translator.translate(valueWithUnit.value))
is ValueWithUnit.TherapyEventTTReason -> therapyEvent = therapyEvent.addWithSeparator(translator.translate(valueWithUnit.value)) is ValueWithUnit.TherapyEventTTReason -> therapyEvent = therapyEvent.addWithSeparator(translator.translate(valueWithUnit.value))
is ValueWithUnit.OfflineEventReason -> therapyEvent = therapyEvent.addWithSeparator(translator.translate(valueWithUnit.value))
is ValueWithUnit.TherapyEventType -> therapyEvent = therapyEvent.addWithSeparator(translator.translate(valueWithUnit.value)) is ValueWithUnit.TherapyEventType -> therapyEvent = therapyEvent.addWithSeparator(translator.translate(valueWithUnit.value))
is ValueWithUnit.Timestamp -> timestamp = dateUtil.dateAndTimeAndSecondsString(valueWithUnit.value) is ValueWithUnit.Timestamp -> timestamp = dateUtil.dateAndTimeAndSecondsString(valueWithUnit.value)
@ -215,9 +217,9 @@ class UserEntryPresentationHelper @Inject constructor(
return "$timestampRec;$dateTimestampRev;$utcOffset;$action;$therapyEvent;$source;$note;$simpleString;$timestamp;$bg;$gram;$insulin;$unitPerHour;$percent;$hour;$minute;$noUnit" return "$timestampRec;$dateTimestampRev;$utcOffset;$action;$therapyEvent;$source;$note;$simpleString;$timestamp;$bg;$gram;$insulin;$unitPerHour;$percent;$hour;$minute;$noUnit"
} }
private fun csvString(action: Action): String = "\"" + translator.translate(action).replace("\"", "\"\"").replace("\n"," / ") + "\"" private fun csvString(action: Action): String = "\"" + translator.translate(action).replace("\"", "\"\"").replace("\n", " / ") + "\""
private fun csvString(id: Int): String = if (id != 0) "\"" + resourceHelper.gs(id).replace("\"", "\"\"").replace("\n"," / ") + "\"" else "" private fun csvString(id: Int): String = if (id != 0) "\"" + resourceHelper.gs(id).replace("\"", "\"\"").replace("\n", " / ") + "\"" else ""
private fun csvString(s: String): String = if (s != "") "\"" + s.replace("\"", "\"\"").replace("\n"," / ") + "\"" else "" private fun csvString(s: String): String = if (s != "") "\"" + s.replace("\"", "\"\"").replace("\n", " / ") + "\"" else ""
private fun String.addWithSeparator(add: Any) = private fun String.addWithSeparator(add: Any) =
this + (if (this.isBlank()) "" else " / ") + add.toString() this + (if (this.isBlank()) "" else " / ") + add.toString()

View file

@ -494,6 +494,9 @@
<string name="ue_none">No Unit</string> <string name="ue_none">No Unit</string>
<string name="ue_export_to_csv">Export User Entries to Excel (csv)</string> <string name="ue_export_to_csv">Export User Entries to Excel (csv)</string>
<string name="ue_csv_header" translatable="false">"%1$s;%2$s;%3$s;%4$s;%5$s;%6$s;%7$s;%8$s;%9$s;%10$s;%11$s;%12$s;%13$s;%14$s;%15$s;%16$s;%17$s"</string> <string name="ue_csv_header" translatable="false">"%1$s;%2$s;%3$s;%4$s;%5$s;%6$s;%7$s;%8$s;%9$s;%10$s;%11$s;%12$s;%13$s;%14$s;%15$s;%16$s;%17$s"</string>
<string name="uel_loop_change">LOOP CHANGED</string>
<string name="uel_loop_removed">LOOP REMOVED</string>
<string name="uel_other">OTHER</string>
<plurals name="days"> <plurals name="days">
<item quantity="one">%1$d day</item> <item quantity="one">%1$d day</item>

File diff suppressed because it is too large Load diff

View file

@ -6,14 +6,14 @@ import androidx.room.TypeConverters
import info.nightscout.androidaps.database.daos.* import info.nightscout.androidaps.database.daos.*
import info.nightscout.androidaps.database.entities.* import info.nightscout.androidaps.database.entities.*
const val DATABASE_VERSION = 20 const val DATABASE_VERSION = 21
@Database(version = DATABASE_VERSION, @Database(version = DATABASE_VERSION,
entities = [APSResult::class, Bolus::class, BolusCalculatorResult::class, Carbs::class, entities = [APSResult::class, Bolus::class, BolusCalculatorResult::class, Carbs::class,
EffectiveProfileSwitch::class, ExtendedBolus::class, GlucoseValue::class, ProfileSwitch::class, EffectiveProfileSwitch::class, ExtendedBolus::class, GlucoseValue::class, ProfileSwitch::class,
TemporaryBasal::class, TemporaryTarget::class, TherapyEvent::class, TotalDailyDose::class, APSResultLink::class, TemporaryBasal::class, TemporaryTarget::class, TherapyEvent::class, TotalDailyDose::class, APSResultLink::class,
MultiwaveBolusLink::class, PreferenceChange::class, VersionChange::class, UserEntry::class, MultiwaveBolusLink::class, PreferenceChange::class, VersionChange::class, UserEntry::class,
Food::class, DeviceStatus::class], Food::class, DeviceStatus::class, OfflineEvent::class],
exportSchema = true) exportSchema = true)
@TypeConverters(Converters::class) @TypeConverters(Converters::class)
internal abstract class AppDatabase : RoomDatabase() { internal abstract class AppDatabase : RoomDatabase() {
@ -56,4 +56,6 @@ internal abstract class AppDatabase : RoomDatabase() {
abstract val deviceStatusDao: DeviceStatusDao abstract val deviceStatusDao: DeviceStatusDao
abstract val offlineEventDao: OfflineEventDao
} }

View file

@ -768,6 +768,64 @@ open class AppRepository @Inject internal constructor(
database.extendedBolusDao.getLastId() database.extendedBolusDao.getLastId()
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.toWrappedSingle() .toWrappedSingle()
// OFFLINE EVENT
/*
* 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
* of the update ("historic") entry. The sync counter should be incremented to that id if it was synced successfully.
*
* It is a Maybe as there might be no next element.
* */
fun getNextSyncElementOfflineEvent(id: Long): Maybe<Pair<OfflineEvent, Long>> =
database.offlineEventDao.getNextModifiedOrNewAfter(id)
.flatMap { nextIdElement ->
val nextIdElemReferenceId = nextIdElement.referenceId
if (nextIdElemReferenceId == null) {
Maybe.just(nextIdElement to nextIdElement.id)
} else {
database.offlineEventDao.getCurrentFromHistoric(nextIdElemReferenceId)
.map { it to nextIdElement.id }
}
}
fun compatGetOfflineEventData(): Single<List<OfflineEvent>> =
database.offlineEventDao.getOfflineEventData()
.subscribeOn(Schedulers.io())
fun getOfflineEventDataFromTime(timestamp: Long, ascending: Boolean): Single<List<OfflineEvent>> =
database.offlineEventDao.getOfflineEventDataFromTime(timestamp)
.map { if (!ascending) it.reversed() else it }
.subscribeOn(Schedulers.io())
fun getOfflineEventDataIncludingInvalidFromTime(timestamp: Long, ascending: Boolean): Single<List<OfflineEvent>> =
database.offlineEventDao.getOfflineEventDataIncludingInvalidFromTime(timestamp)
.map { if (!ascending) it.reversed() else it }
.subscribeOn(Schedulers.io())
fun getOfflineEventDataFromTimeToTime(start: Long, end: Long, ascending: Boolean): Single<List<OfflineEvent>> =
database.offlineEventDao.getOfflineEventDataFromTimeToTime(start, end)
.map { if (!ascending) it.reversed() else it }
.subscribeOn(Schedulers.io())
fun getModifiedOfflineEventsDataFromId(lastId: Long): Single<List<OfflineEvent>> =
database.offlineEventDao.getModifiedFrom(lastId)
.subscribeOn(Schedulers.io())
fun getOfflineEventActiveAt(timestamp: Long): Single<ValueWrapper<OfflineEvent>> =
database.offlineEventDao.getOfflineEventActiveAt(timestamp)
.subscribeOn(Schedulers.io())
.toWrappedSingle()
fun deleteAllOfflineEventEntries() =
database.offlineEventDao.deleteAllEntries()
fun getLastOfflineEventIdWrapped(): Single<ValueWrapper<Long>> =
database.offlineEventDao.getLastId()
.subscribeOn(Schedulers.io())
.toWrappedSingle()
} }
@Suppress("USELESS_CAST") @Suppress("USELESS_CAST")

View file

@ -183,4 +183,9 @@ class Converters {
return list return list
} }
@TypeConverter
fun fromOfflineEventReason(reason: OfflineEvent.Reason?) = reason?.name
@TypeConverter
fun toOfflineEventReason(reason: String?) = reason?.let { OfflineEvent.Reason.valueOf(it) }
} }

View file

@ -26,13 +26,11 @@ open class DatabaseModule {
// .addMigrations(migration6to7) // .addMigrations(migration6to7)
// .addMigrations(migration7to8) // .addMigrations(migration7to8)
// .addMigrations(migration11to12) // .addMigrations(migration11to12)
.addMigrations(migration20to21)
.addCallback(object : Callback() { .addCallback(object : Callback() {
override fun onOpen(db: SupportSQLiteDatabase) { override fun onOpen(db: SupportSQLiteDatabase) {
super.onOpen(db) super.onOpen(db)
db.execSQL("CREATE INDEX IF NOT EXISTS `index_temporaryBasals_end` ON `temporaryBasals` (`timestamp` + `duration`)") createCustomIndexes(db)
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()
@ -41,43 +39,34 @@ open class DatabaseModule {
@Qualifier @Qualifier
annotation class DbFileName annotation class DbFileName
private val migration5to6 = object : Migration(5, 6) { private fun createCustomIndexes(database: SupportSQLiteDatabase) {
database.execSQL("CREATE INDEX IF NOT EXISTS `index_temporaryBasals_end` ON `temporaryBasals` (`timestamp` + `duration`)")
database.execSQL("CREATE INDEX IF NOT EXISTS `index_extendedBoluses_end` ON `extendedBoluses` (`timestamp` + `duration`)")
database.execSQL("CREATE INDEX IF NOT EXISTS `index_temporaryTargets_end` ON `temporaryTargets` (`timestamp` + `duration`)")
database.execSQL("CREATE INDEX IF NOT EXISTS `index_carbs_end` ON `carbs` (`timestamp` + `duration`)")
database.execSQL("CREATE INDEX IF NOT EXISTS `index_offlineEvents_end` ON `offlineEvents` (`timestamp` + `duration`)")
}
private fun dropCustomIndexes(database: SupportSQLiteDatabase) {
database.execSQL("DROP INDEX IF EXISTS `index_temporaryBasals_end`")
database.execSQL("DROP INDEX IF EXISTS `index_extendedBoluses_end`")
database.execSQL("DROP INDEX IF EXISTS `index_temporaryTargets_end`")
database.execSQL("DROP INDEX IF EXISTS `index_carbs_end`")
database.execSQL("DROP INDEX IF EXISTS `index_offlineEvents_end`")
}
private val migration20to21 = object : Migration(20,21) {
override fun migrate(database: SupportSQLiteDatabase) { override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("DROP TABLE IF EXISTS userEntry") database.execSQL("DROP TABLE IF EXISTS offlineEvents")
database.execSQL("CREATE TABLE userEntry (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `timestamp` INTEGER NOT NULL, `utcOffset` INTEGER NOT NULL, `action` TEXT NOT NULL, `s` TEXT NOT NULL, `values` TEXT NOT NULL)") database.execSQL("CREATE TABLE IF NOT EXISTS `offlineEvents` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `version` INTEGER NOT NULL, `dateCreated` INTEGER NOT NULL, `isValid` INTEGER NOT NULL, `referenceId` INTEGER, `timestamp` INTEGER NOT NULL, `utcOffset` INTEGER NOT NULL, `reason` TEXT NOT NULL, `duration` INTEGER NOT NULL, `nightscoutSystemId` TEXT, `nightscoutId` TEXT, `pumpType` TEXT, `pumpSerial` TEXT, `temporaryId` INTEGER, `pumpId` INTEGER, `startId` INTEGER, `endId` INTEGER, FOREIGN KEY(`referenceId`) REFERENCES `offlineEvents`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )")
database.execSQL("CREATE INDEX IF NOT EXISTS `index_offlineEvents_id` ON offlineEvents (`id`)")
database.execSQL("CREATE INDEX IF NOT EXISTS `index_offlineEvents_isValid` ON offlineEvents (`isValid`)")
database.execSQL("CREATE INDEX IF NOT EXISTS `index_offlineEvents_nightscoutId` ON offlineEvents (`nightscoutId`)")
database.execSQL("CREATE INDEX IF NOT EXISTS `index_offlineEvents_referenceId` ON offlineEvents (`referenceId`)")
database.execSQL("CREATE INDEX IF NOT EXISTS `index_offlineEvents_timestamp` ON offlineEvents (`timestamp`)")
// Custom indexes must be dropped on migration to pass room schema checking after upgrade
dropCustomIndexes(database)
} }
} }
private val migration6to7 = object : Migration(6, 7) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("DROP TABLE IF EXISTS foods")
database.execSQL("CREATE TABLE IF NOT EXISTS foods (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `version` INTEGER NOT NULL, `dateCreated` INTEGER NOT NULL, `isValid` INTEGER NOT NULL, `referenceId` INTEGER, `name` TEXT NOT NULL, `category` TEXT, `subCategory` TEXT, `portion` REAL NOT NULL, `carbs` INTEGER NOT NULL, `fat` INTEGER, `protein` INTEGER, `energy` INTEGER, `unit` TEXT NOT NULL, `gi` INTEGER, `nightscoutSystemId` TEXT, `nightscoutId` TEXT, `pumpType` TEXT, `pumpSerial` TEXT, `pumpId` INTEGER, `startId` INTEGER, `endId` INTEGER, FOREIGN KEY(`referenceId`) REFERENCES `foods`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )")
database.execSQL("CREATE INDEX IF NOT EXISTS `index_foods_referenceId` ON `foods` (`referenceId`)")
}
}
private val migration7to8 = object : Migration(7, 8) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("DROP TABLE IF EXISTS bolusCalculatorResults")
database.execSQL("CREATE TABLE IF NOT EXISTS bolusCalculatorResults (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `version` INTEGER NOT NULL, `dateCreated` INTEGER NOT NULL, `isValid` INTEGER NOT NULL, `referenceId` INTEGER, `timestamp` INTEGER NOT NULL, `utcOffset` INTEGER NOT NULL, `targetBGLow` REAL NOT NULL, `targetBGHigh` REAL NOT NULL, `isf` REAL NOT NULL, `ic` REAL NOT NULL, `bolusIOB` REAL NOT NULL, `wasBolusIOBUsed` INTEGER NOT NULL, `basalIOB` REAL NOT NULL, `wasBasalIOBUsed` INTEGER NOT NULL, `glucoseValue` REAL NOT NULL, `wasGlucoseUsed` INTEGER NOT NULL, `glucoseDifference` REAL NOT NULL, `glucoseInsulin` REAL NOT NULL, `glucoseTrend` REAL NOT NULL, `wasTrendUsed` INTEGER NOT NULL, `trendInsulin` REAL NOT NULL, `cob` REAL NOT NULL, `wasCOBUsed` INTEGER NOT NULL, `cobInsulin` REAL NOT NULL, `carbs` REAL NOT NULL, `wereCarbsUsed` INTEGER NOT NULL, `carbsInsulin` REAL NOT NULL, `otherCorrection` REAL NOT NULL, `wasSuperbolusUsed` INTEGER NOT NULL, `superbolusInsulin` REAL NOT NULL, `wasTempTargetUsed` INTEGER NOT NULL, `totalInsulin` REAL NOT NULL, `percentageCorrection` INTEGER NOT NULL, `profileName` TEXT NOT NULL, `nightscoutSystemId` TEXT, `nightscoutId` TEXT, `pumpType` TEXT, `pumpSerial` TEXT, `pumpId` INTEGER, `startId` INTEGER, `endId` INTEGER, FOREIGN KEY(`referenceId`) REFERENCES `bolusCalculatorResults`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )")
database.execSQL("CREATE INDEX IF NOT EXISTS `index_bolusCalculatorResults_referenceId` ON bolusCalculatorResults (`referenceId`)")
database.execSQL("CREATE INDEX IF NOT EXISTS `index_bolusCalculatorResults_timestamp` ON bolusCalculatorResults (`timestamp`)")
database.execSQL("DROP TABLE IF EXISTS mealLinks")
database.execSQL("CREATE TABLE IF NOT EXISTS mealLinks (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `version` INTEGER NOT NULL, `dateCreated` INTEGER NOT NULL, `isValid` INTEGER NOT NULL, `referenceId` INTEGER, `timestamp` INTEGER NOT NULL, `utcOffset` INTEGER NOT NULL, `bolusId` INTEGER, `carbsId` INTEGER, `bolusCalcResultId` INTEGER, `superbolusTempBasalId` INTEGER, `noteId` INTEGER, `nightscoutSystemId` TEXT, `nightscoutId` TEXT, `pumpType` TEXT, `pumpSerial` TEXT, `pumpId` INTEGER, `startId` INTEGER, `endId` INTEGER, FOREIGN KEY(`bolusId`) REFERENCES `boluses`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION , FOREIGN KEY(`carbsId`) REFERENCES `carbs`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION , FOREIGN KEY(`bolusCalcResultId`) REFERENCES `bolusCalculatorResults`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION , FOREIGN KEY(`superbolusTempBasalId`) REFERENCES `temporaryBasals`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION , FOREIGN KEY(`noteId`) REFERENCES `therapyEvents`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION , FOREIGN KEY(`referenceId`) REFERENCES `mealLinks`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )")
database.execSQL("CREATE INDEX IF NOT EXISTS `index_mealLinks_referenceId` ON mealLinks (`referenceId`)")
database.execSQL("CREATE INDEX IF NOT EXISTS `index_mealLinks_bolusId` ON `mealLinks (`bolusId`)")
database.execSQL("CREATE INDEX IF NOT EXISTS `index_mealLinks_carbsId` ON mealLinks (`carbsId`)")
database.execSQL("CREATE INDEX IF NOT EXISTS `index_mealLinks_bolusCalcResultId` ON mealLinks (`bolusCalcResultId`)")
database.execSQL("CREATE INDEX IF NOT EXISTS `index_mealLinks_superbolusTempBasalId` ON mealLinks (`superbolusTempBasalId`)")
database.execSQL("CREATE INDEX IF NOT EXISTS `index_mealLinks_noteId` ON mealLinks (`noteId`)")
}
}
private val migration11to12 = object : Migration(11,12) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("DROP TABLE IF EXISTS userEntry")
database.execSQL("CREATE TABLE IF NOT EXISTS userEntry (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `timestamp` INTEGER NOT NULL, `utcOffset` INTEGER NOT NULL, `action` TEXT NOT NULL, `source` TEXT NOT NULL, `note` TEXT NOT NULL, `values` TEXT NOT NULL)")
}
}
} }

View file

@ -2,6 +2,7 @@ package info.nightscout.androidaps.database
import info.nightscout.androidaps.database.daos.* import info.nightscout.androidaps.database.daos.*
import info.nightscout.androidaps.database.daos.delegated.* import info.nightscout.androidaps.database.daos.delegated.*
import info.nightscout.androidaps.database.entities.OfflineEvent
import info.nightscout.androidaps.database.interfaces.DBEntry import info.nightscout.androidaps.database.interfaces.DBEntry
internal class DelegatedAppDatabase(val changes: MutableList<DBEntry>, val database: AppDatabase) { internal class DelegatedAppDatabase(val changes: MutableList<DBEntry>, val database: AppDatabase) {
@ -25,5 +26,6 @@ internal class DelegatedAppDatabase(val changes: MutableList<DBEntry>, val datab
val preferenceChangeDao: PreferenceChangeDao = DelegatedPreferenceChangeDao(changes, database.preferenceChangeDao) val preferenceChangeDao: PreferenceChangeDao = DelegatedPreferenceChangeDao(changes, database.preferenceChangeDao)
val foodDao: FoodDao = DelegatedFoodDao(changes, database.foodDao) val foodDao: FoodDao = DelegatedFoodDao(changes, database.foodDao)
val deviceStatusDao: DeviceStatusDao = DelegatedDeviceStatusDao(changes, database.deviceStatusDao) val deviceStatusDao: DeviceStatusDao = DelegatedDeviceStatusDao(changes, database.deviceStatusDao)
val offlineEventDao: OfflineEventDao = DelegatedOfflineEventDao(changes, database.offlineEventDao)
fun clearAllTables() = database.clearAllTables() fun clearAllTables() = database.clearAllTables()
} }

View file

@ -14,6 +14,7 @@ const val TABLE_MULTIWAVE_BOLUS_LINKS = "multiwaveBolusLinks"
const val TABLE_PROFILE_SWITCHES = "profileSwitches" const val TABLE_PROFILE_SWITCHES = "profileSwitches"
const val TABLE_TEMPORARY_BASALS = "temporaryBasals" const val TABLE_TEMPORARY_BASALS = "temporaryBasals"
const val TABLE_TEMPORARY_TARGETS = "temporaryTargets" const val TABLE_TEMPORARY_TARGETS = "temporaryTargets"
const val TABLE_OFFLINE_EVENTS = "offlineEvents"
const val TABLE_TOTAL_DAILY_DOSES = "totalDailyDoses" const val TABLE_TOTAL_DAILY_DOSES = "totalDailyDoses"
const val TABLE_THERAPY_EVENTS = "therapyEvents" const val TABLE_THERAPY_EVENTS = "therapyEvents"
const val TABLE_PREFERENCE_CHANGES = "preferenceChanges" const val TABLE_PREFERENCE_CHANGES = "preferenceChanges"

View file

@ -0,0 +1,51 @@
package info.nightscout.androidaps.database.daos
import androidx.room.Dao
import androidx.room.Query
import info.nightscout.androidaps.database.TABLE_OFFLINE_EVENTS
import info.nightscout.androidaps.database.entities.OfflineEvent
import io.reactivex.Maybe
import io.reactivex.Single
@Suppress("FunctionName")
@Dao
internal interface OfflineEventDao : TraceableDao<OfflineEvent> {
@Query("SELECT * FROM $TABLE_OFFLINE_EVENTS WHERE id = :id")
override fun findById(id: Long): OfflineEvent?
@Query("DELETE FROM $TABLE_OFFLINE_EVENTS")
override fun deleteAllEntries()
@Query("SELECT id FROM $TABLE_OFFLINE_EVENTS ORDER BY id DESC limit 1")
fun getLastId(): Maybe<Long>
@Query("SELECT * FROM $TABLE_OFFLINE_EVENTS WHERE nightscoutId = :nsId AND referenceId IS NULL")
fun findByNSId(nsId: String): OfflineEvent?
@Query("SELECT * FROM $TABLE_OFFLINE_EVENTS WHERE timestamp <= :timestamp AND (timestamp + duration) > :timestamp AND referenceId IS NULL AND isValid = 1 ORDER BY timestamp DESC LIMIT 1")
fun getOfflineEventActiveAt(timestamp: Long): Maybe<OfflineEvent>
@Query("SELECT * FROM $TABLE_OFFLINE_EVENTS WHERE timestamp >= :timestamp AND isValid = 1 AND referenceId IS NULL ORDER BY timestamp ASC")
fun getOfflineEventDataFromTime(timestamp: Long): Single<List<OfflineEvent>>
@Query("SELECT * FROM $TABLE_OFFLINE_EVENTS WHERE timestamp >= :timestamp AND referenceId IS NULL ORDER BY timestamp ASC")
fun getOfflineEventDataIncludingInvalidFromTime(timestamp: Long): Single<List<OfflineEvent>>
@Query("SELECT * FROM $TABLE_OFFLINE_EVENTS WHERE timestamp BETWEEN :start AND :end AND isValid = 1 AND referenceId IS NULL ORDER BY timestamp ASC")
fun getOfflineEventDataFromTimeToTime(start: Long, end: Long): Single<List<OfflineEvent>>
@Query("SELECT * FROM $TABLE_OFFLINE_EVENTS WHERE isValid = 1 AND referenceId IS NULL ORDER BY timestamp ASC")
fun getOfflineEventData(): Single<List<OfflineEvent>>
// This query will be used with v3 to get all changed records
@Query("SELECT * FROM $TABLE_OFFLINE_EVENTS WHERE id > :id AND referenceId IS NULL OR id IN (SELECT DISTINCT referenceId FROM $TABLE_OFFLINE_EVENTS WHERE id > :id) ORDER BY id ASC")
fun getModifiedFrom(id: Long): Single<List<OfflineEvent>>
// for WS we need 1 record only
@Query("SELECT * FROM $TABLE_OFFLINE_EVENTS WHERE id > :id ORDER BY id ASC limit 1")
fun getNextModifiedOrNewAfter(id: Long): Maybe<OfflineEvent>
@Query("SELECT * FROM $TABLE_OFFLINE_EVENTS WHERE id = :referenceId")
fun getCurrentFromHistoric(referenceId: Long): Maybe<OfflineEvent>
}

View file

@ -0,0 +1,18 @@
package info.nightscout.androidaps.database.daos.delegated
import info.nightscout.androidaps.database.daos.OfflineEventDao
import info.nightscout.androidaps.database.entities.OfflineEvent
import info.nightscout.androidaps.database.interfaces.DBEntry
internal class DelegatedOfflineEventDao(changes: MutableList<DBEntry>, private val dao: OfflineEventDao) : DelegatedDao(changes), OfflineEventDao by dao {
override fun insertNewEntry(entry: OfflineEvent): Long {
changes.add(entry)
return dao.insertNewEntry(entry)
}
override fun updateExistingEntry(entry: OfflineEvent): Long {
changes.add(entry)
return dao.updateExistingEntry(entry)
}
}

View file

@ -0,0 +1,64 @@
package info.nightscout.androidaps.database.entities
import androidx.room.Embedded
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.Index
import androidx.room.PrimaryKey
import info.nightscout.androidaps.database.TABLE_OFFLINE_EVENTS
import info.nightscout.androidaps.database.embedments.InterfaceIDs
import info.nightscout.androidaps.database.interfaces.DBEntryWithTimeAndDuration
import info.nightscout.androidaps.database.interfaces.TraceableDBEntry
import java.util.*
@Entity(tableName = TABLE_OFFLINE_EVENTS,
foreignKeys = [ForeignKey(
entity = OfflineEvent::class,
parentColumns = ["id"],
childColumns = ["referenceId"])],
indices = [
Index("id"),
Index("isValid"),
Index("nightscoutId"),
Index("referenceId"),
Index("timestamp")
])
data class OfflineEvent(
@PrimaryKey(autoGenerate = true)
override var id: Long = 0,
override var version: Int = 0,
override var dateCreated: Long = -1,
override var isValid: Boolean = true,
override var referenceId: Long? = null,
@Embedded
override var interfaceIDs_backing: InterfaceIDs? = InterfaceIDs(),
override var timestamp: Long,
override var utcOffset: Long = TimeZone.getDefault().getOffset(timestamp).toLong(),
var reason: Reason,
override var duration: Long // in millis
) : TraceableDBEntry, DBEntryWithTimeAndDuration {
fun contentEqualsTo(other: OfflineEvent): Boolean =
timestamp == other.timestamp &&
utcOffset == other.utcOffset &&
reason == other.reason &&
duration == other.duration &&
isValid == other.isValid
fun isRecordDeleted(other: OfflineEvent): Boolean =
isValid && !other.isValid
enum class Reason {
DISCONNECT_PUMP,
SUSPEND,
DISABLE_LOOP,
SUPER_BOLUS,
OTHER
;
companion object {
fun fromString(reason: String?) = values().firstOrNull { it.name == reason } ?: OTHER
}
}
}

View file

@ -43,6 +43,8 @@ data class UserEntry(
OPEN_LOOP_MODE (ColorGroup.Loop), OPEN_LOOP_MODE (ColorGroup.Loop),
LOOP_DISABLED (ColorGroup.Loop), LOOP_DISABLED (ColorGroup.Loop),
LOOP_ENABLED (ColorGroup.Loop), LOOP_ENABLED (ColorGroup.Loop),
LOOP_CHANGE (ColorGroup.Loop),
LOOP_REMOVED (ColorGroup.Loop),
RECONNECT (ColorGroup.Pump), RECONNECT (ColorGroup.Pump),
DISCONNECT (ColorGroup.Pump), DISCONNECT (ColorGroup.Pump),
RESUME (ColorGroup.Loop), RESUME (ColorGroup.Loop),

View file

@ -1,7 +1,5 @@
package info.nightscout.androidaps.database.entities package info.nightscout.androidaps.database.entities
import androidx.annotation.StringRes
sealed class ValueWithUnit { sealed class ValueWithUnit {
object UNKNOWN : ValueWithUnit() // formerly None used as fallback object UNKNOWN : ValueWithUnit() // formerly None used as fallback
@ -34,6 +32,8 @@ sealed class ValueWithUnit {
data class TherapyEventTTReason(val value: TemporaryTarget.Reason) : ValueWithUnit() data class TherapyEventTTReason(val value: TemporaryTarget.Reason) : ValueWithUnit()
data class OfflineEventReason(val value: OfflineEvent.Reason) : ValueWithUnit()
fun value(): Any? { fun value(): Any? {
return when(this) { return when(this) {
is Gram -> this.value is Gram -> this.value
@ -47,6 +47,7 @@ sealed class ValueWithUnit {
is SimpleString -> this.value is SimpleString -> this.value
is TherapyEventMeterType -> this.value is TherapyEventMeterType -> this.value
is TherapyEventTTReason -> this.value is TherapyEventTTReason -> this.value
is OfflineEventReason -> this.value
is TherapyEventType -> this.value is TherapyEventType -> this.value
is Timestamp -> this.value is Timestamp -> this.value
is UnitPerHour -> this.value is UnitPerHour -> this.value

View file

@ -0,0 +1,25 @@
package info.nightscout.androidaps.database.transactions
import info.nightscout.androidaps.database.entities.OfflineEvent
import info.nightscout.androidaps.database.interfaces.end
class CancelCurrentOfflineEventIfAnyTransaction(
val timestamp: Long
) : Transaction<CancelCurrentOfflineEventIfAnyTransaction.TransactionResult>() {
override fun run(): TransactionResult {
val result = TransactionResult()
val current = database.offlineEventDao.getOfflineEventActiveAt(timestamp).blockingGet()
if (current != null) {
current.end = timestamp
database.offlineEventDao.updateExistingEntry(current)
result.updated.add(current)
}
return result
}
class TransactionResult {
val updated = mutableListOf<OfflineEvent>()
}
}

View file

@ -0,0 +1,30 @@
package info.nightscout.androidaps.database.transactions
import info.nightscout.androidaps.database.entities.OfflineEvent
import info.nightscout.androidaps.database.interfaces.end
class InsertAndCancelCurrentOfflineEventTransaction(
val offlineEvent: OfflineEvent
) : Transaction<InsertAndCancelCurrentOfflineEventTransaction.TransactionResult>() {
constructor(timestamp: Long, duration: Long, reason: OfflineEvent.Reason) :
this(OfflineEvent(timestamp = timestamp, reason = reason, duration = duration))
override fun run(): TransactionResult {
val result = TransactionResult()
val current = database.offlineEventDao.getOfflineEventActiveAt(offlineEvent.timestamp).blockingGet()
if (current != null) {
current.end = offlineEvent.timestamp
database.offlineEventDao.updateExistingEntry(current)
result.updated.add(current)
}
database.offlineEventDao.insertNewEntry(offlineEvent)
result.inserted.add(offlineEvent)
return result
}
class TransactionResult {
val inserted = mutableListOf<OfflineEvent>()
val updated = mutableListOf<OfflineEvent>()
}
}

View file

@ -3,9 +3,9 @@ package info.nightscout.androidaps.database.transactions
import info.nightscout.androidaps.database.entities.TemporaryTarget import info.nightscout.androidaps.database.entities.TemporaryTarget
import info.nightscout.androidaps.database.interfaces.end import info.nightscout.androidaps.database.interfaces.end
class InsertTemporaryTargetAndCancelCurrentTransaction( class InsertAndCancelCurrentTemporaryTargetTransaction(
val temporaryTarget: TemporaryTarget val temporaryTarget: TemporaryTarget
) : Transaction<InsertTemporaryTargetAndCancelCurrentTransaction.TransactionResult>() { ) : Transaction<InsertAndCancelCurrentTemporaryTargetTransaction.TransactionResult>() {
constructor(timestamp: Long, duration: Long, reason: TemporaryTarget.Reason, lowTarget: Double, highTarget: Double) : constructor(timestamp: Long, duration: Long, reason: TemporaryTarget.Reason, lowTarget: Double, highTarget: Double) :
this(TemporaryTarget(timestamp = timestamp, reason = reason, lowTarget = lowTarget, highTarget = highTarget, duration = duration)) this(TemporaryTarget(timestamp = timestamp, reason = reason, lowTarget = lowTarget, highTarget = highTarget, duration = duration))

View file

@ -0,0 +1,10 @@
package info.nightscout.androidaps.database.transactions
class InvalidateOfflineEventTransaction(val id: Long) : Transaction<Unit>() {
override fun run() {
val offlineEvent = database.offlineEventDao.findById(id)
?: throw IllegalArgumentException("There is no such OfflineEvent with the specified ID.")
offlineEvent.isValid = false
database.offlineEventDao.updateExistingEntry(offlineEvent)
}
}

View file

@ -0,0 +1,73 @@
package info.nightscout.androidaps.database.transactions
import info.nightscout.androidaps.database.entities.OfflineEvent
import info.nightscout.androidaps.database.interfaces.end
import kotlin.math.abs
/**
* Sync the OfflineEvent from NS
*/
class SyncNsOfflineEventTransaction(private val offlineEvent: OfflineEvent, private val invalidateByNsOnly: Boolean) : Transaction<SyncNsOfflineEventTransaction.TransactionResult>() {
override fun run(): TransactionResult {
val result = TransactionResult()
if (offlineEvent.duration != 0L) {
// not ending event
val current: OfflineEvent? =
offlineEvent.interfaceIDs.nightscoutId?.let {
database.offlineEventDao.findByNSId(it)
}
if (current != null) {
// nsId exists, allow only invalidation
if (current.isValid && !offlineEvent.isValid) {
current.isValid = false
database.offlineEventDao.updateExistingEntry(current)
result.invalidated.add(current)
}
return result
}
if (invalidateByNsOnly) return result
// not known nsId
val running = database.offlineEventDao.getOfflineEventActiveAt(offlineEvent.timestamp).blockingGet()
if (running != null && abs(running.timestamp - offlineEvent.timestamp) < 1000 && running.interfaceIDs.nightscoutId == null) { // allow missing milliseconds
// the same record, update nsId only
running.interfaceIDs.nightscoutId = offlineEvent.interfaceIDs.nightscoutId
database.offlineEventDao.updateExistingEntry(running)
result.updatedNsId.add(running)
} else if (running != null) {
// another running record. end current and insert new
running.end = offlineEvent.timestamp
database.offlineEventDao.updateExistingEntry(running)
database.offlineEventDao.insertNewEntry(offlineEvent)
result.ended.add(running)
result.inserted.add(offlineEvent)
} else {
database.offlineEventDao.insertNewEntry(offlineEvent)
result.inserted.add(offlineEvent)
}
return result
} else {
// ending event
val running = database.offlineEventDao.getOfflineEventActiveAt(offlineEvent.timestamp).blockingGet()
if (running != null) {
running.end = offlineEvent.timestamp
database.offlineEventDao.updateExistingEntry(running)
result.ended.add(running)
}
}
return result
}
class TransactionResult {
val updatedNsId = mutableListOf<OfflineEvent>()
val inserted = mutableListOf<OfflineEvent>()
val invalidated = mutableListOf<OfflineEvent>()
val ended = mutableListOf<OfflineEvent>()
}
}

View file

@ -0,0 +1,12 @@
package info.nightscout.androidaps.database.transactions
import info.nightscout.androidaps.database.entities.OfflineEvent
class UpdateNsIdOfflineEventTransaction(val offlineEvent: OfflineEvent) : Transaction<Unit>() {
override fun run() {
val current = database.offlineEventDao.findById(offlineEvent.id)
if (current != null && current.interfaceIDs.nightscoutId != offlineEvent.interfaceIDs.nightscoutId)
database.offlineEventDao.updateExistingEntry(offlineEvent)
}
}

View file

@ -1,9 +0,0 @@
package info.nightscout.androidaps.database.transactions
import info.nightscout.androidaps.database.entities.TemporaryTarget
class UpdateTemporaryTargetTransaction(val temporaryTarget: TemporaryTarget) : Transaction<Unit>() {
override fun run() {
database.temporaryTargetDao.updateExistingEntry(temporaryTarget)
}
}