migrate BG code

This commit is contained in:
Milos Kozak 2021-02-06 00:30:27 +01:00
parent cfe23cef96
commit 7763a96d6a
49 changed files with 978 additions and 962 deletions

View file

@ -18,6 +18,9 @@ import javax.inject.Inject;
import dagger.android.AndroidInjector;
import dagger.android.DaggerApplication;
import info.nightscout.androidaps.database.AppRepository;
import info.nightscout.androidaps.database.transactions.VersionChangeTransaction;
import info.nightscout.androidaps.db.CompatDBHelper;
import info.nightscout.androidaps.db.DatabaseHelper;
import info.nightscout.androidaps.db.StaticInjector;
import info.nightscout.androidaps.dependencyInjection.DaggerAppComponent;
@ -38,11 +41,14 @@ import info.nightscout.androidaps.services.Intents;
import info.nightscout.androidaps.utils.ActivityMonitor;
import info.nightscout.androidaps.utils.locale.LocaleHelper;
import info.nightscout.androidaps.utils.sharedPreferences.SP;
import io.reactivex.disposables.CompositeDisposable;
public class MainApp extends DaggerApplication {
static DatabaseHelper sDatabaseHelper = null;
private final CompositeDisposable disposable = new CompositeDisposable();
@Inject PluginStore pluginStore;
@Inject AAPSLogger aapsLogger;
@Inject ActivityMonitor activityMonitor;
@ -54,6 +60,8 @@ public class MainApp extends DaggerApplication {
@Inject ConfigBuilderPlugin configBuilderPlugin;
@Inject KeepAliveReceiver.KeepAliveManager keepAliveManager;
@Inject List<PluginBase> plugins;
@Inject CompatDBHelper compatDBHelper;
@Inject AppRepository repository;
@Inject StaticInjector staticInjector; // TODO avoid , here fake only to initialize
@ -73,6 +81,15 @@ public class MainApp extends DaggerApplication {
aapsLogger.error("Uncaught exception crashing app", ex);
});
*/
String gitRemote = BuildConfig.REMOTE;
String commitHash = BuildConfig.HEAD;
if (gitRemote.contains("NoGitSystemAvailable")) {
gitRemote = null;
commitHash = null;
}
disposable.add(repository.runTransaction(new VersionChangeTransaction(BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE, gitRemote, commitHash)).subscribe());
disposable.add(compatDBHelper.dbChangeDisposable());
registerActivityLifecycleCallbacks(activityMonitor);
JodaTimeAndroid.init(this);
@ -120,7 +137,7 @@ public class MainApp extends DaggerApplication {
filter.addAction(Intents.ACTION_NEW_TREATMENT);
filter.addAction(Intents.ACTION_CHANGED_TREATMENT);
filter.addAction(Intents.ACTION_REMOVED_TREATMENT);
filter.addAction(Intents.ACTION_NEW_SGV);
//filter.addAction(Intents.ACTION_NEW_SGV);
filter.addAction(Intents.ACTION_NEW_PROFILE);
filter.addAction(Intents.ACTION_NEW_MBG);
filter.addAction(Intents.ACTION_NEW_CAL);

View file

@ -0,0 +1,43 @@
package info.nightscout.androidaps.db
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.GlucoseValue
import info.nightscout.androidaps.database.entities.TemporaryTarget
import info.nightscout.androidaps.database.interfaces.TraceableDBEntry
import info.nightscout.androidaps.events.EventNewBG
import info.nightscout.androidaps.events.EventTempTargetChange
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventNewHistoryData
import io.reactivex.disposables.Disposable
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class CompatDBHelper @Inject constructor(
val aapsLogger: AAPSLogger,
val repository: AppRepository,
val rxBus: RxBusWrapper
) {
fun dbChangeDisposable(): Disposable = repository
.changeObservable()
.doOnSubscribe {
rxBus.send(EventNewBG(null))
}
.subscribe {
it.filterIsInstance<GlucoseValue>().firstOrNull()?.let {
aapsLogger.debug(LTag.DATABASE, "Firing EventNewHistoryData")
rxBus.send(EventNewHistoryData(it.timestamp))
}
it.filterIsInstance<GlucoseValue>().lastOrNull()?.let {
aapsLogger.debug(LTag.DATABASE, "Firing EventNewBg")
rxBus.send(EventNewBG(it))
}
it.filterIsInstance<TemporaryTarget>().firstOrNull()?.let {
aapsLogger.debug(LTag.DATABASE, "Firing EventTempTargetChange")
rxBus.send(EventTempTargetChange())
}
}
}

View file

@ -33,13 +33,11 @@ import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import info.nightscout.androidaps.Constants;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.dana.comm.RecordTypes;
import info.nightscout.androidaps.data.NonOverlappingIntervals;
import info.nightscout.androidaps.data.Profile;
import info.nightscout.androidaps.events.EventCareportalEventChange;
import info.nightscout.androidaps.events.EventExtendedBolusChange;
import info.nightscout.androidaps.events.EventNewBG;
import info.nightscout.androidaps.events.EventProfileNeedsUpdate;
import info.nightscout.androidaps.events.EventRefreshOverview;
import info.nightscout.androidaps.events.EventReloadProfileSwitchData;
@ -54,9 +52,7 @@ import info.nightscout.androidaps.logging.AAPSLogger;
import info.nightscout.androidaps.logging.LTag;
import info.nightscout.androidaps.plugins.bus.RxBusWrapper;
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload;
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventNewHistoryBgData;
import info.nightscout.androidaps.plugins.general.openhumans.OpenHumansUploader;
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin;
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventNewHistoryData;
import info.nightscout.androidaps.plugins.pump.insight.database.InsightBolusID;
import info.nightscout.androidaps.plugins.pump.insight.database.InsightHistoryOffset;
@ -64,7 +60,6 @@ import info.nightscout.androidaps.plugins.pump.insight.database.InsightPumpID;
import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin;
import info.nightscout.androidaps.utils.JsonHelper;
import info.nightscout.androidaps.utils.PercentageSplitter;
import info.nightscout.androidaps.utils.T;
/**
* This Helper contains all resource to provide a central DB management functionality. Only methods handling
@ -81,7 +76,6 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
@Inject OpenHumansUploader openHumansUploader;
public static final String DATABASE_NAME = "AndroidAPSDb";
public static final String DATABASE_BGREADINGS = "BgReadings";
public static final String DATABASE_TEMPORARYBASALS = "TemporaryBasals";
public static final String DATABASE_EXTENDEDBOLUSES = "ExtendedBoluses";
public static final String DATABASE_TEMPTARGETS = "TempTargets";
@ -101,10 +95,6 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
private static final ScheduledExecutorService bgWorker = Executors.newSingleThreadScheduledExecutor();
private static ScheduledFuture<?> scheduledBgPost = null;
private static final ScheduledExecutorService bgHistoryWorker = Executors.newSingleThreadScheduledExecutor();
private static ScheduledFuture<?> scheduledBgHistoryPost = null;
private static long oldestBgHistoryChange = 0;
private static final ScheduledExecutorService tempBasalsWorker = Executors.newSingleThreadScheduledExecutor();
private static ScheduledFuture<?> scheduledTemBasalsPost = null;
@ -135,7 +125,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
try {
aapsLogger.info(LTag.DATABASE, "onCreate");
TableUtils.createTableIfNotExists(connectionSource, TempTarget.class);
TableUtils.createTableIfNotExists(connectionSource, BgReading.class);
//TableUtils.createTableIfNotExists(connectionSource, BgReading.class);
TableUtils.createTableIfNotExists(connectionSource, DanaRHistoryRecord.class);
TableUtils.createTableIfNotExists(connectionSource, DbRequest.class);
TableUtils.createTableIfNotExists(connectionSource, TemporaryBasal.class);
@ -167,7 +157,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
if (oldVersion < 7) {
aapsLogger.info(LTag.DATABASE, "onUpgrade");
TableUtils.dropTable(connectionSource, TempTarget.class, true);
TableUtils.dropTable(connectionSource, BgReading.class, true);
//TableUtils.dropTable(connectionSource, BgReading.class, true);
TableUtils.dropTable(connectionSource, DanaRHistoryRecord.class, true);
TableUtils.dropTable(connectionSource, DbRequest.class, true);
TableUtils.dropTable(connectionSource, TemporaryBasal.class, true);
@ -217,7 +207,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
public void resetDatabases() {
try {
TableUtils.dropTable(connectionSource, TempTarget.class, true);
TableUtils.dropTable(connectionSource, BgReading.class, true);
//TableUtils.dropTable(connectionSource, BgReading.class, true);
TableUtils.dropTable(connectionSource, DanaRHistoryRecord.class, true);
TableUtils.dropTable(connectionSource, DbRequest.class, true);
TableUtils.dropTable(connectionSource, TemporaryBasal.class, true);
@ -241,7 +231,6 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
aapsLogger.error("Unhandled exception", e);
}
virtualPumpPlugin.setFakingStatus(true);
scheduleBgChange(null); // trigger refresh
scheduleTemporaryBasalChange();
scheduleExtendedBolusChange();
scheduleTemporaryTargetChange();
@ -384,136 +373,6 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
aapsLogger.debug(LTag.DATABASE, "Rounding " + date + " to " + rounded);
return rounded;
}
// ------------------- BgReading handling -----------------------
public boolean createIfNotExists(BgReading bgReading, String from) {
try {
bgReading.date = roundDateToSec(bgReading.date);
BgReading old = getDaoBgReadings().queryForId(bgReading.date);
if (old == null) {
getDaoBgReadings().create(bgReading);
openHumansUploader.enqueueBGReading(bgReading);
aapsLogger.debug(LTag.DATABASE, "BG: New record from: " + from + " " + bgReading.toString());
scheduleBgChange(bgReading);
return true;
}
if (!old.isEqual(bgReading)) {
aapsLogger.debug(LTag.DATABASE, "BG: Similiar found: " + old.toString());
old.copyFrom(bgReading);
getDaoBgReadings().update(old);
openHumansUploader.enqueueBGReading(old);
aapsLogger.debug(LTag.DATABASE, "BG: Updating record from: " + from + " New data: " + old.toString());
scheduleBgHistoryChange(old.date); // trigger cache invalidation
return false;
}
} catch (SQLException e) {
aapsLogger.error("Unhandled exception", e);
}
return false;
}
public void update(BgReading bgReading) {
bgReading.date = roundDateToSec(bgReading.date);
try {
getDaoBgReadings().update(bgReading);
openHumansUploader.enqueueBGReading(bgReading);
aapsLogger.debug(LTag.DATABASE, "BG: Updating record from: "+ bgReading.toString());
scheduleBgHistoryChange(bgReading.date); // trigger cache invalidation
} catch (SQLException e) {
aapsLogger.error("Unhandled exception", e);
}
}
private void scheduleBgChange(@Nullable final BgReading bgReading) {
class PostRunnable implements Runnable {
public void run() {
aapsLogger.debug(LTag.DATABASE, "Firing EventNewBg");
rxBus.send(new EventNewBG(bgReading));
scheduledBgPost = null;
}
}
// prepare task for execution in 1 sec
// cancel waiting task to prevent sending multiple posts
if (scheduledBgPost != null)
scheduledBgPost.cancel(false);
Runnable task = new PostRunnable();
final int sec = 1;
scheduledBgPost = bgWorker.schedule(task, sec, TimeUnit.SECONDS);
}
private void scheduleBgHistoryChange(@Nullable final long timestamp) {
class PostRunnable implements Runnable {
public void run() {
aapsLogger.debug(LTag.DATABASE, "Firing EventNewBg");
rxBus.send(new EventNewHistoryBgData(oldestBgHistoryChange));
scheduledBgHistoryPost = null;
oldestBgHistoryChange = 0;
}
}
// prepare task for execution in 1 sec
// cancel waiting task to prevent sending multiple posts
if (scheduledBgHistoryPost != null)
scheduledBgHistoryPost.cancel(false);
Runnable task = new PostRunnable();
final int sec = 3;
if (oldestBgHistoryChange == 0 || oldestBgHistoryChange > timestamp)
oldestBgHistoryChange = timestamp;
scheduledBgHistoryPost = bgHistoryWorker.schedule(task, sec, TimeUnit.SECONDS);
}
public List<BgReading> getBgreadingsDataFromTime(long mills, boolean ascending) {
try {
Dao<BgReading, Long> daoBgreadings = getDaoBgReadings();
List<BgReading> bgReadings;
QueryBuilder<BgReading, Long> queryBuilder = daoBgreadings.queryBuilder();
queryBuilder.orderBy("date", ascending);
Where where = queryBuilder.where();
where.ge("date", mills).and().ge("value", 39).and().eq("isValid", true);
PreparedQuery<BgReading> preparedQuery = queryBuilder.prepare();
bgReadings = daoBgreadings.query(preparedQuery);
return bgReadings;
} catch (SQLException e) {
aapsLogger.error("Unhandled exception", e);
}
return new ArrayList<>();
}
public List<BgReading> getBgreadingsDataFromTime(long start, long end, boolean ascending) {
try {
Dao<BgReading, Long> daoBgreadings = getDaoBgReadings();
List<BgReading> bgReadings;
QueryBuilder<BgReading, Long> queryBuilder = daoBgreadings.queryBuilder();
queryBuilder.orderBy("date", ascending);
Where where = queryBuilder.where();
where.between("date", start, end).and().ge("value", 39).and().eq("isValid", true);
PreparedQuery<BgReading> preparedQuery = queryBuilder.prepare();
bgReadings = daoBgreadings.query(preparedQuery);
return bgReadings;
} catch (SQLException e) {
aapsLogger.error("Unhandled exception", e);
}
return new ArrayList<>();
}
public List<BgReading> getAllBgreadingsDataFromTime(long mills, boolean ascending) {
try {
Dao<BgReading, Long> daoBgreadings = getDaoBgReadings();
List<BgReading> bgReadings;
QueryBuilder<BgReading, Long> queryBuilder = daoBgreadings.queryBuilder();
queryBuilder.orderBy("date", ascending);
Where where = queryBuilder.where();
where.ge("date", mills);
PreparedQuery<BgReading> preparedQuery = queryBuilder.prepare();
bgReadings = daoBgreadings.query(preparedQuery);
return bgReadings;
} catch (SQLException e) {
aapsLogger.error("Unhandled exception", e);
}
return new ArrayList<BgReading>();
}
public List<BgReading> getAllBgReadings() {
try {
return getDaoBgReadings().queryForAll();
@ -2010,7 +1869,8 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
return null;
}
// Copied from xDrip+
/*
TODO implement again for database branch // Copied from xDrip+
String calculateDirection(BgReading bgReading) {
// Rework to get bgreaings from internal DB and calculate on that base
@ -2056,7 +1916,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
// aapsLogger.error(LTag.GLUCOSE, "Direction set to: " + arrow);
return arrow;
}
*/
// ---------------- Open Humans Queue handling ---------------
public void clearOpenHumansQueue() {

View file

@ -20,10 +20,6 @@ public class DatabaseHelperProvider implements DatabaseHelperInterface {
@Inject DatabaseHelperProvider() {
}
@NotNull @Override public List<BgReading> getAllBgreadingsDataFromTime(long mills, boolean ascending) {
return MainApp.getDbHelper().getAllBgreadingsDataFromTime(mills, ascending);
}
@Override public void createOrUpdate(@NotNull CareportalEvent careportalEvent) {
MainApp.getDbHelper().createOrUpdate(careportalEvent);
}

View file

@ -9,6 +9,7 @@ import info.nightscout.androidaps.core.di.CoreModule
import info.nightscout.androidaps.dana.di.DanaModule
import info.nightscout.androidaps.danar.di.DanaRModule
import info.nightscout.androidaps.danars.di.DanaRSModule
import info.nightscout.androidaps.database.DatabaseModule
import info.nightscout.androidaps.plugins.pump.common.dagger.RileyLinkModule
import info.nightscout.androidaps.plugins.pump.omnipod.dagger.OmnipodModule
import javax.inject.Singleton
@ -17,6 +18,7 @@ import javax.inject.Singleton
@Component(
modules = [
AndroidInjectionModule::class,
DatabaseModule::class,
PluginsModule::class,
SkinsModule::class,
ActivitiesModule::class,

View file

@ -13,6 +13,7 @@ import android.widget.AdapterView.OnItemSelectedListener
import android.widget.ArrayAdapter
import android.widget.CompoundButton
import androidx.fragment.app.FragmentManager
import dagger.android.HasAndroidInjector
import dagger.android.support.DaggerDialogFragment
import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.MainApp
@ -38,6 +39,7 @@ import info.nightscout.androidaps.utils.extensions.toVisibility
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.androidaps.utils.sharedPreferences.SP
import info.nightscout.androidaps.utils.valueToUnits
import info.nightscout.androidaps.utils.wizard.BolusWizard
import io.reactivex.disposables.CompositeDisposable
import java.text.DecimalFormat
@ -47,6 +49,7 @@ import kotlin.math.abs
class WizardDialog : DaggerDialogFragment() {
@Inject lateinit var injector: HasAndroidInjector
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var aapsSchedulers: AapsSchedulers
@Inject lateinit var constraintChecker: ConstraintChecker
@ -332,7 +335,7 @@ class WizardDialog : DaggerDialogFragment() {
binding.notes.text.toString(), carbTime)
wizard?.let { wizard ->
binding.bg.text = String.format(resourceHelper.gs(R.string.format_bg_isf), BgReading().value(Profile.toMgdl(bg, profileFunction.getUnits())).valueToUnitsToString(profileFunction.getUnits()), wizard.sens)
binding.bg.text = String.format(resourceHelper.gs(R.string.format_bg_isf), BgReading(injector).value(Profile.toMgdl(bg, profileFunction.getUnits())).valueToUnitsToString(profileFunction.getUnits()), wizard.sens)
binding.bginsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromBG)
binding.carbs.text = String.format(resourceHelper.gs(R.string.format_carbs_ic), carbs.toDouble(), wizard.ic)

View file

@ -1,5 +1,5 @@
package info.nightscout.androidaps.events
import info.nightscout.androidaps.db.BgReading
import info.nightscout.androidaps.database.entities.GlucoseValue
class EventNewBG(val bgReading: BgReading?) : EventLoop()
class EventNewBG(val glucoseValue: GlucoseValue?) : EventLoop()

View file

@ -1,6 +1,7 @@
package info.nightscout.androidaps.historyBrowser
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.logging.AAPSLogger
@ -32,9 +33,10 @@ class IobCobCalculatorPluginHistory @Inject constructor(
sensitivityAAPSPlugin: SensitivityAAPSPlugin,
sensitivityWeightedAveragePlugin: SensitivityWeightedAveragePlugin,
fabricPrivacy: FabricPrivacy,
dateUtil: DateUtil
dateUtil: DateUtil,
repository: AppRepository
) : IobCobCalculatorPlugin(injector, aapsLogger, aapsSchedulers, rxBus, sp, resourceHelper, profileFunction,
activePlugin, treatmentsPluginHistory, sensitivityOref1Plugin, sensitivityAAPSPlugin, sensitivityWeightedAveragePlugin, fabricPrivacy, dateUtil) {
activePlugin, treatmentsPluginHistory, sensitivityOref1Plugin, sensitivityAAPSPlugin, sensitivityWeightedAveragePlugin, fabricPrivacy, dateUtil, repository) {
override fun onStart() { // do not attach to rxbus
}

View file

@ -30,7 +30,7 @@ import info.nightscout.androidaps.activities.ErrorHelperActivity;
import info.nightscout.androidaps.data.DetailedBolusInfo;
import info.nightscout.androidaps.data.Profile;
import info.nightscout.androidaps.data.PumpEnactResult;
import info.nightscout.androidaps.db.BgReading;
import info.nightscout.androidaps.database.entities.GlucoseValue;
import info.nightscout.androidaps.db.CareportalEvent;
import info.nightscout.androidaps.db.Source;
import info.nightscout.androidaps.db.TemporaryBasal;
@ -201,14 +201,14 @@ public class LoopPlugin extends PluginBase implements LoopInterface {
// Autosens calculation not triggered by a new BG
if (!(event.getCause() instanceof EventNewBG)) return;
BgReading bgReading = iobCobCalculatorPlugin.actualBg();
GlucoseValue glucoseValue = iobCobCalculatorPlugin.actualBg();
// BG outdated
if (bgReading == null) return;
if (glucoseValue == null) return;
// already looped with that value
if (bgReading.date <= lastBgTriggeredRun) return;
if (glucoseValue.getTimestamp() <= lastBgTriggeredRun) return;
lastBgTriggeredRun = bgReading.date;
invoke("AutosenseCalculation for " + bgReading, true);
lastBgTriggeredRun = glucoseValue.getTimestamp();
invoke("AutosenseCalculation for " + glucoseValue, true);
}, fabricPrivacy::logException)
);
}

View file

@ -8,7 +8,6 @@ import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.Config
import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.IobTotal
import info.nightscout.androidaps.db.BgReading
import info.nightscout.androidaps.events.Event
import info.nightscout.androidaps.events.EventExtendedBolusChange
import info.nightscout.androidaps.events.EventNewBasalProfile
@ -123,13 +122,13 @@ class DataBroadcastPlugin @Inject constructor(
}
private fun bgStatus(bundle: Bundle) {
val lastBG: BgReading = iobCobCalculatorPlugin.lastBg() ?: return
val lastBG = iobCobCalculatorPlugin.lastBg() ?: return
val glucoseStatus = GlucoseStatus(injector).glucoseStatusData ?: return
bundle.putDouble("glucoseMgdl", lastBG.value) // last BG in mgdl
bundle.putLong("glucoseTimeStamp", lastBG.date) // timestamp
bundle.putLong("glucoseTimeStamp", lastBG.timestamp) // timestamp
bundle.putString("units", profileFunction.getUnits()) // units used in AAPS "mg/dl" or "mmol"
bundle.putString("slopeArrow", lastBG.directionToSymbol(databaseHelper)) // direction arrow as string
bundle.putString("slopeArrow", lastBG.trendArrow.text) // direction arrow as string
bundle.putDouble("deltaMgdl", glucoseStatus.delta) // bg delta in mgdl
bundle.putDouble("avgDeltaMgdl", glucoseStatus.avgdelta) // average bg delta
bundle.putDouble("high", defaultValueHelper.determineHighLine()) // predefined top value of in range (green area)

View file

@ -8,8 +8,11 @@ import android.view.ViewGroup
import dagger.android.support.DaggerFragment
import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.R
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.databinding.MaintenanceFragmentBinding
import info.nightscout.androidaps.events.EventNewBG
import info.nightscout.androidaps.interfaces.ImportExportPrefsInterface
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.general.food.FoodPlugin
import info.nightscout.androidaps.plugins.general.maintenance.activities.LogSettingActivity
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
@ -20,11 +23,12 @@ import javax.inject.Inject
class MaintenanceFragment : DaggerFragment() {
@Inject lateinit var maintenancePlugin: MaintenancePlugin
@Inject lateinit var mainApp: MainApp
@Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var treatmentsPlugin: TreatmentsPlugin
@Inject lateinit var foodPlugin: FoodPlugin
@Inject lateinit var importExportPrefs: ImportExportPrefsInterface
@Inject lateinit var repository: AppRepository
private var _binding: MaintenanceFragmentBinding? = null
@ -49,6 +53,8 @@ class MaintenanceFragment : DaggerFragment() {
// additional service interface and plugin registry
foodPlugin.service?.resetFood()
treatmentsPlugin.service.resetTreatments()
Thread { repository.clearDatabases() } .start()
rxBus.send(EventNewBG(null))
})
}
}

View file

@ -16,6 +16,7 @@ import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.BuildConfig
import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.R
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.db.*
import info.nightscout.androidaps.events.EventPreferenceChange
import info.nightscout.androidaps.interfaces.PluginBase
@ -56,7 +57,8 @@ class OpenHumansUploader @Inject constructor(
private val sp: SP,
private val rxBus: RxBusWrapper,
private val context: Context,
private val treatmentsPlugin: TreatmentsPlugin
private val treatmentsPlugin: TreatmentsPlugin,
val repository: AppRepository
) : PluginBase(
PluginDescription()
.mainType(PluginType.GENERAL)
@ -163,13 +165,13 @@ class OpenHumansUploader @Inject constructor(
fun enqueueBGReading(bgReading: BgReading?) = bgReading?.let {
insertQueueItem("BgReadings") {
put("date", bgReading.date)
put("isValid", bgReading.isValid)
put("value", bgReading.value)
put("direction", bgReading.direction)
put("raw", bgReading.raw)
put("source", bgReading.source)
put("nsId", bgReading._id)
put("date", bgReading.data.dateCreated)
put("isValid", bgReading.data.isValid)
put("value", bgReading.data.value)
put("direction", bgReading.data.trendArrow)
put("raw", bgReading.data.raw)
put("source", bgReading.data.sourceSensor)
put("nsId", bgReading.data.interfaceIDs.nightscoutId)
}
}

View file

@ -56,6 +56,7 @@ import info.nightscout.androidaps.skins.SkinProvider
import info.nightscout.androidaps.utils.*
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.buildHelper.BuildHelper
import info.nightscout.androidaps.utils.extensions.directionToIcon
import info.nightscout.androidaps.utils.extensions.toVisibility
import info.nightscout.androidaps.utils.protection.ProtectionCheck
import info.nightscout.androidaps.utils.resources.ResourceHelper
@ -94,6 +95,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
@Inject lateinit var treatmentsPlugin: TreatmentsPlugin
@Inject lateinit var iobCobCalculatorPlugin: IobCobCalculatorPlugin
@Inject lateinit var dexcomPlugin: DexcomPlugin
@Inject lateinit var dexcomMediator: DexcomPlugin.DexcomMediator
@Inject lateinit var xdripPlugin: XdripPlugin
@Inject lateinit var notificationStore: NotificationStore
@Inject lateinit var actionStringHandler: ActionStringHandler
@ -308,7 +310,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
if (xdripPlugin.isEnabled(PluginType.BGSOURCE))
openCgmApp("com.eveningoutpost.dexdrip")
else if (dexcomPlugin.isEnabled(PluginType.BGSOURCE)) {
dexcomPlugin.findDexcomPackageName()?.let {
dexcomMediator.findDexcomPackageName()?.let {
openCgmApp(it)
}
?: ToastUtils.showToastInUiThread(activity, resourceHelper.gs(R.string.dexcom_app_not_installed))
@ -320,7 +322,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
CalibrationDialog().show(childFragmentManager, "CalibrationDialog")
} else if (dexcomPlugin.isEnabled(PluginType.BGSOURCE)) {
try {
dexcomPlugin.findDexcomPackageName()?.let {
dexcomMediator.findDexcomPackageName()?.let {
startActivity(Intent("com.dexcom.cgm.activities.MeterEntryActivity").setPackage(it))
}
?: ToastUtils.showToastInUiThread(activity, resourceHelper.gs(R.string.dexcom_app_not_installed))
@ -575,9 +577,9 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
else -> resourceHelper.gc(R.color.inrange)
}
binding.infoLayout.bg.text = lastBG.valueToUnitsToString(units)
binding.infoLayout.bg.text = lastBG.valueToUnitsString(units)
binding.infoLayout.bg.setTextColor(color)
binding.infoLayout.arrow.setImageResource(lastBG.directionToIcon(databaseHelper))
binding.infoLayout.arrow.setImageResource(lastBG.trendArrow.directionToIcon())
binding.infoLayout.arrow.setColorFilter(color)
val glucoseStatus = GlucoseStatus(injector).glucoseStatusData
@ -601,8 +603,8 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
} else flag and Paint.STRIKE_THRU_TEXT_FLAG.inv()
overview_bg.paintFlags = flag
}
binding.infoLayout.timeAgo.text = DateUtil.minAgo(resourceHelper, lastBG.date)
binding.infoLayout.timeAgoShort.text = "(" + DateUtil.minAgoShort(lastBG.date) + ")"
binding.infoLayout.timeAgo.text = DateUtil.minAgo(resourceHelper, lastBG.timestamp)
binding.infoLayout.timeAgoShort.text = "(" + DateUtil.minAgoShort(lastBG.timestamp) + ")"
}
val closedLoopEnabled = constraintChecker.isClosedLoopAllowed()

View file

@ -27,6 +27,7 @@ import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
import info.nightscout.androidaps.utils.DecimalFormatter
import info.nightscout.androidaps.utils.Round
import info.nightscout.androidaps.utils.convertToBGReadings
import info.nightscout.androidaps.utils.resources.ResourceHelper
import java.util.*
import javax.inject.Inject
@ -35,7 +36,7 @@ import kotlin.math.max
import kotlin.math.min
class GraphData(
injector: HasAndroidInjector,
private val injector: HasAndroidInjector,
private val graph: GraphView,
private val iobCobCalculatorPlugin: IobCobCalculatorPlugin,
private val treatmentsPlugin: TreatmentsInterface
@ -62,7 +63,7 @@ class GraphData(
@Suppress("UNUSED_PARAMETER")
fun addBgReadings(fromTime: Long, toTime: Long, lowLine: Double, highLine: Double, predictions: MutableList<BgReading>?) {
var maxBgValue = Double.MIN_VALUE
bgReadingsArray = iobCobCalculatorPlugin.bgReadings
bgReadingsArray = iobCobCalculatorPlugin.bgReadings?.convertToBGReadings(injector)
if (bgReadingsArray?.isEmpty() != false) {
aapsLogger.debug("No BG data.")
maxY = 10.0
@ -71,13 +72,13 @@ class GraphData(
}
val bgListArray: MutableList<DataPointWithLabelInterface> = ArrayList()
for (bg in bgReadingsArray!!) {
if (bg.date < fromTime || bg.date > toTime) continue
if (bg.value > maxBgValue) maxBgValue = bg.value
if (bg.data.timestamp < fromTime || bg.data.timestamp > toTime) continue
if (bg.data.value > maxBgValue) maxBgValue = bg.data.value
bgListArray.add(bg)
}
if (predictions != null) {
predictions.sortWith(Comparator { o1: BgReading, o2: BgReading -> o1.x.compareTo(o2.x) })
for (prediction in predictions) if (prediction.value >= 40) bgListArray.add(prediction)
for (prediction in predictions) if (prediction.data.value >= 40) bgListArray.add(prediction)
}
maxBgValue = Profile.fromMgdlToUnits(maxBgValue, units)
maxBgValue = addUpperChartMargin(maxBgValue)
@ -282,10 +283,10 @@ class GraphData(
bgReadingsArray?.let { bgReadingsArray ->
for (r in bgReadingsArray.indices) {
val reading = bgReadingsArray[r]
if (reading.date > date) continue
return Profile.fromMgdlToUnits(reading.value, units)
if (reading.data.timestamp > date) continue
return Profile.fromMgdlToUnits(reading.data.value, units)
}
return if (bgReadingsArray.isNotEmpty()) Profile.fromMgdlToUnits(bgReadingsArray[0].value, units) else Profile.fromMgdlToUnits(100.0, units)
return if (bgReadingsArray.isNotEmpty()) Profile.fromMgdlToUnits(bgReadingsArray[0].data.value, units) else Profile.fromMgdlToUnits(100.0, units)
} ?: return Profile.fromMgdlToUnits(100.0, units)
}

View file

@ -25,6 +25,7 @@ import info.nightscout.androidaps.utils.androidNotification.openAppIntent
import info.nightscout.androidaps.utils.resources.IconsProvider
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.androidaps.utils.valueToUnitsString
import io.reactivex.disposables.CompositeDisposable
import javax.inject.Inject
import javax.inject.Singleton
@ -135,12 +136,12 @@ class PersistentNotificationPlugin @Inject constructor(
val lastBG = iobCobCalculatorPlugin.lastBg()
val glucoseStatus = GlucoseStatus(injector).glucoseStatusData
if (lastBG != null) {
line1aa = lastBG.valueToUnitsToString(units)
line1aa = lastBG.valueToUnitsString(units)
line1 = line1aa
if (glucoseStatus != null) {
line1 += (" Δ" + Profile.toSignedUnitsString(glucoseStatus.delta, glucoseStatus.delta * Constants.MGDL_TO_MMOLL, units)
+ " avgΔ" + Profile.toSignedUnitsString(glucoseStatus.avgdelta, glucoseStatus.avgdelta * Constants.MGDL_TO_MMOLL, units))
line1aa += " " + lastBG.directionToSymbol(databaseHelper)
line1aa += " " + lastBG.trendArrow.symbol
} else {
line1 += " " +
resourceHelper.gs(R.string.old_data) +

View file

@ -305,11 +305,11 @@ class SmsCommunicatorPlugin @Inject constructor(
var reply = ""
val units = profileFunction.getUnits()
if (actualBG != null) {
reply = resourceHelper.gs(R.string.sms_actualbg) + " " + actualBG.valueToUnitsToString(units) + ", "
reply = resourceHelper.gs(R.string.sms_actualbg) + " " + actualBG.valueToUnitsString(units) + ", "
} else if (lastBG != null) {
val agoMsec = System.currentTimeMillis() - lastBG.date
val agoMsec = System.currentTimeMillis() - lastBG.timestamp
val agoMin = (agoMsec / 60.0 / 1000.0).toInt()
reply = resourceHelper.gs(R.string.sms_lastbg) + " " + lastBG.valueToUnitsToString(units) + " " + String.format(resourceHelper.gs(R.string.sms_minago), agoMin) + ", "
reply = resourceHelper.gs(R.string.sms_lastbg) + " " + lastBG.valueToUnitsString(units) + " " + String.format(resourceHelper.gs(R.string.sms_minago), agoMin) + ", "
}
val glucoseStatus = GlucoseStatus(injector).glucoseStatusData
if (glucoseStatus != null) reply += resourceHelper.gs(R.string.sms_delta) + " " + Profile.toUnitsString(glucoseStatus.delta, glucoseStatus.delta * Constants.MGDL_TO_MMOLL, units) + " " + units + ", "

View file

@ -93,11 +93,11 @@ class TidepoolPlugin @Inject constructor(
disposable += rxBus
.toObservable(EventNewBG::class.java)
.observeOn(aapsSchedulers.io)
.filter { it.bgReading != null } // better would be optional in API level >24
.map { it.bgReading }
.filter { it.glucoseValue != null } // better would be optional in API level >24
.map { it.glucoseValue }
.subscribe({ bgReading ->
if (bgReading!!.date < uploadChunk.getLastEnd())
uploadChunk.setLastEnd(bgReading.date)
if (bgReading!!.timestamp < uploadChunk.getLastEnd())
uploadChunk.setLastEnd(bgReading.timestamp )
if (isEnabled(PluginType.GENERAL)
&& (!sp.getBoolean(R.string.key_tidepool_only_while_charging, false) || receiverStatusStore.isCharging)
&& (!sp.getBoolean(R.string.key_tidepool_only_while_unmetered, false) || receiverStatusStore.isWifiConnected)

View file

@ -1,8 +1,10 @@
package info.nightscout.androidaps.plugins.general.tidepool.comm
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.Intervals
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.db.ProfileSwitch
import info.nightscout.androidaps.db.TemporaryBasal
import info.nightscout.androidaps.interfaces.ActivePluginProvider
@ -16,6 +18,7 @@ import info.nightscout.androidaps.plugins.general.tidepool.utils.GsonInstance
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.convertToBGReadings
import info.nightscout.androidaps.utils.sharedPreferences.SP
import java.util.*
import javax.inject.Inject
@ -24,12 +27,14 @@ import kotlin.math.max
@Singleton
class UploadChunk @Inject constructor(
private val injector: HasAndroidInjector,
private val sp: SP,
private val rxBus: RxBusWrapper,
private val aapsLogger: AAPSLogger,
private val profileFunction: ProfileFunction,
private val treatmentsPlugin: TreatmentsPlugin,
private val activePlugin: ActivePluginProvider,
private val repository: AppRepository,
private val dateUtil: DateUtil
) {
@ -102,9 +107,11 @@ class UploadChunk @Inject constructor(
val start: Long = 0
val end = DateUtil.now()
val bgReadingList = MainApp.getDbHelper().getBgreadingsDataFromTime(start, end, true)
return if (bgReadingList.size > 0)
bgReadingList[0].date
val bgReadingList = repository.compatGetBgReadingsDataFromTime(start, end, true)
.blockingGet()
.convertToBGReadings(injector)
return if (bgReadingList.isNotEmpty())
bgReadingList[0].data.timestamp
else -1
}
@ -131,7 +138,9 @@ class UploadChunk @Inject constructor(
}
private fun getBgReadings(start: Long, end: Long): List<SensorGlucoseElement> {
val readings = MainApp.getDbHelper().getBgreadingsDataFromTime(start, end, true)
val readings = repository.compatGetBgReadingsDataFromTime(start, end, true)
.blockingGet()
.convertToBGReadings(injector)
val selection = SensorGlucoseElement.fromBgReadings(readings)
if (selection.isNotEmpty())
rxBus.send(EventTidepoolStatus("${selection.size} CGMs selected for upload"))

View file

@ -5,7 +5,7 @@ import info.nightscout.androidaps.db.BgReading
import java.util.*
class SensorGlucoseElement(bgReading: BgReading)
: BaseElement(bgReading.date, UUID.nameUUIDFromBytes(("AAPS-cgm" + bgReading.date).toByteArray()).toString()) {
: BaseElement(bgReading.data.timestamp, UUID.nameUUIDFromBytes(("AAPS-cgm" + bgReading.data.timestamp).toByteArray()).toString()) {
@Expose
internal var units: String = "mg/dL"
@ -14,7 +14,7 @@ class SensorGlucoseElement(bgReading: BgReading)
init {
this.type = "cbg"
value = bgReading.value.toInt()
value = bgReading.data.value.toInt()
}
companion object {

View file

@ -31,11 +31,7 @@ import info.nightscout.androidaps.danaRv2.DanaRv2Plugin
import info.nightscout.androidaps.plugins.pump.insight.LocalInsightPlugin
import info.nightscout.androidaps.plugins.treatments.CarbsGenerator
import info.nightscout.androidaps.queue.Callback
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.DecimalFormatter
import info.nightscout.androidaps.utils.HardLimits
import info.nightscout.androidaps.utils.SafeParse
import info.nightscout.androidaps.utils.ToastUtils
import info.nightscout.androidaps.utils.*
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP
import info.nightscout.androidaps.utils.wizard.BolusWizard

View file

@ -31,32 +31,33 @@ import dagger.android.AndroidInjection;
import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.Config;
import info.nightscout.androidaps.Constants;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.data.IobTotal;
import info.nightscout.androidaps.data.Profile;
import info.nightscout.androidaps.database.AppRepository;
import info.nightscout.androidaps.database.entities.GlucoseValue;
import info.nightscout.androidaps.db.BgReading;
import info.nightscout.androidaps.db.TemporaryBasal;
import info.nightscout.androidaps.db.Treatment;
import info.nightscout.androidaps.interfaces.ActivePluginProvider;
import info.nightscout.androidaps.interfaces.PluginType;
import info.nightscout.androidaps.interfaces.ProfileFunction;
import info.nightscout.androidaps.logging.AAPSLogger;
import info.nightscout.androidaps.logging.LTag;
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin;
import info.nightscout.androidaps.interfaces.ProfileFunction;
import info.nightscout.androidaps.plugins.general.nsclient.data.NSDeviceStatus;
import info.nightscout.androidaps.plugins.general.wear.ActionStringHandler;
import info.nightscout.androidaps.plugins.general.wear.WearPlugin;
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus;
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin;
import info.nightscout.androidaps.db.Treatment;
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin;
import info.nightscout.androidaps.receivers.ReceiverStatusStore;
import info.nightscout.androidaps.utils.DecimalFormatter;
import info.nightscout.androidaps.utils.DefaultValueHelper;
import info.nightscout.androidaps.utils.GlucoseValueUtilsKt;
import info.nightscout.androidaps.utils.ToastUtils;
import info.nightscout.androidaps.utils.resources.ResourceHelper;
import info.nightscout.androidaps.utils.sharedPreferences.SP;
import kotlin.Suppress;
public class WatchUpdaterService extends WearableListenerService implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
@Inject public HasAndroidInjector injector;
@ -72,6 +73,7 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
@Inject public IobCobCalculatorPlugin iobCobCalculatorPlugin;
@Inject public TreatmentsPlugin treatmentsPlugin;
@Inject public ActionStringHandler actionStringHandler;
@Inject public AppRepository repository;
@Inject ReceiverStatusStore receiverStatusStore;
@Inject Config config;
@ -275,7 +277,7 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
private void sendData() {
BgReading lastBG = iobCobCalculatorPlugin.lastBg();
GlucoseValue lastBG = iobCobCalculatorPlugin.lastBg();
// Log.d(TAG, logPrefix + "LastBg=" + lastBG);
if (lastBG != null) {
GlucoseStatus glucoseStatus = new GlucoseStatus(injector).getGlucoseStatusData();
@ -297,25 +299,25 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
}
private DataMap dataMapSingleBG(BgReading lastBG, GlucoseStatus glucoseStatus) {
private DataMap dataMapSingleBG(GlucoseValue lastBG, GlucoseStatus glucoseStatus) {
String units = profileFunction.getUnits();
double convert2MGDL = 1.0;
if (units.equals(Constants.MMOL))
convert2MGDL = Constants.MMOLL_TO_MGDL;
double lowLine = defaultValueHelper.determineLowLine()*convert2MGDL;
double highLine = defaultValueHelper.determineHighLine()*convert2MGDL;
double lowLine = defaultValueHelper.determineLowLine() * convert2MGDL;
double highLine = defaultValueHelper.determineHighLine() * convert2MGDL;
long sgvLevel = 0L;
if (lastBG.value > highLine) {
if (lastBG.getValue() > highLine) {
sgvLevel = 1;
} else if (lastBG.value < lowLine) {
} else if (lastBG.getValue() < lowLine) {
sgvLevel = -1;
}
DataMap dataMap = new DataMap();
dataMap.putString("sgvString", lastBG.valueToUnitsToString(units));
dataMap.putString("sgvString", GlucoseValueUtilsKt.valueToUnitsString(lastBG, units));
dataMap.putString("glucoseUnits", units);
dataMap.putLong("timestamp", lastBG.date);
dataMap.putLong("timestamp", lastBG.getTimestamp());
if (glucoseStatus == null) {
dataMap.putString("slopeArrow", "");
dataMap.putString("delta", "--");
@ -326,7 +328,7 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
dataMap.putString("avgDelta", deltastring(glucoseStatus.avgdelta, glucoseStatus.avgdelta * Constants.MGDL_TO_MMOLL, units));
}
dataMap.putLong("sgvLevel", sgvLevel);
dataMap.putDouble("sgvDouble", lastBG.value);
dataMap.putDouble("sgvDouble", lastBG.getValue());
dataMap.putDouble("high", highLine);
dataMap.putDouble("low", lowLine);
return dataMap;
@ -381,11 +383,11 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
googleApiConnect();
}
long startTime = System.currentTimeMillis() - (long) (60000 * 60 * 5.5);
BgReading last_bg = iobCobCalculatorPlugin.lastBg();
GlucoseValue last_bg = iobCobCalculatorPlugin.lastBg();
if (last_bg == null) return;
List<BgReading> graph_bgs = MainApp.getDbHelper().getBgreadingsDataFromTime(startTime, true);
List<GlucoseValue> graph_bgs = repository.compatGetBgReadingsDataFromTime(startTime, true).blockingGet();
GlucoseStatus glucoseStatus = new GlucoseStatus(injector).getGlucoseStatusData(true);
if (!graph_bgs.isEmpty()) {
@ -395,7 +397,7 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
return;
}
final ArrayList<DataMap> dataMaps = new ArrayList<>(graph_bgs.size());
for (BgReading bg : graph_bgs) {
for (GlucoseValue bg : graph_bgs) {
DataMap dataMap = dataMapSingleBG(bg, glucoseStatus);
if (dataMap != null) {
dataMaps.add(dataMap);
@ -541,8 +543,8 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
if (!predArray.isEmpty()) {
final String units = profileFunction.getUnits();
for (BgReading bg : predArray) {
if (bg.value < 40) continue;
predictions.add(predictionMap(bg.date, bg.value, bg.getPredectionColor()));
if (bg.getValue() < 40) continue;
predictions.add(predictionMap(bg.getDate(), bg.getValue(), bg.getPredictionColor()));
}
}
}

View file

@ -8,6 +8,7 @@ import java.util.List;
import javax.inject.Inject;
import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.database.entities.GlucoseValue;
import info.nightscout.androidaps.db.BgReading;
import info.nightscout.androidaps.logging.AAPSLogger;
import info.nightscout.androidaps.logging.LTag;
@ -71,7 +72,7 @@ public class GlucoseStatus {
synchronized (iobCobCalculatorPlugin.getDataLock()) {
List<BgReading> data = iobCobCalculatorPlugin.getBgReadings();
List<GlucoseValue> data = iobCobCalculatorPlugin.getBgReadings();
if (data == null) {
aapsLogger.debug(LTag.GLUCOSE, "data=null");
@ -84,18 +85,18 @@ public class GlucoseStatus {
return null;
}
if (data.get(0).date < DateUtil.now() - 7 * 60 * 1000L && !allowOldData) {
if (data.get(0).getTimestamp() < DateUtil.now() - 7 * 60 * 1000L && !allowOldData) {
aapsLogger.debug(LTag.GLUCOSE, "olddata");
return null;
}
BgReading now = data.get(0);
long now_date = now.date;
GlucoseValue now = data.get(0);
long now_date = now.getTimestamp();
double change;
if (sizeRecords == 1) {
GlucoseStatus status = new GlucoseStatus(injector);
status.glucose = now.value;
status.glucose = now.getValue();
status.noise = 0d;
status.short_avgdelta = 0d;
status.delta = 0d;
@ -112,18 +113,18 @@ public class GlucoseStatus {
ArrayList<Double> long_deltas = new ArrayList<>();
// Use the latest sgv value in the now calculations
now_value_list.add(now.value);
now_value_list.add(now.getValue());
for (int i = 1; i < sizeRecords; i++) {
if (data.get(i).value > 38) {
BgReading then = data.get(i);
long then_date = then.date;
if (data.get(i).getValue() > 38) {
GlucoseValue then = data.get(i);
long then_date = then.getTimestamp();
double avgdelta;
long minutesago;
minutesago = Math.round((now_date - then_date) / (1000d * 60));
// multiply by 5 to get the same units as delta, i.e. mg/dL/5m
change = now.value - then.value;
change = now.getValue() - then.getValue();
avgdelta = change / minutesago * 5;
aapsLogger.debug(LTag.GLUCOSE, then.toString() + " minutesago=" + minutesago + " avgdelta=" + avgdelta);
@ -131,8 +132,8 @@ public class GlucoseStatus {
// use the average of all data points in the last 2.5m for all further "now" calculations
if (0 < minutesago && minutesago < 2.5) {
// Keep and average all values within the last 2.5 minutes
now_value_list.add(then.value);
now.value = average(now_value_list);
now_value_list.add(then.getValue());
now.setValue(average(now_value_list));
// short_deltas are calculated from everything ~5-15 minutes ago
} else if (2.5 < minutesago && minutesago < 17.5) {
//console.error(minutesago, avgdelta);
@ -152,7 +153,7 @@ public class GlucoseStatus {
}
GlucoseStatus status = new GlucoseStatus(injector);
status.glucose = now.value;
status.glucose = now.getValue();
status.date = now_date;
status.noise = 0d; //for now set to nothing as not all CGMs report noise

View file

@ -1,9 +1,9 @@
package info.nightscout.androidaps.plugins.iob.iobCobCalculator
import info.nightscout.androidaps.db.BgReading
import info.nightscout.androidaps.database.entities.GlucoseValue
class InMemoryGlucoseValue @JvmOverloads constructor(var timestamp: Long = 0L, var value: Double = 0.0, var interpolated : Boolean = false) {
class InMemoryGlucoseValue @JvmOverloads constructor(var timestamp: Long = 0L, var value: Double = 0.0, var interpolated: Boolean = false) {
constructor(gv: BgReading) : this(gv.date, gv.value)
constructor(gv: GlucoseValue) : this(gv.timestamp, gv.value)
// var generated : value doesn't correspond to real value with timestamp close to real BG
}

View file

@ -10,6 +10,7 @@ import org.json.JSONArray;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import javax.inject.Singleton;
@ -21,6 +22,8 @@ import info.nightscout.androidaps.R;
import info.nightscout.androidaps.data.IobTotal;
import info.nightscout.androidaps.data.MealData;
import info.nightscout.androidaps.data.Profile;
import info.nightscout.androidaps.database.AppRepository;
import info.nightscout.androidaps.database.entities.GlucoseValue;
import info.nightscout.androidaps.db.BgReading;
import info.nightscout.androidaps.db.TemporaryBasal;
import info.nightscout.androidaps.db.Treatment;
@ -72,6 +75,7 @@ public class IobCobCalculatorPlugin extends PluginBase implements IobCobCalculat
private final SensitivityWeightedAveragePlugin sensitivityWeightedAveragePlugin;
private final FabricPrivacy fabricPrivacy;
private final DateUtil dateUtil;
private final AppRepository repository;
private final CompositeDisposable disposable = new CompositeDisposable();
@ -80,14 +84,14 @@ public class IobCobCalculatorPlugin extends PluginBase implements IobCobCalculat
private LongSparseArray<AutosensData> autosensDataTable = new LongSparseArray<>(); // oldest at index 0
private LongSparseArray<BasalData> basalDataTable = new LongSparseArray<>(); // oldest at index 0
private volatile List<GlucoseValue> bgReadings = null; // newest at index 0
private volatile List<InMemoryGlucoseValue> bucketed_data = null;
// we need to make sure that bucketed_data will always have the same timestamp for correct use of cached values
// once referenceTime != null all bucketed data should be (x * 5min) from referenceTime
Long referenceTime = null;
private Boolean lastUsed5minCalculation = null; // true if used 5min bucketed data
private volatile List<BgReading> bgReadings = null; // newest at index 0
private volatile List<InMemoryGlucoseValue> bucketed_data = null;
private final Object dataLock = new Object();
boolean stopCalculationTrigger = false;
@ -108,7 +112,8 @@ public class IobCobCalculatorPlugin extends PluginBase implements IobCobCalculat
SensitivityAAPSPlugin sensitivityAAPSPlugin,
SensitivityWeightedAveragePlugin sensitivityWeightedAveragePlugin,
FabricPrivacy fabricPrivacy,
DateUtil dateUtil
DateUtil dateUtil,
AppRepository repository
) {
super(new PluginDescription()
.mainType(PluginType.GENERAL)
@ -131,6 +136,7 @@ public class IobCobCalculatorPlugin extends PluginBase implements IobCobCalculat
this.sensitivityWeightedAveragePlugin = sensitivityWeightedAveragePlugin;
this.fabricPrivacy = fabricPrivacy;
this.dateUtil = dateUtil;
this.repository = repository;
}
@Override
@ -169,6 +175,7 @@ public class IobCobCalculatorPlugin extends PluginBase implements IobCobCalculat
disposable.add(rxBus
.toObservable(EventNewBG.class)
.observeOn(aapsSchedulers.getIo())
.debounce(1L, TimeUnit.SECONDS)
.subscribe(event -> {
stopCalculation("onEventNewBG");
runCalculation("onEventNewBG", System.currentTimeMillis(), true, true, event);
@ -227,11 +234,11 @@ public class IobCobCalculatorPlugin extends PluginBase implements IobCobCalculat
return autosensDataTable;
}
public List<BgReading> getBgReadings() {
public List<GlucoseValue> getBgReadings() {
return bgReadings;
}
public void setBgReadings(List<BgReading> bgReadings) {
public void setBgReadings(List<GlucoseValue> bgReadings) {
this.bgReadings = bgReadings;
}
@ -272,10 +279,10 @@ public class IobCobCalculatorPlugin extends PluginBase implements IobCobCalculat
if (DateUtil.isCloseToNow(to)) {
// if close to now expect there can be some readings with time in close future (caused by wrong time setting)
// so read all records
bgReadings = MainApp.getDbHelper().getBgreadingsDataFromTime(start, false);
bgReadings = repository.compatGetBgReadingsDataFromTime(start, false).blockingGet();
getAapsLogger().debug(LTag.AUTOSENS, "BG data loaded. Size: " + bgReadings.size() + " Start date: " + dateUtil.dateAndTimeString(start));
} else {
bgReadings = MainApp.getDbHelper().getBgreadingsDataFromTime(start, to, false);
bgReadings = repository.compatGetBgReadingsDataFromTime(start, to, false).blockingGet();
getAapsLogger().debug(LTag.AUTOSENS, "BG data loaded. Size: " + bgReadings.size() + " Start date: " + dateUtil.dateAndTimeString(start) + " End date: " + dateUtil.dateAndTimeString(to));
}
}
@ -287,8 +294,8 @@ public class IobCobCalculatorPlugin extends PluginBase implements IobCobCalculat
}
long totalDiff = 0;
for (int i = 1; i < bgReadings.size(); ++i) {
long bgTime = bgReadings.get(i).date;
long lastbgTime = bgReadings.get(i - 1).date;
long bgTime = bgReadings.get(i).getTimestamp();
long lastbgTime = bgReadings.get(i - 1).getTimestamp();
long diff = lastbgTime - bgTime;
diff %= T.mins(5).msecs();
if (diff > T.mins(2).plus(T.secs(30)).msecs())
@ -331,27 +338,27 @@ public class IobCobCalculatorPlugin extends PluginBase implements IobCobCalculat
}
@Nullable
public BgReading findNewer(long time) {
BgReading lastFound = bgReadings.get(0);
if (lastFound.date < time) return null;
public GlucoseValue findNewer(long time) {
GlucoseValue lastFound = bgReadings.get(0);
if (lastFound.getTimestamp() < time) return null;
for (int i = 1; i < bgReadings.size(); ++i) {
if (bgReadings.get(i).date == time) return bgReadings.get(i);
if (bgReadings.get(i).date > time) continue;
if (bgReadings.get(i).getTimestamp() == time) return bgReadings.get(i);
if (bgReadings.get(i).getTimestamp() > time) continue;
lastFound = bgReadings.get(i - 1);
if (bgReadings.get(i).date < time) break;
if (bgReadings.get(i).getTimestamp() < time) break;
}
return lastFound;
}
@Nullable
public BgReading findOlder(long time) {
BgReading lastFound = bgReadings.get(bgReadings.size() - 1);
if (lastFound.date > time) return null;
public GlucoseValue findOlder(long time) {
GlucoseValue lastFound = bgReadings.get(bgReadings.size() - 1);
if (lastFound.getTimestamp() > time) return null;
for (int i = bgReadings.size() - 2; i >= 0; --i) {
if (bgReadings.get(i).date == time) return bgReadings.get(i);
if (bgReadings.get(i).date < time) continue;
if (bgReadings.get(i).getTimestamp() == time) return bgReadings.get(i);
if (bgReadings.get(i).getTimestamp() < time) continue;
lastFound = bgReadings.get(i + 1);
if (bgReadings.get(i).date > time) break;
if (bgReadings.get(i).getTimestamp() > time) break;
}
return lastFound;
}
@ -363,25 +370,25 @@ public class IobCobCalculatorPlugin extends PluginBase implements IobCobCalculat
}
bucketed_data = new ArrayList<>();
long currentTime = bgReadings.get(0).date - bgReadings.get(0).date % T.mins(5).msecs();
long currentTime = bgReadings.get(0).getTimestamp() - bgReadings.get(0).getTimestamp() % T.mins(5).msecs();
currentTime = adjustToReferenceTime(currentTime);
getAapsLogger().debug("Adjusted time " + dateUtil.dateAndTimeAndSecondsString(currentTime));
//log.debug("First reading: " + new Date(currentTime).toLocaleString());
while (true) {
// test if current value is older than current time
BgReading newer = findNewer(currentTime);
BgReading older = findOlder(currentTime);
GlucoseValue newer = findNewer(currentTime);
GlucoseValue older = findOlder(currentTime);
if (newer == null || older == null)
break;
if (older.date == newer.date) { // direct hit
if (older.getTimestamp() == newer.getTimestamp()) { // direct hit
bucketed_data.add(new InMemoryGlucoseValue(newer));
} else {
double bgDelta = newer.value - older.value;
long timeDiffToNew = newer.date - currentTime;
double bgDelta = newer.getTimestamp() - older.getTimestamp();
long timeDiffToNew = newer.getTimestamp() - currentTime;
double currentBg = newer.value - (double) timeDiffToNew / (newer.date - older.date) * bgDelta;
double currentBg = newer.getValue() - (double) timeDiffToNew / (newer.getTimestamp() - older.getTimestamp()) * bgDelta;
InMemoryGlucoseValue newBgreading = new InMemoryGlucoseValue(currentTime, Math.round(currentBg), true);
bucketed_data.add(newBgreading);
//log.debug("BG: " + newBgreading.value + " (" + new Date(newBgreading.date).toLocaleString() + ") Prev: " + older.value + " (" + new Date(older.date).toLocaleString() + ") Newer: " + newer.value + " (" + new Date(newer.date).toLocaleString() + ")");
@ -400,27 +407,27 @@ public class IobCobCalculatorPlugin extends PluginBase implements IobCobCalculat
bucketed_data = new ArrayList<>();
bucketed_data.add(new InMemoryGlucoseValue(bgReadings.get(0)));
getAapsLogger().debug(LTag.AUTOSENS, "Adding. bgTime: " + DateUtil.toISOString(bgReadings.get(0).date) + " lastbgTime: " + "none-first-value" + " " + bgReadings.get(0).toString());
getAapsLogger().debug(LTag.AUTOSENS, "Adding. bgTime: " + DateUtil.toISOString(bgReadings.get(0).getTimestamp()) + " lastbgTime: " + "none-first-value" + " " + bgReadings.get(0).toString());
int j = 0;
for (int i = 1; i < bgReadings.size(); ++i) {
long bgTime = bgReadings.get(i).date;
long lastbgTime = bgReadings.get(i - 1).date;
long bgTime = bgReadings.get(i).getTimestamp();
long lastbgTime = bgReadings.get(i - 1).getTimestamp();
//log.error("Processing " + i + ": " + new Date(bgTime).toString() + " " + bgReadings.get(i).value + " Previous: " + new Date(lastbgTime).toString() + " " + bgReadings.get(i - 1).value);
if (bgReadings.get(i).value < 39 || bgReadings.get(i - 1).value < 39) {
if (bgReadings.get(i).getValue() < 39 || bgReadings.get(i - 1).getValue() < 39) {
throw new IllegalStateException("<39");
}
long elapsed_minutes = (bgTime - lastbgTime) / (60 * 1000);
if (Math.abs(elapsed_minutes) > 8) {
// interpolate missing data points
double lastbg = bgReadings.get(i - 1).value;
double lastbg = bgReadings.get(i - 1).getValue();
elapsed_minutes = Math.abs(elapsed_minutes);
//console.error(elapsed_minutes);
long nextbgTime;
while (elapsed_minutes > 5) {
nextbgTime = lastbgTime - 5 * 60 * 1000;
j++;
double gapDelta = bgReadings.get(i).value - lastbg;
double gapDelta = bgReadings.get(i).getValue() - lastbg;
//console.error(gapDelta, lastbg, elapsed_minutes);
double nextbg = lastbg + (5d / elapsed_minutes * gapDelta);
InMemoryGlucoseValue newBgreading = new InMemoryGlucoseValue(nextbgTime, Math.round(nextbg), true);
@ -433,16 +440,16 @@ public class IobCobCalculatorPlugin extends PluginBase implements IobCobCalculat
lastbgTime = nextbgTime;
}
j++;
InMemoryGlucoseValue newBgreading = new InMemoryGlucoseValue(bgTime, bgReadings.get(i).value);
InMemoryGlucoseValue newBgreading = new InMemoryGlucoseValue(bgTime, bgReadings.get(i).getValue());
bucketed_data.add(newBgreading);
getAapsLogger().debug(LTag.AUTOSENS, "Adding. bgTime: " + DateUtil.toISOString(bgTime) + " lastbgTime: " + DateUtil.toISOString(lastbgTime) + " " + newBgreading.toString());
} else if (Math.abs(elapsed_minutes) > 2) {
j++;
InMemoryGlucoseValue newBgreading = new InMemoryGlucoseValue(bgTime, bgReadings.get(i).value);
InMemoryGlucoseValue newBgreading = new InMemoryGlucoseValue(bgTime, bgReadings.get(i).getValue());
bucketed_data.add(newBgreading);
getAapsLogger().debug(LTag.AUTOSENS, "Adding. bgTime: " + DateUtil.toISOString(bgTime) + " lastbgTime: " + DateUtil.toISOString(lastbgTime) + " " + newBgreading.toString());
} else {
bucketed_data.get(j).setValue((bucketed_data.get(j).getValue() + bgReadings.get(i).value) / 2);
bucketed_data.get(j).setValue((bucketed_data.get(j).getValue() + bgReadings.get(i).getValue()) / 2);
//log.error("***** Average");
}
}
@ -914,14 +921,14 @@ public class IobCobCalculatorPlugin extends PluginBase implements IobCobCalculat
* Return last BgReading from database or null if db is empty
*/
@Nullable
public BgReading lastBg() {
List<BgReading> bgList = getBgReadings();
public GlucoseValue lastBg() {
List<GlucoseValue> bgList = getBgReadings();
if (bgList == null)
return null;
for (int i = 0; i < bgList.size(); i++)
if (bgList.get(i).value >= 39)
if (bgList.get(i).getValue() >= 39)
return bgList.get(i);
return null;
}
@ -931,13 +938,13 @@ public class IobCobCalculatorPlugin extends PluginBase implements IobCobCalculat
* or null if older
*/
@Nullable
public BgReading actualBg() {
BgReading lastBg = lastBg();
public GlucoseValue actualBg() {
GlucoseValue lastBg = lastBg();
if (lastBg == null)
return null;
if (lastBg.date > System.currentTimeMillis() - 9 * 60 * 1000)
if (lastBg.getTimestamp() > System.currentTimeMillis() - 9 * 60 * 1000)
return lastBg;
return null;

View file

@ -5,32 +5,28 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import dagger.android.support.DaggerFragment
import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.R
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.GlucoseValue
import info.nightscout.androidaps.database.transactions.InvalidateGlucoseValueTransaction
import info.nightscout.androidaps.databinding.BgsourceFragmentBinding
import info.nightscout.androidaps.databinding.BgsourceItemBinding
import info.nightscout.androidaps.db.BgReading
import info.nightscout.androidaps.events.EventNewBG
import info.nightscout.androidaps.interfaces.DatabaseHelperInterface
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventNewHistoryBgData
import info.nightscout.androidaps.plugins.source.BGSourceFragment.RecyclerViewAdapter.BgReadingsViewHolder
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.ListDiffCallback
import info.nightscout.androidaps.utils.ListUpdateCallbackHelper
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.*
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.extensions.directionToIcon
import info.nightscout.androidaps.utils.extensions.toVisibility
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign
import java.util.concurrent.TimeUnit
import javax.inject.Inject
class BGSourceFragment : DaggerFragment() {
@ -41,6 +37,7 @@ class BGSourceFragment : DaggerFragment() {
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var databaseHelper: DatabaseHelperInterface
@Inject lateinit var repository: AppRepository
@Inject lateinit var aapsSchedulers: AapsSchedulers
private val disposable = CompositeDisposable()
@ -60,24 +57,27 @@ class BGSourceFragment : DaggerFragment() {
binding.recyclerview.setHasFixedSize(true)
binding.recyclerview.layoutManager = LinearLayoutManager(view.context)
val now = System.currentTimeMillis()
binding.recyclerview.adapter = RecyclerViewAdapter(getBgData(now))
}
@Synchronized
override fun onResume() {
super.onResume()
disposable.add(rxBus
val now = System.currentTimeMillis()
disposable += repository
.compatGetBgReadingsDataFromTime(now - millsToThePast, false)
.observeOn(aapsSchedulers.main)
.subscribe { list -> binding.recyclerview.adapter = RecyclerViewAdapter(list) }
disposable += rxBus
.toObservable(EventNewBG::class.java)
.observeOn(aapsSchedulers.main)
.subscribe({ updateGUI() }, fabricPrivacy::logException)
)
disposable.add(rxBus
.toObservable(EventNewHistoryBgData::class.java)
.observeOn(aapsSchedulers.main)
.subscribe({ updateGUI() }, fabricPrivacy::logException)
)
updateGUI()
.observeOn(aapsSchedulers.io)
.debounce(1L, TimeUnit.SECONDS)
.subscribe({
disposable += repository
.compatGetBgReadingsDataFromTime(now - millsToThePast, false)
.observeOn(aapsSchedulers.main)
.subscribe { list -> binding.recyclerview.swapAdapter(RecyclerViewAdapter(list), true) }
}, fabricPrivacy::logException)
}
@Synchronized
@ -93,83 +93,41 @@ class BGSourceFragment : DaggerFragment() {
binding.recyclerview.adapter = null // avoid leaks
}
private fun updateGUI() {
if (_binding == null) return
val now = System.currentTimeMillis()
(binding.recyclerview.adapter as? RecyclerViewAdapter)?.setData(getBgData(now))
}
inner class RecyclerViewAdapter internal constructor(private var glucoseValues: List<GlucoseValue>) : RecyclerView.Adapter<RecyclerViewAdapter.GlucoseValuesViewHolder>() {
private fun getBgData(now: Long) = MainApp.getDbHelper()
.getAllBgreadingsDataFromTime(now - millsToThePast, false)
inner class RecyclerViewAdapter internal constructor(bgReadings: List<BgReading>) : RecyclerView.Adapter<BgReadingsViewHolder>() {
private var callbackHelper = ListUpdateCallbackHelper(this) { binding.recyclerview.smoothScrollToPosition(0) }
private val currentData: MutableList<BgReading> = mutableListOf<BgReading>().also { it.addAll(bgReadings) }
fun setData(newList: List<BgReading>) {
val diffResult = DiffUtil.calculateDiff(getListDiffCallback(ArrayList(newList), ArrayList(currentData)))
currentData.clear()
currentData.addAll(newList)
diffResult.dispatchUpdatesTo(callbackHelper)
}
private fun getListDiffCallback(newItems: List<BgReading>, oldItems: List<BgReading>): ListDiffCallback<BgReading> =
object : ListDiffCallback<BgReading>(newItems, oldItems) {
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val new = newItems[newItemPosition]
val old = oldItems[oldItemPosition]
return new.hasValidNS == old.hasValidNS &&
new.isValid == old.isValid &&
new.date == old.date &&
new.value == old.value
}
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean =
newItems[newItemPosition].date == oldItems[oldItemPosition].date
}
override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): BgReadingsViewHolder {
override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): GlucoseValuesViewHolder {
val v = LayoutInflater.from(viewGroup.context).inflate(R.layout.bgsource_item, viewGroup, false)
return BgReadingsViewHolder(v)
return GlucoseValuesViewHolder(v)
}
override fun onBindViewHolder(holder: BgReadingsViewHolder, position: Int) {
val bgReading = currentData[position]
holder.binding.ns.visibility = (NSUpload.isIdValid(bgReading._id)).toVisibility()
holder.binding.invalid.visibility = bgReading.isValid.not().toVisibility()
holder.binding.date.text = dateUtil.dateAndTimeString(bgReading.date)
holder.binding.value.text = bgReading.valueToUnitsToString(profileFunction.getUnits())
holder.binding.direction.setImageResource(bgReading.directionToIcon(databaseHelper))
holder.binding.remove.tag = bgReading
holder.binding.remove.visibility = bgReading.isValid.toVisibility()
override fun onBindViewHolder(holder: GlucoseValuesViewHolder, position: Int) {
val glucoseValue = glucoseValues[position]
holder.binding.ns.visibility = (glucoseValue.interfaceIDs.nightscoutId != null).toVisibility()
holder.binding.invalid.visibility = (!glucoseValue.isValid).toVisibility()
holder.binding.date.text = dateUtil.dateAndTimeString(glucoseValue.timestamp)
holder.binding.value.text = glucoseValue.valueToUnitsString(profileFunction.getUnits())
holder.binding.direction.setImageResource(glucoseValue.trendArrow.directionToIcon())
holder.binding.remove.tag = glucoseValue
}
override fun getItemCount(): Int {
return currentData.size
}
override fun getItemCount(): Int = glucoseValues.size
inner class BgReadingsViewHolder(view: View) : RecyclerView.ViewHolder(view) {
inner class GlucoseValuesViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val binding = BgsourceItemBinding.bind(view)
init {
binding.remove.paintFlags = binding.remove.paintFlags or Paint.UNDERLINE_TEXT_FLAG
binding.remove.setOnClickListener { v: View ->
val bgReading = v.tag as BgReading
val glucoseValue = v.tag as GlucoseValue
activity?.let { activity ->
val text = dateUtil.dateAndTimeString(bgReading.date) + "\n" + bgReading.valueToUnitsToString(profileFunction.getUnits())
val text = dateUtil.dateAndTimeString(glucoseValue.timestamp) + "\n" + glucoseValue.valueToUnitsString(profileFunction.getUnits())
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.removerecord), text, Runnable {
bgReading.isValid = false
MainApp.getDbHelper().update(bgReading)
disposable += repository.runTransaction(InvalidateGlucoseValueTransaction(glucoseValue.id)).subscribe()
})
}
}
binding.remove.paintFlags = binding.remove.paintFlags or Paint.UNDERLINE_TEXT_FLAG
}
}
}
}
val BgReading.hasValidNS
get() = NSUpload.isIdValid(this._id)

View file

@ -8,34 +8,36 @@ import androidx.work.Worker
import androidx.work.WorkerParameters
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.Config
import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.R
import info.nightscout.androidaps.activities.RequestDexcomPermissionActivity
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.GlucoseValue
import info.nightscout.androidaps.database.transactions.CgmSourceTransaction
import info.nightscout.androidaps.db.BgReading
import info.nightscout.androidaps.db.CareportalEvent
import info.nightscout.androidaps.db.Source
import info.nightscout.androidaps.interfaces.BgSourceInterface
import info.nightscout.androidaps.interfaces.PluginBase
import info.nightscout.androidaps.interfaces.PluginDescription
import info.nightscout.androidaps.interfaces.PluginType
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload
import info.nightscout.androidaps.receivers.BundleStore
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.XDripBroadcast
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP
import org.json.JSONObject
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class DexcomPlugin @Inject constructor(
injector: HasAndroidInjector,
private val mainApp: MainApp,
resourceHelper: ResourceHelper,
aapsLogger: AAPSLogger,
private val dexcomMediator: DexcomMediator,
config: Config
) : PluginBase(PluginDescription()
.mainType(PluginType.BGSOURCE)
@ -48,6 +50,8 @@ class DexcomPlugin @Inject constructor(
aapsLogger, resourceHelper, injector
), BgSourceInterface {
private val disposable = CompositeDisposable()
init {
if (!config.NSCLIENT) {
pluginDescription.setDefault()
@ -60,19 +64,12 @@ class DexcomPlugin @Inject constructor(
override fun onStart() {
super.onStart()
if (ContextCompat.checkSelfPermission(mainApp, PERMISSION) != PackageManager.PERMISSION_GRANTED) {
val intent = Intent(mainApp, RequestDexcomPermissionActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
mainApp.startActivity(intent)
}
dexcomMediator.requestPermissionIfNeeded()
}
fun findDexcomPackageName(): String? {
val packageManager = mainApp.packageManager
for (packageInfo in packageManager.getInstalledPackages(0)) {
if (PACKAGE_NAMES.contains(packageInfo.packageName)) return packageInfo.packageName
}
return null
override fun onStop() {
disposable.clear()
super.onStop()
}
// cannot be inner class because of needed injection
@ -87,6 +84,8 @@ class DexcomPlugin @Inject constructor(
@Inject lateinit var nsUpload: NSUpload
@Inject lateinit var sp: SP
@Inject lateinit var bundleStore: BundleStore
@Inject lateinit var broadcastToXDrip: XDripBroadcast
@Inject lateinit var repository: AppRepository
init {
(context.applicationContext as HasAndroidInjector).androidInjector().inject(this)
@ -97,72 +96,52 @@ class DexcomPlugin @Inject constructor(
val bundle = bundleStore.pickup(inputData.getLong("storeKey", -1))
?: return Result.failure()
try {
val sensorType = bundle.getString("sensorType") ?: ""
val glucoseValues = bundle.getBundle("glucoseValues") ?: return Result.failure()
for (i in 0 until glucoseValues.size()) {
glucoseValues.getBundle(i.toString())?.let { glucoseValue ->
val bgReading = BgReading()
bgReading.value = glucoseValue.getInt("glucoseValue").toDouble()
bgReading.direction = glucoseValue.getString("trendArrow")
bgReading.date = glucoseValue.getLong("timestamp") * 1000
bgReading.raw = 0.0
if (MainApp.getDbHelper().createIfNotExists(bgReading, "Dexcom$sensorType")) {
if (sp.getBoolean(R.string.key_dexcomg5_nsupload, false)) {
nsUpload.uploadBg(bgReading, "AndroidAPS-Dexcom$sensorType")
}
if (sp.getBoolean(R.string.key_dexcomg5_xdripupload, false)) {
nsUpload.sendToXdrip(bgReading)
}
}
}
val sourceSensor = when (bundle.getString("sensorType") ?: "") {
"G6" -> GlucoseValue.SourceSensor.DEXCOM_G6_NATIVE
"G5" -> GlucoseValue.SourceSensor.DEXCOM_G5_NATIVE
else -> GlucoseValue.SourceSensor.DEXCOM_NATIVE_UNKNOWN
}
val glucoseValuesBundle = bundle.getBundle("glucoseValues")
?: return Result.failure()
val glucoseValues = mutableListOf<CgmSourceTransaction.TransactionGlucoseValue>()
for (i in 0 until glucoseValuesBundle.size()) {
val glucoseValueBundle = glucoseValuesBundle.getBundle(i.toString())!!
glucoseValues += CgmSourceTransaction.TransactionGlucoseValue(
timestamp = glucoseValueBundle.getLong("timestamp") * 1000,
value = glucoseValueBundle.getInt("glucoseValue").toDouble(),
noise = null,
raw = null,
trendArrow = GlucoseValue.TrendArrow.fromString(glucoseValueBundle.getString("trendArrow")!!),
sourceSensor = sourceSensor
)
}
val calibrations = mutableListOf<CgmSourceTransaction.Calibration>()
bundle.getBundle("meters")?.let { meters ->
for (i in 0 until meters.size()) {
val meter = meters.getBundle(i.toString())
meter?.let {
meters.getBundle(i.toString())?.let {
val timestamp = it.getLong("timestamp") * 1000
val now = DateUtil.now()
if (timestamp > now - T.months(1).msecs() && timestamp < now)
if (MainApp.getDbHelper().getCareportalEventFromTimestamp(timestamp) == null) {
val jsonObject = JSONObject()
jsonObject.put("enteredBy", "AndroidAPS-Dexcom$sensorType")
jsonObject.put("created_at", DateUtil.toISOString(timestamp))
jsonObject.put("eventType", CareportalEvent.BGCHECK)
jsonObject.put("glucoseType", "Finger")
jsonObject.put("glucose", meter.getInt("meterValue"))
jsonObject.put("units", Constants.MGDL)
val careportalEvent = CareportalEvent(injector)
careportalEvent.date = timestamp
careportalEvent.source = Source.USER
careportalEvent.eventType = CareportalEvent.BGCHECK
careportalEvent.json = jsonObject.toString()
MainApp.getDbHelper().createOrUpdate(careportalEvent)
nsUpload.uploadCareportalEntryToNS(jsonObject)
}
if (timestamp > now - T.months(1).msecs() && timestamp < now) {
calibrations.add(CgmSourceTransaction.Calibration(it.getLong("timestamp") * 1000,
it.getInt("meterValue").toDouble()))
}
}
}
}
if (sp.getBoolean(R.string.key_dexcom_lognssensorchange, false) && bundle.containsKey("sensorInsertionTime")) {
bundle.let {
val sensorInsertionTime = it.getLong("sensorInsertionTime") * 1000
val now = DateUtil.now()
if (sensorInsertionTime > now - T.months(1).msecs() && sensorInsertionTime < now)
if (MainApp.getDbHelper().getCareportalEventFromTimestamp(sensorInsertionTime) == null) {
val jsonObject = JSONObject()
jsonObject.put("enteredBy", "AndroidAPS-Dexcom$sensorType")
jsonObject.put("created_at", DateUtil.toISOString(sensorInsertionTime))
jsonObject.put("eventType", CareportalEvent.SENSORCHANGE)
val careportalEvent = CareportalEvent(injector)
careportalEvent.date = sensorInsertionTime
careportalEvent.source = Source.USER
careportalEvent.eventType = CareportalEvent.SENSORCHANGE
careportalEvent.json = jsonObject.toString()
MainApp.getDbHelper().createOrUpdate(careportalEvent)
nsUpload.uploadCareportalEntryToNS(jsonObject)
}
}
val sensorStartTime = if (sp.getBoolean(R.string.key_dexcom_lognssensorchange, false) && bundle.containsKey("sensorInsertionTime")) {
bundle.getLong("sensorInsertionTime", 0) * 1000
} else {
null
}
dexcomPlugin.disposable += repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, calibrations, sensorStartTime)).subscribe({ savedValues ->
savedValues.forEach {
broadcastToXDrip(it)
if (sp.getBoolean(R.string.key_dexcomg5_nsupload, false))
nsUpload.uploadBg(BgReading(injector, it), sourceSensor.text)
}
}, {
aapsLogger.error(LTag.BGSOURCE, "Error while saving values from Dexcom App", it)
})
} catch (e: Exception) {
aapsLogger.error("Error while processing intent from Dexcom App", e)
}
@ -178,4 +157,23 @@ class DexcomPlugin @Inject constructor(
"com.dexcom.g6.region3.mgdl", "com.dexcom.g6.region3.mmol")
const val PERMISSION = "com.dexcom.cgm.EXTERNAL_PERMISSION"
}
class DexcomMediator @Inject constructor(val context: Context) {
fun requestPermissionIfNeeded() {
if (ContextCompat.checkSelfPermission(context, PERMISSION) != PackageManager.PERMISSION_GRANTED) {
val intent = Intent(context, RequestDexcomPermissionActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
context.startActivity(intent)
}
}
fun findDexcomPackageName(): String? {
val packageManager = context.packageManager
for (packageInfo in packageManager.getInstalledPackages(0)) {
if (PACKAGE_NAMES.contains(packageInfo.packageName)) return packageInfo.packageName
}
return null
}
}
}

View file

@ -7,6 +7,9 @@ import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.R
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.GlucoseValue
import info.nightscout.androidaps.database.transactions.CgmSourceTransaction
import info.nightscout.androidaps.db.BgReading
import info.nightscout.androidaps.db.CareportalEvent
import info.nightscout.androidaps.interfaces.BgSourceInterface
@ -18,8 +21,11 @@ import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload
import info.nightscout.androidaps.receivers.BundleStore
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.XDripBroadcast
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign
import org.json.JSONException
import org.json.JSONObject
import java.util.*
@ -44,18 +50,28 @@ class EversensePlugin @Inject constructor(
override var sensorBatteryLevel = -1
private val disposable = CompositeDisposable()
override fun onStop() {
disposable.clear()
super.onStop()
}
// cannot be inner class because of needed injection
class EversenseWorker(
context: Context,
params: WorkerParameters
) : Worker(context, params) {
@Inject lateinit var injector: HasAndroidInjector
@Inject lateinit var eversensePlugin: EversensePlugin
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var sp: SP
@Inject lateinit var nsUpload: NSUpload
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var bundleStore: BundleStore
@Inject lateinit var repository: AppRepository
@Inject lateinit var broadcastToXDrip: XDripBroadcast
init {
(context.applicationContext as HasAndroidInjector).androidInjector().inject(this)
@ -73,7 +89,7 @@ class EversensePlugin @Inject constructor(
if (bundle.containsKey("batteryLevel")) {
aapsLogger.debug(LTag.BGSOURCE, "batteryLevel: " + bundle.getString("batteryLevel"))
//sensorBatteryLevel = bundle.getString("batteryLevel").toInt()
// TODO: Philoul: Line to check I don't have eversense so I don't know what kind of information is sent...
// TODO: Line to check I don't have eversense so I don't know what kind of information is sent...
}
if (bundle.containsKey("signalStrength")) aapsLogger.debug(LTag.BGSOURCE, "signalStrength: " + bundle.getString("signalStrength"))
if (bundle.containsKey("transmitterVersionNumber")) aapsLogger.debug(LTag.BGSOURCE, "transmitterVersionNumber: " + bundle.getString("transmitterVersionNumber"))
@ -85,6 +101,7 @@ class EversensePlugin @Inject constructor(
if (bundle.containsKey("transmitterVersionNumber")) aapsLogger.debug(LTag.BGSOURCE, "transmitterVersionNumber: " + bundle.getString("transmitterVersionNumber"))
if (bundle.containsKey("transmitterConnectionState")) aapsLogger.debug(LTag.BGSOURCE, "transmitterConnectionState: " + bundle.getString("transmitterConnectionState"))
if (bundle.containsKey("glucoseLevels")) {
val glucoseValues = mutableListOf<CgmSourceTransaction.TransactionGlucoseValue>()
val glucoseLevels = bundle.getIntArray("glucoseLevels")
val glucoseRecordNumbers = bundle.getIntArray("glucoseRecordNumbers")
val glucoseTimestamps = bundle.getLongArray("glucoseTimestamps")
@ -92,19 +109,24 @@ class EversensePlugin @Inject constructor(
aapsLogger.debug(LTag.BGSOURCE, "glucoseLevels" + Arrays.toString(glucoseLevels))
aapsLogger.debug(LTag.BGSOURCE, "glucoseRecordNumbers" + Arrays.toString(glucoseRecordNumbers))
aapsLogger.debug(LTag.BGSOURCE, "glucoseTimestamps" + Arrays.toString(glucoseTimestamps))
for (i in glucoseLevels.indices) {
val bgReading = BgReading()
bgReading.value = glucoseLevels[i].toDouble()
bgReading.date = glucoseTimestamps[i]
bgReading.raw = 0.0
val isNew = MainApp.getDbHelper().createIfNotExists(bgReading, "Eversense")
if (isNew && sp.getBoolean(R.string.key_dexcomg5_nsupload, false)) {
nsUpload.uploadBg(bgReading, "AndroidAPS-Eversense")
for (i in glucoseLevels.indices)
glucoseValues += CgmSourceTransaction.TransactionGlucoseValue(
timestamp = glucoseTimestamps[i],
value = glucoseLevels[i].toDouble(),
raw = glucoseLevels[i].toDouble(),
noise = null,
trendArrow = GlucoseValue.TrendArrow.NONE,
sourceSensor = GlucoseValue.SourceSensor.EVERSENSE
)
eversensePlugin.disposable += repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, emptyList(), null)).subscribe({ savedValues ->
savedValues.forEach {
broadcastToXDrip(it)
if (sp.getBoolean(R.string.key_dexcomg5_nsupload, false))
nsUpload.uploadBg(BgReading(injector, it), GlucoseValue.SourceSensor.EVERSENSE.text)
}
if (isNew && sp.getBoolean(R.string.key_dexcomg5_xdripupload, false)) {
nsUpload.sendToXdrip(bgReading)
}
}
}, {
aapsLogger.error(LTag.BGSOURCE, "Error while saving values from Eversense App", it)
})
}
}
if (bundle.containsKey("calibrationGlucoseLevels")) {

View file

@ -4,8 +4,10 @@ import android.content.Context
import androidx.work.Worker
import androidx.work.WorkerParameters
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.R
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.GlucoseValue
import info.nightscout.androidaps.database.transactions.CgmSourceTransaction
import info.nightscout.androidaps.db.BgReading
import info.nightscout.androidaps.interfaces.BgSourceInterface
import info.nightscout.androidaps.interfaces.PluginBase
@ -13,7 +15,12 @@ import info.nightscout.androidaps.interfaces.PluginDescription
import info.nightscout.androidaps.interfaces.PluginType
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload
import info.nightscout.androidaps.utils.XDripBroadcast
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign
import javax.inject.Inject
import javax.inject.Singleton
@ -32,14 +39,26 @@ class GlimpPlugin @Inject constructor(
aapsLogger, resourceHelper, injector
), BgSourceInterface {
private val disposable = CompositeDisposable()
override fun onStop() {
disposable.clear()
super.onStop()
}
// cannot be inner class because of needed injection
class GlimpWorker(
context: Context,
params: WorkerParameters
) : Worker(context, params) {
@Inject lateinit var injector: HasAndroidInjector
@Inject lateinit var glimpPlugin: GlimpPlugin
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var repository: AppRepository
@Inject lateinit var broadcastToXDrip: XDripBroadcast
@Inject lateinit var sp: SP
@Inject lateinit var nsUpload: NSUpload
init {
(context.applicationContext as HasAndroidInjector).androidInjector().inject(this)
@ -48,12 +67,24 @@ class GlimpPlugin @Inject constructor(
override fun doWork(): Result {
if (!glimpPlugin.isEnabled(PluginType.BGSOURCE)) return Result.failure()
aapsLogger.debug(LTag.BGSOURCE, "Received Glimp Data: $inputData}")
val bgReading = BgReading()
bgReading.value = inputData.getDouble("mySGV", 0.0)
bgReading.direction = inputData.getString("myTrend")
bgReading.date = inputData.getLong("myTimestamp", 0)
bgReading.raw = 0.0
MainApp.getDbHelper().createIfNotExists(bgReading, "GLIMP")
val glucoseValues = mutableListOf<CgmSourceTransaction.TransactionGlucoseValue>()
glucoseValues += CgmSourceTransaction.TransactionGlucoseValue(
timestamp = inputData.getLong("myTimestamp", 0),
value = inputData.getDouble("mySGV", 0.0),
raw = inputData.getDouble("mySGV", 0.0),
noise = null,
trendArrow = GlucoseValue.TrendArrow.fromString(inputData.getString("myTrend")),
sourceSensor = GlucoseValue.SourceSensor.GLIMP
)
glimpPlugin.disposable += repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, emptyList(), null)).subscribe({ savedValues ->
savedValues.forEach {
broadcastToXDrip(it)
if (sp.getBoolean(R.string.key_dexcomg5_nsupload, false))
nsUpload.uploadBg(BgReading(injector, it), GlucoseValue.SourceSensor.GLIMP.text)
}
}, {
aapsLogger.error(LTag.BGSOURCE, "Error while saving values from Glimp App", it)
})
return Result.success()
}
}

View file

@ -4,8 +4,10 @@ import android.content.Context
import androidx.work.Worker
import androidx.work.WorkerParameters
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.R
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.GlucoseValue
import info.nightscout.androidaps.database.transactions.CgmSourceTransaction
import info.nightscout.androidaps.db.BgReading
import info.nightscout.androidaps.interfaces.BgSourceInterface
import info.nightscout.androidaps.interfaces.PluginBase
@ -13,7 +15,14 @@ import info.nightscout.androidaps.interfaces.PluginDescription
import info.nightscout.androidaps.interfaces.PluginType
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload
import info.nightscout.androidaps.receivers.BundleStore
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.XDripBroadcast
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign
import org.json.JSONArray
import org.json.JSONException
import javax.inject.Inject
@ -33,6 +42,13 @@ class MM640gPlugin @Inject constructor(
aapsLogger, resourceHelper, injector
), BgSourceInterface {
private val disposable = CompositeDisposable()
override fun onStop() {
disposable.clear()
super.onStop()
}
// cannot be inner class because of needed injection
class MM640gWorker(
context: Context,
@ -40,7 +56,14 @@ class MM640gPlugin @Inject constructor(
) : Worker(context, params) {
@Inject lateinit var mM640gPlugin: MM640gPlugin
@Inject lateinit var injector: HasAndroidInjector
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var sp: SP
@Inject lateinit var nsUpload: NSUpload
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var bundleStore: BundleStore
@Inject lateinit var repository: AppRepository
@Inject lateinit var broadcastToXDrip: XDripBroadcast
init {
(context.applicationContext as HasAndroidInjector).androidInjector().inject(this)
@ -54,22 +77,32 @@ class MM640gPlugin @Inject constructor(
aapsLogger.debug(LTag.BGSOURCE, "Received MM640g Data: $data")
if (data != null && data.isNotEmpty()) {
try {
val glucoseValues = mutableListOf<CgmSourceTransaction.TransactionGlucoseValue>()
val jsonArray = JSONArray(data)
for (i in 0 until jsonArray.length()) {
val jsonObject = jsonArray.getJSONObject(i)
when (val type = jsonObject.getString("type")) {
"sgv" -> {
val bgReading = BgReading()
bgReading.value = jsonObject.getDouble("sgv")
bgReading.direction = jsonObject.getString("direction")
bgReading.date = jsonObject.getLong("date")
bgReading.raw = jsonObject.getDouble("sgv")
MainApp.getDbHelper().createIfNotExists(bgReading, "MM640g")
}
"sgv" ->
glucoseValues += CgmSourceTransaction.TransactionGlucoseValue(
timestamp = jsonObject.getLong("sgv"),
value = jsonObject.getDouble("sgv"),
raw = jsonObject.getDouble("sgv"),
noise = null,
trendArrow = GlucoseValue.TrendArrow.fromString(jsonObject.getString("direction")),
sourceSensor = GlucoseValue.SourceSensor.MM_600_SERIES
)
else -> aapsLogger.debug(LTag.BGSOURCE, "Unknown entries type: $type")
}
}
mM640gPlugin.disposable += repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, emptyList(), null)).subscribe({ savedValues ->
savedValues.forEach {
broadcastToXDrip(it)
if (sp.getBoolean(R.string.key_dexcomg5_nsupload, false))
nsUpload.uploadBg(BgReading(injector, it), GlucoseValue.SourceSensor.MM_600_SERIES.text)
}
}, {
aapsLogger.error(LTag.BGSOURCE, "Error while saving values from Eversense App", it)
})
} catch (e: JSONException) {
aapsLogger.error("Exception: ", e)
}

View file

@ -5,20 +5,25 @@ import androidx.work.Worker
import androidx.work.WorkerParameters
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.Config
import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.R
import info.nightscout.androidaps.db.BgReading
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.GlucoseValue
import info.nightscout.androidaps.database.transactions.CgmSourceTransaction
import info.nightscout.androidaps.interfaces.BgSourceInterface
import info.nightscout.androidaps.interfaces.PluginBase
import info.nightscout.androidaps.interfaces.PluginDescription
import info.nightscout.androidaps.interfaces.PluginType
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload
import info.nightscout.androidaps.plugins.general.nsclient.data.NSSgv
import info.nightscout.androidaps.utils.JsonHelper.safeGetLong
import info.nightscout.androidaps.utils.JsonHelper.safeGetString
import info.nightscout.androidaps.receivers.BundleStore
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.XDripBroadcast
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign
import org.json.JSONArray
import org.json.JSONObject
import javax.inject.Inject
@ -50,21 +55,27 @@ class NSClientSourcePlugin @Inject constructor(
}
}
private val disposable = CompositeDisposable()
override fun onStop() {
disposable.clear()
super.onStop()
}
override fun advancedFilteringSupported(): Boolean {
return isAdvancedFilteringEnabled
}
private fun storeSgv(sgvJson: JSONObject) {
val nsSgv = NSSgv(sgvJson)
val bgReading = BgReading(injector, nsSgv)
MainApp.getDbHelper().createIfNotExists(bgReading, "NS")
detectSource(safeGetString(sgvJson, "device", "none"), safeGetLong(sgvJson, "mills"))
}
private fun detectSource(source: String, timeStamp: Long) {
if (timeStamp > lastBGTimeStamp) {
isAdvancedFilteringEnabled = source.contains("G5 Native") || source.contains("G6 Native") || source.contains("AndroidAPS-DexcomG5") || source.contains("AndroidAPS-DexcomG6")
lastBGTimeStamp = timeStamp
private fun detectSource(glucoseValue: GlucoseValue) {
if (glucoseValue.timestamp > lastBGTimeStamp) {
isAdvancedFilteringEnabled = arrayOf(
GlucoseValue.SourceSensor.DEXCOM_NATIVE_UNKNOWN,
GlucoseValue.SourceSensor.DEXCOM_G6_NATIVE,
GlucoseValue.SourceSensor.DEXCOM_G5_NATIVE,
GlucoseValue.SourceSensor.DEXCOM_G6_NATIVE_XDRIP,
GlucoseValue.SourceSensor.DEXCOM_G5_NATIVE_XDRIP
).any { it == glucoseValue.sourceSensor }
lastBGTimeStamp = glucoseValue.timestamp
}
}
@ -75,29 +86,53 @@ class NSClientSourcePlugin @Inject constructor(
) : Worker(context, params) {
@Inject lateinit var nsClientSourcePlugin: NSClientSourcePlugin
@Inject lateinit var injector: HasAndroidInjector
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var sp: SP
@Inject lateinit var nsUpload: NSUpload
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var bundleStore: BundleStore
@Inject lateinit var repository: AppRepository
@Inject lateinit var broadcastToXDrip: XDripBroadcast
init {
(context.applicationContext as HasAndroidInjector).androidInjector().inject(this)
}
fun toGv(jsonObject: JSONObject): CgmSourceTransaction.TransactionGlucoseValue {
val sgv = NSSgv(jsonObject)
return CgmSourceTransaction.TransactionGlucoseValue(
timestamp = sgv.mills,
value = sgv.mgdl.toDouble(),
noise = null,
raw = if (sgv.filtered != null) sgv.filtered.toDouble() else sgv.mgdl.toDouble(),
trendArrow = GlucoseValue.TrendArrow.fromString(sgv.direction),
sourceSensor = GlucoseValue.SourceSensor.fromString(sgv.device)
)
}
override fun doWork(): Result {
if (!nsClientSourcePlugin.isEnabled(PluginType.BGSOURCE) && !sp.getBoolean(R.string.key_ns_autobackfill, true)) return Result.failure()
try {
val glucoseValues = mutableListOf<CgmSourceTransaction.TransactionGlucoseValue>()
inputData.getString("sgv")?.let { sgvString ->
aapsLogger.debug(LTag.BGSOURCE, "Received NS Data: $sgvString")
val sgvJson = JSONObject(sgvString)
nsClientSourcePlugin.storeSgv(sgvJson)
glucoseValues += toGv(JSONObject(sgvString))
}
inputData.getString("sgvs")?.let { sgvString ->
aapsLogger.debug(LTag.BGSOURCE, "Received NS Data: $sgvString")
val jsonArray = JSONArray(sgvString)
for (i in 0 until jsonArray.length()) {
val sgvJson = jsonArray.getJSONObject(i)
nsClientSourcePlugin.storeSgv(sgvJson)
}
for (i in 0 until jsonArray.length())
glucoseValues += toGv(jsonArray.getJSONObject(i))
}
nsClientSourcePlugin.disposable += repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, emptyList(), null)).subscribe({ savedValues ->
savedValues.forEach {
broadcastToXDrip(it)
nsClientSourcePlugin.detectSource(it)
}
}, {
aapsLogger.error(LTag.BGSOURCE, "Error while saving values from Eversense App", it)
})
} catch (e: Exception) {
aapsLogger.error("Unhandled exception", e)
return Result.failure()

View file

@ -5,8 +5,10 @@ import androidx.work.Worker
import androidx.work.WorkerParameters
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.R
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.GlucoseValue
import info.nightscout.androidaps.database.transactions.CgmSourceTransaction
import info.nightscout.androidaps.db.BgReading
import info.nightscout.androidaps.interfaces.BgSourceInterface
import info.nightscout.androidaps.interfaces.PluginBase
@ -16,8 +18,11 @@ import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload
import info.nightscout.androidaps.utils.JsonHelper.safeGetString
import info.nightscout.androidaps.utils.XDripBroadcast
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign
import org.json.JSONArray
import org.json.JSONException
import javax.inject.Inject
@ -38,16 +43,26 @@ class PoctechPlugin @Inject constructor(
aapsLogger, resourceHelper, injector
), BgSourceInterface {
private val disposable = CompositeDisposable()
override fun onStop() {
disposable.clear()
super.onStop()
}
// cannot be inner class because of needed injection
class PoctechWorker(
context: Context,
params: WorkerParameters
) : Worker(context, params) {
@Inject lateinit var injector: HasAndroidInjector
@Inject lateinit var poctechPlugin: PoctechPlugin
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var sp: SP
@Inject lateinit var nsUpload: NSUpload
@Inject lateinit var repository: AppRepository
@Inject lateinit var broadcastToXDrip: XDripBroadcast
init {
(context.applicationContext as HasAndroidInjector).androidInjector().inject(this)
@ -57,24 +72,30 @@ class PoctechPlugin @Inject constructor(
if (!poctechPlugin.isEnabled(PluginType.BGSOURCE)) return Result.failure()
aapsLogger.debug(LTag.BGSOURCE, "Received Poctech Data $inputData")
try {
val glucoseValues = mutableListOf<CgmSourceTransaction.TransactionGlucoseValue>()
val jsonArray = JSONArray(inputData.getString("data"))
aapsLogger.debug(LTag.BGSOURCE, "Received Poctech Data size:" + jsonArray.length())
for (i in 0 until jsonArray.length()) {
val json = jsonArray.getJSONObject(i)
val bgReading = BgReading()
bgReading.value = json.getDouble("current")
bgReading.direction = json.getString("direction")
bgReading.date = json.getLong("date")
bgReading.raw = json.getDouble("raw")
if (safeGetString(json, "units", Constants.MGDL) == "mmol/L") bgReading.value = bgReading.value * Constants.MMOLL_TO_MGDL
val isNew = MainApp.getDbHelper().createIfNotExists(bgReading, "Poctech")
if (isNew && sp.getBoolean(R.string.key_dexcomg5_nsupload, false)) {
nsUpload.uploadBg(bgReading, "AndroidAPS-Poctech")
}
if (isNew && sp.getBoolean(R.string.key_dexcomg5_xdripupload, false)) {
nsUpload.sendToXdrip(bgReading)
}
glucoseValues += CgmSourceTransaction.TransactionGlucoseValue(
timestamp = json.getLong("date"),
value = if (safeGetString(json, "units", Constants.MGDL) == "mmol/L") json.getDouble("current")
else json.getDouble("current") * Constants.MMOLL_TO_MGDL,
raw = json.getDouble("raw"),
noise = null,
trendArrow = GlucoseValue.TrendArrow.fromString(json.getString("direction")),
sourceSensor = GlucoseValue.SourceSensor.POCTECH_NATIVE
)
}
poctechPlugin.disposable += repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, emptyList(), null)).subscribe({ savedValues ->
savedValues.forEach {
broadcastToXDrip(it)
if (sp.getBoolean(R.string.key_dexcomg5_nsupload, false))
nsUpload.uploadBg(BgReading(injector, it), GlucoseValue.SourceSensor.POCTECH_NATIVE.text)
}
}, {
aapsLogger.error(LTag.BGSOURCE, "Error while saving values from Poctech App", it)
})
} catch (e: JSONException) {
aapsLogger.error("Exception: ", e)
return Result.failure()

View file

@ -3,8 +3,10 @@ package info.nightscout.androidaps.plugins.source
import android.os.Handler
import android.os.HandlerThread
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.R
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.GlucoseValue
import info.nightscout.androidaps.database.transactions.CgmSourceTransaction
import info.nightscout.androidaps.db.BgReading
import info.nightscout.androidaps.interfaces.BgSourceInterface
import info.nightscout.androidaps.interfaces.PluginBase
@ -16,10 +18,13 @@ import info.nightscout.androidaps.plugins.general.nsclient.NSUpload
import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.XDripBroadcast
import info.nightscout.androidaps.utils.buildHelper.BuildHelper
import info.nightscout.androidaps.utils.extensions.isRunningTest
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign
import java.util.*
import javax.inject.Inject
import javax.inject.Singleton
@ -34,7 +39,10 @@ class RandomBgPlugin @Inject constructor(
private val virtualPumpPlugin: VirtualPumpPlugin,
private val buildHelper: BuildHelper,
private val sp: SP,
private val nsUpload: NSUpload
private val nsUpload: NSUpload,
private val dateUtil: DateUtil,
private val repository: AppRepository,
private val xDripBroadcast: XDripBroadcast
) : PluginBase(PluginDescription()
.mainType(PluginType.BGSOURCE)
.fragmentClass(BGSourceFragment::class.java.name)
@ -61,6 +69,8 @@ class RandomBgPlugin @Inject constructor(
}
}
private val disposable = CompositeDisposable()
override fun advancedFilteringSupported(): Boolean {
return true
}
@ -68,6 +78,7 @@ class RandomBgPlugin @Inject constructor(
override fun onStart() {
super.onStart()
loopHandler.postDelayed(refreshLoop, T.mins(interval).msecs())
disposable.clear()
}
override fun onStop() {
@ -88,16 +99,24 @@ class RandomBgPlugin @Inject constructor(
val currentMinute = cal.get(Calendar.MINUTE) + (cal.get(Calendar.HOUR_OF_DAY) % 2) * 60
val bgMgdl = min + ((max - min) + (max - min) * sin(currentMinute / 120.0 * 2 * PI)) / 2
val bgReading = BgReading()
bgReading.value = bgMgdl
bgReading.date = DateUtil.now()
bgReading.raw = bgMgdl
if (MainApp.getDbHelper().createIfNotExists(bgReading, "RandomBG")) {
if (sp.getBoolean(R.string.key_dexcomg5_nsupload, false))
nsUpload.uploadBg(bgReading, "AndroidAPS-RandomBG")
if (sp.getBoolean(R.string.key_dexcomg5_xdripupload, false))
nsUpload.sendToXdrip(bgReading)
}
aapsLogger.debug(LTag.BGSOURCE, "Generated BG: $bgReading")
val glucoseValues = mutableListOf<CgmSourceTransaction.TransactionGlucoseValue>()
glucoseValues += CgmSourceTransaction.TransactionGlucoseValue(
timestamp = dateUtil._now(),
value = bgMgdl,
raw = 0.0,
noise = null,
trendArrow = GlucoseValue.TrendArrow.NONE,
sourceSensor = GlucoseValue.SourceSensor.RANDOM
)
disposable += repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, emptyList(), null)).subscribe({ savedValues ->
savedValues.forEach {
xDripBroadcast(it)
if (sp.getBoolean(R.string.key_dexcomg5_nsupload, false))
nsUpload.uploadBg(BgReading(injector, it), GlucoseValue.SourceSensor.RANDOM.text)
}
}, {
aapsLogger.error(LTag.BGSOURCE, "Error while saving values from Random plugin", it)
})
aapsLogger.debug(LTag.BGSOURCE, "Generated BG: $bgMgdl ${Date()}")
}
}

View file

@ -4,8 +4,10 @@ import android.content.Context
import androidx.work.Worker
import androidx.work.WorkerParameters
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.R
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.GlucoseValue
import info.nightscout.androidaps.database.transactions.CgmSourceTransaction
import info.nightscout.androidaps.db.BgReading
import info.nightscout.androidaps.interfaces.BgSourceInterface
import info.nightscout.androidaps.interfaces.PluginBase
@ -14,8 +16,11 @@ import info.nightscout.androidaps.interfaces.PluginType
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload
import info.nightscout.androidaps.utils.XDripBroadcast
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign
import javax.inject.Inject
import javax.inject.Singleton
@ -35,16 +40,26 @@ class TomatoPlugin @Inject constructor(
aapsLogger, resourceHelper, injector
), BgSourceInterface {
private val disposable = CompositeDisposable()
override fun onStop() {
disposable.clear()
super.onStop()
}
// cannot be inner class because of needed injection
class TomatoWorker(
context: Context,
params: WorkerParameters
) : Worker(context, params) {
@Inject lateinit var injector: HasAndroidInjector
@Inject lateinit var tomatoPlugin: TomatoPlugin
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var sp: SP
@Inject lateinit var nsUpload: NSUpload
@Inject lateinit var repository: AppRepository
@Inject lateinit var broadcastToXDrip: XDripBroadcast
init {
(context.applicationContext as HasAndroidInjector).androidInjector().inject(this)
@ -52,17 +67,24 @@ class TomatoPlugin @Inject constructor(
override fun doWork(): Result {
if (!tomatoPlugin.isEnabled(PluginType.BGSOURCE)) return Result.failure()
val bgReading = BgReading()
aapsLogger.debug(LTag.BGSOURCE, "Received Tomato Data")
bgReading.value = inputData.getDouble("com.fanqies.tomatofn.Extras.BgEstimate", 0.0)
bgReading.date = inputData.getLong("com.fanqies.tomatofn.Extras.Time", 0)
val isNew = MainApp.getDbHelper().createIfNotExists(bgReading, "Tomato")
if (isNew && sp.getBoolean(R.string.key_dexcomg5_nsupload, false)) {
nsUpload.uploadBg(bgReading, "AndroidAPS-Tomato")
}
if (isNew && sp.getBoolean(R.string.key_dexcomg5_xdripupload, false)) {
nsUpload.sendToXdrip(bgReading)
}
val glucoseValues = mutableListOf<CgmSourceTransaction.TransactionGlucoseValue>()
glucoseValues += CgmSourceTransaction.TransactionGlucoseValue(
timestamp = inputData.getLong("com.fanqies.tomatofn.Extras.Time", 0),
value = inputData.getDouble("com.fanqies.tomatofn.Extras.BgEstimate", 0.0),
raw = 0.0,
noise = null,
trendArrow = GlucoseValue.TrendArrow.NONE,
sourceSensor = GlucoseValue.SourceSensor.LIBRE_1_TOMATO
)
tomatoPlugin.disposable += repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, emptyList(), null)).subscribe({ savedValues ->
savedValues.forEach {
broadcastToXDrip(it)
if (sp.getBoolean(R.string.key_dexcomg5_nsupload, false))
nsUpload.uploadBg(BgReading(injector, it), GlucoseValue.SourceSensor.LIBRE_1_TOMATO.text)
}
}, {
aapsLogger.error(LTag.BGSOURCE, "Error while saving values from Tomato App", it)
})
return Result.success()
}
}

View file

@ -4,9 +4,10 @@ import android.content.Context
import androidx.work.Worker
import androidx.work.WorkerParameters
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.R
import info.nightscout.androidaps.db.BgReading
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.GlucoseValue
import info.nightscout.androidaps.database.transactions.CgmSourceTransaction
import info.nightscout.androidaps.interfaces.BgSourceInterface
import info.nightscout.androidaps.interfaces.PluginBase
import info.nightscout.androidaps.interfaces.PluginDescription
@ -15,6 +16,8 @@ import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.services.Intents
import info.nightscout.androidaps.utils.resources.ResourceHelper
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign
import javax.inject.Inject
import javax.inject.Singleton
@ -39,8 +42,21 @@ class XdripPlugin @Inject constructor(
return advancedFiltering
}
private fun setSource(source: String) {
advancedFiltering = source.contains("G5 Native") || source.contains("G6 Native")
private fun detectSource(glucoseValue: GlucoseValue) {
advancedFiltering = arrayOf(
GlucoseValue.SourceSensor.DEXCOM_NATIVE_UNKNOWN,
GlucoseValue.SourceSensor.DEXCOM_G6_NATIVE,
GlucoseValue.SourceSensor.DEXCOM_G5_NATIVE,
GlucoseValue.SourceSensor.DEXCOM_G6_NATIVE_XDRIP,
GlucoseValue.SourceSensor.DEXCOM_G5_NATIVE_XDRIP
).any { it == glucoseValue.sourceSensor }
}
private val disposable = CompositeDisposable()
override fun onStop() {
disposable.clear()
super.onStop()
}
// cannot be inner class because of needed injection
@ -50,6 +66,7 @@ class XdripPlugin @Inject constructor(
) : Worker(context, params) {
@Inject lateinit var xdripPlugin: XdripPlugin
@Inject lateinit var repository: AppRepository
init {
(context.applicationContext as HasAndroidInjector).androidInjector().inject(this)
@ -58,15 +75,24 @@ class XdripPlugin @Inject constructor(
override fun doWork(): Result {
if (!xdripPlugin.isEnabled(PluginType.BGSOURCE)) return Result.failure()
xdripPlugin.aapsLogger.debug(LTag.BGSOURCE, "Received xDrip data: $inputData")
val bgReading = BgReading()
bgReading.value = inputData.getDouble(Intents.EXTRA_BG_ESTIMATE, 0.0)
bgReading.direction = inputData.getString(Intents.EXTRA_BG_SLOPE_NAME)
bgReading.date = inputData.getLong(Intents.EXTRA_TIMESTAMP, 0)
bgReading.raw = inputData.getDouble(Intents.EXTRA_RAW, 0.0)
val glucoseValues = mutableListOf<CgmSourceTransaction.TransactionGlucoseValue>()
glucoseValues += CgmSourceTransaction.TransactionGlucoseValue(
timestamp = inputData.getLong(Intents.EXTRA_TIMESTAMP, 0),
value = inputData.getDouble(Intents.EXTRA_BG_ESTIMATE, 0.0),
raw = inputData.getDouble(Intents.EXTRA_RAW, 0.0),
noise = null,
trendArrow = GlucoseValue.TrendArrow.fromString(inputData.getString(Intents.EXTRA_BG_SLOPE_NAME)),
sourceSensor = GlucoseValue.SourceSensor.fromString(inputData.getString(Intents.XDRIP_DATA_SOURCE_DESCRIPTION)
?: "")
)
xdripPlugin.disposable += repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, emptyList(), null)).subscribe({ savedValues ->
savedValues.forEach {
xdripPlugin.detectSource(it)
}
}, {
xdripPlugin.aapsLogger.error(LTag.BGSOURCE, "Error while saving values from Eversense App", it)
})
xdripPlugin.sensorBatteryLevel = inputData.getInt(Intents.EXTRA_SENSOR_BATTERY, -1)
val source = inputData.getString(Intents.XDRIP_DATA_SOURCE_DESCRIPTION) ?: ""
xdripPlugin.setSource(source)
MainApp.getDbHelper().createIfNotExists(bgReading, "XDRIP")
return Result.success()
}
}

View file

@ -0,0 +1,18 @@
package info.nightscout.androidaps.utils
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.database.entities.GlucoseValue
import info.nightscout.androidaps.db.BgReading
fun GlucoseValue.valueToUnits(units: String): Double =
if (units == Constants.MGDL) value
else value * Constants.MGDL_TO_MMOLL
fun GlucoseValue.valueToUnitsString(units: String): String =
if (units == Constants.MGDL) DecimalFormatter.to0Decimal(value)
else DecimalFormatter.to1Decimal(value * Constants.MGDL_TO_MMOLL)
fun GlucoseValue.convertToBGReading(injector: HasAndroidInjector): BgReading = BgReading(injector, this)
fun List<GlucoseValue>.convertToBGReadings(injector: HasAndroidInjector): List<BgReading> = map { it.convertToBGReading(injector) }

View file

@ -3,7 +3,6 @@ package info.nightscout.androidaps.utils
import info.nightscout.androidaps.Config
import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.R
import info.nightscout.androidaps.db.BgReading
import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.logging.AAPSLogger
@ -106,9 +105,9 @@ class LocalAlertUtils @Inject constructor(
}
fun checkStaleBGAlert() {
val bgReading: BgReading? = iobCobCalculatorPlugin.lastBg()
val bgReading = iobCobCalculatorPlugin.lastBg()
if (sp.getBoolean(R.string.key_enable_missed_bg_readings_alert, false)
&& bgReading != null && bgReading.date + missedReadingsThreshold() < System.currentTimeMillis() && sp.getLong("nextMissedReadingsAlarm", 0L) < System.currentTimeMillis()) {
&& bgReading != null && bgReading.timestamp + missedReadingsThreshold() < System.currentTimeMillis() && sp.getLong("nextMissedReadingsAlarm", 0L) < System.currentTimeMillis()) {
val n = Notification(Notification.BG_READINGS_MISSED, resourceHelper.gs(R.string.missed_bg_readings), Notification.URGENT)
n.soundId = R.raw.alarm
sp.putLong("nextMissedReadingsAlarm", System.currentTimeMillis() + missedReadingsThreshold())

View file

@ -0,0 +1,60 @@
package info.nightscout.androidaps.utils
import android.content.Context
import android.content.Intent
import android.os.Bundle
import info.nightscout.androidaps.R
import info.nightscout.androidaps.database.entities.GlucoseValue
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.utils.sharedPreferences.SP
import org.json.JSONArray
import org.json.JSONException
import org.json.JSONObject
import java.text.SimpleDateFormat
import java.util.*
import javax.inject.Inject
class XDripBroadcast @Inject constructor(
private val context: Context,
private val aapsLogger: AAPSLogger,
private val sp: SP
) {
operator fun invoke(glucoseValue: GlucoseValue) {
if (sp.getBoolean(R.string.key_dexcomg5_xdripupload, false)) {
val format = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ", Locale.US)
try {
val entriesBody = JSONArray()
val json = JSONObject()
json.put("sgv", glucoseValue.value)
json.put("direction", glucoseValue.trendArrow.text)
json.put("device", "G5")
json.put("type", "sgv")
json.put("date", glucoseValue.timestamp)
json.put("dateString", format.format(glucoseValue.timestamp))
entriesBody.put(json)
val bundle = Bundle()
bundle.putString("action", "add")
bundle.putString("collection", "entries")
bundle.putString("data", entriesBody.toString())
val intent = Intent(XDRIP_PLUS_NS_EMULATOR)
intent.putExtras(bundle).addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES)
context.sendBroadcast(intent)
val receivers = context.packageManager.queryBroadcastReceivers(intent, 0)
if (receivers.size < 1) {
//NSUpload.log.debug("No xDrip receivers found. ")
aapsLogger.debug(LTag.BGSOURCE, "No xDrip receivers found.")
} else {
aapsLogger.debug(LTag.BGSOURCE, "${receivers.size} xDrip receivers")
}
} catch (e: JSONException) {
aapsLogger.error(LTag.BGSOURCE, "Unhandled exception", e)
}
}
}
companion object {
const val XDRIP_PLUS_NS_EMULATOR = "com.eveningoutpost.dexdrip.NS_EMULATOR"
}
}

View file

@ -3,9 +3,9 @@ package info.nightscout.androidaps.utils.stats
import android.text.Spanned
import android.util.LongSparseArray
import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.Profile
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.HtmlHelper
@ -19,18 +19,20 @@ import javax.inject.Singleton
class TirCalculator @Inject constructor(
private val resourceHelper: ResourceHelper,
private val profileFunction: ProfileFunction,
private val dateUtil: DateUtil
){
private val dateUtil: DateUtil,
private val repository: AppRepository
) {
fun calculate(days: Long, lowMgdl: Double, highMgdl: Double): LongSparseArray<TIR> {
if (lowMgdl < 39) throw RuntimeException("Low below 39")
if (lowMgdl > highMgdl) throw RuntimeException("Low > High")
val startTime = MidnightTime.calc(DateUtil.now() - T.days(days).msecs())
val endTime = MidnightTime.calc(DateUtil.now())
val bgReadings = MainApp.getDbHelper().getBgreadingsDataFromTime(startTime, endTime, true)
val bgReadings = repository.compatGetBgReadingsDataFromTime(startTime, endTime, true).blockingGet()
val result = LongSparseArray<TIR>()
for (bg in bgReadings) {
val midnight = MidnightTime.calc(bg.date)
val midnight = MidnightTime.calc(bg.timestamp)
var tir = result[midnight]
if (tir == null) {
tir = TIR(midnight, lowMgdl, highMgdl)

View file

@ -3,10 +3,10 @@ package info.nightscout.androidaps.utils.wizard
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.Profile
import info.nightscout.androidaps.db.BgReading
import info.nightscout.androidaps.database.entities.GlucoseValue
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
@ -14,6 +14,7 @@ import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.JsonHelper.safeGetInt
import info.nightscout.androidaps.utils.JsonHelper.safeGetString
import info.nightscout.androidaps.utils.sharedPreferences.SP
import info.nightscout.androidaps.utils.valueToUnits
import org.json.JSONException
import org.json.JSONObject
import java.util.*
@ -71,7 +72,7 @@ class QuickWizardEntry @Inject constructor(private val injector: HasAndroidInjec
fun isActive(): Boolean = Profile.secondsFromMidnight() >= validFrom() && Profile.secondsFromMidnight() <= validTo()
fun doCalc(profile: Profile, profileName: String, lastBG: BgReading, _synchronized: Boolean): BolusWizard {
fun doCalc(profile: Profile, profileName: String, lastBG: GlucoseValue, _synchronized: Boolean): BolusWizard {
val tempTarget = treatmentsPlugin.tempTargetFromHistory
//BG
var bg = 0.0

View file

@ -42,4 +42,8 @@ android {
}
dependencies {
implementation project(':database')
}
apply from: 'core_dependencies.gradle'

View file

@ -42,7 +42,9 @@ dependencies {
api 'com.google.firebase:firebase-database-ktx'
//RxBus
api "io.reactivex.rxjava2:rxandroid:${rxandroid_version}"
api "io.reactivex.rxjava2:rxjava:$rxjava_version"
api "io.reactivex.rxjava2:rxkotlin:$rxkotlin_version"
api "io.reactivex.rxjava2:rxandroid:$rxandroid_version"
api "org.apache.commons:commons-lang3:$commonslang3_version"
//CryptoUtil

View file

@ -1,334 +0,0 @@
package info.nightscout.androidaps.db;
import androidx.annotation.NonNull;
import com.j256.ormlite.field.DatabaseField;
import com.j256.ormlite.table.DatabaseTable;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import javax.inject.Inject;
import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.Constants;
import info.nightscout.androidaps.core.R;
import info.nightscout.androidaps.interfaces.DatabaseHelperInterface;
import info.nightscout.androidaps.logging.AAPSLogger;
import info.nightscout.androidaps.logging.LTag;
import info.nightscout.androidaps.interfaces.ProfileFunction;
import info.nightscout.androidaps.plugins.general.nsclient.data.NSSgv;
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.DataPointWithLabelInterface;
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.PointsWithLabelGraphSeries;
import info.nightscout.androidaps.utils.DateUtil;
import info.nightscout.androidaps.utils.DecimalFormatter;
import info.nightscout.androidaps.utils.DefaultValueHelper;
import info.nightscout.androidaps.utils.T;
import info.nightscout.androidaps.utils.resources.ResourceHelper;
@DatabaseTable(tableName = "BgReadings")
public class BgReading implements DataPointWithLabelInterface {
@Inject public AAPSLogger aapsLogger;
@Inject public DefaultValueHelper defaultValueHelper;
@Inject public ProfileFunction profileFunction;
@Inject public ResourceHelper resourceHelper;
@Inject public DateUtil dateUtil;
@DatabaseField(id = true)
public long date;
@DatabaseField
public boolean isValid = true;
@DatabaseField
public double value;
@DatabaseField
public String direction;
@DatabaseField
public double raw;
@DatabaseField
public int source = Source.NONE;
@DatabaseField
public String _id = null; // NS _id
public boolean isCOBPrediction = false; // true when drawing predictions as bg points (COB)
public boolean isaCOBPrediction = false; // true when drawing predictions as bg points (aCOB)
public boolean isIOBPrediction = false; // true when drawing predictions as bg points (IOB)
public boolean isUAMPrediction = false; // true when drawing predictions as bg points (UAM)
public boolean isZTPrediction = false; // true when drawing predictions as bg points (ZT)
public BgReading() {
StaticInjector.Companion.getInstance().androidInjector().inject(this);
}
public BgReading(HasAndroidInjector injector) {
injector.androidInjector().inject(this);
}
public BgReading(HasAndroidInjector injector, NSSgv sgv) {
injector.androidInjector().inject(this);
date = sgv.getMills();
value = sgv.getMgdl();
raw = sgv.getFiltered() != null ? sgv.getFiltered() : value;
direction = sgv.getDirection();
_id = sgv.getId();
}
public Double valueToUnits(String units) {
if (units.equals(Constants.MGDL))
return value;
else
return value * Constants.MGDL_TO_MMOLL;
}
public String valueToUnitsToString(String units) {
if (units.equals(Constants.MGDL)) return DecimalFormatter.to0Decimal(value);
else return DecimalFormatter.to1Decimal(value * Constants.MGDL_TO_MMOLL);
}
public String directionToSymbol(DatabaseHelperInterface databaseHelper) {
String symbol = "";
if (direction == null)
direction = calculateDirection(databaseHelper);
if (direction.compareTo("DoubleDown") == 0) {
symbol = "\u21ca";
} else if (direction.compareTo("SingleDown") == 0) {
symbol = "\u2193";
} else if (direction.compareTo("FortyFiveDown") == 0) {
symbol = "\u2198";
} else if (direction.compareTo("Flat") == 0) {
symbol = "\u2192";
} else if (direction.compareTo("FortyFiveUp") == 0) {
symbol = "\u2197";
} else if (direction.compareTo("SingleUp") == 0) {
symbol = "\u2191";
} else if (direction.compareTo("DoubleUp") == 0) {
symbol = "\u21c8";
} else if (isSlopeNameInvalid(direction)) {
symbol = "??";
}
return symbol;
}
public int directionToIcon(DatabaseHelperInterface databaseHelper) {
int symbol = 0;
if (direction == null)
direction = calculateDirection(databaseHelper);
if (direction.compareTo("DoubleDown") == 0) {
symbol = R.drawable.ic_doubledown;
} else if (direction.compareTo("SingleDown") == 0) {
symbol = R.drawable.ic_singledown;
} else if (direction.compareTo("FortyFiveDown") == 0) {
symbol = R.drawable.ic_fortyfivedown;;
} else if (direction.compareTo("Flat") == 0) {
symbol = R.drawable.ic_flat;;
} else if (direction.compareTo("FortyFiveUp") == 0) {
symbol = R.drawable.ic_fortyfiveup;
} else if (direction.compareTo("SingleUp") == 0) {
symbol = R.drawable.ic_singleup;
} else if (direction.compareTo("DoubleUp") == 0) {
symbol = R.drawable.ic_doubleup;
} else if (isSlopeNameInvalid(direction)) {
symbol = R.drawable.ic_invalid;
}
return symbol;
}
private static boolean isSlopeNameInvalid(String direction) {
return direction.compareTo("NOT_COMPUTABLE") == 0 ||
direction.compareTo("NOT COMPUTABLE") == 0 ||
direction.compareTo("OUT_OF_RANGE") == 0 ||
direction.compareTo("OUT OF RANGE") == 0 ||
direction.compareTo("NONE") == 0 ||
direction.compareTo("NotComputable") == 0;
}
@NonNull @Override
public String toString() {
return "BgReading{" +
"date=" + date +
", date=" + dateUtil.dateAndTimeString(date) +
", value=" + value +
", direction=" + direction +
", raw=" + raw +
'}';
}
public boolean isDataChanging(BgReading other) {
if (date != other.date) {
aapsLogger.debug(LTag.GLUCOSE, "Comparing different");
return false;
}
if (value != other.value)
return true;
return false;
}
public boolean isEqual(BgReading other) {
if (date != other.date) {
aapsLogger.debug(LTag.GLUCOSE, "Comparing different");
return false;
}
if (value != other.value)
return false;
if (raw != other.raw)
return false;
if (!Objects.equals(direction, other.direction))
return false;
if (!Objects.equals(_id, other._id))
return false;
return true;
}
public void copyFrom(BgReading other) {
if (date != other.date) {
aapsLogger.error(LTag.GLUCOSE, "Copying different");
return;
}
value = other.value;
raw = other.raw;
direction = other.direction;
_id = other._id;
}
public BgReading date(long date) {
this.date = date;
return this;
}
public BgReading date(Date date) {
this.date = date.getTime();
return this;
}
public BgReading value(double value) {
this.value = value;
return this;
}
// ------------------ DataPointWithLabelInterface ------------------
@Override
public double getX() {
return date;
}
@Override
public double getY() {
return valueToUnits(profileFunction.getUnits());
}
@Override
public void setY(double y) {
}
@Override
public String getLabel() {
return null;
}
@Override
public long getDuration() {
return 0;
}
@Override
public PointsWithLabelGraphSeries.Shape getShape() {
if (isPrediction())
return PointsWithLabelGraphSeries.Shape.PREDICTION;
else
return PointsWithLabelGraphSeries.Shape.BG;
}
@Override
public float getSize() {
return 1;
}
@Override
public int getColor() {
String units = profileFunction.getUnits();
Double lowLine = defaultValueHelper.determineLowLine();
Double highLine = defaultValueHelper.determineHighLine();
int color = resourceHelper.gc(R.color.inrange);
if (isPrediction())
return getPredectionColor();
else if (valueToUnits(units) < lowLine)
color = resourceHelper.gc(R.color.low);
else if (valueToUnits(units) > highLine)
color = resourceHelper.gc(R.color.high);
return color;
}
public int getPredectionColor() {
if (isIOBPrediction)
return resourceHelper.gc(R.color.iob);
if (isCOBPrediction)
return resourceHelper.gc(R.color.cob);
if (isaCOBPrediction)
return 0x80FFFFFF & resourceHelper.gc(R.color.cob);
if (isUAMPrediction)
return resourceHelper.gc(R.color.uam);
if (isZTPrediction)
return resourceHelper.gc(R.color.zt);
return R.color.white;
}
private boolean isPrediction() {
return isaCOBPrediction || isCOBPrediction || isIOBPrediction || isUAMPrediction || isZTPrediction;
}
// Copied from xDrip+
String calculateDirection(DatabaseHelperInterface databasehelper) {
// Rework to get bgreaings from internal DB and calculate on that base
List<BgReading> bgReadingsList = databasehelper.getAllBgreadingsDataFromTime(this.date - T.mins(10).msecs(), false);
if (bgReadingsList == null || bgReadingsList.size() < 2)
return "NONE";
BgReading current = bgReadingsList.get(1);
BgReading previous = bgReadingsList.get(0);
if (bgReadingsList.get(1).date < bgReadingsList.get(0).date) {
current = bgReadingsList.get(0);
previous = bgReadingsList.get(1);
}
double slope;
// Avoid division by 0
if (current.date == previous.date)
slope = 0;
else
slope = (previous.value - current.value) / (previous.date - current.date);
aapsLogger.error(LTag.GLUCOSE, "Slope is :" + slope + " delta " + (previous.value - current.value) + " date difference " + (current.date - previous.date));
double slope_by_minute = slope * 60000;
String arrow = "NONE";
if (slope_by_minute <= (-3.5)) {
arrow = "DoubleDown";
} else if (slope_by_minute <= (-2)) {
arrow = "SingleDown";
} else if (slope_by_minute <= (-1)) {
arrow = "FortyFiveDown";
} else if (slope_by_minute <= (1)) {
arrow = "Flat";
} else if (slope_by_minute <= (2)) {
arrow = "FortyFiveUp";
} else if (slope_by_minute <= (3.5)) {
arrow = "SingleUp";
} else if (slope_by_minute <= (40)) {
arrow = "DoubleUp";
}
aapsLogger.error(LTag.GLUCOSE, "Direction set to: " + arrow);
return arrow;
}
}

View file

@ -0,0 +1,171 @@
package info.nightscout.androidaps.db
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.core.R
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.GlucoseValue
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.DataPointWithLabelInterface
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.PointsWithLabelGraphSeries
import info.nightscout.androidaps.utils.DecimalFormatter
import info.nightscout.androidaps.utils.DefaultValueHelper
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.resources.ResourceHelper
import javax.inject.Inject
class BgReading : DataPointWithLabelInterface {
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var defaultValueHelper: DefaultValueHelper
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var repository: AppRepository
var data: GlucoseValue
// Compatibility functions
fun setDate(timeStamp: Long) {
data.timestamp = timeStamp
}
fun getDate(): Long = data.timestamp
fun getValue(): Double = data.value
fun setValue(value: Double) {
data.value = value
}
var isCOBPrediction = false // true when drawing predictions as bg points (COB)
var isaCOBPrediction = false // true when drawing predictions as bg points (aCOB)
var isIOBPrediction = false // true when drawing predictions as bg points (IOB)
var isUAMPrediction = false // true when drawing predictions as bg points (UAM)
var isZTPrediction = false // true when drawing predictions as bg points (ZT)
@Deprecated("Create only with data")
constructor(injector: HasAndroidInjector) {
injector.androidInjector().inject(this)
data = GlucoseValue(
timestamp = 0,
utcOffset = 0,
raw = null,
value = 0.0,
trendArrow = GlucoseValue.TrendArrow.NONE,
noise = null,
sourceSensor = GlucoseValue.SourceSensor.UNKNOWN
)
}
constructor(injector: HasAndroidInjector, glucoseValue: GlucoseValue) {
injector.androidInjector().inject(this)
data = glucoseValue
}
fun valueToUnits(units: String): Double =
if (units == Constants.MGDL) data.value else data.value * Constants.MGDL_TO_MMOLL
fun valueToUnitsToString(units: String): String =
if (units == Constants.MGDL) DecimalFormatter.to0Decimal(data.value)
else DecimalFormatter.to1Decimal(data.value * Constants.MGDL_TO_MMOLL)
fun directionToSymbol(): String =
if (data.trendArrow == GlucoseValue.TrendArrow.NONE) calculateDirection().symbol
else data.trendArrow.symbol
fun date(date: Long): BgReading {
data.timestamp = date
return this
}
fun value(value: Double): BgReading {
data.value = value
return this
}
// ------------------ DataPointWithLabelInterface ------------------
override fun getX(): Double {
return data.timestamp.toDouble()
}
override fun getY(): Double {
return valueToUnits(profileFunction.getUnits())
}
override fun setY(y: Double) {}
override fun getLabel(): String? = null
override fun getDuration(): Long = 0
override fun getShape(): PointsWithLabelGraphSeries.Shape =
if (isPrediction) PointsWithLabelGraphSeries.Shape.PREDICTION
else PointsWithLabelGraphSeries.Shape.BG
override fun getSize(): Float = 1f
override fun getColor(): Int {
val units = profileFunction.getUnits()
val lowLine = defaultValueHelper.determineLowLine()
val highLine = defaultValueHelper.determineHighLine()
return when {
isPrediction -> predictionColor
valueToUnits(units) < lowLine -> resourceHelper.gc(R.color.low)
valueToUnits(units) > highLine -> resourceHelper.gc(R.color.high)
else -> resourceHelper.gc(R.color.inrange)
}
}
val predictionColor: Int
get() {
return when {
isIOBPrediction -> resourceHelper.gc(R.color.iob)
isCOBPrediction -> resourceHelper.gc(R.color.cob)
isaCOBPrediction -> -0x7f000001 and resourceHelper.gc(R.color.cob)
isUAMPrediction -> resourceHelper.gc(R.color.uam)
isZTPrediction -> resourceHelper.gc(R.color.zt)
else -> R.color.white
}
}
private val isPrediction: Boolean
get() = isaCOBPrediction || isCOBPrediction || isIOBPrediction || isUAMPrediction || isZTPrediction
// Copied from xDrip+
fun calculateDirection(): GlucoseValue.TrendArrow {
// Rework to get bgreaings from internal DB and calculate on that base
val bgReadingsList = repository.compatGetBgReadingsDataFromTime(data.timestamp - T.mins(10).msecs(), false)
.blockingGet()
if (bgReadingsList == null || bgReadingsList.size < 2) return GlucoseValue.TrendArrow.NONE
var current = bgReadingsList[1]
var previous = bgReadingsList[0]
if (bgReadingsList[1].timestamp < bgReadingsList[0].timestamp) {
current = bgReadingsList[0]
previous = bgReadingsList[1]
}
val slope: Double
// Avoid division by 0
slope = if (current.timestamp == previous.timestamp) 0.0 else (previous.value - current.value) / (previous.timestamp - current.timestamp)
aapsLogger.error(LTag.GLUCOSE, "Slope is :" + slope + " delta " + (previous.value - current.value) + " date difference " + (current.timestamp - previous.timestamp))
val slope_by_minute = slope * 60000
var arrow = GlucoseValue.TrendArrow.NONE
if (slope_by_minute <= -3.5) {
arrow = GlucoseValue.TrendArrow.DOUBLE_DOWN
} else if (slope_by_minute <= -2) {
arrow = GlucoseValue.TrendArrow.SINGLE_DOWN
} else if (slope_by_minute <= -1) {
arrow = GlucoseValue.TrendArrow.FORTY_FIVE_DOWN
} else if (slope_by_minute <= 1) {
arrow = GlucoseValue.TrendArrow.FLAT
} else if (slope_by_minute <= 2) {
arrow = GlucoseValue.TrendArrow.FORTY_FIVE_UP
} else if (slope_by_minute <= 3.5) {
arrow = GlucoseValue.TrendArrow.SINGLE_UP
} else if (slope_by_minute <= 40) {
arrow = GlucoseValue.TrendArrow.DOUBLE_UP
}
aapsLogger.error(LTag.GLUCOSE, "Direction set to: $arrow")
return arrow
}
private fun isSlopeNameInvalid(direction: String?): Boolean {
return direction!!.compareTo("NOT_COMPUTABLE") == 0 || direction.compareTo("NOT COMPUTABLE") == 0 || direction.compareTo("OUT_OF_RANGE") == 0 || direction.compareTo("OUT OF RANGE") == 0 || direction.compareTo("NONE") == 0 || direction.compareTo("NotComputable") == 0
}
}

View file

@ -5,7 +5,6 @@ import info.nightscout.androidaps.db.*
interface DatabaseHelperInterface {
fun getAllBgreadingsDataFromTime(mills: Long, ascending: Boolean): List<BgReading>
fun createOrUpdate(careportalEvent: CareportalEvent)
fun createOrUpdate(record: DanaRHistoryRecord)
fun createOrUpdate(record: OmnipodHistoryRecord)

View file

@ -2,6 +2,8 @@ package info.nightscout.androidaps.plugins.aps.loop;
import android.text.Spanned;
import androidx.annotation.NonNull;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
@ -107,7 +109,7 @@ public class APSResult {
return String.format(resourceHelper.gs(R.string.carbsreq), carbsReq, carbsReqWithin);
}
@Override
@NonNull @Override
public String toString() {
final PumpInterface pump = activePlugin.getActivePump();
if (isChangeRequested()) {
@ -131,7 +133,7 @@ public class APSResult {
ret += ("SMB: " + DecimalFormatter.toPumpSupportedBolus(smb, activePlugin.getActivePump(), resourceHelper) + "\n");
if (isCarbsRequired()) {
ret += getCarbsRequiredText()+"\n";
ret += getCarbsRequiredText() + "\n";
}
// reason
@ -169,7 +171,7 @@ public class APSResult {
ret += ("<b>" + "SMB" + "</b>: " + DecimalFormatter.toPumpSupportedBolus(smb, activePlugin.getActivePump(), resourceHelper) + "<br>");
if (isCarbsRequired()) {
ret += getCarbsRequiredText()+"<br>";
ret += getCarbsRequiredText() + "<br>";
}
// reason
@ -239,50 +241,50 @@ public class APSResult {
if (predBGs.has("IOB")) {
JSONArray iob = predBGs.getJSONArray("IOB");
for (int i = 1; i < iob.length(); i++) {
BgReading bg = new BgReading();
bg.value = iob.getInt(i);
bg.date = startTime + i * 5 * 60 * 1000L;
bg.isIOBPrediction = true;
BgReading bg = new BgReading(injector);
bg.setValue(iob.getInt(i));
bg.setDate(startTime + i * 5 * 60 * 1000L);
bg.setIOBPrediction(true);
array.add(bg);
}
}
if (predBGs.has("aCOB")) {
JSONArray iob = predBGs.getJSONArray("aCOB");
for (int i = 1; i < iob.length(); i++) {
BgReading bg = new BgReading();
bg.value = iob.getInt(i);
bg.date = startTime + i * 5 * 60 * 1000L;
bg.isaCOBPrediction = true;
BgReading bg = new BgReading(injector);
bg.setValue(iob.getInt(i));
bg.setDate(startTime + i * 5 * 60 * 1000L);
bg.setIsaCOBPrediction(true);
array.add(bg);
}
}
if (predBGs.has("COB")) {
JSONArray iob = predBGs.getJSONArray("COB");
for (int i = 1; i < iob.length(); i++) {
BgReading bg = new BgReading();
bg.value = iob.getInt(i);
bg.date = startTime + i * 5 * 60 * 1000L;
bg.isCOBPrediction = true;
BgReading bg = new BgReading(injector);
bg.setValue(iob.getInt(i));
bg.setDate(startTime + i * 5 * 60 * 1000L);
bg.setCOBPrediction(true);
array.add(bg);
}
}
if (predBGs.has("UAM")) {
JSONArray iob = predBGs.getJSONArray("UAM");
for (int i = 1; i < iob.length(); i++) {
BgReading bg = new BgReading();
bg.value = iob.getInt(i);
bg.date = startTime + i * 5 * 60 * 1000L;
bg.isUAMPrediction = true;
BgReading bg = new BgReading(injector);
bg.setValue(iob.getInt(i));
bg.setDate(startTime + i * 5 * 60 * 1000L);
bg.setUAMPrediction(true);
array.add(bg);
}
}
if (predBGs.has("ZT")) {
JSONArray iob = predBGs.getJSONArray("ZT");
for (int i = 1; i < iob.length(); i++) {
BgReading bg = new BgReading();
bg.value = iob.getInt(i);
bg.date = startTime + i * 5 * 60 * 1000L;
bg.isZTPrediction = true;
BgReading bg = new BgReading(injector);
bg.setValue(iob.getInt(i));
bg.setDate(startTime + i * 5 * 60 * 1000L);
bg.setZTPrediction(true);
array.add(bg);
}
}

View file

@ -406,10 +406,10 @@ public class NSUpload {
JSONObject data = new JSONObject();
try {
data.put("device", source);
data.put("date", reading.date);
data.put("dateString", DateUtil.toISOString(reading.date));
data.put("sgv", reading.value);
data.put("direction", reading.direction);
data.put("date", reading.getDate());
data.put("dateString", DateUtil.toISOString(reading.getDate()));
data.put("sgv", reading.getValue());
data.put("direction", reading.getData().getTrendArrow().getText());
data.put("type", "sgv");
} catch (JSONException e) {
aapsLogger.error("Unhandled exception", e);
@ -461,46 +461,6 @@ public class NSUpload {
}
public void sendToXdrip(BgReading bgReading) {
final String XDRIP_PLUS_NS_EMULATOR = "com.eveningoutpost.dexdrip.NS_EMULATOR";
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ", Locale.US);
try {
final JSONArray entriesBody = new JSONArray();
JSONObject json = new JSONObject();
json.put("sgv", bgReading.value);
if (bgReading.direction == null) {
json.put("direction", "NONE");
} else {
json.put("direction", bgReading.direction);
}
json.put("device", "G5");
json.put("type", "sgv");
json.put("date", bgReading.date);
json.put("dateString", format.format(bgReading.date));
entriesBody.put(json);
final Bundle bundle = new Bundle();
bundle.putString("action", "add");
bundle.putString("collection", "entries");
bundle.putString("data", entriesBody.toString());
final Intent intent = new Intent(XDRIP_PLUS_NS_EMULATOR);
intent.putExtras(bundle).addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
context.sendBroadcast(intent);
List<ResolveInfo> receivers = context.getPackageManager().queryBroadcastReceivers(intent, 0);
if (receivers.size() < 1) {
aapsLogger.debug("No xDrip receivers found. ");
} else {
aapsLogger.debug(receivers.size() + " xDrip receivers");
}
} catch (JSONException e) {
aapsLogger.error("Unhandled exception", e);
}
}
public void createNSTreatment(JSONObject data, ProfileStore profileStore, ProfileFunction profileFunction, long eventTime) {
if (JsonHelper.safeGetString(data, "eventType", "").equals(CareportalEvent.PROFILESWITCH)) {
ProfileSwitch profileSwitch = profileFunction.prepareProfileSwitch(

View file

@ -0,0 +1,18 @@
package info.nightscout.androidaps.utils.extensions
import info.nightscout.androidaps.core.R
import info.nightscout.androidaps.database.entities.GlucoseValue
fun GlucoseValue.TrendArrow.directionToIcon(): Int {
return when {
this == GlucoseValue.TrendArrow.DOUBLE_DOWN -> R.drawable.ic_doubledown
this == GlucoseValue.TrendArrow.SINGLE_DOWN -> R.drawable.ic_singledown
this == GlucoseValue.TrendArrow.FORTY_FIVE_DOWN -> R.drawable.ic_fortyfivedown
this == GlucoseValue.TrendArrow.FLAT -> R.drawable.ic_flat
this == GlucoseValue.TrendArrow.FORTY_FIVE_UP -> R.drawable.ic_fortyfiveup
this == GlucoseValue.TrendArrow.SINGLE_UP -> R.drawable.ic_singleup
this == GlucoseValue.TrendArrow.DOUBLE_UP -> R.drawable.ic_doubleup
this == GlucoseValue.TrendArrow.NONE -> R.drawable.ic_invalid
else -> R.drawable.ic_invalid
}
}

View file

@ -18,7 +18,7 @@ import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.pump.common.defs.PumpType
import info.nightscout.androidaps.queue.Callback
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.extensions.plusAssign
import io.reactivex.rxkotlin.plusAssign
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import io.reactivex.disposables.CompositeDisposable
import java.text.DecimalFormat

View file

@ -106,8 +106,8 @@ class AppRepository @Inject internal constructor(
database.temporaryTargetDao.getLastHistoryRecord(lastId)
}
inline fun <reified T> Maybe<T>.toWrappedSingle(): Single<ValueWrapper<T>> =
@Suppress("USELESS_CAST")
inline fun <reified T : Any> Maybe<T>.toWrappedSingle(): Single<ValueWrapper<T>> =
this.map { ValueWrapper.Existing(it) as ValueWrapper<T> }
.switchIfEmpty(Maybe.just(ValueWrapper.Absent()))
.toSingle()