NSCv3: more work on sync

This commit is contained in:
Milos Kozak 2022-12-14 21:49:08 +01:00
parent bd84d3a2f7
commit 09e61772ab
16 changed files with 181 additions and 112 deletions

View file

@ -16,7 +16,31 @@ interface NsClient : Sync {
fun textLog(): Spanned fun textLog(): Spanned
fun clearLog() fun clearLog()
enum class Collection { ENTRIES, TREATMENTS}
/**
* NSC v3 does first load of all data
* next loads are using srvModified property for sync
* not used for NSCv1
*
* @return true if inside first load of NSCv3, true for NSCv1
*/
fun isFirstLoad(collection: Collection): Boolean = true
/**
* Update newest loaded timestamp for entries collection (first load or NSCv1)
* Update newest srvModified (sync loads)
*
* @param latestReceived timestamp
*
*/
fun updateLatestBgReceivedIfNewer(latestReceived: Long) fun updateLatestBgReceivedIfNewer(latestReceived: Long)
/**
* Update newest loaded timestamp for treatments collection (first load or NSCv1)
* Update newest srvModified (sync loads)
*
* @param latestReceived timestamp
*
*/
fun updateLatestTreatmentReceivedIfNewer(latestReceived: Long) fun updateLatestTreatmentReceivedIfNewer(latestReceived: Long)
fun handleClearAlarm(originalAlarm: NSAlarm, silenceTimeInMilliseconds: Long) fun handleClearAlarm(originalAlarm: NSAlarm, silenceTimeInMilliseconds: Long)

View file

@ -4,7 +4,7 @@ import android.content.Context
import info.nightscout.sdk.exceptions.DateHeaderOutOfToleranceException import info.nightscout.sdk.exceptions.DateHeaderOutOfToleranceException
import info.nightscout.sdk.exceptions.InvalidAccessTokenException import info.nightscout.sdk.exceptions.InvalidAccessTokenException
import info.nightscout.sdk.exceptions.InvalidFormatNightscoutException import info.nightscout.sdk.exceptions.InvalidFormatNightscoutException
import info.nightscout.sdk.exceptions.TodoNightscoutException import info.nightscout.sdk.exceptions.UnsuccessfullNightscoutException
import info.nightscout.sdk.exceptions.UnknownResponseNightscoutException import info.nightscout.sdk.exceptions.UnknownResponseNightscoutException
import info.nightscout.sdk.interfaces.NSAndroidClient import info.nightscout.sdk.interfaces.NSAndroidClient
import info.nightscout.sdk.localmodel.Status import info.nightscout.sdk.localmodel.Status
@ -101,9 +101,9 @@ class NSAndroidClientImpl(
val response = api.lastModified() val response = api.lastModified()
if (response.isSuccessful) { if (response.isSuccessful) {
return@callWrapper response.body()?.result ?: throw TodoNightscoutException() return@callWrapper response.body()?.result ?: throw UnsuccessfullNightscoutException()
} else { } else {
throw TodoNightscoutException() // TODO: react to response errors (offline, ...) throw UnsuccessfullNightscoutException()
} }
} }
@ -114,17 +114,19 @@ class NSAndroidClientImpl(
if (response.isSuccessful) { if (response.isSuccessful) {
return@callWrapper response.body()?.result?.map(RemoteEntry::toSgv).toNotNull() return@callWrapper response.body()?.result?.map(RemoteEntry::toSgv).toNotNull()
} else { } else {
throw TodoNightscoutException() // TODO: react to response errors (offline, ...) throw UnsuccessfullNightscoutException()
} }
} }
override suspend fun getSgvsModifiedSince(from: Long): List<NSSgvV3> = callWrapper(dispatcher) { override suspend fun getSgvsModifiedSince(from: Long, limit: Long): NSAndroidClient.ReadResponse<List<NSSgvV3>> = callWrapper(dispatcher) {
val response = api.getSgvsModifiedSince(from) val response = api.getSgvsModifiedSince(from, limit)
val eTagString = response.headers()["ETag"]
val eTag = eTagString?.substring(3, eTagString.length - 1)?.toLong() ?: throw UnsuccessfullNightscoutException()
if (response.isSuccessful) { if (response.isSuccessful) {
return@callWrapper response.body()?.result?.map(RemoteEntry::toSgv).toNotNull() return@callWrapper NSAndroidClient.ReadResponse(eTag, response.body()?.result?.map(RemoteEntry::toSgv).toNotNull())
} else { } else {
throw TodoNightscoutException() // TODO: react to response errors (offline, ...) throw UnsuccessfullNightscoutException()
} }
} }
@ -134,7 +136,17 @@ class NSAndroidClientImpl(
if (response.isSuccessful) { if (response.isSuccessful) {
return@callWrapper response.body()?.result?.map(RemoteEntry::toSgv).toNotNull() return@callWrapper response.body()?.result?.map(RemoteEntry::toSgv).toNotNull()
} else { } else {
throw TodoNightscoutException() // TODO: react to response errors (offline, ...) throw UnsuccessfullNightscoutException()
}
}
override suspend fun getTreatmentsNewerThan(from: Long, limit: Long): List<NSTreatment> = callWrapper(dispatcher) {
val response = api.getTreatmentsNewerThan(from, limit)
if (response.isSuccessful) {
return@callWrapper response.body()?.result?.map(RemoteTreatment::toTreatment).toNotNull()
} else {
throw UnsuccessfullNightscoutException()
} }
} }
@ -142,11 +154,11 @@ class NSAndroidClientImpl(
val response = api.getTreatmentsModifiedSince(from, limit) val response = api.getTreatmentsModifiedSince(from, limit)
val eTagString = response.headers()["ETag"] val eTagString = response.headers()["ETag"]
val eTag = eTagString?.substring(3, eTagString.length - 1)?.toLong() ?: throw TodoNightscoutException() val eTag = eTagString?.substring(3, eTagString.length - 1)?.toLong() ?: throw UnsuccessfullNightscoutException()
if (response.isSuccessful) { if (response.isSuccessful) {
return@callWrapper NSAndroidClient.ReadResponse(eTag, response.body()?.result?.map(RemoteTreatment::toTreatment).toNotNull()) return@callWrapper NSAndroidClient.ReadResponse(eTag, response.body()?.result?.map(RemoteTreatment::toTreatment).toNotNull())
} else { } else {
throw TodoNightscoutException() // TODO: react to response errors (offline, ...) throw UnsuccessfullNightscoutException()
} }
} }
@ -156,7 +168,7 @@ class NSAndroidClientImpl(
if (response.isSuccessful) { if (response.isSuccessful) {
return@callWrapper response.body()?.result.toNotNull() return@callWrapper response.body()?.result.toNotNull()
} else { } else {
throw TodoNightscoutException() // TODO: react to response errors (offline, ...) throw UnsuccessfullNightscoutException()
} }
} }
@ -174,7 +186,7 @@ class NSAndroidClientImpl(
lastModified = response.body()?.result?.lastModified lastModified = response.body()?.result?.lastModified
) )
} else { } else {
throw TodoNightscoutException() // TODO: react to response errors (offline, ...) throw UnsuccessfullNightscoutException()
} }
} }
@ -191,7 +203,7 @@ class NSAndroidClientImpl(
lastModified = response.body()?.result?.lastModified lastModified = response.body()?.result?.lastModified
) )
} else { } else {
throw TodoNightscoutException() // TODO: react to response errors (offline, ...) throw UnsuccessfullNightscoutException()
} }
} }

