Merge branch 'meallink' into meallink_ValueWithUnit

# Conflicts:
#	app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.kt
This commit is contained in:
Philoul 2021-03-31 22:18:50 +02:00
commit 3d9ef71045
26 changed files with 3409 additions and 240 deletions

View file

@ -17,6 +17,7 @@ abstract class ReceiversModule {
@ContributesAndroidInjector abstract fun contributesChargingStateReceiver(): ChargingStateReceiver
@ContributesAndroidInjector abstract fun contributesDataReceiver(): DataReceiver
@ContributesAndroidInjector abstract fun contributesKeepAliveReceiver(): KeepAliveReceiver
@ContributesAndroidInjector abstract fun contributesKeepAliveWorker(): KeepAliveReceiver.KeepAliveWorker
@ContributesAndroidInjector abstract fun contributesRileyLinkBluetoothStateReceiver(): RileyLinkBluetoothStateReceiver
@ContributesAndroidInjector abstract fun contributesSmsReceiver(): SmsReceiver
@ContributesAndroidInjector abstract fun contributesTimeDateOrTZChangeReceiver(): TimeDateOrTZChangeReceiver

View file

@ -37,6 +37,7 @@ import info.nightscout.androidaps.plugins.aps.loop.events.EventLoopUpdateGui
import info.nightscout.androidaps.plugins.aps.loop.events.EventNewOpenLoopNotification
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.plugins.configBuilder.RunningConfiguration
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload
import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification
import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification
@ -53,6 +54,7 @@ import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.HardLimits
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.extensions.buildDeviceStatus
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.androidaps.utils.sharedPreferences.SP
@ -84,7 +86,8 @@ open class LoopPlugin @Inject constructor(
private val nsUpload: NSUpload,
private val dateUtil: DateUtil,
private val uel: UserEntryLogger,
private val repository: AppRepository
private val repository: AppRepository,
private val runningConfiguration: RunningConfiguration
) : PluginBase(PluginDescription()
.mainType(PluginType.LOOP)
.fragmentClass(LoopFragment::class.java.name)
@ -322,7 +325,12 @@ open class LoopPlugin @Inject constructor(
lastRun!!.lastTBRRequest = 0
lastRun!!.lastSMBEnact = 0
lastRun!!.lastSMBRequest = 0
nsUpload.uploadDeviceStatus(this, iobCobCalculatorPlugin, profileFunction, activePlugin.activePump, receiverStatusStore, BuildConfig.VERSION_NAME + "-" + BuildConfig.BUILDVERSION)
buildDeviceStatus(dateUtil, this, iobCobCalculatorPlugin, profileFunction,
activePlugin.activePump, receiverStatusStore, runningConfiguration,
BuildConfig.VERSION_NAME + "-" + BuildConfig.BUILDVERSION)?.also {
repository.insert(it)
}
if (isSuspended) {
aapsLogger.debug(LTag.APS, resourceHelper.gs(R.string.loopsuspended))
rxBus.send(EventLoopSetLastRunGui(resourceHelper.gs(R.string.loopsuspended)))
@ -495,7 +503,6 @@ open class LoopPlugin @Inject constructor(
fun acceptChangeRequest() {
val profile = profileFunction.getProfile()
val lp = this
applyTBRRequest(lastRun!!.constraintsProcessed, profile, object : Callback() {
override fun run() {
if (result.enacted) {
@ -503,7 +510,11 @@ open class LoopPlugin @Inject constructor(
lastRun!!.lastTBRRequest = lastRun!!.lastAPSRun
lastRun!!.lastTBREnact = DateUtil.now()
lastRun!!.lastOpenModeAccept = DateUtil.now()
nsUpload.uploadDeviceStatus(lp, iobCobCalculatorPlugin, profileFunction, activePlugin.activePump, receiverStatusStore, BuildConfig.VERSION_NAME + "-" + BuildConfig.BUILDVERSION)
buildDeviceStatus(dateUtil, this@LoopPlugin, iobCobCalculatorPlugin, profileFunction,
activePlugin.activePump, receiverStatusStore, runningConfiguration,
BuildConfig.VERSION_NAME + "-" + BuildConfig.BUILDVERSION)?.also {
repository.insert(it)
}
sp.incInt(R.string.key_ObjectivesmanualEnacts)
}
rxBus.send(EventAcceptOpenLoopChange())

View file

@ -13,6 +13,7 @@ import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.database.entities.UserEntry.ValueWithUnit
import info.nightscout.androidaps.databinding.MaintenanceFragmentBinding
import info.nightscout.androidaps.events.EventNewBG
import info.nightscout.androidaps.interfaces.DataSyncSelector
import info.nightscout.androidaps.interfaces.DatabaseHelperInterface
import info.nightscout.androidaps.interfaces.ImportExportPrefsInterface
import info.nightscout.androidaps.logging.AAPSLogger
@ -38,6 +39,7 @@ class MaintenanceFragment : DaggerFragment() {
@Inject lateinit var repository: AppRepository
@Inject lateinit var databaseHelper: DatabaseHelperInterface
@Inject lateinit var uel: UserEntryLogger
@Inject lateinit var dataSyncSelector: DataSyncSelector
private val compositeDisposable = CompositeDisposable()
@ -66,6 +68,7 @@ class MaintenanceFragment : DaggerFragment() {
fromAction {
databaseHelper.resetDatabases()
repository.clearDatabases()
dataSyncSelector.resetToNextFullSync()
}
.subscribeOn(aapsSchedulers.io)
.observeOn(aapsSchedulers.main)

View file

@ -2,6 +2,7 @@ package info.nightscout.androidaps.plugins.general.nsclient
import info.nightscout.androidaps.R
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.DeviceStatus
import info.nightscout.androidaps.database.entities.*
import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.DataSyncSelector
@ -28,6 +29,7 @@ class DataSyncSelectorImplementation @Inject constructor(
sp.remove(R.string.key_ns_bolus_last_synced_id)
sp.remove(R.string.key_ns_carbs_last_synced_id)
sp.remove(R.string.key_ns_bolus_calculator_result_last_synced_id)
sp.remove(R.string.key_ns_device_status_last_synced_id)
}
override fun confirmLastBolusIdIfGreater(lastSynced: Long) {
@ -294,4 +296,34 @@ class DataSyncSelectorImplementation @Inject constructor(
return false
}
override fun confirmLastDeviceStatusIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_device_status_last_synced_id, 0)) {
aapsLogger.debug(LTag.NSCLIENT, "Setting DeviceStatus data sync from $lastSynced")
sp.putLong(R.string.key_ns_device_status_last_synced_id, lastSynced)
}
}
override fun changedDeviceStatuses(): List<DeviceStatus> {
val startId = sp.getLong(R.string.key_ns_device_status_last_synced_id, 0)
return appRepository.getModifiedDeviceStatusDataFromId(startId).blockingGet().also {
aapsLogger.debug(LTag.NSCLIENT, "Loading DeviceStatus data for sync from $startId. Records ${it.size}")
}
}
override fun processChangedDeviceStatusesCompat(): Boolean {
val startId = sp.getLong(R.string.key_ns_device_status_last_synced_id, 0)
appRepository.getNextSyncElementDeviceStatus(startId).blockingGet()?.let { deviceStatus ->
aapsLogger.info(LTag.DATABASE, "Loading DeviceStatus data Start: $startId ID: ${deviceStatus.id}")
when {
// without nsId = create new
deviceStatus.interfaceIDs.nightscoutId == null ->
nsClientPlugin.nsClientService?.dbAdd("devicestatus", deviceStatus.toJson(), deviceStatus)
// with nsId = ignore
deviceStatus.interfaceIDs.nightscoutId != null -> Any()
}
return true
}
return false
}
}

View file

@ -32,10 +32,11 @@ import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.Config;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.database.AppRepository;
import info.nightscout.androidaps.database.entities.Carbs;
import info.nightscout.androidaps.database.entities.DeviceStatus;
import info.nightscout.androidaps.database.transactions.UpdateNsIdBolusCalculatorResultTransaction;
import info.nightscout.androidaps.database.transactions.UpdateNsIdBolusTransaction;
import info.nightscout.androidaps.database.transactions.UpdateNsIdCarbsTransaction;
import info.nightscout.androidaps.database.transactions.UpdateNsIdDeviceStatusTransaction;
import info.nightscout.androidaps.database.transactions.UpdateNsIdFoodTransaction;
import info.nightscout.androidaps.database.transactions.UpdateNsIdGlucoseValueTransaction;
import info.nightscout.androidaps.database.transactions.UpdateNsIdTemporaryTargetTransaction;
@ -339,6 +340,22 @@ public class NSClientService extends DaggerService {
dataSyncSelector.processChangedBolusCalculatorResultsCompat();
return;
}
if (ack.getOriginalObject() instanceof DeviceStatus) {
DeviceStatus deviceStatus = (DeviceStatus) ack.getOriginalObject();
deviceStatus.getInterfaceIDs().setNightscoutId(ack.getId());
disposable.add(repository.runTransactionForResult(new UpdateNsIdDeviceStatusTransaction(deviceStatus))
.observeOn(aapsSchedulers.getIo())
.subscribe(
result -> aapsLogger.debug(LTag.DATABASE, "Updated ns id of DeviceStatus " + deviceStatus),
error -> aapsLogger.error(LTag.DATABASE, "Updated ns id of DeviceStatus failed", error)
));
dataSyncSelector.confirmLastDeviceStatusIdIfGreater(deviceStatus.getId());
rxBus.send(new EventNSClientNewLog("DBADD", "Acked DeviceStatus" + deviceStatus.getInterfaceIDs().getNightscoutId()));
// Send new if waiting
dataSyncSelector.processChangedBolusCalculatorResultsCompat();
return;
}
// old way
if (ack.nsClientID != null) {
uploadQueue.removeByNsClientIdIfExists(ack.json);
@ -977,6 +994,7 @@ public class NSClientService extends DaggerService {
dataSyncSelector.processChangedTempTargetsCompat();
dataSyncSelector.processChangedFoodsCompat();
dataSyncSelector.processChangedTherapyEventsCompat();
dataSyncSelector.processChangedDeviceStatusesCompat();
if (uploadQueue.size() == 0)
return;

View file

@ -3,13 +3,19 @@ package info.nightscout.androidaps.receivers
import android.app.AlarmManager
import android.app.PendingIntent
import android.app.PendingIntent.CanceledException
import android.app.PendingIntent.FLAG_IMMUTABLE
import android.content.Context
import android.content.Intent
import android.os.PowerManager
import android.os.SystemClock
import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager
import androidx.work.Worker
import androidx.work.WorkerParameters
import dagger.android.DaggerBroadcastReceiver
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.BuildConfig
import info.nightscout.androidaps.Config
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.events.EventProfileNeedsUpdate
import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.CommandQueueProvider
@ -18,55 +24,124 @@ import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload
import info.nightscout.androidaps.plugins.configBuilder.RunningConfiguration
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
import info.nightscout.androidaps.queue.commands.Command
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.LocalAlertUtils
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.extensions.buildDeviceStatus
import javax.inject.Inject
import kotlin.math.abs
class KeepAliveReceiver : DaggerBroadcastReceiver() {
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var activePlugin: ActivePluginProvider
@Inject lateinit var commandQueue: CommandQueueProvider
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var loopPlugin: LoopPlugin
@Inject lateinit var iobCobCalculatorPlugin: IobCobCalculatorPlugin
@Inject lateinit var localAlertUtils: LocalAlertUtils
@Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var receiverStatusStore: ReceiverStatusStore
@Inject lateinit var config: Config
@Inject lateinit var nsUpload: NSUpload
@Inject lateinit var dateUtil: DateUtil
companion object {
private val KEEP_ALIVE_MILLISECONDS = T.mins(5).msecs()
private val STATUS_UPDATE_FREQUENCY = T.mins(15).msecs()
private val IOB_UPDATE_FREQUENCY_IN_MINS = 5L
private var lastReadStatus: Long = 0
private var lastRun: Long = 0
private var lastIobUpload: Long = 0
}
override fun onReceive(context: Context, intent: Intent) {
super.onReceive(context, intent)
aapsLogger.debug(LTag.CORE, "KeepAlive received")
val pm = context.getSystemService(Context.POWER_SERVICE) as PowerManager
val wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "AndroidAPS:KeepAliveReceiver")
wl.acquire(T.mins(2).msecs())
localAlertUtils.shortenSnoozeInterval()
localAlertUtils.checkStaleBGAlert()
checkPump()
checkAPS()
wl.release()
WorkManager.getInstance(context)
.enqueue(OneTimeWorkRequest.Builder(KeepAliveWorker::class.java).build())
}
class KeepAliveWorker(
context: Context,
params: WorkerParameters
) : Worker(context, params) {
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var localAlertUtils: LocalAlertUtils
@Inject lateinit var repository: AppRepository
@Inject lateinit var config: Config
@Inject lateinit var iobCobCalculatorPlugin: IobCobCalculatorPlugin
@Inject lateinit var loopPlugin: LoopPlugin
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var activePlugin: ActivePluginProvider
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var runningConfiguration: RunningConfiguration
@Inject lateinit var receiverStatusStore: ReceiverStatusStore
@Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var commandQueue: CommandQueueProvider
@Inject lateinit var fabricPrivacy: FabricPrivacy
init {
(context.applicationContext as HasAndroidInjector).androidInjector().inject(this)
}
companion object {
private val STATUS_UPDATE_FREQUENCY = T.mins(15).msecs()
private const val IOB_UPDATE_FREQUENCY_IN_MINUTES = 5L
private var lastReadStatus: Long = 0
private var lastRun: Long = 0
private var lastIobUpload: Long = 0
}
override fun doWork(): Result {
localAlertUtils.shortenSnoozeInterval()
localAlertUtils.checkStaleBGAlert()
checkPump()
checkAPS()
return Result.success()
}
// Usually deviceStatus is uploaded through LoopPlugin after every loop cycle.
// if there is no BG available, we have to upload anyway to have correct
// IOB displayed in NS
private fun checkAPS() {
var shouldUploadStatus = false
if (config.NSCLIENT) return
if (config.PUMPCONTROL) shouldUploadStatus = true
else if (!loopPlugin.isEnabled() || iobCobCalculatorPlugin.actualBg() == null)
shouldUploadStatus = true
else if (DateUtil.isOlderThan(activePlugin.activeAPS.lastAPSRun, 5)) shouldUploadStatus = true
if (DateUtil.isOlderThan(lastIobUpload, IOB_UPDATE_FREQUENCY_IN_MINUTES) && shouldUploadStatus) {
lastIobUpload = dateUtil._now()
buildDeviceStatus(dateUtil, loopPlugin, iobCobCalculatorPlugin, profileFunction,
activePlugin.activePump, receiverStatusStore, runningConfiguration,
BuildConfig.VERSION_NAME + "-" + BuildConfig.BUILDVERSION)?.also {
repository.insert(it)
}
}
}
private fun checkPump() {
val pump = activePlugin.activePump
val profile = profileFunction.getProfile() ?: return
val lastConnection = pump.lastDataTime()
val isStatusOutdated = lastConnection + STATUS_UPDATE_FREQUENCY < System.currentTimeMillis()
val isBasalOutdated = abs(profile.basal - pump.baseBasalRate) > pump.pumpDescription.basalStep
aapsLogger.debug(LTag.CORE, "Last connection: " + dateUtil.dateAndTimeString(lastConnection))
// sometimes keep alive broadcast stops
// as as workaround test if readStatus was requested before an alarm is generated
if (lastReadStatus != 0L && lastReadStatus > System.currentTimeMillis() - T.mins(5).msecs()) {
localAlertUtils.checkPumpUnreachableAlarm(lastConnection, isStatusOutdated, loopPlugin.isDisconnected)
}
if (!pump.isThisProfileSet(profile) && !commandQueue.isRunning(Command.CommandType.BASAL_PROFILE)) {
rxBus.send(EventProfileNeedsUpdate())
} else if (isStatusOutdated && !pump.isBusy()) {
lastReadStatus = System.currentTimeMillis()
commandQueue.readStatus("KeepAlive. Status outdated.", null)
} else if (isBasalOutdated && !pump.isBusy()) {
lastReadStatus = System.currentTimeMillis()
commandQueue.readStatus("KeepAlive. Basal outdated.", null)
}
if (lastRun != 0L && System.currentTimeMillis() - lastRun > T.mins(10).msecs()) {
aapsLogger.error(LTag.CORE, "KeepAlive fail")
fabricPrivacy.logCustom("KeepAliveFail")
}
lastRun = System.currentTimeMillis()
}
}
class KeepAliveManager @Inject constructor(
@ -82,7 +157,7 @@ class KeepAliveReceiver : DaggerBroadcastReceiver() {
localAlertUtils.preSnoozeAlarms()
val am = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
val i = Intent(context, KeepAliveReceiver::class.java)
val pi = PendingIntent.getBroadcast(context, 0, i, 0)
val pi = PendingIntent.getBroadcast(context, 0, i, FLAG_IMMUTABLE)
try {
pi.send()
} catch (e: CanceledException) {
@ -94,53 +169,9 @@ class KeepAliveReceiver : DaggerBroadcastReceiver() {
fun cancelAlarm(context: Context) {
aapsLogger.debug(LTag.CORE, "KeepAlive canceled")
val intent = Intent(context, KeepAliveReceiver::class.java)
val sender = PendingIntent.getBroadcast(context, 0, intent, 0)
val sender = PendingIntent.getBroadcast(context, 0, intent, FLAG_IMMUTABLE)
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
alarmManager.cancel(sender)
}
}
// Usually deviceStatus is uploaded through LoopPlugin after every loop cycle.
// if there is no BG available, we have to upload anyway to have correct
// IOB displayed in NS
private fun checkAPS() {
var shouldUploadStatus = false
if (config.NSCLIENT) return
if (config.PUMPCONTROL) shouldUploadStatus = true
else if (!loopPlugin.isEnabled() || iobCobCalculatorPlugin.actualBg() == null)
shouldUploadStatus = true
else if (DateUtil.isOlderThan(activePlugin.activeAPS.lastAPSRun, 5)) shouldUploadStatus = true
if (DateUtil.isOlderThan(lastIobUpload, IOB_UPDATE_FREQUENCY_IN_MINS) && shouldUploadStatus) {
lastIobUpload = DateUtil.now()
nsUpload.uploadDeviceStatus(loopPlugin, iobCobCalculatorPlugin, profileFunction, activePlugin.activePump, receiverStatusStore, BuildConfig.VERSION_NAME + "-" + BuildConfig.BUILDVERSION)
}
}
private fun checkPump() {
val pump = activePlugin.activePump
val profile = profileFunction.getProfile() ?: return
val lastConnection = pump.lastDataTime()
val isStatusOutdated = lastConnection + STATUS_UPDATE_FREQUENCY < System.currentTimeMillis()
val isBasalOutdated = abs(profile.basal - pump.baseBasalRate) > pump.pumpDescription.basalStep
aapsLogger.debug(LTag.CORE, "Last connection: " + dateUtil.dateAndTimeString(lastConnection))
// sometimes keep alive broadcast stops
// as as workaround test if readStatus was requested before an alarm is generated
if (lastReadStatus != 0L && lastReadStatus > System.currentTimeMillis() - T.mins(5).msecs()) {
localAlertUtils.checkPumpUnreachableAlarm(lastConnection, isStatusOutdated, loopPlugin.isDisconnected)
}
if (!pump.isThisProfileSet(profile) && !commandQueue.isRunning(Command.CommandType.BASAL_PROFILE)) {
rxBus.send(EventProfileNeedsUpdate())
} else if (isStatusOutdated && !pump.isBusy()) {
lastReadStatus = System.currentTimeMillis()
commandQueue.readStatus("KeepAlive. Status outdated.", null)
} else if (isBasalOutdated && !pump.isBusy()) {
lastReadStatus = System.currentTimeMillis()
commandQueue.readStatus("KeepAlive. Basal outdated.", null)
}
if (lastRun != 0L && System.currentTimeMillis() - lastRun > T.mins(10).msecs()) {
aapsLogger.error(LTag.CORE, "KeepAlive fail")
fabricPrivacy.logCustom("KeepAliveFail")
}
lastRun = System.currentTimeMillis()
}
}

View file

@ -43,6 +43,7 @@
<string name="key_ns_bolus_calculator_result_last_synced_id" translatable="false">ns_bolus_calculator_result_last_synced_id</string>
<string name="key_ns_carbs_last_synced_id" translatable="false">ns_carbs_last_synced_id</string>
<string name="key_ns_bolus_last_synced_id" translatable="false">ns_bolus_last_synced_id</string>
<string name="key_ns_device_status_last_synced_id" translatable="false">ns_device_status_last_synced_id</string>
<string name="treatmentssafety_title">Treatments safety</string>
<string name="treatmentssafety_maxbolus_title">Max allowed bolus [U]</string>

View file

@ -1,5 +1,6 @@
package info.nightscout.androidaps.interfaces
import info.nightscout.androidaps.database.entities.DeviceStatus
import info.nightscout.androidaps.database.entities.*
interface DataSyncSelector {
@ -48,4 +49,9 @@ interface DataSyncSelector {
fun changedFoods() : List<Food>
// Until NS v3
fun processChangedFoodsCompat(): Boolean
fun confirmLastDeviceStatusIdIfGreater(lastSynced: Long)
fun changedDeviceStatuses() : List<DeviceStatus>
// Until NS v3
fun processChangedDeviceStatusesCompat(): Boolean
}

View file

@ -38,6 +38,12 @@ interface PumpInterface {
val reservoirLevel: Double
val batteryLevel: Int // in percent as integer
/**
* Request a bolus to be delivered, carbs to be stored on pump or both.
*
* @param detailedBolusInfo it's the caller's responsibility to ensure the request can be satisfied by the pump,
* e.g. DBI will not contain carbs if the pump can't store carbs.
*/
fun deliverTreatment(detailedBolusInfo: DetailedBolusInfo): PumpEnactResult
fun stopBolusDelivering()
fun setTempBasalAbsolute(absoluteRate: Double, durationInMinutes: Int, profile: Profile, enforceNew: Boolean): PumpEnactResult

View file

@ -1,35 +1,22 @@
package info.nightscout.androidaps.plugins.general.nsclient;
import android.os.Build;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.Date;
import javax.inject.Inject;
import javax.inject.Singleton;
import info.nightscout.androidaps.core.R;
import info.nightscout.androidaps.data.DetailedBolusInfo;
import info.nightscout.androidaps.data.IobTotal;
import info.nightscout.androidaps.data.Profile;
import info.nightscout.androidaps.database.entities.TherapyEvent;
import info.nightscout.androidaps.db.DbRequest;
import info.nightscout.androidaps.db.ExtendedBolus;
import info.nightscout.androidaps.db.ProfileSwitch;
import info.nightscout.androidaps.db.TemporaryBasal;
import info.nightscout.androidaps.interfaces.IobCobCalculator;
import info.nightscout.androidaps.interfaces.LoopInterface;
import info.nightscout.androidaps.interfaces.ProfileFunction;
import info.nightscout.androidaps.interfaces.PumpInterface;
import info.nightscout.androidaps.interfaces.UploadQueueInterface;
import info.nightscout.androidaps.logging.AAPSLogger;
import info.nightscout.androidaps.logging.LTag;
import info.nightscout.androidaps.plugins.aps.loop.APSResult;
import info.nightscout.androidaps.plugins.aps.loop.DeviceStatus;
import info.nightscout.androidaps.plugins.configBuilder.RunningConfiguration;
import info.nightscout.androidaps.receivers.ReceiverStatusStore;
import info.nightscout.androidaps.utils.DateUtil;
import info.nightscout.androidaps.utils.sharedPreferences.SP;
@ -165,70 +152,6 @@ public class NSUpload {
}
}
public void uploadDeviceStatus(LoopInterface loopPlugin, IobCobCalculator iobCobCalculatorPlugin, ProfileFunction profileFunction, PumpInterface pumpInterface, ReceiverStatusStore receiverStatusStore, String version) {
Profile profile = profileFunction.getProfile();
String profileName = profileFunction.getProfileName();
if (profile == null) {
aapsLogger.error("Profile is null. Skipping upload");
return;
}
DeviceStatus deviceStatus = new DeviceStatus(aapsLogger);
try {
LoopInterface.LastRun lastRun = loopPlugin.getLastRun();
if (lastRun != null && lastRun.getLastAPSRun() > System.currentTimeMillis() - 300 * 1000L) {
// do not send if result is older than 1 min
APSResult apsResult = lastRun.getRequest();
apsResult.json().put("timestamp", DateUtil.toISOString(lastRun.getLastAPSRun()));
deviceStatus.suggested = apsResult.json();
deviceStatus.iob = lastRun.getRequest().getIob().json();
deviceStatus.iob.put("time", DateUtil.toISOString(lastRun.getLastAPSRun()));
JSONObject requested = new JSONObject();
if (lastRun.getTbrSetByPump() != null && lastRun.getTbrSetByPump().getEnacted()) { // enacted
deviceStatus.enacted = lastRun.getRequest().json();
deviceStatus.enacted.put("rate", lastRun.getTbrSetByPump().json(profile).get("rate"));
deviceStatus.enacted.put("duration", lastRun.getTbrSetByPump().json(profile).get("duration"));
deviceStatus.enacted.put("recieved", true);
requested.put("duration", lastRun.getRequest().getDuration());
requested.put("rate", lastRun.getRequest().getRate());
requested.put("temp", "absolute");
deviceStatus.enacted.put("requested", requested);
}
if (lastRun.getTbrSetByPump() != null && lastRun.getTbrSetByPump().getEnacted()) { // enacted
if (deviceStatus.enacted == null) {
deviceStatus.enacted = lastRun.getRequest().json();
}
deviceStatus.enacted.put("smb", lastRun.getTbrSetByPump().getBolusDelivered());
requested.put("smb", lastRun.getRequest().getSmb());
deviceStatus.enacted.put("requested", requested);
}
} else {
aapsLogger.debug(LTag.NSCLIENT, "OpenAPS data too old to upload, sending iob only");
IobTotal[] iob = iobCobCalculatorPlugin.calculateIobArrayInDia(profile);
if (iob.length > 0) {
deviceStatus.iob = iob[0].json();
deviceStatus.iob.put("time", DateUtil.toISOString(dateUtil._now()));
}
}
deviceStatus.device = "openaps://" + Build.MANUFACTURER + " " + Build.MODEL;
JSONObject pumpstatus = pumpInterface.getJSONStatus(profile, profileName, version);
deviceStatus.pump = pumpstatus;
deviceStatus.uploaderBattery = receiverStatusStore.getBatteryLevel();
deviceStatus.created_at = DateUtil.toISOString(new Date());
deviceStatus.configuration = runningConfiguration.configuration();
uploadQueue.add(new DbRequest("dbAdd", "devicestatus", deviceStatus.mongoRecord(), System.currentTimeMillis()));
} catch (JSONException e) {
aapsLogger.error("Unhandled exception", e);
}
}
public void uploadTreatmentRecord(DetailedBolusInfo detailedBolusInfo) {
throw new IllegalStateException("DO NOT USE");

View file

@ -1,12 +1,88 @@
package info.nightscout.androidaps.plugins.aps.loop;
package info.nightscout.androidaps.utils.extensions
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import android.os.Build
import info.nightscout.androidaps.database.entities.DeviceStatus
import info.nightscout.androidaps.interfaces.IobCobCalculator
import info.nightscout.androidaps.interfaces.LoopInterface
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.interfaces.PumpInterface
import info.nightscout.androidaps.plugins.configBuilder.RunningConfiguration
import info.nightscout.androidaps.receivers.ReceiverStatusStore
import info.nightscout.androidaps.utils.DateUtil
import org.json.JSONObject
import info.nightscout.androidaps.logging.AAPSLogger;
import info.nightscout.androidaps.logging.LTag;
import info.nightscout.androidaps.logging.StacktraceLoggerWrapper;
fun DeviceStatus.toJson(): JSONObject =
JSONObject()
.put("created_at", DateUtil.toISOString(timestamp))
.also {
if (device != null) it.put("device", device)
if (pump != null) it.put("pump", JSONObject(pump))
it.put("openaps", JSONObject().also { openaps ->
if (enacted != null) openaps.put("enacted", JSONObject(enacted))
if (suggested != null) openaps.put("suggested", JSONObject(suggested))
if (iob != null) openaps.put("iob", iob)
})
if (uploaderBattery != 0) it.put("uploaderBattery", uploaderBattery)
if (configuration != null) it.put("configuration", JSONObject(configuration))
}
fun buildDeviceStatus(
dateUtil: DateUtil,
loopPlugin: LoopInterface,
iobCobCalculatorPlugin: IobCobCalculator,
profileFunction: ProfileFunction,
pumpInterface: PumpInterface,
receiverStatusStore: ReceiverStatusStore,
runningConfiguration: RunningConfiguration,
version: String
): DeviceStatus? {
val profile = profileFunction.getProfile() ?: return null
val profileName = profileFunction.getProfileName() ?: return null
val lastRun = loopPlugin.lastRun
var apsResult: JSONObject? = null
var iob: JSONObject? = null
var enacted: JSONObject? = null
if (lastRun != null && lastRun.lastAPSRun > dateUtil._now() - 300 * 1000L) {
// do not send if result is older than 1 min
apsResult = lastRun.request?.json()?.also {
it.put("timestamp", DateUtil.toISOString(lastRun.lastAPSRun))
}
iob = lastRun.request?.iob?.json()?.also {
it.put("time", DateUtil.toISOString(lastRun.lastAPSRun))
}
val requested = JSONObject()
if (lastRun.tbrSetByPump?.enacted == true) { // enacted
enacted = lastRun.request?.json()?.also {
it.put("rate", lastRun.tbrSetByPump!!.json(profile)["rate"])
it.put("duration", lastRun.tbrSetByPump!!.json(profile)["duration"])
it.put("received", true)
}
requested.put("duration", lastRun.request?.duration)
requested.put("rate", lastRun.request?.rate)
requested.put("temp", "absolute")
requested.put("smb", lastRun.request?.smb)
enacted?.put("requested", requested)
enacted?.put("smb", lastRun.tbrSetByPump?.bolusDelivered)
}
} else {
val calcIob = iobCobCalculatorPlugin.calculateIobArrayInDia(profile)
if (calcIob.isNotEmpty()) {
iob = calcIob[0].json()
iob.put("time", DateUtil.toISOString(dateUtil._now()))
}
}
return DeviceStatus(
timestamp = dateUtil._now(),
suggested = apsResult?.toString(),
iob = iob?.toString(),
enacted = enacted?.toString(),
device = "openaps://" + Build.MANUFACTURER + " " + Build.MODEL,
pump = pumpInterface.getJSONStatus(profile, profileName, version).toString(),
uploaderBattery = receiverStatusStore.batteryLevel,
configuration = runningConfiguration.configuration().toString()
)
}
/*
{
@ -364,41 +440,3 @@ import info.nightscout.androidaps.logging.StacktraceLoggerWrapper;
"created_at": "2016-06-24T09:27:49.230Z"
}
*/
public class DeviceStatus {
private final AAPSLogger aapsLogger;
public String device = null;
public JSONObject pump = null;
public JSONObject enacted = null;
public JSONObject suggested = null;
public JSONObject iob = null;
public int uploaderBattery = 0;
public String created_at = null;
public JSONObject configuration = null;
public DeviceStatus(AAPSLogger aapsLogger) {
this.aapsLogger = aapsLogger;
}
public JSONObject mongoRecord() {
JSONObject record = new JSONObject();
try {
if (device != null) record.put("device", device);
if (pump != null) record.put("pump", pump);
JSONObject openaps = new JSONObject();
if (enacted != null) openaps.put("enacted", enacted);
if (suggested != null) openaps.put("suggested", suggested);
if (iob != null) openaps.put("iob", iob);
record.put("openaps", openaps);
if (uploaderBattery != 0) record.put("uploaderBattery", uploaderBattery);
if (created_at != null) record.put("created_at", created_at);
if (configuration != null) record.put("configuration", configuration);
} catch (JSONException e) {
aapsLogger.error("Unhandled exception", e);
}
return record;
}
}

View file

@ -71,7 +71,7 @@ public class DanaRKoreanPlugin extends AbstractDanaRPlugin {
PumpSync pumpSync,
FabricPrivacy fabricPrivacy
) {
super(injector, danaPump, resourceHelper, constraintChecker, aapsLogger, aapsSchedulers, commandQueue, rxBus, activePlugin, sp, dateUtil);
super(injector, danaPump, resourceHelper, constraintChecker, aapsLogger, aapsSchedulers, commandQueue, rxBus, activePlugin, sp, dateUtil, pumpSync);
this.aapsLogger = aapsLogger;
this.context = context;
this.resourceHelper = resourceHelper;

View file

@ -24,6 +24,7 @@ import info.nightscout.androidaps.events.EventAppExit;
import info.nightscout.androidaps.interfaces.ActivePluginProvider;
import info.nightscout.androidaps.interfaces.CommandQueueProvider;
import info.nightscout.androidaps.interfaces.Constraint;
import info.nightscout.androidaps.interfaces.PumpSync;
import info.nightscout.androidaps.logging.AAPSLogger;
import info.nightscout.androidaps.logging.LTag;
import info.nightscout.androidaps.plugins.bus.RxBusWrapper;
@ -69,9 +70,10 @@ public class DanaRv2Plugin extends AbstractDanaRPlugin {
CommandQueueProvider commandQueue,
DetailedBolusInfoStorage detailedBolusInfoStorage,
DateUtil dateUtil,
FabricPrivacy fabricPrivacy
FabricPrivacy fabricPrivacy,
PumpSync pumpSync
) {
super(injector, danaPump, resourceHelper, constraintChecker, aapsLogger, aapsSchedulers, commandQueue, rxBus, activePlugin, sp, dateUtil);
super(injector, danaPump, resourceHelper, constraintChecker, aapsLogger, aapsSchedulers, commandQueue, rxBus, activePlugin, sp, dateUtil, pumpSync);
this.aapsLogger = aapsLogger;
this.context = context;
this.resourceHelper = resourceHelper;

View file

@ -3,7 +3,6 @@ package info.nightscout.androidaps.danaRv2.comm
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.danar.R
import info.nightscout.androidaps.danar.comm.MessageBase
import info.nightscout.androidaps.data.DetailedBolusInfo
import info.nightscout.androidaps.db.ExtendedBolus
import info.nightscout.androidaps.db.Source
import info.nightscout.androidaps.db.TemporaryBasal
@ -91,26 +90,26 @@ class MsgHistoryEvents_v2 constructor(
info.nightscout.androidaps.dana.DanaPump.BOLUS -> {
val detailedBolusInfo = detailedBolusInfoStorage.findDetailedBolusInfo(datetime, param1 / 100.0)
?: DetailedBolusInfo()
detailedBolusInfo.bolusTimestamp = datetime
detailedBolusInfo.pumpType = PumpType.DANA_RV2
detailedBolusInfo.pumpSerial = danaPump.serialNumber
detailedBolusInfo.bolusPumpId = datetime
detailedBolusInfo.insulin = param1 / 100.0
val newRecord = activePlugin.activeTreatments.addToHistoryTreatment(detailedBolusInfo, false)
val newRecord = pumpSync.syncBolusWithPumpId(
timestamp = datetime,
amount = param1 / 100.0,
type = detailedBolusInfo?.bolusType,
pumpId = datetime,
pumpType = PumpType.DANA_RV2,
pumpSerial = danaPump.serialNumber)
aapsLogger.debug(LTag.PUMPBTCOMM, (if (newRecord) "**NEW** " else "") + "EVENT BOLUS (" + recordCode + ") " + dateUtil.dateAndTimeString(datetime) + " (" + datetime + ")" + " Bolus: " + param1 / 100.0 + "U Duration: " + param2 + "min")
status = "BOLUS " + dateUtil.timeString(datetime)
}
info.nightscout.androidaps.dana.DanaPump.DUALBOLUS -> {
val detailedBolusInfo = detailedBolusInfoStorage.findDetailedBolusInfo(datetime, param1 / 100.0)
?: DetailedBolusInfo()
detailedBolusInfo.bolusTimestamp = datetime
detailedBolusInfo.pumpType = PumpType.DANA_RV2
detailedBolusInfo.pumpSerial = danaPump.serialNumber
detailedBolusInfo.bolusPumpId = datetime
detailedBolusInfo.insulin = param1 / 100.0
val newRecord = activePlugin.activeTreatments.addToHistoryTreatment(detailedBolusInfo, false)
val newRecord = pumpSync.syncBolusWithPumpId(
timestamp = datetime,
amount = param1 / 100.0,
type = detailedBolusInfo?.bolusType,
pumpId = datetime,
pumpType = PumpType.DANA_RV2,
pumpSerial = danaPump.serialNumber)
aapsLogger.debug(LTag.PUMPBTCOMM, (if (newRecord) "**NEW** " else "") + "EVENT DUALBOLUS (" + recordCode + ") " + dateUtil.dateAndTimeString(datetime) + " (" + datetime + ")" + " Bolus: " + param1 / 100.0 + "U Duration: " + param2 + "min")
status = "DUALBOLUS " + dateUtil.timeString(datetime)
}
@ -155,13 +154,12 @@ class MsgHistoryEvents_v2 constructor(
}
info.nightscout.androidaps.dana.DanaPump.CARBS -> {
val emptyCarbsInfo = DetailedBolusInfo()
emptyCarbsInfo.carbs = param1.toDouble()
emptyCarbsInfo.carbsTimestamp = datetime
emptyCarbsInfo.pumpType = PumpType.DANA_RV2
emptyCarbsInfo.pumpSerial = danaPump.serialNumber
emptyCarbsInfo.carbsPumpId = datetime
val newRecord = activePlugin.activeTreatments.addToHistoryTreatment(emptyCarbsInfo, false)
val newRecord = pumpSync.syncCarbsWithTimestamp(
timestamp = datetime,
amount = param1.toDouble(),
pumpId = datetime,
pumpType = PumpType.DANA_RV2,
pumpSerial = danaPump.serialNumber)
aapsLogger.debug(LTag.PUMPBTCOMM, (if (newRecord) "**NEW** " else "") + "EVENT CARBS (" + recordCode + ") " + dateUtil.dateAndTimeString(datetime) + " (" + datetime + ")" + " Carbs: " + param1 + "g")
status = "CARBS " + dateUtil.timeString(datetime)
}

View file

@ -29,6 +29,7 @@ import info.nightscout.androidaps.interfaces.PluginType;
import info.nightscout.androidaps.interfaces.PumpDescription;
import info.nightscout.androidaps.interfaces.PumpInterface;
import info.nightscout.androidaps.interfaces.PumpPluginBase;
import info.nightscout.androidaps.interfaces.PumpSync;
import info.nightscout.androidaps.logging.AAPSLogger;
import info.nightscout.androidaps.logging.LTag;
import info.nightscout.androidaps.plugins.bus.RxBusWrapper;
@ -64,6 +65,7 @@ public abstract class AbstractDanaRPlugin extends PumpPluginBase implements Pump
protected SP sp;
protected DateUtil dateUtil;
protected AapsSchedulers aapsSchedulers;
protected PumpSync pumpSync;
protected AbstractDanaRPlugin(
HasAndroidInjector injector,
@ -76,7 +78,8 @@ public abstract class AbstractDanaRPlugin extends PumpPluginBase implements Pump
RxBusWrapper rxBus,
ActivePluginProvider activePlugin,
SP sp,
DateUtil dateUtil
DateUtil dateUtil,
PumpSync pumpSync
) {
super(new PluginDescription()
.mainType(PluginType.PUMP)
@ -95,6 +98,7 @@ public abstract class AbstractDanaRPlugin extends PumpPluginBase implements Pump
this.sp = sp;
this.dateUtil = dateUtil;
this.aapsSchedulers = aapsSchedulers;
this.pumpSync = pumpSync;
}
@Override protected void onStart() {

View file

@ -25,6 +25,7 @@ import info.nightscout.androidaps.interfaces.ActivePluginProvider;
import info.nightscout.androidaps.interfaces.CommandQueueProvider;
import info.nightscout.androidaps.interfaces.Constraint;
import info.nightscout.androidaps.interfaces.PluginType;
import info.nightscout.androidaps.interfaces.PumpSync;
import info.nightscout.androidaps.logging.AAPSLogger;
import info.nightscout.androidaps.logging.LTag;
import info.nightscout.androidaps.plugins.bus.RxBusWrapper;
@ -34,6 +35,7 @@ import info.nightscout.androidaps.plugins.pump.common.defs.PumpType;
import info.nightscout.androidaps.utils.DateUtil;
import info.nightscout.androidaps.utils.FabricPrivacy;
import info.nightscout.androidaps.utils.Round;
import info.nightscout.androidaps.utils.T;
import info.nightscout.androidaps.utils.resources.ResourceHelper;
import info.nightscout.androidaps.utils.rx.AapsSchedulers;
import info.nightscout.androidaps.utils.sharedPreferences.SP;
@ -63,9 +65,10 @@ public class DanaRPlugin extends AbstractDanaRPlugin {
CommandQueueProvider commandQueue,
DanaPump danaPump,
DateUtil dateUtil,
FabricPrivacy fabricPrivacy
FabricPrivacy fabricPrivacy,
PumpSync pumpSync
) {
super(injector, danaPump, resourceHelper, constraintChecker, aapsLogger, aapsSchedulers, commandQueue, rxBus, activePlugin, sp, dateUtil);
super(injector, danaPump, resourceHelper, constraintChecker, aapsLogger, aapsSchedulers, commandQueue, rxBus, activePlugin, sp, dateUtil, pumpSync);
this.aapsLogger = aapsLogger;
this.context = context;
this.resourceHelper = resourceHelper;
@ -176,7 +179,21 @@ public class DanaRPlugin extends AbstractDanaRPlugin {
aapsLogger.debug(LTag.PUMP, "deliverTreatment: OK. Asked: " + detailedBolusInfo.insulin + " Delivered: " + result.getBolusDelivered());
detailedBolusInfo.insulin = t.insulin;
detailedBolusInfo.timestamp = System.currentTimeMillis();
activePlugin.getActiveTreatments().addToHistoryTreatment(detailedBolusInfo, false);
if (detailedBolusInfo.insulin > 0)
pumpSync.syncBolusWithPumpId(
detailedBolusInfo.timestamp,
detailedBolusInfo.insulin,
detailedBolusInfo.getBolusType(),
dateUtil._now(),
PumpType.DANA_R,
serialNumber());
if (detailedBolusInfo.carbs > 0)
pumpSync.syncCarbsWithTimestamp(
detailedBolusInfo.timestamp + T.mins(detailedBolusInfo.carbTime).msecs(),
detailedBolusInfo.carbs,
null,
PumpType.DANA_R,
serialNumber());
return result;
} else {
PumpEnactResult result = new PumpEnactResult(getInjector());

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.entities.*
const val DATABASE_VERSION = 9
const val DATABASE_VERSION = 10
@Database(version = DATABASE_VERSION,
entities = [APSResult::class, Bolus::class, BolusCalculatorResult::class, Carbs::class,
EffectiveProfileSwitch::class, ExtendedBolus::class, GlucoseValue::class, ProfileSwitch::class,
TemporaryBasal::class, TemporaryTarget::class, TherapyEvent::class, TotalDailyDose::class, APSResultLink::class,
MultiwaveBolusLink::class, PreferenceChange::class, VersionChange::class, UserEntry::class,
Food::class],
Food::class, DeviceStatus::class],
exportSchema = true)
@TypeConverters(Converters::class)
internal abstract class AppDatabase : RoomDatabase() {
@ -54,4 +54,6 @@ internal abstract class AppDatabase : RoomDatabase() {
abstract val foodDao: FoodDao
abstract val deviceStatusDao: DeviceStatusDao
}

View file

@ -312,7 +312,6 @@ open class AppRepository @Inject internal constructor(
// CARBS
val timeBackForExpand = 8 * 60 * 60 * 1000
private fun expandCarbs(carbs: Carbs): List<Carbs> =
if (carbs.duration == 0L) {
listOf(carbs)
@ -330,6 +329,8 @@ open class AppRepository @Inject internal constructor(
private fun Single<List<Carbs>>.expand() = this.map { it.map(::expandCarbs).flatten() }
private fun Single<List<Carbs>>.filterOutExtended() = this.map { it.filter { c -> c.duration == 0L } }
private fun Single<List<Carbs>>.fromTo(from: Long, to: Long) = this.map { it.filter { c -> c.timestamp in from..to } }
private fun Single<List<Carbs>>.until(to: Long) = this.map { it.filter { c -> c.timestamp <= to } }
private fun Single<List<Carbs>>.from(start: Long) = this.map { it.filter { c -> c.timestamp >= start } }
private fun Single<List<Carbs>>.sort() = this.map { it.sortedBy { c -> c.timestamp } }
/*
@ -375,8 +376,9 @@ open class AppRepository @Inject internal constructor(
.subscribeOn(Schedulers.io())
fun getCarbsDataFromTimeExpanded(timestamp: Long, ascending: Boolean): Single<List<Carbs>> =
database.carbsDao.getCarbsFromTime(timestamp - timeBackForExpand)
database.carbsDao.getCarbsFromTimeExpandable(timestamp)
.expand()
.from(timestamp)
.map { if (!ascending) it.reversed() else it }
.subscribeOn(Schedulers.io())
@ -386,7 +388,7 @@ open class AppRepository @Inject internal constructor(
.subscribeOn(Schedulers.io())
fun getCarbsDataFromTimeToTimeExpanded(from: Long, to: Long, ascending: Boolean): Single<List<Carbs>> =
database.carbsDao.getCarbsFromTimeToTime(from - timeBackForExpand, to)
database.carbsDao.getCarbsFromTimeToTimeExpandable(from, to)
.expand()
.fromTo(from, to)
.sort()
@ -394,12 +396,19 @@ open class AppRepository @Inject internal constructor(
.subscribeOn(Schedulers.io())
fun getCarbsIncludingInvalidFromTime(timestamp: Long, ascending: Boolean): Single<List<Carbs>> =
database.carbsDao.getCarbsIncludingInvalidFromTime(timestamp - timeBackForExpand)
database.carbsDao.getCarbsIncludingInvalidFromTime(timestamp)
.map { if (!ascending) it.reversed() else it }
.subscribeOn(Schedulers.io())
fun getCarbsIncludingInvalidFromTimeExpanded(timestamp: Long, ascending: Boolean): Single<List<Carbs>> =
database.carbsDao.getCarbsIncludingInvalidFromTimeExpandable(timestamp)
.expand()
.from(timestamp)
.map { if (!ascending) it.reversed() else it }
.subscribeOn(Schedulers.io())
fun getCarbsIncludingInvalidFromTimeToTimeExpanded(from: Long, to: Long, ascending: Boolean): Single<List<Carbs>> =
database.carbsDao.getCarbsIncludingInvalidFromTimeToTime(from - timeBackForExpand, to)
database.carbsDao.getCarbsIncludingInvalidFromTimeToTimeExpandable(from, to)
.expand()
.fromTo(from, to)
.sort()
@ -446,6 +455,26 @@ open class AppRepository @Inject internal constructor(
fun deleteAllBolusCalculatorResults() =
database.bolusCalculatorResultDao.deleteAllEntries()
// DEVICE STATUS
fun insert(deviceStatus: DeviceStatus) : Long =
database.deviceStatusDao.insert(deviceStatus)
/*
* 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 getNextSyncElementDeviceStatus(id: Long): Maybe<DeviceStatus> =
database.deviceStatusDao.getNextModifiedOrNewAfter(id)
.subscribeOn(Schedulers.io())
fun getModifiedDeviceStatusDataFromId(lastId: Long): Single<List<DeviceStatus>> =
database.deviceStatusDao.getModifiedFrom(lastId)
.subscribeOn(Schedulers.io())
}
@Suppress("USELESS_CAST")

View file

@ -24,5 +24,6 @@ internal class DelegatedAppDatabase(val changes: MutableList<DBEntry>, val datab
val userEntryDao: UserEntryDao = DelegatedUserEntryDao(changes, database.userEntryDao)
val preferenceChangeDao: PreferenceChangeDao = DelegatedPreferenceChangeDao(changes, database.preferenceChangeDao)
val foodDao: FoodDao = DelegatedFoodDao(changes, database.foodDao)
val deviceStatusDao: DeviceStatusDao = DelegatedDeviceStatusDao(changes, database.deviceStatusDao)
fun clearAllTables() = database.clearAllTables()
}

View file

@ -5,11 +5,11 @@ const val TABLE_APS_RESULT_LINKS = "apsResultLinks"
const val TABLE_BOLUSES = "boluses"
const val TABLE_BOLUS_CALCULATOR_RESULTS = "bolusCalculatorResults"
const val TABLE_CARBS = "carbs"
const val TABLE_DEVICE_STATUS = "deviceStatus"
const val TABLE_EFFECTIVE_PROFILE_SWITCHES = "effectiveProfileSwitches"
const val TABLE_EXTENDED_BOLUSES = "extendedBoluses"
const val TABLE_GLUCOSE_VALUES = "glucoseValues"
const val TABLE_FOODS = "foods"
const val TABLE_MEAL_LINKS = "mealLinks"
const val TABLE_MULTIWAVE_BOLUS_LINKS = "multiwaveBolusLinks"
const val TABLE_PROFILE_SWITCHES = "profileSwitches"
const val TABLE_TEMPORARY_BASALS = "temporaryBasals"
@ -18,4 +18,4 @@ const val TABLE_TOTAL_DAILY_DOSES = "totalDailyDoses"
const val TABLE_THERAPY_EVENTS = "therapyEvents"
const val TABLE_PREFERENCE_CHANGES = "preferenceChanges"
const val TABLE_VERSION_CHANGES = "versionChanges"
const val TABLE_USER_ENTRY = "userEntry"
const val TABLE_USER_ENTRY = "userEntry"

View file

@ -35,15 +35,27 @@ internal interface CarbsDao : TraceableDao<Carbs> {
@Query("SELECT * FROM $TABLE_CARBS WHERE isValid = 1 AND timestamp >= :timestamp AND referenceId IS NULL ORDER BY id DESC")
fun getCarbsFromTime(timestamp: Long): Single<List<Carbs>>
@Query("SELECT * FROM $TABLE_CARBS WHERE isValid = 1 AND timestamp + duration >= :timestamp AND referenceId IS NULL ORDER BY id DESC")
fun getCarbsFromTimeExpandable(timestamp: Long): Single<List<Carbs>>
@Query("SELECT * FROM $TABLE_CARBS WHERE isValid = 1 AND timestamp >= :from AND timestamp <= :to AND referenceId IS NULL ORDER BY id DESC")
fun getCarbsFromTimeToTime(from: Long, to: Long): Single<List<Carbs>>
@Query("SELECT * FROM $TABLE_CARBS WHERE isValid = 1 AND timestamp + duration >= :from AND timestamp <= :to AND referenceId IS NULL ORDER BY id DESC")
fun getCarbsFromTimeToTimeExpandable(from: Long, to: Long): Single<List<Carbs>>
@Query("SELECT * FROM $TABLE_CARBS WHERE timestamp >= :timestamp AND referenceId IS NULL ORDER BY id DESC")
fun getCarbsIncludingInvalidFromTime(timestamp: Long): Single<List<Carbs>>
@Query("SELECT * FROM $TABLE_CARBS WHERE timestamp + duration >= :timestamp AND referenceId IS NULL ORDER BY id DESC")
fun getCarbsIncludingInvalidFromTimeExpandable(timestamp: Long): Single<List<Carbs>>
@Query("SELECT * FROM $TABLE_CARBS WHERE timestamp >= :from AND timestamp <= :to AND referenceId IS NULL ORDER BY id DESC")
fun getCarbsIncludingInvalidFromTimeToTime(from: Long, to: Long): Single<List<Carbs>>
@Query("SELECT * FROM $TABLE_CARBS WHERE timestamp + duration >= :from AND timestamp <= :to AND referenceId IS NULL ORDER BY id DESC")
fun getCarbsIncludingInvalidFromTimeToTimeExpandable(from: Long, to: Long): Single<List<Carbs>>
// This query will be used with v3 to get all changed records
@Query("SELECT * FROM $TABLE_CARBS WHERE id > :id AND referenceId IS NULL OR id IN (SELECT DISTINCT referenceId FROM $TABLE_CARBS WHERE id > :id) ORDER BY id ASC")
fun getModifiedFrom(id: Long): Single<List<Carbs>>

View file

@ -0,0 +1,41 @@
package info.nightscout.androidaps.database.daos
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Update
import info.nightscout.androidaps.database.entities.DeviceStatus
import info.nightscout.androidaps.database.TABLE_DEVICE_STATUS
import io.reactivex.Maybe
import io.reactivex.Single
@Suppress("FunctionName")
@Dao
internal interface DeviceStatusDao {
@Insert
fun insert(entry: DeviceStatus): Long
@Update
fun update(entry: DeviceStatus)
@Query("SELECT * FROM $TABLE_DEVICE_STATUS WHERE id = :id")
fun findById(id: Long): DeviceStatus?
@Query("DELETE FROM $TABLE_DEVICE_STATUS")
fun deleteAllEntries()
@Query("DELETE FROM $TABLE_DEVICE_STATUS WHERE id NOT IN (SELECT MAX(id) FROM $TABLE_DEVICE_STATUS)")
fun deleteAllEntriesExceptLast()
@Query("SELECT * FROM $TABLE_DEVICE_STATUS WHERE nightscoutId = :nsId")
fun findByNSId(nsId: String): DeviceStatus?
// This query will be used with v3 to get all changed records
@Query("SELECT * FROM $TABLE_DEVICE_STATUS WHERE id > :id ORDER BY id ASC")
fun getModifiedFrom(id: Long): Single<List<DeviceStatus>>
// for WS we need 1 record only
@Query("SELECT * FROM $TABLE_DEVICE_STATUS WHERE id > :id ORDER BY id ASC limit 1")
fun getNextModifiedOrNewAfter(id: Long): Maybe<DeviceStatus>
}

View file

@ -0,0 +1,6 @@
package info.nightscout.androidaps.database.daos.delegated
import info.nightscout.androidaps.database.daos.DeviceStatusDao
import info.nightscout.androidaps.database.interfaces.DBEntry
internal class DelegatedDeviceStatusDao(changes: MutableList<DBEntry>, private val dao: DeviceStatusDao) : DelegatedDao(changes), DeviceStatusDao by dao

View file

@ -0,0 +1,44 @@
package info.nightscout.androidaps.database.entities
import androidx.room.Embedded
import androidx.room.Entity
import androidx.room.Index
import androidx.room.PrimaryKey
import info.nightscout.androidaps.database.TABLE_DEVICE_STATUS
import info.nightscout.androidaps.database.embedments.InterfaceIDs
import info.nightscout.androidaps.database.interfaces.DBEntryWithTime
import java.util.*
@Entity(tableName = TABLE_DEVICE_STATUS,
foreignKeys = [],
indices = [Index("timestamp")])
data class DeviceStatus(
@PrimaryKey(autoGenerate = true)
var id: Long = 0,
@Embedded
var interfaceIDs_backing: InterfaceIDs? = null,
override var timestamp: Long,
override var utcOffset: Long = TimeZone.getDefault().getOffset(timestamp).toLong(),
var device: String? = null,
var pump: String? = null,
var enacted: String? = null,
var suggested: String? = null,
var iob: String? = null,
var uploaderBattery: Int = 0,
var configuration: String? = null
) : DBEntryWithTime {
var interfaceIDs: InterfaceIDs
get() {
var value = this.interfaceIDs_backing
if (value == null) {
value = InterfaceIDs()
interfaceIDs_backing = value
}
return value
}
set(value) {
interfaceIDs_backing = value
}
}

View file

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