DeviceStatus

This commit is contained in:
Milos Kozak 2021-03-31 21:54:49 +02:00
parent 93b1b37021
commit a5ea0d9027
18 changed files with 3324 additions and 206 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

@ -32,6 +32,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
@ -48,6 +49,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
@ -78,7 +80,8 @@ open class LoopPlugin @Inject constructor(
private val fabricPrivacy: FabricPrivacy,
private val nsUpload: NSUpload,
private val dateUtil: DateUtil,
private val repository: AppRepository
private val repository: AppRepository,
private val runningConfiguration: RunningConfiguration
) : PluginBase(PluginDescription()
.mainType(PluginType.LOOP)
.fragmentClass(LoopFragment::class.java.name)
@ -316,7 +319,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)))
@ -489,7 +497,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) {
@ -497,7 +504,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

@ -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

@ -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;
}
}

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

@ -455,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

@ -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)
}
}