View file

@ -9,13 +9,14 @@ import info.nightscout.sdk.remotemodel.LastModified
import info.nightscout.sdk.remotemodel.RemoteDeviceStatus import info.nightscout.sdk.remotemodel.RemoteDeviceStatus
import io.reactivex.rxjava3.core.Single import io.reactivex.rxjava3.core.Single
import kotlinx.coroutines.rx3.rxSingle import kotlinx.coroutines.rx3.rxSingle
import retrofit2.http.Query
class NSAndroidRxClientImpl(private val client: NSAndroidClient) : NSAndroidRxClient { class NSAndroidRxClientImpl(private val client: NSAndroidClient) : NSAndroidRxClient {
override fun getVersion(): Single<String> = rxSingle { client.getVersion() } override fun getVersion(): Single<String> = rxSingle { client.getVersion() }
override fun getStatus(): Single<Status> = rxSingle { client.getStatus() } override fun getStatus(): Single<Status> = rxSingle { client.getStatus() }
override fun getLastModified(): Single<LastModified> = rxSingle { client.getLastModified() } override fun getLastModified(): Single<LastModified> = rxSingle { client.getLastModified() }
override fun getSgvsModifiedSince(from: Long): Single<List<NSSgvV3>> = rxSingle { client.getSgvsModifiedSince(from) } override fun getSgvsModifiedSince(from: Long, limit: Long): Single<NSAndroidClient.ReadResponse<List<NSSgvV3>>> = rxSingle { client.getSgvsModifiedSince(from, limit) }
override fun getTreatmentsModifiedSince(from: Long, limit: Long): Single<NSAndroidClient.ReadResponse<List<NSTreatment>>> = override fun getTreatmentsModifiedSince(from: Long, limit: Long): Single<NSAndroidClient.ReadResponse<List<NSTreatment>>> =
rxSingle { client.getTreatmentsModifiedSince(from, limit) } rxSingle { client.getTreatmentsModifiedSince(from, limit) }
override fun getDeviceStatusModifiedSince(from: Long): Single<List<RemoteDeviceStatus>> = override fun getDeviceStatusModifiedSince(from: Long): Single<List<RemoteDeviceStatus>> =

View file

@ -1,3 +0,0 @@
package info.nightscout.sdk.exceptions
class TodoNightscoutException : NightscoutException()

View file

@ -0,0 +1,3 @@
package info.nightscout.sdk.exceptions
class UnsuccessfullNightscoutException : NightscoutException()

View file

@ -21,8 +21,9 @@ interface NSAndroidClient {
suspend fun getLastModified(): LastModified suspend fun getLastModified(): LastModified
suspend fun getSgvs(): List<NSSgvV3> suspend fun getSgvs(): List<NSSgvV3>
suspend fun getSgvsModifiedSince(from: Long): List<NSSgvV3> suspend fun getSgvsModifiedSince(from: Long, limit: Long): ReadResponse<List<NSSgvV3>>
suspend fun getSgvsNewerThan(from: Long, limit: Long): List<NSSgvV3> suspend fun getSgvsNewerThan(from: Long, limit: Long): List<NSSgvV3>
suspend fun getTreatmentsNewerThan(from: Long, limit: Long): List<NSTreatment>
suspend fun getTreatmentsModifiedSince(from: Long, limit: Long): ReadResponse<List<NSTreatment>> suspend fun getTreatmentsModifiedSince(from: Long, limit: Long): ReadResponse<List<NSTreatment>>
suspend fun getDeviceStatusModifiedSince(from: Long): List<RemoteDeviceStatus> suspend fun getDeviceStatusModifiedSince(from: Long): List<RemoteDeviceStatus>
suspend fun createTreatment(nsTreatment: NSTreatment): CreateUpdateResponse suspend fun createTreatment(nsTreatment: NSTreatment): CreateUpdateResponse

View file

@ -12,7 +12,7 @@ interface NSAndroidRxClient {
fun getVersion(): Single<String> fun getVersion(): Single<String>
fun getStatus(): Single<Status> fun getStatus(): Single<Status>
fun getLastModified(): Single<LastModified> fun getLastModified(): Single<LastModified>
fun getSgvsModifiedSince(from: Long): Single<List<NSSgvV3>> fun getSgvsModifiedSince(from: Long, limit: Long): Single<NSAndroidClient.ReadResponse<List<NSSgvV3>>>
fun getTreatmentsModifiedSince(from: Long, limit: Long): Single<NSAndroidClient.ReadResponse<List<NSTreatment>>> fun getTreatmentsModifiedSince(from: Long, limit: Long): Single<NSAndroidClient.ReadResponse<List<NSTreatment>>>
fun getDeviceStatusModifiedSince(from: Long): Single<List<RemoteDeviceStatus>> fun getDeviceStatusModifiedSince(from: Long): Single<List<RemoteDeviceStatus>>
} }

View file

@ -45,7 +45,10 @@ internal interface NightscoutRemoteService {
suspend fun getSgvsNewerThan(@Query(value = "date\$gt", encoded = true) date: Long, @Query("limit") limit: Long): Response<NSResponse<List<RemoteEntry>>> suspend fun getSgvsNewerThan(@Query(value = "date\$gt", encoded = true) date: Long, @Query("limit") limit: Long): Response<NSResponse<List<RemoteEntry>>>
@GET("v3/entries/history/{from}") @GET("v3/entries/history/{from}")
suspend fun getSgvsModifiedSince(@Path("from") from: Long): Response<NSResponse<List<RemoteEntry>>> suspend fun getSgvsModifiedSince(@Path("from") from: Long, @Query("limit") limit: Long): Response<NSResponse<List<RemoteEntry>>>
@GET("v3/treatments")
suspend fun getTreatmentsNewerThan(@Query(value = "date\$gt", encoded = true) date: Long, @Query("limit") limit: Long): Response<NSResponse<List<RemoteTreatment>>>
@GET("v3/treatments/history/{from}") @GET("v3/treatments/history/{from}")
suspend fun getTreatmentsModifiedSince(@Path("from") from: Long, @Query("limit") limit: Long): Response<NSResponse<List<RemoteTreatment>>> suspend fun getTreatmentsModifiedSince(@Path("from") from: Long, @Query("limit") limit: Long): Response<NSResponse<List<RemoteTreatment>>>

View file

@ -15,9 +15,9 @@ data class LastModified(
@Serializable @Serializable
data class Collections( data class Collections(
@SerializedName("devicestatus") var devicestatus: Long, // devicestatus collection @SerializedName("devicestatus") var devicestatus: Long = 0, // devicestatus collection
@SerializedName("entries") var entries: Long, // entries collection @SerializedName("entries") var entries: Long = 0, // entries collection
@SerializedName("profile") var profile: Long, // profile collection @SerializedName("profile") var profile: Long = 0, // profile collection
@SerializedName("treatments") var treatments: Long // treatments collection @SerializedName("treatments") var treatments: Long = 0 // treatments collection
) )
} }

View file

@ -54,6 +54,7 @@ import info.nightscout.interfaces.nsclient.StoreDataForDb
import info.nightscout.interfaces.pump.VirtualPump import info.nightscout.interfaces.pump.VirtualPump
import info.nightscout.interfaces.source.NSClientSource import info.nightscout.interfaces.source.NSClientSource
import info.nightscout.interfaces.ui.UiInteraction import info.nightscout.interfaces.ui.UiInteraction
import info.nightscout.plugins.sync.nsclientV3.NSClientV3Plugin
import info.nightscout.rx.bus.RxBus import info.nightscout.rx.bus.RxBus
import info.nightscout.rx.events.EventNSClientNewLog import info.nightscout.rx.events.EventNSClientNewLog
import info.nightscout.rx.logging.AAPSLogger import info.nightscout.rx.logging.AAPSLogger

View file

@ -39,6 +39,7 @@ import info.nightscout.plugins.sync.nsclientV3.workers.LoadLastModificationWorke
import info.nightscout.plugins.sync.nsclientV3.workers.LoadStatusWorker import info.nightscout.plugins.sync.nsclientV3.workers.LoadStatusWorker
import info.nightscout.rx.AapsSchedulers import info.nightscout.rx.AapsSchedulers
import info.nightscout.rx.bus.RxBus import info.nightscout.rx.bus.RxBus
import info.nightscout.rx.events.EventAppExit
import info.nightscout.rx.events.EventChargingState import info.nightscout.rx.events.EventChargingState
import info.nightscout.rx.events.EventNSClientNewLog import info.nightscout.rx.events.EventNSClientNewLog
import info.nightscout.rx.events.EventNetworkChange import info.nightscout.rx.events.EventNetworkChange
@ -60,7 +61,6 @@ import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
import kotlin.math.max
@Singleton @Singleton
class NSClientV3Plugin @Inject constructor( class NSClientV3Plugin @Inject constructor(
@ -113,22 +113,14 @@ class NSClientV3Plugin @Inject constructor(
} }
internal lateinit var nsAndroidClient: NSAndroidClient internal lateinit var nsAndroidClient: NSAndroidClient
// private lateinit var nsAndroidRxClient: NSAndroidRxClient
val isAllowed get() = nsClientReceiverDelegate.allowed private val isAllowed get() = nsClientReceiverDelegate.allowed
val blockingReason get() = nsClientReceiverDelegate.blockingReason private val blockingReason get() = nsClientReceiverDelegate.blockingReason
private val maxAge = T.days(77).msecs() val maxAge = T.days(77).msecs()
internal var newestDataOnServer: LastModified? = null // timestamp of last modification for every collection internal var newestDataOnServer: LastModified? = null // timestamp of last modification for every collection provided by server
internal var lastLoadedSrvModified = internal var lastLoadedSrvModified = LastModified(LastModified.Collections()) // max srvLastModified timestamp of last fetched data for every collection
LastModified( internal var firstLoadContinueTimestamp = LastModified(LastModified.Collections()) // timestamp of last fetched data for every collection during initial load
LastModified.Collections(
dateUtil.now() - maxAge,
dateUtil.now() - maxAge,
dateUtil.now() - maxAge,
dateUtil.now() - maxAge
)
) // timestamp of last fetched data for every collection
override fun onStart() { override fun onStart() {
// context.bindService(Intent(context, NSClientService::class.java), mConnection, Context.BIND_AUTO_CREATE) // context.bindService(Intent(context, NSClientService::class.java), mConnection, Context.BIND_AUTO_CREATE)
@ -137,16 +129,9 @@ class NSClientV3Plugin @Inject constructor(
lastLoadedSrvModified = Json.decodeFromString( lastLoadedSrvModified = Json.decodeFromString(
sp.getString( sp.getString(
R.string.key_ns_client_v3_last_modified, R.string.key_ns_client_v3_last_modified,
Json.encodeToString( Json.encodeToString(LastModified.serializer(), LastModified(LastModified.Collections()))
LastModified.serializer(),
LastModified(LastModified.Collections(dateUtil.now() - maxAge, dateUtil.now() - maxAge, dateUtil.now() - maxAge, dateUtil.now() - maxAge))
) )
) )
)
lastLoadedSrvModified.collections.entries = max(dateUtil.now() - maxAge, lastLoadedSrvModified.collections.entries)
lastLoadedSrvModified.collections.treatments = max(dateUtil.now() - maxAge, lastLoadedSrvModified.collections.treatments)
lastLoadedSrvModified.collections.profile = max(dateUtil.now() - maxAge, lastLoadedSrvModified.collections.profile)
lastLoadedSrvModified.collections.devicestatus = max(dateUtil.now() - maxAge, lastLoadedSrvModified.collections.devicestatus)
setClient() setClient()
@ -166,10 +151,10 @@ class NSClientV3Plugin @Inject constructor(
if (ev.isChanged(rh.gs(R.string.key_ns_client_token)) || ev.isChanged(rh.gs(info.nightscout.core.utils.R.string.key_nsclientinternal_url))) if (ev.isChanged(rh.gs(R.string.key_ns_client_token)) || ev.isChanged(rh.gs(info.nightscout.core.utils.R.string.key_nsclientinternal_url)))
setClient() setClient()
}, fabricPrivacy::logException) }, fabricPrivacy::logException)
// disposable += rxBus disposable += rxBus
// .toObservable(EventAppExit::class.java) .toObservable(EventAppExit::class.java)
// .observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
// .subscribe({ if (nsClientService != null) context.unbindService(mConnection) }, fabricPrivacy::logException) .subscribe({ WorkManager.getInstance(context).cancelUniqueWork(JOB_NAME) }, fabricPrivacy::logException)
disposable += rxBus disposable += rxBus
.toObservable(EventNSClientNewLog::class.java) .toObservable(EventNSClientNewLog::class.java)
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
@ -187,8 +172,8 @@ class NSClientV3Plugin @Inject constructor(
.subscribe({ event -> resend(event.reason) }, fabricPrivacy::logException) .subscribe({ event -> resend(event.reason) }, fabricPrivacy::logException)
runLoop = Runnable { runLoop = Runnable {
executeLoop()
handler.postDelayed(runLoop, REFRESH_INTERVAL) handler.postDelayed(runLoop, REFRESH_INTERVAL)
executeLoop()
} }
handler.postDelayed(runLoop, REFRESH_INTERVAL) handler.postDelayed(runLoop, REFRESH_INTERVAL)
executeLoop() executeLoop()
@ -214,7 +199,6 @@ class NSClientV3Plugin @Inject constructor(
override val hasWritePermission: Boolean get() = nsAndroidClient.lastStatus?.apiPermissions?.isFull() ?: false override val hasWritePermission: Boolean get() = nsAndroidClient.lastStatus?.apiPermissions?.isFull() ?: false
override val connected: Boolean get() = nsAndroidClient.lastStatus != null override val connected: Boolean get() = nsAndroidClient.lastStatus != null
override fun clearLog() { override fun clearLog() {
handler.post { handler.post {
synchronized(listLog) { listLog.clear() } synchronized(listLog) { listLog.clear() }
@ -283,27 +267,23 @@ class NSClientV3Plugin @Inject constructor(
// }) // })
} }
override fun updateLatestBgReceivedIfNewer(latestReceived: Long) { override fun isFirstLoad(collection: NsClient.Collection) =
if (latestReceived > lastLoadedSrvModified.collections.entries) { when (collection) {
lastLoadedSrvModified.collections.entries = latestReceived NsClient.Collection.ENTRIES -> lastLoadedSrvModified.collections.entries == 0L
storeLastFetched() NsClient.Collection.TREATMENTS -> lastLoadedSrvModified.collections.treatments == 0L
} }
override fun updateLatestBgReceivedIfNewer(latestReceived: Long) {
if (isFirstLoad(NsClient.Collection.ENTRIES)) firstLoadContinueTimestamp.collections.entries = latestReceived
} }
override fun updateLatestTreatmentReceivedIfNewer(latestReceived: Long) { override fun updateLatestTreatmentReceivedIfNewer(latestReceived: Long) {
lastLoadedSrvModified.collections.treatments = latestReceived if (isFirstLoad(NsClient.Collection.TREATMENTS)) firstLoadContinueTimestamp.collections.treatments = latestReceived
storeLastFetched()
} }
override fun resetToFullSync() { override fun resetToFullSync() {
lastLoadedSrvModified = LastModified( firstLoadContinueTimestamp = LastModified(LastModified.Collections())
LastModified.Collections( lastLoadedSrvModified = LastModified(LastModified.Collections())
dateUtil.now() - maxAge,
dateUtil.now() - maxAge,
dateUtil.now() - maxAge,
dateUtil.now() - maxAge
)
)
storeLastFetched() storeLastFetched()
} }
@ -316,9 +296,10 @@ class NSClientV3Plugin @Inject constructor(
} }
enum class Operation { CREATE, UPDATE } enum class Operation { CREATE, UPDATE }
private val gson: Gson = GsonBuilder().create() private val gson: Gson = GsonBuilder().create()
private fun dbOperation(collection: String, dataPair: DataSyncSelector.DataPair, progress: String, operation: Operation) { private fun dbOperation(collection: String, dataPair: DataSyncSelector.DataPair, progress: String, operation: Operation) {
val call = when(operation) { val call = when (operation) {
Operation.CREATE -> nsAndroidClient::createTreatment Operation.CREATE -> nsAndroidClient::createTreatment
Operation.UPDATE -> nsAndroidClient::updateTreatment Operation.UPDATE -> nsAndroidClient::updateTreatment
} }
@ -343,11 +324,11 @@ class NSClientV3Plugin @Inject constructor(
val id = if (dataPair.value is TraceableDBEntry) (dataPair.value as TraceableDBEntry).interfaceIDs.nightscoutId else "" val id = if (dataPair.value is TraceableDBEntry) (dataPair.value as TraceableDBEntry).interfaceIDs.nightscoutId else ""
rxBus.send( rxBus.send(
EventNSClientNewLog( EventNSClientNewLog(
when(operation) { when (operation) {
Operation.CREATE -> "ADD $collection" Operation.CREATE -> "ADD $collection"
Operation.UPDATE -> "UPDATE $collection" Operation.UPDATE -> "UPDATE $collection"
}, },
when(operation) { when (operation) {
Operation.CREATE -> "Sent ${dataPair.javaClass.simpleName} ${gson.toJson(data)} $progress" Operation.CREATE -> "Sent ${dataPair.javaClass.simpleName} ${gson.toJson(data)} $progress"
Operation.UPDATE -> "Sent ${dataPair.javaClass.simpleName} $id ${gson.toJson(data)} $progress" Operation.UPDATE -> "Sent ${dataPair.javaClass.simpleName} $id ${gson.toJson(data)} $progress"
} }
@ -382,7 +363,8 @@ class NSClientV3Plugin @Inject constructor(
} }
} }
} }
private fun storeLastFetched() {
fun storeLastFetched() {
sp.putString(R.string.key_ns_client_v3_last_modified, Json.encodeToString(LastModified.serializer(), lastLoadedSrvModified)) sp.putString(R.string.key_ns_client_v3_last_modified, Json.encodeToString(LastModified.serializer(), lastLoadedSrvModified))
} }
@ -391,20 +373,19 @@ class NSClientV3Plugin @Inject constructor(
} }
fun scheduleNewExecution() { fun scheduleNewExecution() {
val toTime = lastLoadedSrvModified.collections.entries + T.mins(6).plus(T.secs(0)).msecs() var toTime = lastLoadedSrvModified.collections.entries + T.mins(6).plus(T.secs(0)).msecs()
if (toTime > dateUtil.now()) { if (toTime < dateUtil.now()) toTime = dateUtil.now() + T.mins(1).plus(T.secs(0)).msecs()
handler.postDelayed({ executeLoop() }, toTime - dateUtil.now()) handler.postDelayed({ executeLoop() }, toTime - dateUtil.now())
rxBus.send(EventNSClientNewLog("NEXT", dateUtil.dateAndTimeAndSecondsString(toTime))) rxBus.send(EventNSClientNewLog("NEXT", dateUtil.dateAndTimeAndSecondsString(toTime)))
} }
}
private fun executeLoop() { private fun executeLoop() {
if (sp.getBoolean(R.string.key_ns_client_paused, false)) { if (sp.getBoolean(R.string.key_ns_client_paused, false)) {
rxBus.send(EventNSClientNewLog("NSCLIENT", "paused")) rxBus.send(EventNSClientNewLog("RUN", "paused"))
return return
} }
if (!isAllowed) { if (!isAllowed) {
rxBus.send(EventNSClientNewLog("NSCLIENT", blockingReason)) rxBus.send(EventNSClientNewLog("RUN", blockingReason))
return return
} }
if (workIsRunning(arrayOf(JOB_NAME))) if (workIsRunning(arrayOf(JOB_NAME)))

View file

@ -8,15 +8,19 @@ import androidx.work.WorkerParameters
import androidx.work.workDataOf import androidx.work.workDataOf
import info.nightscout.core.utils.receivers.DataWorkerStorage import info.nightscout.core.utils.receivers.DataWorkerStorage
import info.nightscout.core.utils.worker.LoggingWorker import info.nightscout.core.utils.worker.LoggingWorker
import info.nightscout.interfaces.sync.NsClient
import info.nightscout.interfaces.workflow.WorkerClasses import info.nightscout.interfaces.workflow.WorkerClasses
import info.nightscout.plugins.sync.nsShared.StoreDataForDbImpl import info.nightscout.plugins.sync.nsShared.StoreDataForDbImpl
import info.nightscout.plugins.sync.nsclientV3.NSClientV3Plugin import info.nightscout.plugins.sync.nsclientV3.NSClientV3Plugin
import info.nightscout.rx.bus.RxBus import info.nightscout.rx.bus.RxBus
import info.nightscout.rx.events.EventNSClientNewLog import info.nightscout.rx.events.EventNSClientNewLog
import info.nightscout.sdk.interfaces.NSAndroidClient
import info.nightscout.sdk.localmodel.entry.NSSgvV3
import info.nightscout.shared.sharedPreferences.SP import info.nightscout.shared.sharedPreferences.SP
import info.nightscout.shared.utils.DateUtil import info.nightscout.shared.utils.DateUtil
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import javax.inject.Inject import javax.inject.Inject
import kotlin.math.max
class LoadBgWorker( class LoadBgWorker(
context: Context, params: WorkerParameters context: Context, params: WorkerParameters
@ -37,20 +41,26 @@ class LoadBgWorker(
override fun doWorkAndLog(): Result { override fun doWorkAndLog(): Result {
var ret = Result.success() var ret = Result.success()
val isFirstLoad = nsClientV3Plugin.isFirstLoad(NsClient.Collection.ENTRIES)
val lastLoaded =
if (isFirstLoad) max(nsClientV3Plugin.firstLoadContinueTimestamp.collections.entries, dateUtil.now() - nsClientV3Plugin.maxAge)
else max(nsClientV3Plugin.lastLoadedSrvModified.collections.entries, dateUtil.now() - nsClientV3Plugin.maxAge)
runBlocking { runBlocking {
if ((nsClientV3Plugin.newestDataOnServer?.collections?.entries ?: Long.MAX_VALUE) > nsClientV3Plugin.lastLoadedSrvModified.collections.entries) if ((nsClientV3Plugin.newestDataOnServer?.collections?.entries ?: Long.MAX_VALUE) > lastLoaded)
try { try {
//val sgvs = nsClientV3Plugin.nsAndroidClient.getSgvsModifiedSince(nsClientV3Plugin.lastFetched.collections.entries) val sgvs: List<NSSgvV3>
val sgvs = nsClientV3Plugin.nsAndroidClient.getSgvsNewerThan(nsClientV3Plugin.lastLoadedSrvModified.collections.entries, 500) val response: NSAndroidClient.ReadResponse<List<NSSgvV3>>?
if (isFirstLoad) sgvs = nsClientV3Plugin.nsAndroidClient.getSgvsNewerThan(lastLoaded, 500)
else {
response = nsClientV3Plugin.nsAndroidClient.getSgvsModifiedSince(lastLoaded, 500)
sgvs = response.values
nsClientV3Plugin.lastLoadedSrvModified.collections.entries = response.lastServerModified
nsClientV3Plugin.storeLastFetched()
}
aapsLogger.debug("SGVS: $sgvs") aapsLogger.debug("SGVS: $sgvs")
if (sgvs.isNotEmpty()) { if (sgvs.isNotEmpty()) {
rxBus.send( val action = if (isFirstLoad) "RCV-FIRST" else "RCV"
EventNSClientNewLog( rxBus.send(EventNSClientNewLog(action, "${sgvs.size} SVGs from ${dateUtil.dateAndTimeAndSecondsString(lastLoaded)}"))
"RCV",
"${sgvs.size} SVGs from ${dateUtil.dateAndTimeAndSecondsString(nsClientV3Plugin.lastLoadedSrvModified.collections.entries)}"
)
)
// Objective0 // Objective0
sp.putBoolean(info.nightscout.core.utils.R.string.key_objectives_bg_is_available_in_ns, true) sp.putBoolean(info.nightscout.core.utils.R.string.key_objectives_bg_is_available_in_ns, true)
// Schedule processing of fetched data and continue of loading // Schedule processing of fetched data and continue of loading
@ -60,7 +70,13 @@ class LoadBgWorker(
OneTimeWorkRequest.Builder(workerClasses.nsClientSourceWorker).setInputData(dataWorkerStorage.storeInputData(sgvs)).build() OneTimeWorkRequest.Builder(workerClasses.nsClientSourceWorker).setInputData(dataWorkerStorage.storeInputData(sgvs)).build()
).then(OneTimeWorkRequest.Builder(LoadBgWorker::class.java).build()).enqueue() ).then(OneTimeWorkRequest.Builder(LoadBgWorker::class.java).build()).enqueue()
} else { } else {
rxBus.send(EventNSClientNewLog("END", "No SGVs from ${dateUtil.dateAndTimeAndSecondsString(nsClientV3Plugin.lastLoadedSrvModified.collections.entries)}")) // End first load
if (isFirstLoad) {
nsClientV3Plugin.lastLoadedSrvModified.collections.entries = lastLoaded
nsClientV3Plugin.storeLastFetched()
}
rxBus.send(EventNSClientNewLog("RCV END", "No SGVs from ${dateUtil
.dateAndTimeAndSecondsString(lastLoaded)}"))
WorkManager.getInstance(context) WorkManager.getInstance(context)
.beginUniqueWork( .beginUniqueWork(
NSClientV3Plugin.JOB_NAME, NSClientV3Plugin.JOB_NAME,
@ -75,7 +91,13 @@ class LoadBgWorker(
ret = Result.failure(workDataOf("Error" to error.toString())) ret = Result.failure(workDataOf("Error" to error.toString()))
} }
else { else {
rxBus.send(EventNSClientNewLog("END", "No new SGVs from ${dateUtil.dateAndTimeAndSecondsString(nsClientV3Plugin.lastLoadedSrvModified.collections.entries)}")) // End first load
if (isFirstLoad) {
nsClientV3Plugin.lastLoadedSrvModified.collections.entries = lastLoaded
nsClientV3Plugin.storeLastFetched()
}
rxBus.send(EventNSClientNewLog("RCV END", "No new SGVs from ${dateUtil
.dateAndTimeAndSecondsString(lastLoaded)}"))
nsClientV3Plugin.scheduleNewExecution() // Idea is to run after 5 min after last BG nsClientV3Plugin.scheduleNewExecution() // Idea is to run after 5 min after last BG
WorkManager.getInstance(context) WorkManager.getInstance(context)
.beginUniqueWork( .beginUniqueWork(

View file

@ -40,8 +40,9 @@ class LoadDeviceStatusWorker(
if (deviceStatuses.isNotEmpty()) { if (deviceStatuses.isNotEmpty()) {
rxBus.send(EventNSClientNewLog("RCV", "${deviceStatuses.size} DSs from ${dateUtil.dateAndTimeAndSecondsString(from)}")) rxBus.send(EventNSClientNewLog("RCV", "${deviceStatuses.size} DSs from ${dateUtil.dateAndTimeAndSecondsString(from)}"))
nsDeviceStatusHandler.handleNewData(deviceStatuses.toTypedArray()) nsDeviceStatusHandler.handleNewData(deviceStatuses.toTypedArray())
rxBus.send(EventNSClientNewLog("DONE DS", ""))
} else { } else {
rxBus.send(EventNSClientNewLog("END", "No DSs from ${dateUtil.dateAndTimeAndSecondsString(from)}")) rxBus.send(EventNSClientNewLog("RCV END", "No DSs from ${dateUtil.dateAndTimeAndSecondsString(from)}"))
} }
WorkManager.getInstance(context) WorkManager.getInstance(context)
.enqueueUniqueWork( .enqueueUniqueWork(

View file

@ -9,12 +9,16 @@ import androidx.work.workDataOf
import info.nightscout.core.utils.receivers.DataWorkerStorage import info.nightscout.core.utils.receivers.DataWorkerStorage
import info.nightscout.core.utils.worker.LoggingWorker import info.nightscout.core.utils.worker.LoggingWorker
import info.nightscout.interfaces.nsclient.StoreDataForDb import info.nightscout.interfaces.nsclient.StoreDataForDb
import info.nightscout.interfaces.sync.NsClient
import info.nightscout.plugins.sync.nsclientV3.NSClientV3Plugin import info.nightscout.plugins.sync.nsclientV3.NSClientV3Plugin
import info.nightscout.rx.bus.RxBus import info.nightscout.rx.bus.RxBus
import info.nightscout.rx.events.EventNSClientNewLog import info.nightscout.rx.events.EventNSClientNewLog
import info.nightscout.sdk.interfaces.NSAndroidClient
import info.nightscout.sdk.localmodel.treatment.NSTreatment
import info.nightscout.shared.utils.DateUtil import info.nightscout.shared.utils.DateUtil
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import javax.inject.Inject import javax.inject.Inject
import kotlin.math.max
class LoadTreatmentsWorker( class LoadTreatmentsWorker(
context: Context, context: Context,
@ -31,34 +35,47 @@ class LoadTreatmentsWorker(
override fun doWorkAndLog(): Result { override fun doWorkAndLog(): Result {
var ret = Result.success() var ret = Result.success()
val isFirstLoad = nsClientV3Plugin.isFirstLoad(NsClient.Collection.TREATMENTS)
val lastLoaded =
if (isFirstLoad) max(nsClientV3Plugin.firstLoadContinueTimestamp.collections.treatments, dateUtil.now() - nsClientV3Plugin.maxAge)
else max(nsClientV3Plugin.lastLoadedSrvModified.collections.treatments, dateUtil.now() - nsClientV3Plugin.maxAge)
runBlocking { runBlocking {
if ((nsClientV3Plugin.newestDataOnServer?.collections?.treatments ?: Long.MAX_VALUE) > nsClientV3Plugin.lastLoadedSrvModified.collections.treatments) if ((nsClientV3Plugin.newestDataOnServer?.collections?.treatments ?: Long.MAX_VALUE) > lastLoaded)
try { try {
val treatments = nsClientV3Plugin.nsAndroidClient.getTreatmentsModifiedSince(nsClientV3Plugin.lastLoadedSrvModified.collections.treatments, 500) val treatments: List<NSTreatment>
val response: NSAndroidClient.ReadResponse<List<NSTreatment>>?
if (isFirstLoad) {
treatments = nsClientV3Plugin.nsAndroidClient.getTreatmentsNewerThan(lastLoaded, 500)
response = NSAndroidClient.ReadResponse(0, treatments)
}
else {
response = nsClientV3Plugin.nsAndroidClient.getTreatmentsModifiedSince(lastLoaded, 500)
treatments = response.values
nsClientV3Plugin.lastLoadedSrvModified.collections.treatments = response.lastServerModified
nsClientV3Plugin.storeLastFetched()
}
aapsLogger.debug("TREATMENTS: $treatments") aapsLogger.debug("TREATMENTS: $treatments")
if (treatments.values.isNotEmpty()) { if (treatments.isNotEmpty()) {
rxBus.send( val action = if (isFirstLoad) "RCV-FIRST" else "RCV"
EventNSClientNewLog( rxBus.send(EventNSClientNewLog(action, "${treatments.size} TRs from ${dateUtil.dateAndTimeAndSecondsString(lastLoaded)}"))
"RCV",
"${treatments.values.size} TRs from ${dateUtil.dateAndTimeAndSecondsString(nsClientV3Plugin.lastLoadedSrvModified.collections.treatments)}"
)
)
// Schedule processing of fetched data and continue of loading // Schedule processing of fetched data and continue of loading
WorkManager.getInstance(context) WorkManager.getInstance(context)
.beginUniqueWork( .beginUniqueWork(
NSClientV3Plugin.JOB_NAME, NSClientV3Plugin.JOB_NAME,
ExistingWorkPolicy.APPEND_OR_REPLACE, ExistingWorkPolicy.APPEND_OR_REPLACE,
OneTimeWorkRequest.Builder(ProcessTreatmentsWorker::class.java) OneTimeWorkRequest.Builder(ProcessTreatmentsWorker::class.java)
.setInputData(dataWorkerStorage.storeInputData(treatments)) .setInputData(dataWorkerStorage.storeInputData(response))
.build() .build()
).then(OneTimeWorkRequest.Builder(LoadTreatmentsWorker::class.java).build()) ).then(OneTimeWorkRequest.Builder(LoadTreatmentsWorker::class.java).build())
.enqueue() .enqueue()
} else { } else {
rxBus.send( // End first load
EventNSClientNewLog( if (isFirstLoad) {
"END", "No TRs from ${dateUtil.dateAndTimeAndSecondsString(nsClientV3Plugin.lastLoadedSrvModified.collections.treatments)}" nsClientV3Plugin.lastLoadedSrvModified.collections.treatments = lastLoaded
) nsClientV3Plugin.storeLastFetched()
) }
rxBus.send(EventNSClientNewLog("RCV END", "No TRs from ${dateUtil
.dateAndTimeAndSecondsString(lastLoaded)}"))
storeDataForDb.storeTreatmentsToDb() storeDataForDb.storeTreatmentsToDb()
WorkManager.getInstance(context) WorkManager.getInstance(context)
.enqueueUniqueWork( .enqueueUniqueWork(
@ -72,7 +89,13 @@ class LoadTreatmentsWorker(
ret = Result.failure(workDataOf("Error" to error.toString())) ret = Result.failure(workDataOf("Error" to error.toString()))
} }
else { else {
rxBus.send(EventNSClientNewLog("END", "No new TRs from ${dateUtil.dateAndTimeAndSecondsString(nsClientV3Plugin.lastLoadedSrvModified.collections.treatments)}")) // End first load
if (isFirstLoad) {
nsClientV3Plugin.lastLoadedSrvModified.collections.treatments = lastLoaded
nsClientV3Plugin.storeLastFetched()
}
rxBus.send(EventNSClientNewLog("RCV END", "No new TRs from ${dateUtil
.dateAndTimeAndSecondsString(lastLoaded)}"))
storeDataForDb.storeTreatmentsToDb() storeDataForDb.storeTreatmentsToDb()
WorkManager.getInstance(context) WorkManager.getInstance(context)
.enqueueUniqueWork( .enqueueUniqueWork(

View file

@ -62,12 +62,12 @@ class ProcessTreatmentsWorker(
val treatments = dataWorkerStorage.pickupObject(inputData.getLong(DataWorkerStorage.STORE_KEY, -1)) as NSAndroidClient.ReadResponse<List<NSTreatment>>? val treatments = dataWorkerStorage.pickupObject(inputData.getLong(DataWorkerStorage.STORE_KEY, -1)) as NSAndroidClient.ReadResponse<List<NSTreatment>>?
?: return Result.failure(workDataOf("Error" to "missing input data")) ?: return Result.failure(workDataOf("Error" to "missing input data"))
var latestDateInReceivedData: Long = 0
val ret = Result.success() val ret = Result.success()
for (treatment in treatments.values) { for (treatment in treatments.values) {
aapsLogger.debug(LTag.DATABASE, "Received NS treatment: $treatment") aapsLogger.debug(LTag.DATABASE, "Received NS treatment: $treatment")
if (treatment.date > latestDateInReceivedData) latestDateInReceivedData = treatment.date
//Find latest date in treatment
val mills = treatment.date
when (treatment) { when (treatment) {
is NSBolus -> is NSBolus ->
if (sp.getBoolean(info.nightscout.core.utils.R.string.key_ns_receive_insulin, false) || config.NSCLIENT) if (sp.getBoolean(info.nightscout.core.utils.R.string.key_ns_receive_insulin, false) || config.NSCLIENT)
@ -136,7 +136,7 @@ class ProcessTreatmentsWorker(
} }
} }
} }
activePlugin.activeNsClient?.updateLatestTreatmentReceivedIfNewer(treatments.lastServerModified) activePlugin.activeNsClient?.updateLatestTreatmentReceivedIfNewer(latestDateInReceivedData)
// xDripBroadcast.sendTreatments(treatments) // xDripBroadcast.sendTreatments(treatments)
return ret return ret
} }

View file

@ -151,7 +151,7 @@
android:title="@string/connection_settings_title"> android:title="@string/connection_settings_title">
<SwitchPreference <SwitchPreference
android:defaultValue="false" android:defaultValue="true"
android:key="@string/key_ns_cellular" android:key="@string/key_ns_cellular"
android:title="@string/ns_cellular" /> android:title="@string/ns_cellular" />