improve BG sync

This commit is contained in:
Milos Kozak 2021-02-08 13:40:09 +01:00
parent 887819edf4
commit 145765e611
12 changed files with 68 additions and 29 deletions

View file

@ -18,12 +18,15 @@ import info.nightscout.androidaps.events.EventNewBG
import info.nightscout.androidaps.interfaces.DatabaseHelperInterface import info.nightscout.androidaps.interfaces.DatabaseHelperInterface
import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.utils.* import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.extensions.directionToIcon import info.nightscout.androidaps.utils.extensions.directionToIcon
import info.nightscout.androidaps.utils.extensions.toVisibility import info.nightscout.androidaps.utils.extensions.toVisibility
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.androidaps.utils.valueToUnitsString
import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign import io.reactivex.rxkotlin.plusAssign
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
@ -89,8 +92,8 @@ class BGSourceFragment : DaggerFragment() {
@Synchronized @Synchronized
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() super.onDestroyView()
_binding = null
binding.recyclerview.adapter = null // avoid leaks binding.recyclerview.adapter = null // avoid leaks
_binding = null
} }
inner class RecyclerViewAdapter internal constructor(private var glucoseValues: List<GlucoseValue>) : RecyclerView.Adapter<RecyclerViewAdapter.GlucoseValuesViewHolder>() { inner class RecyclerViewAdapter internal constructor(private var glucoseValues: List<GlucoseValue>) : RecyclerView.Adapter<RecyclerViewAdapter.GlucoseValuesViewHolder>() {

View file

@ -132,14 +132,19 @@ class DexcomPlugin @Inject constructor(
} else { } else {
null null
} }
dexcomPlugin.disposable += repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, calibrations, sensorStartTime)).subscribe({ savedValues -> dexcomPlugin.disposable += repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, calibrations, sensorStartTime)).subscribe({ result ->
savedValues.forEach { result.inserted.forEach {
broadcastToXDrip(it) broadcastToXDrip(it)
if (sp.getBoolean(R.string.key_dexcomg5_nsupload, false)) { if (sp.getBoolean(R.string.key_dexcomg5_nsupload, false)) {
if (it.interfaceIDs.nightscoutId != null)
nsUpload.updateBg(it, sourceSensor.text)
else
nsUpload.uploadBg(it, sourceSensor.text) nsUpload.uploadBg(it, sourceSensor.text)
//aapsLogger.debug("XXXXX: dbAdd $it")
}
}
result.updated.forEach {
broadcastToXDrip(it)
if (sp.getBoolean(R.string.key_dexcomg5_nsupload, false)) {
nsUpload.updateBg(it, sourceSensor.text)
//aapsLogger.debug("XXXXX: dpUpdate $it")
} }
} }
}, { }, {

View file

@ -118,7 +118,7 @@ class EversensePlugin @Inject constructor(
sourceSensor = GlucoseValue.SourceSensor.EVERSENSE sourceSensor = GlucoseValue.SourceSensor.EVERSENSE
) )
eversensePlugin.disposable += repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, emptyList(), null)).subscribe({ savedValues -> eversensePlugin.disposable += repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, emptyList(), null)).subscribe({ savedValues ->
savedValues.forEach { savedValues.inserted.forEach {
broadcastToXDrip(it) broadcastToXDrip(it)
if (sp.getBoolean(R.string.key_dexcomg5_nsupload, false)) if (sp.getBoolean(R.string.key_dexcomg5_nsupload, false))
nsUpload.uploadBg(it, GlucoseValue.SourceSensor.EVERSENSE.text) nsUpload.uploadBg(it, GlucoseValue.SourceSensor.EVERSENSE.text)

View file

@ -76,7 +76,7 @@ class GlimpPlugin @Inject constructor(
sourceSensor = GlucoseValue.SourceSensor.GLIMP sourceSensor = GlucoseValue.SourceSensor.GLIMP
) )
glimpPlugin.disposable += repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, emptyList(), null)).subscribe({ savedValues -> glimpPlugin.disposable += repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, emptyList(), null)).subscribe({ savedValues ->
savedValues.forEach { savedValues.inserted.forEach {
broadcastToXDrip(it) broadcastToXDrip(it)
if (sp.getBoolean(R.string.key_dexcomg5_nsupload, false)) if (sp.getBoolean(R.string.key_dexcomg5_nsupload, false))
nsUpload.uploadBg(it, GlucoseValue.SourceSensor.GLIMP.text) nsUpload.uploadBg(it, GlucoseValue.SourceSensor.GLIMP.text)

View file

@ -94,7 +94,7 @@ class MM640gPlugin @Inject constructor(
} }
} }
mM640gPlugin.disposable += repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, emptyList(), null)).subscribe({ savedValues -> mM640gPlugin.disposable += repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, emptyList(), null)).subscribe({ savedValues ->
savedValues.forEach { savedValues.all().forEach {
broadcastToXDrip(it) broadcastToXDrip(it)
if (sp.getBoolean(R.string.key_dexcomg5_nsupload, false)) if (sp.getBoolean(R.string.key_dexcomg5_nsupload, false))
nsUpload.uploadBg(it, GlucoseValue.SourceSensor.MM_600_SERIES.text) nsUpload.uploadBg(it, GlucoseValue.SourceSensor.MM_600_SERIES.text)

View file

@ -127,13 +127,19 @@ class NSClientSourcePlugin @Inject constructor(
for (i in 0 until jsonArray.length()) for (i in 0 until jsonArray.length())
glucoseValues += toGv(jsonArray.getJSONObject(i)) glucoseValues += toGv(jsonArray.getJSONObject(i))
} }
nsClientSourcePlugin.disposable += repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, emptyList(), null)).subscribe({ savedValues -> nsClientSourcePlugin.disposable += repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, emptyList(), null, !nsClientSourcePlugin.isEnabled())).subscribe({ result ->
savedValues.forEach { result.updated.forEach {
//aapsLogger.debug("XXXXX: Updated $it")
broadcastToXDrip(it)
nsClientSourcePlugin.detectSource(it)
}
result.inserted.forEach {
//aapsLogger.debug("XXXXX: Inserted $it")
broadcastToXDrip(it) broadcastToXDrip(it)
nsClientSourcePlugin.detectSource(it) nsClientSourcePlugin.detectSource(it)
} }
}, { }, {
aapsLogger.error(LTag.BGSOURCE, "Error while saving values from Eversense App", it) aapsLogger.error(LTag.BGSOURCE, "Error while saving values from NSClient App", it)
}) })
} catch (e: Exception) { } catch (e: Exception) {
aapsLogger.error("Unhandled exception", e) aapsLogger.error("Unhandled exception", e)

View file

@ -87,7 +87,7 @@ class PoctechPlugin @Inject constructor(
) )
} }
poctechPlugin.disposable += repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, emptyList(), null)).subscribe({ savedValues -> poctechPlugin.disposable += repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, emptyList(), null)).subscribe({ savedValues ->
savedValues.forEach { savedValues.inserted.forEach {
broadcastToXDrip(it) broadcastToXDrip(it)
if (sp.getBoolean(R.string.key_dexcomg5_nsupload, false)) if (sp.getBoolean(R.string.key_dexcomg5_nsupload, false))
nsUpload.uploadBg(it, GlucoseValue.SourceSensor.POCTECH_NATIVE.text) nsUpload.uploadBg(it, GlucoseValue.SourceSensor.POCTECH_NATIVE.text)

View file

@ -108,7 +108,7 @@ class RandomBgPlugin @Inject constructor(
sourceSensor = GlucoseValue.SourceSensor.RANDOM sourceSensor = GlucoseValue.SourceSensor.RANDOM
) )
disposable += repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, emptyList(), null)).subscribe({ savedValues -> disposable += repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, emptyList(), null)).subscribe({ savedValues ->
savedValues.forEach { savedValues.inserted.forEach {
xDripBroadcast(it) xDripBroadcast(it)
if (sp.getBoolean(R.string.key_dexcomg5_nsupload, false)) if (sp.getBoolean(R.string.key_dexcomg5_nsupload, false))
nsUpload.uploadBg(it, GlucoseValue.SourceSensor.RANDOM.text) nsUpload.uploadBg(it, GlucoseValue.SourceSensor.RANDOM.text)

View file

@ -76,7 +76,7 @@ class TomatoPlugin @Inject constructor(
sourceSensor = GlucoseValue.SourceSensor.LIBRE_1_TOMATO sourceSensor = GlucoseValue.SourceSensor.LIBRE_1_TOMATO
) )
tomatoPlugin.disposable += repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, emptyList(), null)).subscribe({ savedValues -> tomatoPlugin.disposable += repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, emptyList(), null)).subscribe({ savedValues ->
savedValues.forEach { savedValues.inserted.forEach {
broadcastToXDrip(it) broadcastToXDrip(it)
if (sp.getBoolean(R.string.key_dexcomg5_nsupload, false)) if (sp.getBoolean(R.string.key_dexcomg5_nsupload, false))
nsUpload.uploadBg(it, GlucoseValue.SourceSensor.LIBRE_1_TOMATO.text) nsUpload.uploadBg(it, GlucoseValue.SourceSensor.LIBRE_1_TOMATO.text)

View file

@ -86,7 +86,7 @@ class XdripPlugin @Inject constructor(
?: "") ?: "")
) )
xdripPlugin.disposable += repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, emptyList(), null)).subscribe({ savedValues -> xdripPlugin.disposable += repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, emptyList(), null)).subscribe({ savedValues ->
savedValues.forEach { savedValues.all().forEach {
xdripPlugin.detectSource(it) xdripPlugin.detectSource(it)
} }
}, { }, {

View file

@ -40,8 +40,7 @@ data class GlucoseValue(
trendArrow == other.trendArrow && trendArrow == other.trendArrow &&
noise == other.noise && noise == other.noise &&
sourceSensor == other.sourceSensor && sourceSensor == other.sourceSensor &&
isValid == other.isValid && isValid == other.isValid
interfaceIDs.nightscoutId == other.interfaceIDs.nightscoutId
fun isRecordDeleted(other: GlucoseValue): Boolean = fun isRecordDeleted(other: GlucoseValue): Boolean =
isValid && !other.isValid isValid && !other.isValid

View file

@ -9,11 +9,14 @@ import info.nightscout.androidaps.database.entities.TherapyEvent
class CgmSourceTransaction( class CgmSourceTransaction(
private val glucoseValues: List<TransactionGlucoseValue>, private val glucoseValues: List<TransactionGlucoseValue>,
private val calibrations: List<Calibration>, private val calibrations: List<Calibration>,
private val sensorInsertionTime: Long? private val sensorInsertionTime: Long?,
) : Transaction<List<GlucoseValue>>() { private val syncer: Boolean = false // caller is not native source ie. NS
// syncer is allowed create records
// update synchronization ID
) : Transaction<CgmSourceTransaction.TransactionResult>() {
override fun run(): List<GlucoseValue> { override fun run(): TransactionResult {
val insertedGlucoseValues = mutableListOf<GlucoseValue>() val result = TransactionResult()
glucoseValues.forEach { glucoseValues.forEach {
val current = database.glucoseValueDao.findByTimestampAndSensor(it.timestamp, it.sourceSensor) val current = database.glucoseValueDao.findByTimestampAndSensor(it.timestamp, it.sourceSensor)
val glucoseValue = GlucoseValue( val glucoseValue = GlucoseValue(
@ -25,15 +28,26 @@ class CgmSourceTransaction(
sourceSensor = it.sourceSensor sourceSensor = it.sourceSensor
) )
glucoseValue.interfaceIDs.nightscoutId = it.nightscoutId glucoseValue.interfaceIDs.nightscoutId = it.nightscoutId
// if nsId is not provided in new record, copy from current if exists
if (glucoseValue.interfaceIDs.nightscoutId == null)
current?.let { existing -> glucoseValue.interfaceIDs.nightscoutId = existing.interfaceIDs.nightscoutId }
when { when {
// new record, create new
current == null -> { current == null -> {
database.glucoseValueDao.insertNewEntry(glucoseValue) database.glucoseValueDao.insertNewEntry(glucoseValue)
insertedGlucoseValues.add(glucoseValue) result.inserted.add(glucoseValue)
} }
// different record, update
!current.contentEqualsTo(glucoseValue) -> { !current.contentEqualsTo(glucoseValue) && !syncer -> {
glucoseValue.id = current.id glucoseValue.id = current.id
database.glucoseValueDao.updateExistingEntry(glucoseValue) database.glucoseValueDao.updateExistingEntry(glucoseValue)
result.updated.add(glucoseValue)
}
// update NS id if didn't exist and now provided
current.interfaceIDs.nightscoutId == null && it.nightscoutId != null && syncer -> {
glucoseValue.id = current.id
database.glucoseValueDao.updateExistingEntry(glucoseValue)
result.updated.add(glucoseValue)
} }
} }
} }
@ -54,7 +68,7 @@ class CgmSourceTransaction(
)) ))
} }
} }
return insertedGlucoseValues return result
} }
data class TransactionGlucoseValue( data class TransactionGlucoseValue(
@ -71,4 +85,16 @@ class CgmSourceTransaction(
val timestamp: Long, val timestamp: Long,
val value: Double val value: Double
) )
class TransactionResult {
val inserted = mutableListOf<GlucoseValue>()
val updated = mutableListOf<GlucoseValue>()
fun all(): MutableList<GlucoseValue> =
mutableListOf<GlucoseValue>().also { result ->
result.addAll(inserted)
result.addAll(updated)
}
}
} }