NSCv3: optimize, use coroutines istead of recursion

This commit is contained in:
Milos Kozak 2023-02-10 22:00:26 +01:00
parent 40e14ba0e3
commit af4b7829f9
72 changed files with 2094 additions and 1848 deletions

View file

@ -4,6 +4,8 @@
<option name="WRAP_WHEN_TYPING_REACHES_RIGHT_MARGIN" value="true" />
<JetCodeStyleSettings>
<option name="ALIGN_IN_COLUMNS_CASE_BRANCH" value="true" />
<option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="2147483647" />
<option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="2147483647" />
<option name="BLANK_LINES_AROUND_BLOCK_WHEN_BRANCHES" value="1" />
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings>

View file

@ -15,7 +15,6 @@ class EventNSClientNewLog(val action: String, val logText: String?) : Event() {
stringBuilder.append(action)
stringBuilder.append("</b> ")
stringBuilder.append(logText)
stringBuilder.append("<br>")
return stringBuilder
}
}

View file

@ -1,12 +1,9 @@
package info.nightscout.androidaps.workflow
import info.nightscout.interfaces.workflow.WorkerClasses
import info.nightscout.plugins.profile.ProfilePlugin
import info.nightscout.source.NSClientSourcePlugin
import javax.inject.Inject
class WorkerClassesImpl @Inject constructor() : WorkerClasses {
override val nsClientSourceWorker = NSClientSourcePlugin.NSClientSourceWorker::class.java
override val nsProfileWorker = ProfilePlugin.NSProfileWorker::class.java
//override val nsProfileWorker = ProfilePlugin.NSProfileWorker::class.java
}

View file

@ -47,4 +47,5 @@ interface StoreDataForDb {
fun storeGlucoseValuesToDb()
fun storeFoodsToDb()
fun scheduleNsIdUpdate()
fun updateNsIds()
}

View file

@ -36,5 +36,6 @@ interface ProfileSource {
var currentProfileIndex: Int
fun currentProfile(): SingleProfile?
fun storeSettings(activity: FragmentActivity? = null, emptyCreated: Boolean = false)
fun loadFromStore(store: ProfileStore)
}

View file

@ -18,9 +18,11 @@ import org.json.JSONObject
interface DataSyncSelector {
interface DataPair {
val value: Any
val id: Long
}
data class PairTemporaryTarget(override val value: TemporaryTarget, override val id: Long) : DataPair
data class PairGlucoseValue(override val value: GlucoseValue, override val id: Long) : DataPair
data class PairTherapyEvent(override val value: TherapyEvent, override val id: Long) : DataPair
@ -38,49 +40,7 @@ interface DataSyncSelector {
fun queueSize(): Long
fun doUpload()
fun resetToNextFullSync()
fun confirmLastBolusIdIfGreater(lastSynced: Long)
fun processChangedBoluses()
fun confirmLastCarbsIdIfGreater(lastSynced: Long)
fun processChangedCarbs()
fun confirmLastBolusCalculatorResultsIdIfGreater(lastSynced: Long)
fun processChangedBolusCalculatorResults()
fun confirmLastTempTargetsIdIfGreater(lastSynced: Long)
fun processChangedTempTargets()
fun confirmLastGlucoseValueIdIfGreater(lastSynced: Long)
fun processChangedGlucoseValues()
fun confirmLastTherapyEventIdIfGreater(lastSynced: Long)
fun processChangedTherapyEvents()
fun confirmLastFoodIdIfGreater(lastSynced: Long)
fun processChangedFoods()
fun confirmLastDeviceStatusIdIfGreater(lastSynced: Long)
fun processChangedDeviceStatuses()
fun confirmLastTemporaryBasalIdIfGreater(lastSynced: Long)
fun processChangedTemporaryBasals()
fun confirmLastExtendedBolusIdIfGreater(lastSynced: Long)
fun processChangedExtendedBoluses()
fun confirmLastProfileSwitchIdIfGreater(lastSynced: Long)
fun processChangedProfileSwitches()
fun confirmLastEffectiveProfileSwitchIdIfGreater(lastSynced: Long)
fun processChangedEffectiveProfileSwitches()
fun confirmLastOfflineEventIdIfGreater(lastSynced: Long)
fun processChangedOfflineEvents()
fun confirmLastProfileStore(lastSynced: Long)
fun processChangedProfileStore()
suspend fun doUpload()
}

View file

@ -0,0 +1,46 @@
package info.nightscout.interfaces.sync
interface DataSyncSelectorV1 : DataSyncSelector {
fun confirmLastBolusIdIfGreater(lastSynced: Long)
suspend fun processChangedBoluses()
fun confirmLastCarbsIdIfGreater(lastSynced: Long)
suspend fun processChangedCarbs()
fun confirmLastBolusCalculatorResultsIdIfGreater(lastSynced: Long)
suspend fun processChangedBolusCalculatorResults()
fun confirmLastTempTargetsIdIfGreater(lastSynced: Long)
suspend fun processChangedTempTargets()
fun confirmLastGlucoseValueIdIfGreater(lastSynced: Long)
suspend fun processChangedGlucoseValues()
fun confirmLastTherapyEventIdIfGreater(lastSynced: Long)
suspend fun processChangedTherapyEvents()
fun confirmLastFoodIdIfGreater(lastSynced: Long)
suspend fun processChangedFoods()
fun confirmLastDeviceStatusIdIfGreater(lastSynced: Long)
suspend fun processChangedDeviceStatuses()
fun confirmLastTemporaryBasalIdIfGreater(lastSynced: Long)
suspend fun processChangedTemporaryBasals()
fun confirmLastExtendedBolusIdIfGreater(lastSynced: Long)
suspend fun processChangedExtendedBoluses()
fun confirmLastProfileSwitchIdIfGreater(lastSynced: Long)
suspend fun processChangedProfileSwitches()
fun confirmLastEffectiveProfileSwitchIdIfGreater(lastSynced: Long)
suspend fun processChangedEffectiveProfileSwitches()
fun confirmLastOfflineEventIdIfGreater(lastSynced: Long)
suspend fun processChangedOfflineEvents()
fun confirmLastProfileStore(lastSynced: Long)
suspend fun processChangedProfileStore()
}

View file

@ -0,0 +1,3 @@
package info.nightscout.interfaces.sync
interface DataSyncSelectorV3 : DataSyncSelector

View file

@ -0,0 +1,3 @@
package info.nightscout.interfaces.sync
interface DataSyncSelectorXdrip : DataSyncSelector

View file

@ -1,7 +1,7 @@
package info.nightscout.interfaces.sync
import android.text.Spanned
import info.nightscout.interfaces.nsclient.NSAlarm
import info.nightscout.rx.events.EventNSClientNewLog
/**
* Plugin providing communication with Nightscout server
@ -24,14 +24,14 @@ interface NsClient : Sync {
fun resend(reason: String)
/**
* @return List last of messages for fragment in HTML format
* List of log messages for fragment
*/
fun textLog(): Spanned
val listLog: MutableList<EventNSClientNewLog>
/**
* Clear list of stored messages displayed in fragment
* Used data sync selector
*/
fun clearLog()
val dataSyncSelector: DataSyncSelector
/**
* Version of NS server
@ -89,8 +89,9 @@ interface NsClient : Sync {
* @param collection target ns collection
* @param dataPair data to upload (data.first) and id of changed record (data.second)
* @param progress progress of sync in format "number/number". Only for display in fragment
* @return true for successful upload
*/
fun nsAdd(collection: String, dataPair: DataSyncSelector.DataPair, progress: String)
suspend fun nsAdd(collection: String, dataPair: DataSyncSelector.DataPair, progress: String): Boolean
/**
* Upload updated record to NS
@ -98,6 +99,7 @@ interface NsClient : Sync {
* @param collection target ns collection
* @param dataPair data to upload (data.first) and id of changed record (data.second)
* @param progress progress of sync in format "number/number". Only for display in fragment
* @return true for successful upload
*/
fun nsUpdate(collection: String, dataPair: DataSyncSelector.DataPair, progress: String)
suspend fun nsUpdate(collection: String, dataPair: DataSyncSelector.DataPair, progress: String): Boolean
}

View file

@ -1,8 +1,5 @@
package info.nightscout.interfaces.workflow
import androidx.work.ListenableWorker
interface WorkerClasses {
val nsClientSourceWorker: Class<out ListenableWorker>
val nsProfileWorker: Class<out ListenableWorker>
// val nsProfileWorker: Class<out ListenableWorker>
}

View file

@ -119,7 +119,11 @@ class NSAndroidClientImpl(
val response = api.getSgvs()
if (response.isSuccessful) {
return@callWrapper NSAndroidClient.ReadResponse(code = response.raw().networkResponse?.code ?: response.code(), lastServerModified = 0, values = response.body()?.result?.map(RemoteEntry::toSgv).toNotNull())
return@callWrapper NSAndroidClient.ReadResponse(
code = response.raw().networkResponse?.code ?: response.code(),
lastServerModified = 0,
values = response.body()?.result?.map(RemoteEntry::toSgv).toNotNull()
)
} else if (response.code() in 400..499)
throw InvalidParameterNightscoutException(response.errorBody()?.string() ?: response.message())
else
@ -132,7 +136,11 @@ class NSAndroidClientImpl(
if (response.isSuccessful) {
val eTagString = response.headers()["ETag"]
val eTag = eTagString?.substring(3, eTagString.length - 1)?.toLong()
return@callWrapper NSAndroidClient.ReadResponse(code = response.raw().networkResponse?.code ?: response.code(), lastServerModified = eTag, values = response.body()?.result?.map(RemoteEntry::toSgv).toNotNull())
return@callWrapper NSAndroidClient.ReadResponse(
code = response.raw().networkResponse?.code ?: response.code(),
lastServerModified = eTag,
values = response.body()?.result?.map(RemoteEntry::toSgv).toNotNull()
)
} else if (response.code() in 400..499)
throw InvalidParameterNightscoutException(response.errorBody()?.string() ?: response.message())
else
@ -143,39 +151,37 @@ class NSAndroidClientImpl(
val response = api.getSgvsNewerThan(from, limit)
if (response.isSuccessful) {
return@callWrapper NSAndroidClient.ReadResponse(code = response.raw().networkResponse?.code ?: response.code(), lastServerModified = 0, values = response.body()?.result?.map(RemoteEntry::toSgv).toNotNull())
return@callWrapper NSAndroidClient.ReadResponse(
code = response.raw().networkResponse?.code ?: response.code(),
lastServerModified = 0,
values = response.body()?.result?.map(RemoteEntry::toSgv).toNotNull()
)
} else if (response.code() in 400..499)
throw InvalidParameterNightscoutException(response.errorBody()?.string() ?: response.message())
else
throw UnsuccessfullNightscoutException()
}
override suspend fun createSvg(nsSgvV3: NSSgvV3): CreateUpdateResponse = callWrapper(dispatcher) {
override suspend fun createSgv(nsSgvV3: NSSgvV3): CreateUpdateResponse = callWrapper(dispatcher) {
val remoteEntry = nsSgvV3.toRemoteEntry()
remoteEntry.app = "AAPS"
val response = api.createEntry(remoteEntry)
val responseBody = response.body()
val errorResponse = response.errorBody()?.string()
if (response.code() == 200) {
if (response.code() == 200 || response.code() == 201) {
return@callWrapper CreateUpdateResponse(
response = response.code(),
identifier = null,
isDeduplication = true
)
} else if (response.code() == 201) {
return@callWrapper CreateUpdateResponse(
response = response.code(),
identifier = responseBody?.result?.identifier,
isDeduplication = responseBody?.result?.isDeduplication ?: false,
deduplicatedIdentifier = responseBody?.result?.deduplicatedIdentifier,
lastModified = responseBody?.result?.lastModified
identifier = responseBody?.identifier,
isDeduplication = responseBody?.isDeduplication ?: false,
deduplicatedIdentifier = responseBody?.deduplicatedIdentifier,
lastModified = responseBody?.lastModified
)
} else if (response.code() == 400 && errorResponse?.contains("Bad or missing utcOffset field") == true && nsSgvV3.utcOffset != 0L) {
// Record can be originally uploaded without utcOffset
// because utcOffset is mandatory and cannot be change, try 0
nsSgvV3.utcOffset = 0
return@callWrapper createSvg(nsSgvV3)
return@callWrapper createSgv(nsSgvV3)
} else if (response.code() == 400 && errorResponse?.contains("cannot be modified by the client") == true) {
// there is different field to field in AAPS
// not possible to upload
@ -234,7 +240,11 @@ class NSAndroidClientImpl(
val response = api.getTreatmentsNewerThan(createdAt, limit)
if (response.isSuccessful) {
return@callWrapper NSAndroidClient.ReadResponse(code = response.raw().networkResponse?.code ?: response.code(), lastServerModified = 0, values = response.body()?.result?.map(RemoteTreatment::toTreatment).toNotNull())
return@callWrapper NSAndroidClient.ReadResponse(
code = response.raw().networkResponse?.code ?: response.code(),
lastServerModified = 0,
values = response.body()?.result?.map(RemoteTreatment::toTreatment).toNotNull()
)
} else if (response.code() in 400..499)
throw InvalidParameterNightscoutException(response.errorBody()?.string() ?: response.message())
else
@ -273,22 +283,13 @@ class NSAndroidClientImpl(
nsDeviceStatus.app = "AAPS"
val response = api.createDeviceStatus(nsDeviceStatus.toRemoteDeviceStatus())
if (response.isSuccessful) {
if (response.code() == 200) {
if (response.code() == 200 || response.code() == 201) {
return@callWrapper CreateUpdateResponse(
response = response.code(),
identifier = null,
isDeduplication = true,
deduplicatedIdentifier = null,
lastModified = null
)
} else if (response.code() == 201) {
return@callWrapper CreateUpdateResponse(
response = response.code(),
identifier = response.body()?.result?.identifier
?: throw UnknownResponseNightscoutException(),
isDeduplication = response.body()?.result?.isDeduplication ?: false,
deduplicatedIdentifier = response.body()?.result?.deduplicatedIdentifier,
lastModified = response.body()?.result?.lastModified
identifier = response.body()?.identifier,
isDeduplication = response.body()?.isDeduplication,
deduplicatedIdentifier = response.body()?.deduplicatedIdentifier,
lastModified = response.body()?.lastModified
)
} else throw UnknownResponseNightscoutException()
} else if (response.code() in 400..499) {
@ -307,19 +308,13 @@ class NSAndroidClientImpl(
remoteTreatment.app = "AAPS"
val response = api.createTreatment(remoteTreatment)
val errorResponse = response.errorBody()?.string()
if (response.code() == 200) {
if (response.code() == 200 || response.code() == 201) {
return@callWrapper CreateUpdateResponse(
response = response.code(),
identifier = null,
isDeduplication = true
)
} else if (response.code() == 201) {
return@callWrapper CreateUpdateResponse(
response = response.code(),
identifier = response.body()?.result?.identifier,
isDeduplication = response.body()?.result?.isDeduplication ?: false,
deduplicatedIdentifier = response.body()?.result?.deduplicatedIdentifier,
lastModified = response.body()?.result?.lastModified
identifier = response.body()?.identifier,
isDeduplication = response.body()?.isDeduplication ?: false,
deduplicatedIdentifier = response.body()?.deduplicatedIdentifier,
lastModified = response.body()?.lastModified
)
} else if (response.code() == 400 && errorResponse?.contains("Bad or missing utcOffset field") == true && nsTreatment.utcOffset != 0L) {
// Record can be originally uploaded without utcOffset
@ -384,7 +379,11 @@ class NSAndroidClientImpl(
val response = api.getFoods(limit)
if (response.isSuccessful) {
return@callWrapper NSAndroidClient.ReadResponse(code = response.raw().networkResponse?.code ?: response.code(), lastServerModified = 0, values = response.body()?.result?.map(RemoteFood::toNSFood).toNotNull())
return@callWrapper NSAndroidClient.ReadResponse(
code = response.raw().networkResponse?.code ?: response.code(),
lastServerModified = 0,
values = response.body()?.result?.map(RemoteFood::toNSFood).toNotNull()
)
} else if (response.code() in 400..499)
throw InvalidParameterNightscoutException(response.errorBody()?.string() ?: response.message())
else
@ -410,21 +409,13 @@ class NSAndroidClientImpl(
remoteFood.app = "AAPS"
val response = api.createFood(remoteFood)
if (response.isSuccessful) {
if (response.code() == 200) {
if (response.code() == 200 || response.code() == 201) {
return@callWrapper CreateUpdateResponse(
response = response.code(),
identifier = null,
isDeduplication = true,
deduplicatedIdentifier = null,
lastModified = null
)
} else if (response.code() == 201) {
return@callWrapper CreateUpdateResponse(
response = response.code(),
identifier = response.body()?.result?.identifier,
isDeduplication = response.body()?.result?.isDeduplication ?: false,
deduplicatedIdentifier = response.body()?.result?.deduplicatedIdentifier,
lastModified = response.body()?.result?.lastModified
identifier = response.body()?.identifier,
isDeduplication = response.body()?.isDeduplication,
deduplicatedIdentifier = response.body()?.deduplicatedIdentifier,
lastModified = response.body()?.lastModified
)
} else throw UnsuccessfullNightscoutException()
} else if (response.code() in 400..499) {
@ -474,21 +465,13 @@ class NSAndroidClientImpl(
remoteProfileStore.put("app", "AAPS")
val response = api.createProfile(JsonParser.parseString(remoteProfileStore.toString()).asJsonObject)
if (response.isSuccessful) {
if (response.code() == 200) {
if (response.code() == 200 || response.code() == 201) {
return@callWrapper CreateUpdateResponse(
response = response.code(),
identifier = null,
isDeduplication = true,
deduplicatedIdentifier = null,
lastModified = null
)
} else if (response.code() == 201) {
return@callWrapper CreateUpdateResponse(
response = response.code(),
identifier = response.body()?.result?.identifier,
isDeduplication = response.body()?.result?.isDeduplication ?: false,
deduplicatedIdentifier = response.body()?.result?.deduplicatedIdentifier,
lastModified = response.body()?.result?.lastModified
identifier = response.body()?.identifier,
isDeduplication = response.body()?.isDeduplication,
deduplicatedIdentifier = response.body()?.deduplicatedIdentifier,
lastModified = response.body()?.lastModified
)
} else throw UnsuccessfullNightscoutException()
} else if (response.code() in 400..499) {
@ -514,7 +497,6 @@ class NSAndroidClientImpl(
throw UnsuccessfullNightscoutException()
}
override suspend fun getProfileModifiedSince(from: Long): NSAndroidClient.ReadResponse<List<JSONObject>> = callWrapper(dispatcher) {
val response = api.getProfileModifiedSince(from)
@ -528,7 +510,6 @@ class NSAndroidClientImpl(
throw UnsuccessfullNightscoutException()
}
private suspend fun <T> callWrapper(dispatcher: CoroutineDispatcher, block: suspend () -> T): T =
withContext(dispatcher) {
retry(

View file

@ -25,7 +25,7 @@ interface NSAndroidClient {
suspend fun getSgvs(): ReadResponse<List<NSSgvV3>>
suspend fun getSgvsModifiedSince(from: Long, limit: Int): ReadResponse<List<NSSgvV3>>
suspend fun getSgvsNewerThan(from: Long, limit: Int): ReadResponse<List<NSSgvV3>>
suspend fun createSvg(nsSgvV3: NSSgvV3): CreateUpdateResponse
suspend fun createSgv(nsSgvV3: NSSgvV3): CreateUpdateResponse
suspend fun updateSvg(nsSgvV3: NSSgvV3): CreateUpdateResponse
suspend fun getTreatmentsNewerThan(createdAt: String, limit: Int): ReadResponse<List<NSTreatment>>

View file

@ -48,7 +48,7 @@ internal interface NightscoutRemoteService {
suspend fun getSgvsModifiedSince(@Path("from") from: Long, @Query("limit") limit: Int): Response<NSResponse<List<RemoteEntry>>>
@POST("v3/entries")
suspend fun createEntry(@Body remoteEntry: RemoteEntry): Response<NSResponse<RemoteCreateUpdateResponse>>
suspend fun createEntry(@Body remoteEntry: RemoteEntry): Response<RemoteCreateUpdateResponse>
@PATCH("v3/entries/{identifier}")
suspend fun updateEntry(@Body remoteEntry: RemoteEntry, @Path("identifier") identifier: String): Response<NSResponse<RemoteCreateUpdateResponse>>
@ -63,16 +63,16 @@ internal interface NightscoutRemoteService {
suspend fun getTreatmentsModifiedSince(@Path("from") from: Long, @Query("limit") limit: Int): Response<NSResponse<List<RemoteTreatment>>>
@POST("v3/treatments")
suspend fun createTreatment(@Body remoteTreatment: RemoteTreatment): Response<NSResponse<RemoteCreateUpdateResponse>>
suspend fun createTreatment(@Body remoteTreatment: RemoteTreatment): Response<RemoteCreateUpdateResponse>
@PATCH("v3/treatments/{identifier}")
suspend fun updateTreatment(@Body remoteTreatment: RemoteTreatment, @Path("identifier") identifier: String): Response<NSResponse<RemoteCreateUpdateResponse>>
suspend fun updateTreatment(@Body remoteTreatment: RemoteTreatment, @Path("identifier") identifier: String): Response<RemoteCreateUpdateResponse>
@DELETE("v3/treatments/{identifier}")
suspend fun deleteTreatment(@Path("identifier") identifier: String): Response<NSResponse<RemoteCreateUpdateResponse>>
suspend fun deleteTreatment(@Path("identifier") identifier: String): Response<RemoteCreateUpdateResponse>
@POST("v3/devicestatus")
suspend fun createDeviceStatus(@Body remoteDeviceStatus: RemoteDeviceStatus): Response<NSResponse<RemoteCreateUpdateResponse>>
suspend fun createDeviceStatus(@Body remoteDeviceStatus: RemoteDeviceStatus): Response<RemoteCreateUpdateResponse>
@GET("v3/devicestatus/history/{from}")
suspend fun getDeviceStatusModifiedSince(@Path("from") from: Long): Response<NSResponse<List<RemoteDeviceStatus>>>
@ -85,13 +85,13 @@ internal interface NightscoutRemoteService {
suspend fun getFoodsModifiedSince(@Path("from") from: Long, @Query("limit") limit: Int): Response<NSResponse<List<RemoteFood>>>
*/
@POST("v3/food")
suspend fun createFood(@Body remoteFood: RemoteFood): Response<NSResponse<RemoteCreateUpdateResponse>>
suspend fun createFood(@Body remoteFood: RemoteFood): Response<RemoteCreateUpdateResponse>
@PATCH("v3/food")
suspend fun updateFood(@Body remoteFood: RemoteFood, @Path("identifier") identifier: String): Response<NSResponse<RemoteCreateUpdateResponse>>
suspend fun updateFood(@Body remoteFood: RemoteFood, @Path("identifier") identifier: String): Response<RemoteCreateUpdateResponse>
@DELETE("v3/food")
suspend fun deleteFood(@Path("identifier") identifier: String): Response<NSResponse<RemoteCreateUpdateResponse>>
suspend fun deleteFood(@Path("identifier") identifier: String): Response<RemoteCreateUpdateResponse>
@GET("v3/profile/history/{from}")
suspend fun getProfileModifiedSince(@Path("from") from: Long, @Query("limit") limit: Int = 10): Response<NSResponse<List<JSONObject>>>
@ -100,8 +100,7 @@ internal interface NightscoutRemoteService {
@GET("v3/profile?sort\$desc=date&limit=1")
suspend fun getLastProfile(): Response<NSResponse<List<JSONObject>>>
@POST("v3/profile")
suspend fun createProfile(@Body profile: JsonObject): Response<NSResponse<RemoteCreateUpdateResponse>>
suspend fun createProfile(@Body profile: JsonObject): Response<RemoteCreateUpdateResponse>
}

View file

@ -39,6 +39,28 @@ object OKDialog {
.setCanceledOnTouchOutside(false)
}
@SuppressLint("InflateParams")
fun show(context: Context, title: String, message: Spanned, runnable: Runnable? = null) {
var okClicked = false
var notEmptyTitle = title
if (notEmptyTitle.isEmpty()) notEmptyTitle = context.getString(R.string.message)
MaterialAlertDialogBuilder(context, R.style.DialogTheme)
.setCustomTitle(AlertDialogHelper.buildCustomTitle(context, notEmptyTitle))
.setMessage(message)
.setPositiveButton(context.getString(R.string.ok)) { dialog: DialogInterface, _: Int ->
if (okClicked) return@setPositiveButton
else {
okClicked = true
dialog.dismiss()
SystemClock.sleep(100)
runOnUiThread(runnable)
}
}
.show()
.setCanceledOnTouchOutside(false)
}
@SuppressLint("InflateParams")
fun show(activity: FragmentActivity, title: String, message: Spanned, runnable: Runnable? = null) {
var okClicked = false

View file

@ -620,4 +620,9 @@
<item quantity="other">%1$d minutes</item>
</plurals>
<!-- Maintenance-->
<string name="cleanup_db_confirm">Do you want to cleanup the database?\nIt will remove tracked changes and historic data older than 3 months.</string>
<string name="cleanup_db_confirm_sync">Do you want to cleanup the database?\nIt will remove tracked changes and historic data older than 3 months.\nDoing it will speedup full synchronization dramatically.</string>
<string name="cleared_entries">Cleared entries</string>
</resources>

View file

@ -150,10 +150,8 @@ import kotlin.math.roundToInt
database.glucoseValueDao.getModifiedFrom(lastId)
.subscribeOn(Schedulers.io())
fun getLastGlucoseValueIdWrapped(): Single<ValueWrapper<Long>> =
fun getLastGlucoseValueId(): Long? =
database.glucoseValueDao.getLastId()
.subscribeOn(Schedulers.io())
.toWrappedSingle()
fun getLastGlucoseValueWrapped(): Single<ValueWrapper<GlucoseValue>> =
database.glucoseValueDao.getLast()
@ -233,10 +231,8 @@ import kotlin.math.roundToInt
fun deleteAllTempTargetEntries() =
database.temporaryTargetDao.deleteAllEntries()
fun getLastTempTargetIdWrapped(): Single<ValueWrapper<Long>> =
fun getLastTempTargetId(): Long? =
database.temporaryTargetDao.getLastId()
.subscribeOn(Schedulers.io())
.toWrappedSingle()
// USER ENTRY
fun getAllUserEntries(): Single<List<UserEntry>> =
@ -309,10 +305,8 @@ import kotlin.math.roundToInt
.map { if (!ascending) it.reversed() else it }
.subscribeOn(Schedulers.io())
fun getLastProfileSwitchIdWrapped(): Single<ValueWrapper<Long>> =
fun getLastProfileSwitchId(): Long? =
database.profileSwitchDao.getLastId()
.subscribeOn(Schedulers.io())
.toWrappedSingle()
// EFFECTIVE PROFILE SWITCH
/*
@ -368,10 +362,8 @@ import kotlin.math.roundToInt
fun deleteAllEffectiveProfileSwitches() =
database.effectiveProfileSwitchDao.deleteAllEntries()
fun getLastEffectiveProfileSwitchIdWrapped(): Single<ValueWrapper<Long>> =
fun getLastEffectiveProfileSwitchId(): Long? =
database.effectiveProfileSwitchDao.getLastId()
.subscribeOn(Schedulers.io())
.toWrappedSingle()
// THERAPY EVENT
/*
@ -435,10 +427,8 @@ import kotlin.math.roundToInt
database.therapyEventDao.compatGetTherapyEventDataFromToTime(from, to)
.subscribeOn(Schedulers.io())
fun getLastTherapyEventIdWrapped(): Single<ValueWrapper<Long>> =
fun getLastTherapyEventId(): Long? =
database.therapyEventDao.getLastId()
.subscribeOn(Schedulers.io())
.toWrappedSingle()
// FOOD
/*
@ -471,10 +461,8 @@ import kotlin.math.roundToInt
fun deleteAllFoods() =
database.foodDao.deleteAllEntries()
fun getLastFoodIdWrapped(): Single<ValueWrapper<Long>> =
fun getLastFoodId(): Long? =
database.foodDao.getLastId()
.subscribeOn(Schedulers.io())
.toWrappedSingle()
// BOLUS
/*
@ -539,10 +527,8 @@ import kotlin.math.roundToInt
fun deleteAllBoluses() =
database.bolusDao.deleteAllEntries()
fun getLastBolusIdWrapped(): Single<ValueWrapper<Long>> =
fun getLastBolusId(): Long? =
database.bolusDao.getLastId()
.subscribeOn(Schedulers.io())
.toWrappedSingle()
// CARBS
private fun expandCarbs(carbs: Carbs): List<Carbs> =
@ -656,10 +642,8 @@ import kotlin.math.roundToInt
fun deleteAllCarbs() =
database.carbsDao.deleteAllEntries()
fun getLastCarbsIdWrapped(): Single<ValueWrapper<Long>> =
fun getLastCarbsId(): Long? =
database.carbsDao.getLastId()
.subscribeOn(Schedulers.io())
.toWrappedSingle()
// BOLUS CALCULATOR RESULT
/*
@ -698,10 +682,8 @@ import kotlin.math.roundToInt
fun deleteAllBolusCalculatorResults() =
database.bolusCalculatorResultDao.deleteAllEntries()
fun getLastBolusCalculatorResultIdWrapped(): Single<ValueWrapper<Long>> =
fun getLastBolusCalculatorResultId(): Long? =
database.bolusCalculatorResultDao.getLastId()
.subscribeOn(Schedulers.io())
.toWrappedSingle()
// DEVICE STATUS
fun insert(deviceStatus: DeviceStatus): Long =
@ -723,10 +705,8 @@ import kotlin.math.roundToInt
database.deviceStatusDao.getModifiedFrom(lastId)
.subscribeOn(Schedulers.io())
fun getLastDeviceStatusIdWrapped(): Single<ValueWrapper<Long>> =
fun getLastDeviceStatusId(): Long? =
database.deviceStatusDao.getLastId()
.subscribeOn(Schedulers.io())
.toWrappedSingle()
// TEMPORARY BASAL
/*
@ -789,10 +769,8 @@ import kotlin.math.roundToInt
fun getOldestTemporaryBasalRecord(): TemporaryBasal? =
database.temporaryBasalDao.getOldestRecord()
fun getLastTemporaryBasalIdWrapped(): Single<ValueWrapper<Long>> =
fun getLastTemporaryBasalId(): Long? =
database.temporaryBasalDao.getLastId()
.subscribeOn(Schedulers.io())
.toWrappedSingle()
// EXTENDED BOLUS
/*
@ -847,10 +825,8 @@ import kotlin.math.roundToInt
fun getOldestExtendedBolusRecord(): ExtendedBolus? =
database.extendedBolusDao.getOldestRecord()
fun getLastExtendedBolusIdWrapped(): Single<ValueWrapper<Long>> =
fun getLastExtendedBolusId(): Long? =
database.extendedBolusDao.getLastId()
.subscribeOn(Schedulers.io())
.toWrappedSingle()
// TotalDailyDose
fun getLastTotalDailyDoses(count: Int, ascending: Boolean): Single<List<TotalDailyDose>> =
@ -918,10 +894,8 @@ import kotlin.math.roundToInt
fun deleteAllOfflineEventEntries() =
database.offlineEventDao.deleteAllEntries()
fun getLastOfflineEventIdWrapped(): Single<ValueWrapper<Long>> =
fun getLastOfflineEventId(): Long? =
database.offlineEventDao.getLastId()
.subscribeOn(Schedulers.io())
.toWrappedSingle()
suspend fun collectNewEntriesSince(since: Long, until: Long, limit: Int, offset: Int) = NewEntries(
apsResults = database.apsResultDao.getNewEntriesSince(since, until, limit, offset),

View file

@ -24,7 +24,7 @@ internal interface BolusCalculatorResultDao : TraceableDao<BolusCalculatorResult
override fun deleteTrackedChanges(): Int
@Query("SELECT id FROM $TABLE_BOLUS_CALCULATOR_RESULTS ORDER BY id DESC limit 1")
fun getLastId(): Maybe<Long>
fun getLastId(): Long?
@Query("SELECT * FROM $TABLE_BOLUS_CALCULATOR_RESULTS WHERE timestamp = :timestamp AND referenceId IS NULL")
fun findByTimestamp(timestamp: Long): BolusCalculatorResult?

View file

@ -24,7 +24,7 @@ internal interface BolusDao : TraceableDao<Bolus> {
override fun deleteTrackedChanges(): Int
@Query("SELECT id FROM $TABLE_BOLUSES ORDER BY id DESC limit 1")
fun getLastId(): Maybe<Long>
fun getLastId(): Long?
@Query("SELECT * FROM $TABLE_BOLUSES WHERE timestamp = :timestamp AND referenceId IS NULL")
fun findByTimestamp(timestamp: Long): Bolus?

View file

@ -23,7 +23,7 @@ internal interface CarbsDao : TraceableDao<Carbs> {
override fun deleteTrackedChanges(): Int
@Query("SELECT id FROM $TABLE_CARBS ORDER BY id DESC limit 1")
fun getLastId(): Maybe<Long>
fun getLastId(): Long?
@Query("SELECT * FROM $TABLE_CARBS WHERE nightscoutId = :nsId AND referenceId IS NULL")
fun findByNSId(nsId: String): Carbs?

View file

@ -29,7 +29,7 @@ internal interface DeviceStatusDao {
fun deleteOlderThan(than: Long): Int
@Query("SELECT id FROM $TABLE_DEVICE_STATUS ORDER BY id DESC limit 1")
fun getLastId(): Maybe<Long>
fun getLastId(): Long?
@Query("DELETE FROM $TABLE_DEVICE_STATUS WHERE id NOT IN (SELECT MAX(id) FROM $TABLE_DEVICE_STATUS)")
fun deleteAllEntriesExceptLast()

View file

@ -23,7 +23,7 @@ internal interface EffectiveProfileSwitchDao : TraceableDao<EffectiveProfileSwit
override fun deleteTrackedChanges(): Int
@Query("SELECT id FROM $TABLE_EFFECTIVE_PROFILE_SWITCHES ORDER BY id DESC limit 1")
fun getLastId(): Maybe<Long>
fun getLastId(): Long?
@Query("SELECT * FROM $TABLE_EFFECTIVE_PROFILE_SWITCHES WHERE timestamp = :timestamp AND referenceId IS NULL")
fun findByTimestamp(timestamp: Long): EffectiveProfileSwitch?

View file

@ -24,7 +24,7 @@ internal interface ExtendedBolusDao : TraceableDao<ExtendedBolus> {
override fun deleteTrackedChanges(): Int
@Query("SELECT id FROM $TABLE_EXTENDED_BOLUSES ORDER BY id DESC limit 1")
fun getLastId(): Maybe<Long>
fun getLastId(): Long?
@Query("SELECT * FROM $TABLE_EXTENDED_BOLUSES WHERE timestamp = :timestamp AND referenceId IS NULL")
fun findByTimestamp(timestamp: Long): ExtendedBolus?

View file

@ -23,7 +23,7 @@ internal interface FoodDao : TraceableDao<Food> {
override fun deleteTrackedChanges(): Int
@Query("SELECT id FROM $TABLE_FOODS ORDER BY id DESC limit 1")
fun getLastId(): Maybe<Long>
fun getLastId(): Long?
@Query("SELECT * FROM $TABLE_FOODS WHERE nightscoutId = :nsId AND referenceId IS NULL")
fun findByNSId(nsId: String): Food?

View file

@ -26,7 +26,7 @@ internal interface GlucoseValueDao : TraceableDao<GlucoseValue> {
fun getLast(): Maybe<GlucoseValue>
@Query("SELECT id FROM $TABLE_GLUCOSE_VALUES ORDER BY id DESC limit 1")
fun getLastId(): Maybe<Long>
fun getLastId(): Long?
@Query("SELECT * FROM $TABLE_GLUCOSE_VALUES WHERE nightscoutId = :nsId AND referenceId IS NULL")
fun findByNSIdMaybe(nsId: String): Maybe<GlucoseValue>

View file

@ -23,7 +23,7 @@ internal interface OfflineEventDao : TraceableDao<OfflineEvent> {
override fun deleteTrackedChanges(): Int
@Query("SELECT id FROM $TABLE_OFFLINE_EVENTS ORDER BY id DESC limit 1")
fun getLastId(): Maybe<Long>
fun getLastId(): Long?
@Query("SELECT * FROM $TABLE_OFFLINE_EVENTS WHERE nightscoutId = :nsId AND referenceId IS NULL")
fun findByNSId(nsId: String): OfflineEvent?

View file

@ -24,7 +24,7 @@ internal interface ProfileSwitchDao : info.nightscout.database.impl.daos.workaro
override fun deleteTrackedChanges(): Int
@Query("SELECT id FROM $TABLE_PROFILE_SWITCHES ORDER BY id DESC limit 1")
fun getLastId(): Maybe<Long>
fun getLastId(): Long?
@Query("SELECT * FROM $TABLE_PROFILE_SWITCHES WHERE timestamp = :timestamp AND referenceId IS NULL")
fun findByTimestamp(timestamp: Long): ProfileSwitch?

View file

@ -24,7 +24,7 @@ internal interface TemporaryBasalDao : TraceableDao<TemporaryBasal> {
override fun deleteTrackedChanges(): Int
@Query("SELECT id FROM $TABLE_TEMPORARY_BASALS ORDER BY id DESC limit 1")
fun getLastId(): Maybe<Long>
fun getLastId(): Long?
@Query("SELECT * FROM $TABLE_TEMPORARY_BASALS WHERE temporaryId = :temporaryId")
fun findByTempId(temporaryId: Long): TemporaryBasal?

View file

@ -23,7 +23,7 @@ internal interface TemporaryTargetDao : TraceableDao<TemporaryTarget> {
override fun deleteTrackedChanges(): Int
@Query("SELECT id FROM $TABLE_TEMPORARY_TARGETS ORDER BY id DESC limit 1")
fun getLastId(): Maybe<Long>
fun getLastId(): Long?
@Query("SELECT * FROM $TABLE_TEMPORARY_TARGETS WHERE nightscoutId = :nsId AND referenceId IS NULL")
fun findByNSId(nsId: String): TemporaryTarget?

View file

@ -23,7 +23,7 @@ internal interface TherapyEventDao : TraceableDao<TherapyEvent> {
override fun deleteTrackedChanges(): Int
@Query("SELECT id FROM $TABLE_THERAPY_EVENTS ORDER BY id DESC limit 1")
fun getLastId(): Maybe<Long>
fun getLastId(): Long?
@Query("SELECT * FROM $TABLE_THERAPY_EVENTS WHERE type = :type AND timestamp = :timestamp AND referenceId IS NULL")
fun findByTimestamp(type: TherapyEvent.Type, timestamp: Long): TherapyEvent?

View file

@ -24,7 +24,7 @@ import info.nightscout.interfaces.plugin.OwnDatabasePlugin
import info.nightscout.interfaces.protection.ProtectionCheck
import info.nightscout.interfaces.protection.ProtectionCheck.Protection.PREFERENCES
import info.nightscout.interfaces.pump.PumpSync
import info.nightscout.interfaces.sync.DataSyncSelector
import info.nightscout.interfaces.sync.DataSyncSelectorXdrip
import info.nightscout.interfaces.ui.UiInteraction
import info.nightscout.interfaces.utils.HtmlHelper
import info.nightscout.rx.AapsSchedulers
@ -51,7 +51,7 @@ class MaintenanceFragment : DaggerFragment() {
@Inject lateinit var persistenceLayer: PersistenceLayer
@Inject lateinit var protectionCheck: ProtectionCheck
@Inject lateinit var uel: UserEntryLogger
@Inject lateinit var dataSyncSelector: DataSyncSelector
@Inject lateinit var dataSyncSelectorXdrip: DataSyncSelectorXdrip
@Inject lateinit var pumpSync: PumpSync
@Inject lateinit var iobCobCalculator: IobCobCalculator
@Inject lateinit var overviewData: OverviewData
@ -93,7 +93,8 @@ class MaintenanceFragment : DaggerFragment() {
for (plugin in activePlugin.getSpecificPluginsListByInterface(OwnDatabasePlugin::class.java)) {
(plugin as OwnDatabasePlugin).clearAllTables()
}
dataSyncSelector.resetToNextFullSync()
activePlugin.activeNsClient?.dataSyncSelector?.resetToNextFullSync()
dataSyncSelectorXdrip.resetToNextFullSync()
pumpSync.connectNewPump()
overviewData.reset()
iobCobCalculator.ads.reset()
@ -111,7 +112,7 @@ class MaintenanceFragment : DaggerFragment() {
binding.cleanupDb.setOnClickListener {
activity?.let { activity ->
var result = ""
OKDialog.showConfirmation(activity, rh.gs(R.string.maintenance), rh.gs(R.string.cleanup_db_confirm), Runnable {
OKDialog.showConfirmation(activity, rh.gs(R.string.maintenance), rh.gs(info.nightscout.core.ui.R.string.cleanup_db_confirm), Runnable {
disposable += Completable.fromAction { result = persistenceLayer.cleanupDatabase(93, deleteTrackedChanges = true) }
.subscribeOn(aapsSchedulers.io)
.observeOn(aapsSchedulers.main)
@ -119,8 +120,12 @@ class MaintenanceFragment : DaggerFragment() {
onError = { aapsLogger.error("Error cleaning up databases", it) },
onComplete = {
if (result.isNotEmpty())
OKDialog.show(activity, rh.gs(info.nightscout.core.ui.R.string.result), HtmlHelper.fromHtml("<b>" + rh.gs(R.string.cleared_entries) + "</b><br>" + result)
.toSpanned())
OKDialog.show(
activity,
rh.gs(info.nightscout.core.ui.R.string.result),
HtmlHelper.fromHtml("<b>" + rh.gs(info.nightscout.core.ui.R.string.cleared_entries) + "</b><br>" + result)
.toSpanned()
)
aapsLogger.info(LTag.CORE, "Cleaned up databases with result: $result")
}
)

View file

@ -122,8 +122,6 @@
<string name="maintenance_shortname">MAINT</string>
<string name="description_maintenance">Provides several functions for maintenance (eg. log sending, log deletion).</string>
<string name="database_cleanup">Database cleanup</string>
<string name="cleanup_db_confirm">Do you want to cleanup the database?\nIt will remove tracked changes and historic data older than 3 months.</string>
<string name="cleared_entries">Cleared entries</string>
<string name="reset_db_confirm">Do you really want to reset the databases?</string>
<string name="maintenance_settings">Maintenance Settings</string>
<string name="maintenance_email">Email recipient</string>

View file

@ -17,6 +17,7 @@ import info.nightscout.plugins.iob.iobCobCalculator.data.AutosensDataObject
FoodModule::class,
SMSCommunicatorModule::class,
ProfileModule::class,
ProfileModule.Bindings::class,
SkinsModule::class,
SkinsUiModule::class,
ActionsModule::class,

View file

@ -1,7 +1,9 @@
package info.nightscout.plugins.di
import dagger.Binds
import dagger.Module
import dagger.android.ContributesAndroidInjector
import info.nightscout.interfaces.profile.ProfileSource
import info.nightscout.plugins.profile.ProfileFragment
import info.nightscout.plugins.profile.ProfilePlugin
@ -9,6 +11,12 @@ import info.nightscout.plugins.profile.ProfilePlugin
@Suppress("unused")
abstract class ProfileModule {
@ContributesAndroidInjector abstract fun contributesNSProfileWorker(): ProfilePlugin.NSProfileWorker
@ContributesAndroidInjector abstract fun contributesLocalProfileFragment(): ProfileFragment
@Module
interface Bindings {
@Binds fun bindProfileSource(profilePlugin: ProfilePlugin): ProfileSource
}
}

View file

@ -1,9 +1,6 @@
package info.nightscout.plugins.profile
import android.content.Context
import androidx.fragment.app.FragmentActivity
import androidx.work.WorkerParameters
import androidx.work.workDataOf
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.annotations.OpenForTesting
import info.nightscout.core.extensions.blockFromJsonArray
@ -11,8 +8,6 @@ import info.nightscout.core.extensions.pureProfileFromJson
import info.nightscout.core.profile.ProfileSealed
import info.nightscout.core.ui.dialogs.OKDialog
import info.nightscout.core.ui.toast.ToastUtils
import info.nightscout.core.utils.receivers.DataWorkerStorage
import info.nightscout.core.utils.worker.LoggingWorker
import info.nightscout.interfaces.Config
import info.nightscout.interfaces.Constants
import info.nightscout.interfaces.GlucoseUnit
@ -39,7 +34,6 @@ import info.nightscout.rx.logging.LTag
import info.nightscout.shared.interfaces.ResourceHelper
import info.nightscout.shared.sharedPreferences.SP
import info.nightscout.shared.utils.DateUtil
import kotlinx.coroutines.Dispatchers
import org.json.JSONArray
import org.json.JSONException
import org.json.JSONObject
@ -234,7 +228,7 @@ class ProfilePlugin @Inject constructor(
}
@Synchronized
fun loadFromStore(store: ProfileStore) {
override fun loadFromStore(store: ProfileStore) {
try {
val newProfiles: ArrayList<ProfileSource.SingleProfile> = ArrayList()
for (p in store.getProfileList()) {
@ -428,39 +422,4 @@ class ProfilePlugin @Inject constructor(
get() = rawProfile?.getDefaultProfile()?.let {
DecimalFormatter.to2Decimal(ProfileSealed.Pure(it).percentageBasalSum()) + "U "
} ?: "INVALID"
// cannot be inner class because of needed injection
class NSProfileWorker(
context: Context,
params: WorkerParameters
) : LoggingWorker(context, params, Dispatchers.Default) {
@Inject lateinit var injector: HasAndroidInjector
@Inject lateinit var rxBus: RxBus
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var dataWorkerStorage: DataWorkerStorage
@Inject lateinit var sp: SP
@Inject lateinit var config: Config
@Inject lateinit var profilePlugin: ProfilePlugin
@Inject lateinit var instantiator: Instantiator
override suspend fun doWorkAndLog(): Result {
val profileJson = dataWorkerStorage.pickupJSONObject(inputData.getLong(DataWorkerStorage.STORE_KEY, -1))
?: return Result.failure(workDataOf("Error" to "missing input data"))
if (sp.getBoolean(info.nightscout.core.utils.R.string.key_ns_receive_profile_store, true) || config.NSCLIENT) {
val store = instantiator.provideProfileStore(profileJson)
val createdAt = store.getStartDate()
val lastLocalChange = sp.getLong(info.nightscout.core.utils.R.string.key_local_profile_last_change, 0)
aapsLogger.debug(LTag.PROFILE, "Received profileStore: createdAt: $createdAt Local last modification: $lastLocalChange")
@Suppress("LiftReturnOrAssignment")
if (createdAt > lastLocalChange || createdAt % 1000 == 0L) {// whole second means edited in NS
profilePlugin.loadFromStore(store)
aapsLogger.debug(LTag.PROFILE, "Received profileStore: $profileJson")
return Result.success(workDataOf("Data" to profileJson.toString().substring(0..Integer.min(5000, profileJson.length()))))
} else
return Result.success(workDataOf("Result" to "Unchanged. Ignoring"))
}
return Result.success(workDataOf("Result" to "Sync not enabled"))
}
}
}

View file

@ -35,6 +35,7 @@ import info.nightscout.rx.events.EventNewBG
import info.nightscout.rx.logging.AAPSLogger
import info.nightscout.rx.logging.LTag
import info.nightscout.shared.extensions.toVisibility
import info.nightscout.shared.extensions.toVisibilityKeepSpace
import info.nightscout.shared.interfaces.ResourceHelper
import info.nightscout.shared.utils.DateUtil
import info.nightscout.shared.utils.T
@ -135,7 +136,7 @@ class BGSourceFragment : DaggerFragment(), MenuProvider {
override fun onBindViewHolder(holder: GlucoseValuesViewHolder, position: Int) {
val glucoseValue = glucoseValues[position]
holder.binding.ns.visibility = (glucoseValue.interfaceIDs.nightscoutId != null).toVisibility()
holder.binding.ns.visibility = (glucoseValue.interfaceIDs.nightscoutId != null).toVisibilityKeepSpace()
holder.binding.invalid.visibility = (!glucoseValue.isValid).toVisibility()
val newDay = position == 0 || !dateUtil.isSameDay(glucoseValue.timestamp, glucoseValues[position - 1].timestamp)
holder.binding.date.visibility = newDay.toVisibility()

View file

@ -1,38 +1,16 @@
package info.nightscout.source
import android.content.Context
import androidx.work.WorkerParameters
import androidx.work.workDataOf
import dagger.android.HasAndroidInjector
import info.nightscout.core.utils.receivers.DataWorkerStorage
import info.nightscout.core.utils.worker.LoggingWorker
import info.nightscout.database.entities.GlucoseValue
import info.nightscout.database.impl.AppRepository
import info.nightscout.database.transactions.TransactionGlucoseValue
import info.nightscout.interfaces.Config
import info.nightscout.interfaces.notifications.Notification
import info.nightscout.interfaces.nsclient.NSSgv
import info.nightscout.interfaces.nsclient.StoreDataForDb
import info.nightscout.interfaces.plugin.ActivePlugin
import info.nightscout.interfaces.plugin.PluginBase
import info.nightscout.interfaces.plugin.PluginDescription
import info.nightscout.interfaces.plugin.PluginType
import info.nightscout.interfaces.source.BgSource
import info.nightscout.interfaces.source.DoingOwnUploadSource
import info.nightscout.interfaces.source.NSClientSource
import info.nightscout.rx.bus.RxBus
import info.nightscout.rx.events.EventDismissNotification
import info.nightscout.rx.logging.AAPSLogger
import info.nightscout.rx.logging.LTag
import info.nightscout.sdk.localmodel.entry.NSSgvV3
import info.nightscout.shared.interfaces.ResourceHelper
import info.nightscout.shared.sharedPreferences.SP
import info.nightscout.shared.utils.DateUtil
import info.nightscout.shared.utils.T
import kotlinx.coroutines.Dispatchers
import org.json.JSONArray
import org.json.JSONObject
import java.security.InvalidParameterException
import javax.inject.Inject
import javax.inject.Singleton
@ -49,21 +27,15 @@ class NSClientSourcePlugin @Inject constructor(
.pluginIcon(info.nightscout.core.main.R.drawable.ic_nsclient_bg)
.pluginName(R.string.ns_client_bg)
.shortName(R.string.ns_client_bg_short)
.description(R.string.description_source_ns_client),
.description(R.string.description_source_ns_client)
.alwaysEnabled(config.NSCLIENT)
.setDefault(config.NSCLIENT),
aapsLogger, rh, injector
), BgSource, NSClientSource, DoingOwnUploadSource {
private var lastBGTimeStamp: Long = 0
private var isAdvancedFilteringEnabled = false
init {
if (config.NSCLIENT) {
pluginDescription
.alwaysEnabled(true)
.setDefault()
}
}
override fun advancedFilteringSupported(): Boolean = isAdvancedFilteringEnabled
override fun shouldUploadToNs(glucoseValue: GlucoseValue): Boolean = false
@ -80,93 +52,4 @@ class NSClientSourcePlugin @Inject constructor(
lastBGTimeStamp = glucoseValue.timestamp
}
}
// cannot be inner class because of needed injection
class NSClientSourceWorker(
context: Context,
params: WorkerParameters
) : LoggingWorker(context, params, Dispatchers.IO) {
@Inject lateinit var nsClientSourcePlugin: NSClientSourcePlugin
@Inject lateinit var injector: HasAndroidInjector
@Inject lateinit var sp: SP
@Inject lateinit var rxBus: RxBus
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var dataWorkerStorage: DataWorkerStorage
@Inject lateinit var repository: AppRepository
@Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var storeDataForDb: StoreDataForDb
private fun toGv(jsonObject: JSONObject): TransactionGlucoseValue? {
val sgv = NSSgv(jsonObject)
return TransactionGlucoseValue(
timestamp = sgv.mills ?: return null,
value = sgv.mgdl?.toDouble() ?: return null,
noise = null,
raw = sgv.filtered?.toDouble(),
trendArrow = GlucoseValue.TrendArrow.fromString(sgv.direction),
nightscoutId = sgv.id,
sourceSensor = GlucoseValue.SourceSensor.fromString(sgv.device)
)
}
private fun toGv(sgv: NSSgvV3): TransactionGlucoseValue {
return TransactionGlucoseValue(
timestamp = sgv.date ?: throw InvalidParameterException(),
value = sgv.sgv,
noise = sgv.noise,
raw = sgv.filtered,
trendArrow = GlucoseValue.TrendArrow.fromString(sgv.direction?.nsName),
nightscoutId = sgv.identifier,
sourceSensor = GlucoseValue.SourceSensor.fromString(sgv.device),
isValid = sgv.isValid,
utcOffset = T.mins(sgv.utcOffset ?: 0L).msecs()
)
}
@Suppress("SpellCheckingInspection")
override suspend fun doWorkAndLog(): Result {
var ret = Result.success()
val sgvs = dataWorkerStorage.pickupObject(inputData.getLong(DataWorkerStorage.STORE_KEY, -1))
?: return Result.failure(workDataOf("Error" to "missing input data"))
if (!nsClientSourcePlugin.isEnabled() && !sp.getBoolean(info.nightscout.core.utils.R.string.key_ns_receive_cgm, false))
return Result.success(workDataOf("Result" to "Sync not enabled"))
var latestDateInReceivedData: Long = 0
aapsLogger.debug(LTag.BGSOURCE, "Received NS Data: $sgvs")
val glucoseValues = mutableListOf<TransactionGlucoseValue>()
try {
if (sgvs is JSONArray) { // V1 client
for (i in 0 until sgvs.length()) {
val sgv = toGv(sgvs.getJSONObject(i)) ?: continue
if (sgv.timestamp < dateUtil.now() && sgv.timestamp > latestDateInReceivedData) latestDateInReceivedData = sgv.timestamp
glucoseValues += sgv
}
} else if (sgvs is List<*>) { // V3 client
for (i in 0 until sgvs.size) {
val sgv = toGv(sgvs[i] as NSSgvV3)
if (sgv.timestamp < dateUtil.now() && sgv.timestamp > latestDateInReceivedData) latestDateInReceivedData = sgv.timestamp
glucoseValues += sgv
}
}
activePlugin.activeNsClient?.updateLatestBgReceivedIfNewer(latestDateInReceivedData)
// Was that sgv more less 5 mins ago ?
if (T.msecs(dateUtil.now() - latestDateInReceivedData).mins() < 5L) {
rxBus.send(EventDismissNotification(Notification.NS_ALARM))
rxBus.send(EventDismissNotification(Notification.NS_URGENT_ALARM))
}
storeDataForDb.glucoseValues.addAll(glucoseValues)
} catch (e: Exception) {
aapsLogger.error("Unhandled exception", e)
ret = Result.failure(workDataOf("Error" to e.toString()))
}
return ret
}
}
}

View file

@ -29,7 +29,6 @@ abstract class SourceModule {
@ContributesAndroidInjector abstract fun contributesBGSourceFragment(): BGSourceFragment
@ContributesAndroidInjector abstract fun contributesNSClientSourceWorker(): NSClientSourcePlugin.NSClientSourceWorker
@ContributesAndroidInjector abstract fun contributesXdripWorker(): XdripSourcePlugin.XdripSourceWorker
@ContributesAndroidInjector abstract fun contributesDexcomWorker(): DexcomPlugin.DexcomWorker
@ContributesAndroidInjector abstract fun contributesMM640gWorker(): MM640gPlugin.MM640gWorker

View file

@ -11,10 +11,12 @@ import info.nightscout.interfaces.XDripBroadcast
import info.nightscout.interfaces.nsclient.NSSettingsStatus
import info.nightscout.interfaces.nsclient.ProcessedDeviceStatusData
import info.nightscout.interfaces.nsclient.StoreDataForDb
import info.nightscout.interfaces.sync.DataSyncSelector
import info.nightscout.plugins.sync.nsShared.DataSyncSelectorImplementation
import info.nightscout.interfaces.sync.DataSyncSelectorV1
import info.nightscout.interfaces.sync.DataSyncSelectorV3
import info.nightscout.interfaces.sync.DataSyncSelectorXdrip
import info.nightscout.plugins.sync.nsShared.NSClientFragment
import info.nightscout.plugins.sync.nsShared.StoreDataForDbImpl
import info.nightscout.plugins.sync.nsclient.DataSyncSelectorV1Impl
import info.nightscout.plugins.sync.nsclient.data.NSSettingsStatusImpl
import info.nightscout.plugins.sync.nsclient.data.ProcessedDeviceStatusDataImpl
import info.nightscout.plugins.sync.nsclient.services.NSClientService
@ -22,17 +24,10 @@ import info.nightscout.plugins.sync.nsclient.workers.NSClientAddAckWorker
import info.nightscout.plugins.sync.nsclient.workers.NSClientAddUpdateWorker
import info.nightscout.plugins.sync.nsclient.workers.NSClientMbgWorker
import info.nightscout.plugins.sync.nsclient.workers.NSClientUpdateRemoveAckWorker
import info.nightscout.plugins.sync.nsclientV3.workers.DataSyncWorker
import info.nightscout.plugins.sync.nsclientV3.workers.LoadBgWorker
import info.nightscout.plugins.sync.nsclientV3.workers.LoadDeviceStatusWorker
import info.nightscout.plugins.sync.nsclientV3.workers.LoadFoodsWorker
import info.nightscout.plugins.sync.nsclientV3.workers.LoadLastModificationWorker
import info.nightscout.plugins.sync.nsclientV3.workers.LoadProfileStoreWorker
import info.nightscout.plugins.sync.nsclientV3.workers.LoadStatusWorker
import info.nightscout.plugins.sync.nsclientV3.workers.LoadTreatmentsWorker
import info.nightscout.plugins.sync.nsclientV3.workers.ProcessFoodWorker
import info.nightscout.plugins.sync.nsclientV3.workers.ProcessTreatmentsWorker
import info.nightscout.plugins.sync.nsclientV3.DataSyncSelectorV3Impl
import info.nightscout.plugins.sync.nsclientV3.workers.*
import info.nightscout.plugins.sync.tidepool.TidepoolFragment
import info.nightscout.plugins.sync.xdrip.DataSyncSelectorXdripImpl
import info.nightscout.plugins.sync.xdrip.XdripFragment
import info.nightscout.plugins.sync.xdrip.XdripPlugin
import info.nightscout.plugins.sync.xdrip.workers.XdripDataSyncWorker
@ -60,14 +55,9 @@ abstract class SyncModule {
@ContributesAndroidInjector abstract fun contributesLoadBgWorker(): LoadBgWorker
@ContributesAndroidInjector abstract fun contributesLoadFoodsWorker(): LoadFoodsWorker
@ContributesAndroidInjector abstract fun contributesLoadProfileStoreWorker(): LoadProfileStoreWorker
@ContributesAndroidInjector abstract fun contributesStoreBgWorker(): StoreDataForDbImpl.StoreBgWorker
@ContributesAndroidInjector abstract fun contributesStoreFoodWorker(): StoreDataForDbImpl.StoreFoodWorker
@ContributesAndroidInjector abstract fun contributesStoreTreatmentsWorker(): StoreDataForDbImpl.StoreTreatmentsWorker
@ContributesAndroidInjector abstract fun contributesTreatmentWorker(): LoadTreatmentsWorker
@ContributesAndroidInjector abstract fun contributesProcessTreatmentsWorker(): ProcessTreatmentsWorker
@ContributesAndroidInjector abstract fun contributesLoadDeviceStatusWorker(): LoadDeviceStatusWorker
@ContributesAndroidInjector abstract fun contributesDataSyncWorker(): DataSyncWorker
@ContributesAndroidInjector abstract fun contributesFoodWorker(): ProcessFoodWorker
@ContributesAndroidInjector abstract fun contributesTidepoolFragment(): TidepoolFragment
@ContributesAndroidInjector abstract fun contributesXdripFragment(): XdripFragment
@ -85,7 +75,9 @@ abstract class SyncModule {
@Binds fun bindProcessedDeviceStatusData(processedDeviceStatusDataImpl: ProcessedDeviceStatusDataImpl): ProcessedDeviceStatusData
@Binds fun bindNSSettingsStatus(nsSettingsStatusImpl: NSSettingsStatusImpl): NSSettingsStatus
@Binds fun bindDataSyncSelectorInterface(dataSyncSelectorImplementation: DataSyncSelectorImplementation): DataSyncSelector
@Binds fun bindDataSyncSelectorV1Interface(dataSyncSelectorV1Impl: DataSyncSelectorV1Impl): DataSyncSelectorV1
@Binds fun bindDataSyncSelectorV3Interface(dataSyncSelectorV3Impl: DataSyncSelectorV3Impl): DataSyncSelectorV3
@Binds fun bindDataSyncSelectorXdripInterface(dataSyncSelectorXdripImpl: DataSyncSelectorXdripImpl): DataSyncSelectorXdrip
@Binds fun bindStoreDataForDb(storeDataForDbImpl: StoreDataForDbImpl): StoreDataForDb
@Binds fun bindXDripBroadcastInterface(xDripBroadcastImpl: XdripPlugin): XDripBroadcast
}

View file

@ -1,36 +1,51 @@
package info.nightscout.plugins.sync.nsShared
import android.content.Context
import android.os.Bundle
import android.os.Handler
import android.os.HandlerThread
import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.widget.ScrollView
import androidx.core.text.toSpanned
import androidx.core.view.MenuProvider
import androidx.lifecycle.Lifecycle
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import dagger.android.support.DaggerFragment
import info.nightscout.core.ui.dialogs.OKDialog
import info.nightscout.core.utils.fabric.FabricPrivacy
import info.nightscout.database.entities.UserEntry
import info.nightscout.interfaces.Config
import info.nightscout.interfaces.Constants
import info.nightscout.interfaces.db.PersistenceLayer
import info.nightscout.interfaces.logging.UserEntryLogger
import info.nightscout.interfaces.plugin.ActivePlugin
import info.nightscout.interfaces.plugin.PluginBase
import info.nightscout.interfaces.plugin.PluginFragment
import info.nightscout.interfaces.sync.DataSyncSelector
import info.nightscout.interfaces.utils.HtmlHelper
import info.nightscout.plugins.sync.R
import info.nightscout.plugins.sync.databinding.NsClientFragmentBinding
import info.nightscout.plugins.sync.nsShared.events.EventNSClientUpdateGUI
import info.nightscout.plugins.sync.databinding.NsClientLogItemBinding
import info.nightscout.plugins.sync.nsShared.events.EventNSClientUpdateGuiInsert
import info.nightscout.plugins.sync.nsShared.events.EventNSClientUpdateGuiQueue
import info.nightscout.plugins.sync.nsShared.events.EventNSClientUpdateGuiStatus
import info.nightscout.rx.AapsSchedulers
import info.nightscout.rx.bus.RxBus
import info.nightscout.rx.events.EventNSClientNewLog
import info.nightscout.rx.events.EventNSClientRestart
import info.nightscout.rx.logging.AAPSLogger
import info.nightscout.rx.logging.LTag
import info.nightscout.shared.interfaces.ResourceHelper
import info.nightscout.shared.sharedPreferences.SP
import io.reactivex.rxjava3.core.Completable
import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign
import io.reactivex.rxjava3.kotlin.subscribeBy
import java.util.concurrent.TimeUnit
import javax.inject.Inject
class NSClientFragment : DaggerFragment(), MenuProvider, PluginFragment {
@ -40,11 +55,11 @@ class NSClientFragment : DaggerFragment(), MenuProvider, PluginFragment {
@Inject lateinit var rxBus: RxBus
@Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var aapsSchedulers: AapsSchedulers
@Inject lateinit var dataSyncSelector: DataSyncSelector
@Inject lateinit var uel: UserEntryLogger
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var config: Config
@Inject lateinit var persistenceLayer: PersistenceLayer
companion object {
@ -61,11 +76,19 @@ class NSClientFragment : DaggerFragment(), MenuProvider, PluginFragment {
private val disposable = CompositeDisposable()
private var _binding: NsClientFragmentBinding? = null
private lateinit var logAdapter: RecyclerViewAdapter
private var handler = Handler(HandlerThread(this::class.simpleName + "Handler").also { it.start() }.looper)
// This property is only valid between onCreateView and
// onDestroyView.
// This property is only valid between onCreateView and onDestroyView.
private val binding get() = _binding!!
// https://stackoverflow.com/questions/31759171/recyclerview-and-java-lang-indexoutofboundsexception-inconsistency-detected-in
class FixedLinearLayoutManager(context: Context?, @RecyclerView.Orientation orientation: Int = RecyclerView.VERTICAL, reverseLayout: Boolean = false) :
LinearLayoutManager(context, orientation, reverseLayout) {
override fun supportsPredictiveItemAnimations(): Boolean = false
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
NsClientFragmentBinding.inflate(inflater, container, false).also {
_binding = it
@ -78,15 +101,17 @@ class NSClientFragment : DaggerFragment(), MenuProvider, PluginFragment {
binding.autoscroll.isChecked = sp.getBoolean(R.string.key_ns_client_autoscroll, true)
binding.autoscroll.setOnCheckedChangeListener { _, isChecked ->
sp.putBoolean(R.string.key_ns_client_autoscroll, isChecked)
updateGui()
}
binding.paused.isChecked = sp.getBoolean(R.string.key_ns_paused, false)
binding.paused.setOnCheckedChangeListener { _, isChecked ->
uel.log(if (isChecked) UserEntry.Action.NS_PAUSED else UserEntry.Action.NS_RESUME, UserEntry.Sources.NSClient)
nsClientPlugin?.pause(isChecked)
updateGui()
}
logAdapter = RecyclerViewAdapter(nsClientPlugin?.listLog ?: emptyList())
binding.recyclerview.layoutManager = FixedLinearLayoutManager(context)
binding.recyclerview.adapter = logAdapter
}
override fun onCreateMenu(menu: Menu, inflater: MenuInflater) {
@ -100,7 +125,12 @@ class NSClientFragment : DaggerFragment(), MenuProvider, PluginFragment {
override fun onMenuItemSelected(item: MenuItem): Boolean =
when (item.itemId) {
ID_MENU_CLEAR_LOG -> {
nsClientPlugin?.clearLog()
nsClientPlugin?.listLog?.let {
synchronized(it) {
logAdapter.notifyItemRangeRemoved(0, it.size)
it.clear()
}
}
true
}
@ -115,10 +145,40 @@ class NSClientFragment : DaggerFragment(), MenuProvider, PluginFragment {
}
ID_MENU_FULL_SYNC -> {
var result = ""
context?.let { context ->
OKDialog.showConfirmation(
context, rh.gs(R.string.ns_client), rh.gs(R.string.full_sync_comment),
Runnable { nsClientPlugin?.resetToFullSync() }
Runnable {
OKDialog.showConfirmation(requireContext(), rh.gs(R.string.ns_client), rh.gs(info.nightscout.core.ui.R.string.cleanup_db_confirm_sync), Runnable {
disposable += Completable.fromAction { result = persistenceLayer.cleanupDatabase(93, deleteTrackedChanges = true) }
.subscribeOn(aapsSchedulers.io)
.observeOn(aapsSchedulers.main)
.subscribeBy(
onError = { aapsLogger.error("Error cleaning up databases", it) },
onComplete = {
if (result.isNotEmpty())
OKDialog.show(
requireContext(),
rh.gs(info.nightscout.core.ui.R.string.result),
HtmlHelper.fromHtml("<b>" + rh.gs(info.nightscout.core.ui.R.string.cleared_entries) + "</b><br>" + result)
.toSpanned()
)
aapsLogger.info(LTag.CORE, "Cleaned up databases with result: $result")
handler.post {
nsClientPlugin?.resetToFullSync()
nsClientPlugin?.resend("FULL_SYNC")
}
}
)
uel.log(UserEntry.Action.CLEANUP_DATABASES, UserEntry.Sources.NSClient)
}, Runnable {
handler.post {
nsClientPlugin?.resetToFullSync()
nsClientPlugin?.resend("FULL_SYNC")
}
})
}
)
}
true
@ -127,28 +187,78 @@ class NSClientFragment : DaggerFragment(), MenuProvider, PluginFragment {
else -> false
}
@Synchronized override fun onResume() {
super.onResume()
disposable += rxBus
.toObservable(EventNSClientUpdateGUI::class.java)
.observeOn(aapsSchedulers.main)
.subscribe({ updateGui() }, fabricPrivacy::logException)
updateGui()
@Synchronized
override fun onDestroyView() {
super.onDestroyView()
binding.recyclerview.adapter = null // avoid leaks
_binding = null
}
@Synchronized override fun onPause() {
@Synchronized
override fun onResume() {
super.onResume()
disposable += rxBus
.toObservable(EventNSClientUpdateGuiInsert::class.java)
.observeOn(aapsSchedulers.main)
.subscribe(
{
logAdapter.notifyItemInserted(it.position)
if (sp.getBoolean(R.string.key_ns_client_autoscroll, true))
_binding?.recyclerview?.scrollToPosition(it.position)
nsClientPlugin?.listLog?.let { listLog ->
synchronized(listLog) {
if (listLog.size > Constants.MAX_LOG_LINES)
logAdapter.notifyItemRemoved(listLog.size - 1)
}
}
}, fabricPrivacy::logException
)
disposable += rxBus
.toObservable(EventNSClientUpdateGuiQueue::class.java)
.observeOn(aapsSchedulers.main)
.subscribe({ updateQueue() }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventNSClientUpdateGuiStatus::class.java)
.debounce(3L, TimeUnit.SECONDS)
.observeOn(aapsSchedulers.main)
.subscribe({ updateStatus() }, fabricPrivacy::logException)
updateStatus()
updateQueue()
}
@Synchronized
override fun onPause() {
super.onPause()
disposable.clear()
}
private fun updateGui() {
private fun updateQueue() {
val size = nsClientPlugin?.dataSyncSelector?.queueSize() ?: 0L
_binding?.queue?.text = if (size >= 0) size.toString() else rh.gs(info.nightscout.core.ui.R.string.value_unavailable_short)
}
private fun updateStatus() {
if (_binding == null) return
binding.paused.isChecked = sp.getBoolean(R.string.key_ns_paused, false)
binding.log.text = nsClientPlugin?.textLog()
if (sp.getBoolean(R.string.key_ns_client_autoscroll, true)) binding.logScrollview.fullScroll(ScrollView.FOCUS_DOWN)
//if (sp.getBoolean(R.string.key_ns_client_autoscroll, true)) binding.logScrollview.fullScroll(ScrollView.FOCUS_DOWN)
binding.url.text = nsClientPlugin?.address
binding.status.text = nsClientPlugin?.status
val size = dataSyncSelector.queueSize()
binding.queue.text = if (size >= 0) size.toString() else rh.gs(info.nightscout.core.ui.R.string.value_unavailable_short)
}
private inner class RecyclerViewAdapter(private var logList: List<EventNSClientNewLog>) : RecyclerView.Adapter<RecyclerViewAdapter.NsClientLogViewHolder>() {
override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): NsClientLogViewHolder =
NsClientLogViewHolder(LayoutInflater.from(viewGroup.context).inflate(R.layout.ns_client_log_item, viewGroup, false))
override fun onBindViewHolder(holder: NsClientLogViewHolder, position: Int) {
holder.binding.logText.text = HtmlHelper.fromHtml(logList[position].toPreparedHtml().toString())
}
override fun getItemCount() = logList.size
inner class NsClientLogViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val binding = NsClientLogItemBinding.bind(view)
}
}
}

View file

@ -0,0 +1,232 @@
package info.nightscout.plugins.sync.nsShared
import info.nightscout.androidaps.annotations.OpenForTesting
import info.nightscout.core.extensions.foodFromJson
import info.nightscout.database.entities.Food
import info.nightscout.database.entities.GlucoseValue
import info.nightscout.database.transactions.TransactionGlucoseValue
import info.nightscout.interfaces.Config
import info.nightscout.interfaces.Constants
import info.nightscout.interfaces.notifications.Notification
import info.nightscout.interfaces.nsclient.NSSgv
import info.nightscout.interfaces.nsclient.StoreDataForDb
import info.nightscout.interfaces.plugin.ActivePlugin
import info.nightscout.interfaces.profile.Instantiator
import info.nightscout.interfaces.profile.ProfileSource
import info.nightscout.interfaces.source.NSClientSource
import info.nightscout.interfaces.utils.JsonHelper
import info.nightscout.plugins.sync.R
import info.nightscout.plugins.sync.nsclientV3.extensions.*
import info.nightscout.rx.bus.RxBus
import info.nightscout.rx.events.EventDismissNotification
import info.nightscout.rx.events.EventNSClientNewLog
import info.nightscout.rx.logging.AAPSLogger
import info.nightscout.rx.logging.LTag
import info.nightscout.sdk.localmodel.entry.NSSgvV3
import info.nightscout.sdk.localmodel.food.NSFood
import info.nightscout.sdk.localmodel.treatment.*
import info.nightscout.shared.sharedPreferences.SP
import info.nightscout.shared.utils.DateUtil
import info.nightscout.shared.utils.T
import org.json.JSONArray
import org.json.JSONObject
import javax.inject.Inject
import javax.inject.Singleton
@OpenForTesting
@Singleton
class NsIncomingDataProcessor @Inject constructor(
private val aapsLogger: AAPSLogger,
private val nsClientSource: NSClientSource,
private val sp: SP,
private val rxBus: RxBus,
private val dateUtil: DateUtil,
private val activePlugin: ActivePlugin,
private val storeDataForDb: StoreDataForDb,
private val config: Config,
private val instantiator: Instantiator,
private val profileSource: ProfileSource
) {
private fun toGv(jsonObject: JSONObject): TransactionGlucoseValue? {
val sgv = NSSgv(jsonObject)
return TransactionGlucoseValue(
timestamp = sgv.mills ?: return null,
value = sgv.mgdl?.toDouble() ?: return null,
noise = null,
raw = sgv.filtered?.toDouble(),
trendArrow = GlucoseValue.TrendArrow.fromString(sgv.direction),
nightscoutId = sgv.id,
sourceSensor = GlucoseValue.SourceSensor.fromString(sgv.device)
)
}
@Suppress("SpellCheckingInspection")
fun processSgvs(sgvs: Any) {
if (!nsClientSource.isEnabled() && !sp.getBoolean(info.nightscout.core.utils.R.string.key_ns_receive_cgm, false)) return
var latestDateInReceivedData: Long = 0
aapsLogger.debug(LTag.NSCLIENT, "Received NS Data: $sgvs")
val glucoseValues = mutableListOf<TransactionGlucoseValue>()
if (sgvs is JSONArray) { // V1 client
for (i in 0 until sgvs.length()) {
val sgv = toGv(sgvs.getJSONObject(i)) ?: continue
if (sgv.timestamp < dateUtil.now() && sgv.timestamp > latestDateInReceivedData) latestDateInReceivedData = sgv.timestamp
glucoseValues += sgv
}
} else if (sgvs is List<*>) { // V3 client
for (i in 0 until sgvs.size) {
val sgv = (sgvs[i] as NSSgvV3).toTransactionGlucoseValue()
if (sgv.timestamp < dateUtil.now() && sgv.timestamp > latestDateInReceivedData) latestDateInReceivedData = sgv.timestamp
glucoseValues += sgv
}
}
activePlugin.activeNsClient?.updateLatestBgReceivedIfNewer(latestDateInReceivedData)
// Was that sgv more less 5 mins ago ?
if (T.msecs(dateUtil.now() - latestDateInReceivedData).mins() < 5L) {
rxBus.send(EventDismissNotification(Notification.NS_ALARM))
rxBus.send(EventDismissNotification(Notification.NS_URGENT_ALARM))
}
storeDataForDb.glucoseValues.addAll(glucoseValues)
}
fun processTreatments(treatments: List<NSTreatment>) {
try {
var latestDateInReceivedData: Long = 0
for (treatment in treatments) {
aapsLogger.debug(LTag.DATABASE, "Received NS treatment: $treatment")
val date = treatment.date ?: continue
if (date > latestDateInReceivedData) latestDateInReceivedData = date
when (treatment) {
is NSBolus ->
if (sp.getBoolean(info.nightscout.core.utils.R.string.key_ns_receive_insulin, false) || config.NSCLIENT)
storeDataForDb.boluses.add(treatment.toBolus())
is NSCarbs ->
if (sp.getBoolean(info.nightscout.core.utils.R.string.key_ns_receive_carbs, false) || config.NSCLIENT)
storeDataForDb.carbs.add(treatment.toCarbs())
is NSTemporaryTarget ->
if (sp.getBoolean(info.nightscout.core.utils.R.string.key_ns_receive_temp_target, false) || config.NSCLIENT) {
if (treatment.duration > 0L) {
// not ending event
if (treatment.targetBottomAsMgdl() < Constants.MIN_TT_MGDL
|| treatment.targetBottomAsMgdl() > Constants.MAX_TT_MGDL
|| treatment.targetTopAsMgdl() < Constants.MIN_TT_MGDL
|| treatment.targetTopAsMgdl() > Constants.MAX_TT_MGDL
|| treatment.targetBottomAsMgdl() > treatment.targetTopAsMgdl()
) {
aapsLogger.debug(LTag.DATABASE, "Ignored TemporaryTarget $treatment")
continue
}
}
storeDataForDb.temporaryTargets.add(treatment.toTemporaryTarget())
}
is NSTemporaryBasal ->
if (config.isEngineeringMode() && sp.getBoolean(R.string.key_ns_receive_tbr_eb, false) || config.NSCLIENT)
storeDataForDb.temporaryBasals.add(treatment.toTemporaryBasal())
is NSEffectiveProfileSwitch ->
if (sp.getBoolean(info.nightscout.core.utils.R.string.key_ns_receive_profile_switch, false) || config.NSCLIENT) {
treatment.toEffectiveProfileSwitch(dateUtil)?.let { effectiveProfileSwitch ->
storeDataForDb.effectiveProfileSwitches.add(effectiveProfileSwitch)
}
}
is NSProfileSwitch ->
if (sp.getBoolean(info.nightscout.core.utils.R.string.key_ns_receive_profile_switch, false) || config.NSCLIENT) {
treatment.toProfileSwitch(activePlugin, dateUtil)?.let { profileSwitch ->
storeDataForDb.profileSwitches.add(profileSwitch)
}
}
is NSBolusWizard ->
treatment.toBolusCalculatorResult()?.let { bolusCalculatorResult ->
storeDataForDb.bolusCalculatorResults.add(bolusCalculatorResult)
}
is NSTherapyEvent ->
if (sp.getBoolean(info.nightscout.core.utils.R.string.key_ns_receive_therapy_events, false) || config.NSCLIENT)
treatment.toTherapyEvent().let { therapyEvent ->
storeDataForDb.therapyEvents.add(therapyEvent)
}
is NSOfflineEvent ->
if (sp.getBoolean(info.nightscout.core.utils.R.string.key_ns_receive_offline_event, false) && config.isEngineeringMode() || config.NSCLIENT)
treatment.toOfflineEvent().let { offlineEvent ->
storeDataForDb.offlineEvents.add(offlineEvent)
}
is NSExtendedBolus ->
if (config.isEngineeringMode() && sp.getBoolean(R.string.key_ns_receive_tbr_eb, false) || config.NSCLIENT)
treatment.toExtendedBolus().let { extendedBolus ->
storeDataForDb.extendedBoluses.add(extendedBolus)
}
}
}
activePlugin.activeNsClient?.updateLatestTreatmentReceivedIfNewer(latestDateInReceivedData)
} catch (error: Exception) {
aapsLogger.error("Error: ", error)
rxBus.send(EventNSClientNewLog("◄ ERROR", error.localizedMessage))
}
}
fun processFood(data: Any) {
aapsLogger.debug(LTag.DATABASE, "Received Food Data: $data")
try {
val foods = mutableListOf<Food>()
if (data is JSONArray) {
for (index in 0 until data.length()) {
val jsonFood: JSONObject = data.getJSONObject(index)
if (JsonHelper.safeGetString(jsonFood, "type") != "food") continue
when (JsonHelper.safeGetString(jsonFood, "action")) {
"remove" -> {
val delFood = Food(
name = "",
portion = 0.0,
carbs = 0,
isValid = false
).also { it.interfaceIDs.nightscoutId = JsonHelper.safeGetString(jsonFood, "_id") }
foods += delFood
}
else -> {
val food = foodFromJson(jsonFood)
if (food != null) foods += food
else aapsLogger.error(LTag.DATABASE, "Error parsing food", jsonFood.toString())
}
}
}
} else if (data is List<*>) {
for (i in 0 until data.size)
foods += (data[i] as NSFood).toFood()
}
storeDataForDb.foods.addAll(foods)
} catch (error: Exception) {
aapsLogger.error("Error: ", error)
rxBus.send(EventNSClientNewLog("◄ ERROR", error.localizedMessage))
}
}
fun processProfile(profileJson: JSONObject) {
if (sp.getBoolean(info.nightscout.core.utils.R.string.key_ns_receive_profile_store, true) || config.NSCLIENT) {
val store = instantiator.provideProfileStore(profileJson)
val createdAt = store.getStartDate()
val lastLocalChange = sp.getLong(info.nightscout.core.utils.R.string.key_local_profile_last_change, 0)
aapsLogger.debug(LTag.PROFILE, "Received profileStore: createdAt: $createdAt Local last modification: $lastLocalChange")
@Suppress("LiftReturnOrAssignment")
if (createdAt > lastLocalChange || createdAt % 1000 == 0L) {// whole second means edited in NS
profileSource.loadFromStore(store)
aapsLogger.debug(LTag.PROFILE, "Received profileStore: $profileJson")
}
}
}
}

View file

@ -1,9 +1,6 @@
package info.nightscout.plugins.sync.nsShared
import android.content.Context
import android.os.SystemClock
import androidx.work.WorkerParameters
import info.nightscout.core.utils.worker.LoggingWorker
import info.nightscout.database.entities.Bolus
import info.nightscout.database.entities.BolusCalculatorResult
import info.nightscout.database.entities.Carbs
@ -60,7 +57,6 @@ import info.nightscout.rx.logging.AAPSLogger
import info.nightscout.rx.logging.LTag
import info.nightscout.shared.sharedPreferences.SP
import info.nightscout.shared.utils.DateUtil
import kotlinx.coroutines.Dispatchers
import java.util.concurrent.Executors
import java.util.concurrent.ScheduledFuture
import java.util.concurrent.TimeUnit
@ -119,45 +115,6 @@ class StoreDataForDbImpl @Inject constructor(
private val pause = 1000L // to slow down db operations
class StoreBgWorker(
context: Context,
params: WorkerParameters
) : LoggingWorker(context, params, Dispatchers.Default) {
@Inject lateinit var storeDataForDb: StoreDataForDb
override suspend fun doWorkAndLog(): Result {
storeDataForDb.storeGlucoseValuesToDb()
return Result.success()
}
}
class StoreFoodWorker(
context: Context,
params: WorkerParameters
) : LoggingWorker(context, params, Dispatchers.Default) {
@Inject lateinit var storeDataForDb: StoreDataForDb
override suspend fun doWorkAndLog(): Result {
storeDataForDb.storeFoodsToDb()
return Result.success()
}
}
class StoreTreatmentsWorker(
context: Context,
params: WorkerParameters
) : LoggingWorker(context, params, Dispatchers.Default) {
@Inject lateinit var storeDataForDb: StoreDataForDb
override suspend fun doWorkAndLog(): Result {
storeDataForDb.storeTreatmentsToDb()
return Result.success()
}
}
fun <T> HashMap<T, Long>.inc(key: T) =
if (containsKey(key)) merge(key, 1, Long::plus)
else put(key, 1)
@ -803,6 +760,8 @@ class StoreDataForDbImpl @Inject constructor(
private val eventWorker = Executors.newSingleThreadScheduledExecutor()
private var scheduledEventPost: ScheduledFuture<*>? = null
@Synchronized
override fun scheduleNsIdUpdate() {
class PostRunnable : Runnable {
@ -818,7 +777,8 @@ class StoreDataForDbImpl @Inject constructor(
scheduledEventPost = eventWorker.schedule(task, 10, TimeUnit.SECONDS)
}
private fun updateNsIds() {
@Synchronized
override fun updateNsIds() {
repository.runTransactionForResult(UpdateNsIdTemporaryTargetTransaction(nsIdTemporaryTargets))
.doOnError { error ->
aapsLogger.error(LTag.DATABASE, "Updated nsId of TemporaryTarget failed", error)
@ -1003,27 +963,27 @@ class StoreDataForDbImpl @Inject constructor(
private fun sendLog(item: String, clazz: String) {
inserted[clazz]?.let {
rxBus.send(EventNSClientNewLog("INSERT", "$item $it"))
rxBus.send(EventNSClientNewLog("INSERT", "$item $it"))
}
inserted.remove(clazz)
updated[clazz]?.let {
rxBus.send(EventNSClientNewLog("UPDATE", "$item $it"))
rxBus.send(EventNSClientNewLog("UPDATE", "$item $it"))
}
updated.remove(clazz)
invalidated[clazz]?.let {
rxBus.send(EventNSClientNewLog("INVALIDATE", "$item $it"))
rxBus.send(EventNSClientNewLog("INVALIDATE", "$item $it"))
}
invalidated.remove(clazz)
nsIdUpdated[clazz]?.let {
rxBus.send(EventNSClientNewLog("NS_ID", "$item $it"))
rxBus.send(EventNSClientNewLog("NS_ID", "$item $it"))
}
nsIdUpdated.remove(clazz)
durationUpdated[clazz]?.let {
rxBus.send(EventNSClientNewLog("DURATION", "$item $it"))
rxBus.send(EventNSClientNewLog("DURATION", "$item $it"))
}
durationUpdated.remove(clazz)
ended[clazz]?.let {
rxBus.send(EventNSClientNewLog("CUT", "$item $it"))
rxBus.send(EventNSClientNewLog("CUT", "$item $it"))
}
ended.remove(clazz)
}

View file

@ -0,0 +1,5 @@
package info.nightscout.plugins.sync.nsShared.events
import info.nightscout.rx.events.EventUpdateGui
class EventNSClientUpdateGuiInsert(val position: Int) : EventUpdateGui()

View file

@ -2,4 +2,4 @@ package info.nightscout.plugins.sync.nsShared.events
import info.nightscout.rx.events.EventUpdateGui
class EventNSClientUpdateGUI : EventUpdateGui()
class EventNSClientUpdateGuiQueue : EventUpdateGui()

View file

@ -0,0 +1,5 @@
package info.nightscout.plugins.sync.nsShared.events
import info.nightscout.rx.events.EventUpdateGui
class EventNSClientUpdateGuiStatus : EventUpdateGui()

View file

@ -1,12 +1,15 @@
package info.nightscout.plugins.sync.nsShared
package info.nightscout.plugins.sync.nsclient
import info.nightscout.database.ValueWrapper
import info.nightscout.database.impl.AppRepository
import info.nightscout.interfaces.plugin.ActivePlugin
import info.nightscout.interfaces.profile.ProfileFunction
import info.nightscout.interfaces.sync.DataSyncSelector
import info.nightscout.interfaces.sync.DataSyncSelectorV1
import info.nightscout.interfaces.utils.JsonHelper
import info.nightscout.plugins.sync.R
import info.nightscout.plugins.sync.nsShared.events.EventNSClientUpdateGuiQueue
import info.nightscout.plugins.sync.nsShared.events.EventNSClientUpdateGuiStatus
import info.nightscout.rx.bus.RxBus
import info.nightscout.rx.logging.AAPSLogger
import info.nightscout.rx.logging.LTag
import info.nightscout.shared.sharedPreferences.SP
@ -15,14 +18,15 @@ import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class DataSyncSelectorImplementation @Inject constructor(
class DataSyncSelectorV1Impl @Inject constructor(
private val sp: SP,
private val aapsLogger: AAPSLogger,
private val dateUtil: DateUtil,
private val profileFunction: ProfileFunction,
private val activePlugin: ActivePlugin,
private val appRepository: AppRepository
) : DataSyncSelector {
private val appRepository: AppRepository,
private val rxBus: RxBus
) : DataSyncSelectorV1 {
class QueueCounter(
var bolusesRemaining: Long = -1L,
@ -61,8 +65,23 @@ class DataSyncSelectorImplementation @Inject constructor(
override fun queueSize(): Long = queueCounter.size()
override fun doUpload() {
override suspend fun doUpload() {
rxBus.send(EventNSClientUpdateGuiStatus())
if (sp.getBoolean(R.string.key_ns_upload, true) && !isPaused) {
queueCounter.bolusesRemaining = (appRepository.getLastBolusId() ?: 0L) - sp.getLong(R.string.key_ns_bolus_last_synced_id, 0)
queueCounter.carbsRemaining = (appRepository.getLastCarbsId() ?: 0L) - sp.getLong(R.string.key_ns_carbs_last_synced_id, 0)
queueCounter.bcrRemaining = (appRepository.getLastBolusCalculatorResultId() ?: 0L) - sp.getLong(R.string.key_ns_bolus_calculator_result_last_synced_id, 0)
queueCounter.ttsRemaining = (appRepository.getLastTempTargetId() ?: 0L) - sp.getLong(R.string.key_ns_temporary_target_last_synced_id, 0)
queueCounter.foodsRemaining = (appRepository.getLastFoodId() ?: 0L) - sp.getLong(R.string.key_ns_food_last_synced_id, 0)
queueCounter.gvsRemaining = (appRepository.getLastGlucoseValueId() ?: 0L) - sp.getLong(R.string.key_ns_glucose_value_last_synced_id, 0)
queueCounter.tesRemaining = (appRepository.getLastTherapyEventId() ?: 0L) - sp.getLong(R.string.key_ns_therapy_event_last_synced_id, 0)
queueCounter.dssRemaining = (appRepository.getLastDeviceStatusId() ?: 0L) - sp.getLong(R.string.key_ns_device_status_last_synced_id, 0)
queueCounter.tbrsRemaining = (appRepository.getLastTemporaryBasalId() ?: 0L) - sp.getLong(R.string.key_ns_temporary_basal_last_synced_id, 0)
queueCounter.ebsRemaining = (appRepository.getLastExtendedBolusId() ?: 0L) - sp.getLong(R.string.key_ns_extended_bolus_last_synced_id, 0)
queueCounter.pssRemaining = (appRepository.getLastProfileSwitchId() ?: 0L) - sp.getLong(R.string.key_ns_profile_switch_last_synced_id, 0)
queueCounter.epssRemaining = (appRepository.getLastEffectiveProfileSwitchId() ?: 0L) - sp.getLong(R.string.key_ns_effective_profile_switch_last_synced_id, 0)
queueCounter.oesRemaining = (appRepository.getLastOfflineEventId() ?: 0L) - sp.getLong(R.string.key_ns_offline_event_last_synced_id, 0)
rxBus.send(EventNSClientUpdateGuiQueue())
processChangedBoluses()
processChangedCarbs()
processChangedBolusCalculatorResults()
@ -78,6 +97,7 @@ class DataSyncSelectorImplementation @Inject constructor(
processChangedOfflineEvents()
processChangedProfileStore()
}
rxBus.send(EventNSClientUpdateGuiStatus())
}
override fun resetToNextFullSync() {
@ -95,8 +115,8 @@ class DataSyncSelectorImplementation @Inject constructor(
sp.remove(R.string.key_ns_offline_event_last_synced_id)
sp.remove(R.string.key_ns_profile_store_last_synced_timestamp)
val lastDeviceStatusDbIdWrapped = appRepository.getLastDeviceStatusIdWrapped().blockingGet()
if (lastDeviceStatusDbIdWrapped is ValueWrapper.Existing) sp.putLong(R.string.key_ns_device_status_last_synced_id, lastDeviceStatusDbIdWrapped.value)
val lastDeviceStatusDbId = appRepository.getLastDeviceStatusId()
if (lastDeviceStatusDbId != null) sp.putLong(R.string.key_ns_device_status_last_synced_id, lastDeviceStatusDbId)
else sp.remove(R.string.key_ns_device_status_last_synced_id)
}
@ -107,16 +127,16 @@ class DataSyncSelectorImplementation @Inject constructor(
}
}
override tailrec fun processChangedBoluses() {
override tailrec suspend fun processChangedBoluses() {
if (isPaused) return
val lastDbIdWrapped = appRepository.getLastBolusIdWrapped().blockingGet()
val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L
val lastDbId = appRepository.getLastBolusId() ?: 0L
var startId = sp.getLong(R.string.key_ns_bolus_last_synced_id, 0)
if (startId > lastDbId) {
sp.putLong(R.string.key_ns_bolus_last_synced_id, 0)
startId = 0
}
queueCounter.bolusesRemaining = lastDbId - startId
rxBus.send(EventNSClientUpdateGuiQueue())
appRepository.getNextSyncElementBolus(startId).blockingGet()?.let { bolus ->
aapsLogger.info(LTag.NSCLIENT, "Loading Bolus data Start: $startId ${bolus.first} forID: ${bolus.second.id} ")
when {
@ -136,18 +156,10 @@ class DataSyncSelectorImplementation @Inject constructor(
}
// without nsId = create new
bolus.first.interfaceIDs.nightscoutId == null ->
activePlugin.activeNsClient?.nsAdd(
"treatments",
DataSyncSelector.PairBolus(bolus.first, bolus.second.id),
" $startId/$lastDbId"
)
activePlugin.activeNsClient?.nsAdd("treatments", DataSyncSelector.PairBolus(bolus.first, bolus.second.id), " $startId/$lastDbId")
// with nsId = update if it's modified record
bolus.first.interfaceIDs.nightscoutId != null && bolus.first.id != bolus.second.id ->
activePlugin.activeNsClient?.nsUpdate(
"treatments",
DataSyncSelector.PairBolus(bolus.first, bolus.second.id),
"$startId/$lastDbId"
)
activePlugin.activeNsClient?.nsUpdate("treatments", DataSyncSelector.PairBolus(bolus.first, bolus.second.id), "$startId/$lastDbId")
}
return
}
@ -160,16 +172,16 @@ class DataSyncSelectorImplementation @Inject constructor(
}
}
override tailrec fun processChangedCarbs() {
override tailrec suspend fun processChangedCarbs() {
if (isPaused) return
val lastDbIdWrapped = appRepository.getLastCarbsIdWrapped().blockingGet()
val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L
val lastDbId = appRepository.getLastCarbsId() ?: 0L
var startId = sp.getLong(R.string.key_ns_carbs_last_synced_id, 0)
if (startId > lastDbId) {
sp.putLong(R.string.key_ns_carbs_last_synced_id, 0)
startId = 0
}
queueCounter.carbsRemaining = lastDbId - startId
rxBus.send(EventNSClientUpdateGuiQueue())
appRepository.getNextSyncElementCarbs(startId).blockingGet()?.let { carb ->
aapsLogger.info(LTag.NSCLIENT, "Loading Carbs data Start: $startId ${carb.first} forID: ${carb.second.id} ")
when {
@ -209,16 +221,16 @@ class DataSyncSelectorImplementation @Inject constructor(
}
}
override tailrec fun processChangedBolusCalculatorResults() {
override tailrec suspend fun processChangedBolusCalculatorResults() {
if (isPaused) return
val lastDbIdWrapped = appRepository.getLastBolusCalculatorResultIdWrapped().blockingGet()
val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L
val lastDbId = appRepository.getLastBolusCalculatorResultId() ?: 0L
var startId = sp.getLong(R.string.key_ns_bolus_calculator_result_last_synced_id, 0)
if (startId > lastDbId) {
sp.putLong(R.string.key_ns_bolus_calculator_result_last_synced_id, 0)
startId = 0
}
queueCounter.bcrRemaining = lastDbId - startId
rxBus.send(EventNSClientUpdateGuiQueue())
appRepository.getNextSyncElementBolusCalculatorResult(startId).blockingGet()?.let { bolusCalculatorResult ->
aapsLogger.info(LTag.NSCLIENT, "Loading BolusCalculatorResult data Start: $startId ${bolusCalculatorResult.first} forID: ${bolusCalculatorResult.second.id} ")
when {
@ -238,17 +250,10 @@ class DataSyncSelectorImplementation @Inject constructor(
}
// without nsId = create new
bolusCalculatorResult.first.interfaceIDs.nightscoutId == null ->
activePlugin.activeNsClient?.nsAdd(
"treatments",
DataSyncSelector.PairBolusCalculatorResult(bolusCalculatorResult.first, bolusCalculatorResult.second.id),
"$startId/$lastDbId"
)
activePlugin.activeNsClient?.nsAdd("treatments", DataSyncSelector.PairBolusCalculatorResult(bolusCalculatorResult.first, bolusCalculatorResult.second.id), "$startId/$lastDbId")
// with nsId = update if it's modified record
bolusCalculatorResult.first.interfaceIDs.nightscoutId != null && bolusCalculatorResult.first.id != bolusCalculatorResult.second.id ->
activePlugin.activeNsClient?.nsUpdate(
"treatments",
DataSyncSelector.PairBolusCalculatorResult(bolusCalculatorResult.first, bolusCalculatorResult.second.id), "$startId/$lastDbId"
)
activePlugin.activeNsClient?.nsUpdate("treatments", DataSyncSelector.PairBolusCalculatorResult(bolusCalculatorResult.first, bolusCalculatorResult.second.id), "$startId/$lastDbId")
}
return
}
@ -261,16 +266,16 @@ class DataSyncSelectorImplementation @Inject constructor(
}
}
override tailrec fun processChangedTempTargets() {
override tailrec suspend fun processChangedTempTargets() {
if (isPaused) return
val lastDbIdWrapped = appRepository.getLastTempTargetIdWrapped().blockingGet()
val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L
val lastDbId = appRepository.getLastTempTargetId() ?: 0L
var startId = sp.getLong(R.string.key_ns_temporary_target_last_synced_id, 0)
if (startId > lastDbId) {
sp.putLong(R.string.key_ns_temporary_target_last_synced_id, 0)
startId = 0
}
queueCounter.ttsRemaining = lastDbId - startId
rxBus.send(EventNSClientUpdateGuiQueue())
appRepository.getNextSyncElementTemporaryTarget(startId).blockingGet()?.let { tt ->
aapsLogger.info(LTag.NSCLIENT, "Loading TemporaryTarget data Start: $startId ${tt.first} forID: ${tt.second.id} ")
when {
@ -290,18 +295,10 @@ class DataSyncSelectorImplementation @Inject constructor(
}
// without nsId = create new
tt.first.interfaceIDs.nightscoutId == null ->
activePlugin.activeNsClient?.nsAdd(
"treatments",
DataSyncSelector.PairTemporaryTarget(tt.first, tt.second.id),
"$startId/$lastDbId"
)
activePlugin.activeNsClient?.nsAdd("treatments", DataSyncSelector.PairTemporaryTarget(tt.first, tt.second.id), "$startId/$lastDbId")
// existing with nsId = update
tt.first.interfaceIDs.nightscoutId != null ->
activePlugin.activeNsClient?.nsUpdate(
"treatments",
DataSyncSelector.PairTemporaryTarget(tt.first, tt.second.id),
"$startId/$lastDbId"
)
activePlugin.activeNsClient?.nsUpdate("treatments", DataSyncSelector.PairTemporaryTarget(tt.first, tt.second.id), "$startId/$lastDbId")
}
return
}
@ -314,16 +311,16 @@ class DataSyncSelectorImplementation @Inject constructor(
}
}
override tailrec fun processChangedFoods() {
override tailrec suspend fun processChangedFoods() {
if (isPaused) return
val lastDbIdWrapped = appRepository.getLastFoodIdWrapped().blockingGet()
val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L
val lastDbId = appRepository.getLastFoodId() ?: 0L
var startId = sp.getLong(R.string.key_ns_food_last_synced_id, 0)
if (startId > lastDbId) {
sp.putLong(R.string.key_ns_food_last_synced_id, 0)
startId = 0
}
queueCounter.foodsRemaining = lastDbId - startId
rxBus.send(EventNSClientUpdateGuiQueue())
appRepository.getNextSyncElementFood(startId).blockingGet()?.let { food ->
aapsLogger.info(LTag.NSCLIENT, "Loading Food data Start: $startId ${food.first} forID: ${food.second.id} ")
when {
@ -346,11 +343,7 @@ class DataSyncSelectorImplementation @Inject constructor(
activePlugin.activeNsClient?.nsAdd("food", DataSyncSelector.PairFood(food.first, food.second.id), "$startId/$lastDbId")
// with nsId = update
food.first.interfaceIDs.nightscoutId != null ->
activePlugin.activeNsClient?.nsUpdate(
"food",
DataSyncSelector.PairFood(food.first, food.second.id),
"$startId/$lastDbId"
)
activePlugin.activeNsClient?.nsUpdate("food", DataSyncSelector.PairFood(food.first, food.second.id), "$startId/$lastDbId")
}
return
}
@ -363,16 +356,16 @@ class DataSyncSelectorImplementation @Inject constructor(
}
}
override tailrec fun processChangedGlucoseValues() {
override tailrec suspend fun processChangedGlucoseValues() {
if (isPaused) return
val lastDbIdWrapped = appRepository.getLastGlucoseValueIdWrapped().blockingGet()
val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L
val lastDbId = appRepository.getLastGlucoseValueId() ?: 0L
var startId = sp.getLong(R.string.key_ns_glucose_value_last_synced_id, 0)
if (startId > lastDbId) {
sp.putLong(R.string.key_ns_glucose_value_last_synced_id, 0)
startId = 0
}
queueCounter.gvsRemaining = lastDbId - startId
rxBus.send(EventNSClientUpdateGuiQueue())
appRepository.getNextSyncElementGlucoseValue(startId).blockingGet()?.let { gv ->
aapsLogger.info(LTag.NSCLIENT, "Loading GlucoseValue data Start: $startId ${gv.first} forID: ${gv.second.id} ")
if (activePlugin.activeBgSource.shouldUploadToNs(gv.first)) {
@ -396,11 +389,7 @@ class DataSyncSelectorImplementation @Inject constructor(
activePlugin.activeNsClient?.nsAdd("entries", DataSyncSelector.PairGlucoseValue(gv.first, gv.second.id), "$startId/$lastDbId")
// with nsId = update
else -> // gv.first.interfaceIDs.nightscoutId != null
activePlugin.activeNsClient?.nsUpdate(
"entries",
DataSyncSelector.PairGlucoseValue(gv.first, gv.second.id),
"$startId/$lastDbId"
)
activePlugin.activeNsClient?.nsUpdate("entries", DataSyncSelector.PairGlucoseValue(gv.first, gv.second.id), "$startId/$lastDbId")
}
} else {
confirmLastGlucoseValueIdIfGreater(gv.second.id)
@ -417,16 +406,16 @@ class DataSyncSelectorImplementation @Inject constructor(
}
}
override tailrec fun processChangedTherapyEvents() {
override tailrec suspend fun processChangedTherapyEvents() {
if (isPaused) return
val lastDbIdWrapped = appRepository.getLastTherapyEventIdWrapped().blockingGet()
val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L
val lastDbId = appRepository.getLastTherapyEventId() ?: 0L
var startId = sp.getLong(R.string.key_ns_therapy_event_last_synced_id, 0)
if (startId > lastDbId) {
sp.putLong(R.string.key_ns_therapy_event_last_synced_id, 0)
startId = 0
}
queueCounter.tesRemaining = lastDbId - startId
rxBus.send(EventNSClientUpdateGuiQueue())
appRepository.getNextSyncElementTherapyEvent(startId).blockingGet()?.let { te ->
aapsLogger.info(LTag.NSCLIENT, "Loading TherapyEvents data Start: $startId ${te.first} forID: ${te.second.id} ")
when {
@ -449,11 +438,7 @@ class DataSyncSelectorImplementation @Inject constructor(
activePlugin.activeNsClient?.nsAdd("treatments", DataSyncSelector.PairTherapyEvent(te.first, te.second.id), "$startId/$lastDbId")
// nsId = update
te.first.interfaceIDs.nightscoutId != null ->
activePlugin.activeNsClient?.nsUpdate(
"treatments",
DataSyncSelector.PairTherapyEvent(te.first, te.second.id),
"$startId/$lastDbId"
)
activePlugin.activeNsClient?.nsUpdate("treatments", DataSyncSelector.PairTherapyEvent(te.first, te.second.id), "$startId/$lastDbId")
}
return
}
@ -466,16 +451,16 @@ class DataSyncSelectorImplementation @Inject constructor(
}
}
override fun processChangedDeviceStatuses() {
override suspend fun processChangedDeviceStatuses() {
if (isPaused) return
val lastDbIdWrapped = appRepository.getLastDeviceStatusIdWrapped().blockingGet()
val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L
val lastDbId = appRepository.getLastDeviceStatusId() ?: 0L
var startId = sp.getLong(R.string.key_ns_device_status_last_synced_id, 0)
if (startId > lastDbId) {
sp.putLong(R.string.key_ns_device_status_last_synced_id, 0)
startId = 0
}
queueCounter.dssRemaining = lastDbId - startId
rxBus.send(EventNSClientUpdateGuiQueue())
appRepository.getNextSyncElementDeviceStatus(startId).blockingGet()?.let { deviceStatus ->
aapsLogger.info(LTag.NSCLIENT, "Loading DeviceStatus data Start: $startId $deviceStatus")
when {
@ -496,16 +481,16 @@ class DataSyncSelectorImplementation @Inject constructor(
}
}
override tailrec fun processChangedTemporaryBasals() {
override tailrec suspend fun processChangedTemporaryBasals() {
if (isPaused) return
val lastDbIdWrapped = appRepository.getLastTemporaryBasalIdWrapped().blockingGet()
val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L
val lastDbId = appRepository.getLastTemporaryBasalId() ?: 0L
var startId = sp.getLong(R.string.key_ns_temporary_basal_last_synced_id, 0)
if (startId > lastDbId) {
sp.putLong(R.string.key_ns_temporary_basal_last_synced_id, 0)
startId = 0
}
queueCounter.tbrsRemaining = lastDbId - startId
rxBus.send(EventNSClientUpdateGuiQueue())
appRepository.getNextSyncElementTemporaryBasal(startId).blockingGet()?.let { tb ->
aapsLogger.info(LTag.NSCLIENT, "Loading TemporaryBasal data Start: $startId ${tb.first} forID: ${tb.second.id} ")
when {
@ -525,18 +510,10 @@ class DataSyncSelectorImplementation @Inject constructor(
}
// without nsId = create new
tb.first.interfaceIDs.nightscoutId == null ->
activePlugin.activeNsClient?.nsAdd(
"treatments",
DataSyncSelector.PairTemporaryBasal(tb.first, tb.second.id),
"$startId/$lastDbId"
)
activePlugin.activeNsClient?.nsAdd("treatments", DataSyncSelector.PairTemporaryBasal(tb.first, tb.second.id), "$startId/$lastDbId")
// with nsId = update
tb.first.interfaceIDs.nightscoutId != null ->
activePlugin.activeNsClient?.nsUpdate(
"treatments",
DataSyncSelector.PairTemporaryBasal(tb.first, tb.second.id),
"$startId/$lastDbId"
)
activePlugin.activeNsClient?.nsUpdate("treatments", DataSyncSelector.PairTemporaryBasal(tb.first, tb.second.id), "$startId/$lastDbId")
}
return
}
@ -549,16 +526,16 @@ class DataSyncSelectorImplementation @Inject constructor(
}
}
override tailrec fun processChangedExtendedBoluses() {
override tailrec suspend fun processChangedExtendedBoluses() {
if (isPaused) return
val lastDbIdWrapped = appRepository.getLastExtendedBolusIdWrapped().blockingGet()
val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L
val lastDbId = appRepository.getLastExtendedBolusId() ?: 0L
var startId = sp.getLong(R.string.key_ns_extended_bolus_last_synced_id, 0)
if (startId > lastDbId) {
sp.putLong(R.string.key_ns_extended_bolus_last_synced_id, 0)
startId = 0
}
queueCounter.ebsRemaining = lastDbId - startId
rxBus.send(EventNSClientUpdateGuiQueue())
appRepository.getNextSyncElementExtendedBolus(startId).blockingGet()?.let { eb ->
aapsLogger.info(LTag.NSCLIENT, "Loading ExtendedBolus data Start: $startId ${eb.first} forID: ${eb.second.id} ")
val profile = profileFunction.getProfile(eb.first.timestamp)
@ -580,18 +557,10 @@ class DataSyncSelectorImplementation @Inject constructor(
}
// without nsId = create new
eb.first.interfaceIDs.nightscoutId == null ->
activePlugin.activeNsClient?.nsAdd(
"treatments",
DataSyncSelector.PairExtendedBolus(eb.first, eb.second.id),
"$startId/$lastDbId"
)
activePlugin.activeNsClient?.nsAdd("treatments", DataSyncSelector.PairExtendedBolus(eb.first, eb.second.id), "$startId/$lastDbId")
// with nsId = update
eb.first.interfaceIDs.nightscoutId != null ->
activePlugin.activeNsClient?.nsUpdate(
"treatments",
DataSyncSelector.PairExtendedBolus(eb.first, eb.second.id),
"$startId/$lastDbId"
)
activePlugin.activeNsClient?.nsUpdate("treatments", DataSyncSelector.PairExtendedBolus(eb.first, eb.second.id), "$startId/$lastDbId")
}
return
} else {
@ -610,16 +579,16 @@ class DataSyncSelectorImplementation @Inject constructor(
}
}
override tailrec fun processChangedProfileSwitches() {
override tailrec suspend fun processChangedProfileSwitches() {
if (isPaused) return
val lastDbIdWrapped = appRepository.getLastProfileSwitchIdWrapped().blockingGet()
val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L
val lastDbId = appRepository.getLastProfileSwitchId() ?: 0L
var startId = sp.getLong(R.string.key_ns_profile_switch_last_synced_id, 0)
if (startId > lastDbId) {
sp.putLong(R.string.key_ns_profile_switch_last_synced_id, 0)
startId = 0
}
queueCounter.pssRemaining = lastDbId - startId
rxBus.send(EventNSClientUpdateGuiQueue())
appRepository.getNextSyncElementProfileSwitch(startId).blockingGet()?.let { ps ->
aapsLogger.info(LTag.NSCLIENT, "Loading ProfileSwitch data Start: $startId ${ps.first} forID: ${ps.second.id} ")
when {
@ -642,11 +611,7 @@ class DataSyncSelectorImplementation @Inject constructor(
activePlugin.activeNsClient?.nsAdd("treatments", DataSyncSelector.PairProfileSwitch(ps.first, ps.second.id), "$startId/$lastDbId")
// with nsId = update
ps.first.interfaceIDs.nightscoutId != null ->
activePlugin.activeNsClient?.nsUpdate(
"treatments",
DataSyncSelector.PairProfileSwitch(ps.first, ps.second.id),
"$startId/$lastDbId"
)
activePlugin.activeNsClient?.nsUpdate("treatments", DataSyncSelector.PairProfileSwitch(ps.first, ps.second.id), "$startId/$lastDbId")
}
return
}
@ -659,16 +624,16 @@ class DataSyncSelectorImplementation @Inject constructor(
}
}
override tailrec fun processChangedEffectiveProfileSwitches() {
override tailrec suspend fun processChangedEffectiveProfileSwitches() {
if (isPaused) return
val lastDbIdWrapped = appRepository.getLastEffectiveProfileSwitchIdWrapped().blockingGet()
val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L
val lastDbId = appRepository.getLastEffectiveProfileSwitchId() ?: 0L
var startId = sp.getLong(R.string.key_ns_effective_profile_switch_last_synced_id, 0)
if (startId > lastDbId) {
sp.putLong(R.string.key_ns_effective_profile_switch_last_synced_id, 0)
startId = 0
}
queueCounter.epssRemaining = lastDbId - startId
rxBus.send(EventNSClientUpdateGuiQueue())
appRepository.getNextSyncElementEffectiveProfileSwitch(startId).blockingGet()?.let { ps ->
aapsLogger.info(LTag.NSCLIENT, "Loading EffectiveProfileSwitch data Start: $startId ${ps.first} forID: ${ps.second.id} ")
when {
@ -688,18 +653,10 @@ class DataSyncSelectorImplementation @Inject constructor(
}
// without nsId = create new
ps.first.interfaceIDs.nightscoutId == null ->
activePlugin.activeNsClient?.nsAdd(
"treatments",
DataSyncSelector.PairEffectiveProfileSwitch(ps.first, ps.second.id),
"$startId/$lastDbId"
)
activePlugin.activeNsClient?.nsAdd("treatments", DataSyncSelector.PairEffectiveProfileSwitch(ps.first, ps.second.id), "$startId/$lastDbId")
// with nsId = update
ps.first.interfaceIDs.nightscoutId != null ->
activePlugin.activeNsClient?.nsUpdate(
"treatments",
DataSyncSelector.PairEffectiveProfileSwitch(ps.first, ps.second.id),
"$startId/$lastDbId"
)
activePlugin.activeNsClient?.nsUpdate("treatments", DataSyncSelector.PairEffectiveProfileSwitch(ps.first, ps.second.id), "$startId/$lastDbId")
}
return
}
@ -712,16 +669,16 @@ class DataSyncSelectorImplementation @Inject constructor(
}
}
override tailrec fun processChangedOfflineEvents() {
override tailrec suspend fun processChangedOfflineEvents() {
if (isPaused) return
val lastDbIdWrapped = appRepository.getLastOfflineEventIdWrapped().blockingGet()
val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L
val lastDbId = appRepository.getLastOfflineEventId() ?: 0L
var startId = sp.getLong(R.string.key_ns_offline_event_last_synced_id, 0)
if (startId > lastDbId) {
sp.putLong(R.string.key_ns_offline_event_last_synced_id, 0)
startId = 0
}
queueCounter.oesRemaining = lastDbId - startId
rxBus.send(EventNSClientUpdateGuiQueue())
appRepository.getNextSyncElementOfflineEvent(startId).blockingGet()?.let { oe ->
aapsLogger.info(LTag.NSCLIENT, "Loading OfflineEvent data Start: $startId ${oe.first} forID: ${oe.second.id} ")
when {
@ -744,11 +701,7 @@ class DataSyncSelectorImplementation @Inject constructor(
activePlugin.activeNsClient?.nsAdd("treatments", DataSyncSelector.PairOfflineEvent(oe.first, oe.second.id), "$startId/$lastDbId")
// existing with nsId = update
oe.first.interfaceIDs.nightscoutId != null ->
activePlugin.activeNsClient?.nsUpdate(
"treatments",
DataSyncSelector.PairOfflineEvent(oe.first, oe.second.id),
"$startId/$lastDbId"
)
activePlugin.activeNsClient?.nsUpdate("treatments", DataSyncSelector.PairOfflineEvent(oe.first, oe.second.id), "$startId/$lastDbId")
}
return
}
@ -758,7 +711,7 @@ class DataSyncSelectorImplementation @Inject constructor(
sp.putLong(R.string.key_ns_profile_store_last_synced_timestamp, lastSynced)
}
override fun processChangedProfileStore() {
override suspend fun processChangedProfileStore() {
if (isPaused) return
val lastSync = sp.getLong(R.string.key_ns_profile_store_last_synced_timestamp, 0)
val lastChange = sp.getLong(info.nightscout.core.utils.R.string.key_local_profile_last_change, 0)

View file

@ -4,10 +4,7 @@ import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.os.Handler
import android.os.HandlerThread
import android.os.IBinder
import android.text.Spanned
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceScreen
import androidx.preference.SwitchPreference
@ -25,15 +22,15 @@ import info.nightscout.interfaces.plugin.PluginType
import info.nightscout.interfaces.profile.ProfileFunction
import info.nightscout.interfaces.source.DoingOwnUploadSource
import info.nightscout.interfaces.sync.DataSyncSelector
import info.nightscout.interfaces.sync.DataSyncSelectorV1
import info.nightscout.interfaces.sync.NsClient
import info.nightscout.interfaces.sync.Sync
import info.nightscout.interfaces.ui.UiInteraction
import info.nightscout.interfaces.utils.HtmlHelper.fromHtml
import info.nightscout.plugins.sync.R
import info.nightscout.plugins.sync.nsShared.NSClientFragment
import info.nightscout.plugins.sync.nsShared.events.EventNSClientResend
import info.nightscout.plugins.sync.nsShared.events.EventNSClientStatus
import info.nightscout.plugins.sync.nsShared.events.EventNSClientUpdateGUI
import info.nightscout.plugins.sync.nsShared.events.EventNSClientUpdateGuiInsert
import info.nightscout.plugins.sync.nsShared.events.EventNSClientUpdateGuiStatus
import info.nightscout.plugins.sync.nsclient.data.AlarmAck
import info.nightscout.plugins.sync.nsclient.extensions.toJson
import info.nightscout.plugins.sync.nsclient.services.NSClientService
@ -65,8 +62,7 @@ class NSClientPlugin @Inject constructor(
private val sp: SP,
private val receiverDelegate: ReceiverDelegate,
private val config: Config,
private val dataSyncSelector: DataSyncSelector,
private val uiInteraction: UiInteraction,
private val dataSyncSelectorV1: DataSyncSelectorV1,
private val activePlugin: ActivePlugin,
private val dateUtil: DateUtil,
private val profileFunction: ProfileFunction,
@ -84,8 +80,8 @@ class NSClientPlugin @Inject constructor(
) {
private val disposable = CompositeDisposable()
private val handler = Handler(HandlerThread(this::class.simpleName + "Handler").also { it.start() }.looper)
private val listLog: MutableList<EventNSClientNewLog> = ArrayList()
override val listLog: MutableList<EventNSClientNewLog> = ArrayList()
override val dataSyncSelector: DataSyncSelector get() = dataSyncSelectorV1
override var status = ""
var nsClientService: NSClientService? = null
val isAllowed: Boolean
@ -102,7 +98,7 @@ class NSClientPlugin @Inject constructor(
.observeOn(aapsSchedulers.io)
.subscribe({ event ->
status = event.getStatus(context)
rxBus.send(EventNSClientUpdateGUI())
rxBus.send(EventNSClientUpdateGuiStatus())
// Pass to setup wizard
rxBus.send(EventSWSyncStatus(event.getStatus(context)))
}, fabricPrivacy::logException)
@ -159,37 +155,17 @@ class NSClientPlugin @Inject constructor(
}
}
override fun clearLog() {
handler.post {
synchronized(listLog) { listLog.clear() }
rxBus.send(EventNSClientUpdateGUI())
}
}
override fun detectedNsVersion(): String = nsSettingsStatus.getVersion()
private fun addToLog(ev: EventNSClientNewLog) {
synchronized(listLog) {
listLog.add(ev)
listLog.add(0, ev)
rxBus.send(EventNSClientUpdateGuiInsert(0))
// remove the first line if log is too large
if (listLog.size >= Constants.MAX_LOG_LINES) {
listLog.removeAt(0)
listLog.removeAt(listLog.size - 1)
}
}
rxBus.send(EventNSClientUpdateGUI())
}
override fun textLog(): Spanned {
try {
val newTextLog = StringBuilder()
synchronized(listLog) {
for (log in listLog) newTextLog.append(log.toPreparedHtml())
}
return fromHtml(newTextLog.toString())
} catch (e: OutOfMemoryError) {
uiInteraction.showToastAndNotification(context, "Out of memory!\nStop using this phone !!!", info.nightscout.core.ui.R.raw.error)
}
return fromHtml("")
}
override fun resend(reason: String) {
@ -229,7 +205,7 @@ class NSClientPlugin @Inject constructor(
dataSyncSelector.resetToNextFullSync()
}
override fun nsAdd(collection: String, dataPair: DataSyncSelector.DataPair, progress: String) {
override suspend fun nsAdd(collection: String, dataPair: DataSyncSelector.DataPair, progress: String): Boolean {
when (dataPair) {
is DataSyncSelector.PairBolus -> dataPair.value.toJson(true, dateUtil)
is DataSyncSelector.PairCarbs -> dataPair.value.toJson(true, dateUtil)
@ -249,9 +225,10 @@ class NSClientPlugin @Inject constructor(
}?.let { data ->
nsClientService?.dbAdd(collection, data, dataPair, progress)
}
return true
}
override fun nsUpdate(collection: String, dataPair: DataSyncSelector.DataPair, progress: String) {
override suspend fun nsUpdate(collection: String, dataPair: DataSyncSelector.DataPair, progress: String): Boolean {
val id = when (dataPair) {
is DataSyncSelector.PairBolus -> dataPair.value.interfaceIDs.nightscoutId
is DataSyncSelector.PairCarbs -> dataPair.value.interfaceIDs.nightscoutId
@ -284,5 +261,6 @@ class NSClientPlugin @Inject constructor(
}?.let { data ->
nsClientService?.dbUpdate(collection, id, data, dataPair, progress)
}
return true
}
}

View file

@ -24,16 +24,16 @@ import info.nightscout.interfaces.Config
import info.nightscout.interfaces.notifications.Notification
import info.nightscout.interfaces.nsclient.NSAlarm
import info.nightscout.interfaces.nsclient.NSSettingsStatus
import info.nightscout.interfaces.sync.DataSyncSelector
import info.nightscout.interfaces.nsclient.StoreDataForDb
import info.nightscout.interfaces.sync.DataSyncSelectorV1
import info.nightscout.interfaces.ui.UiInteraction
import info.nightscout.interfaces.utils.JsonHelper.safeGetString
import info.nightscout.interfaces.utils.JsonHelper.safeGetStringAllowNull
import info.nightscout.interfaces.workflow.WorkerClasses
import info.nightscout.plugins.sync.R
import info.nightscout.plugins.sync.nsShared.StoreDataForDbImpl
import info.nightscout.plugins.sync.nsShared.NsIncomingDataProcessor
import info.nightscout.plugins.sync.nsShared.events.EventConnectivityOptionChanged
import info.nightscout.plugins.sync.nsShared.events.EventNSClientStatus
import info.nightscout.plugins.sync.nsShared.events.EventNSClientUpdateGUI
import info.nightscout.plugins.sync.nsShared.events.EventNSClientUpdateGuiStatus
import info.nightscout.plugins.sync.nsclient.NSClientPlugin
import info.nightscout.plugins.sync.nsclient.acks.NSAddAck
import info.nightscout.plugins.sync.nsclient.acks.NSAuthAck
@ -42,16 +42,9 @@ import info.nightscout.plugins.sync.nsclient.data.AlarmAck
import info.nightscout.plugins.sync.nsclient.data.NSDeviceStatusHandler
import info.nightscout.plugins.sync.nsclient.workers.NSClientAddUpdateWorker
import info.nightscout.plugins.sync.nsclient.workers.NSClientMbgWorker
import info.nightscout.plugins.sync.nsclientV3.workers.ProcessFoodWorker
import info.nightscout.rx.AapsSchedulers
import info.nightscout.rx.bus.RxBus
import info.nightscout.rx.events.EventAppExit
import info.nightscout.rx.events.EventConfigBuilderChange
import info.nightscout.rx.events.EventDismissNotification
import info.nightscout.rx.events.EventNSClientNewLog
import info.nightscout.rx.events.EventNSClientRestart
import info.nightscout.rx.events.EventNewHistoryData
import info.nightscout.rx.events.EventPreferenceChange
import info.nightscout.rx.events.*
import info.nightscout.rx.logging.AAPSLogger
import info.nightscout.rx.logging.LTag
import info.nightscout.sdk.localmodel.devicestatus.NSDeviceStatus
@ -64,14 +57,19 @@ import io.reactivex.rxjava3.kotlin.plusAssign
import io.socket.client.IO
import io.socket.client.Socket
import io.socket.emitter.Emitter
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.async
import kotlinx.coroutines.runBlocking
import org.json.JSONArray
import org.json.JSONException
import org.json.JSONObject
import java.net.URISyntaxException
import java.util.Locale
import java.util.*
import javax.inject.Inject
class NSClientService : DaggerService() {
@Suppress("SpellCheckingInspection") class NSClientService : DaggerService() {
@Inject lateinit var injector: HasAndroidInjector
@Inject lateinit var aapsLogger: AAPSLogger
@ -86,10 +84,11 @@ class NSClientService : DaggerService() {
@Inject lateinit var config: Config
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var dataWorkerStorage: DataWorkerStorage
@Inject lateinit var dataSyncSelector: DataSyncSelector
@Inject lateinit var dataSyncSelectorV1: DataSyncSelectorV1
@Inject lateinit var repository: AppRepository
@Inject lateinit var uiInteraction: UiInteraction
@Inject lateinit var workerClasses: WorkerClasses
@Inject lateinit var nsIncomingDataProcessor: NsIncomingDataProcessor
@Inject lateinit var storeDataForDb: StoreDataForDb
companion object {
@ -99,6 +98,7 @@ class NSClientService : DaggerService() {
}
private val disposable = CompositeDisposable()
private var scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
private var wakeLock: PowerManager.WakeLock? = null
private val binder: IBinder = LocalBinder()
@ -196,12 +196,12 @@ class NSClientService : DaggerService() {
isConnected = true
hasWriteAuth = ack.write && ack.writeTreatment
rxBus.send(EventNSClientStatus(connectionStatus))
rxBus.send(EventNSClientNewLog("AUTH", connectionStatus))
rxBus.send(EventNSClientNewLog("AUTH", connectionStatus))
if (!ack.write) {
rxBus.send(EventNSClientNewLog("ERROR", "Write permission not granted "))
rxBus.send(EventNSClientNewLog("ERROR", "Write permission not granted "))
}
if (!ack.writeTreatment) {
rxBus.send(EventNSClientNewLog("ERROR", "Write treatment permission not granted "))
rxBus.send(EventNSClientNewLog("ERROR", "Write treatment permission not granted "))
}
if (!hasWriteAuth) {
val noWritePerm = Notification(Notification.NSCLIENT_NO_WRITE_PERMISSION, rh.gs(R.string.no_write_permission), Notification.URGENT)
@ -227,14 +227,14 @@ class NSClientService : DaggerService() {
@Suppress("DEPRECATION")
if (nsAPISecret != "") nsApiHashCode = Hashing.sha1().hashString(nsAPISecret, Charsets.UTF_8).toString()
rxBus.send(EventNSClientStatus("Initializing"))
if (nsClientPlugin.isAllowed != true) {
rxBus.send(EventNSClientNewLog("NSCLIENT", nsClientPlugin.blockingReason))
if (!nsClientPlugin.isAllowed) {
rxBus.send(EventNSClientNewLog("NSCLIENT", nsClientPlugin.blockingReason))
rxBus.send(EventNSClientStatus(nsClientPlugin.blockingReason))
} else if (sp.getBoolean(R.string.key_ns_paused, false)) {
rxBus.send(EventNSClientNewLog("NSCLIENT", "paused"))
rxBus.send(EventNSClientNewLog("NSCLIENT", "paused"))
rxBus.send(EventNSClientStatus("Paused"))
} else if (!nsEnabled) {
rxBus.send(EventNSClientNewLog("NSCLIENT", "disabled"))
rxBus.send(EventNSClientNewLog("NSCLIENT", "disabled"))
rxBus.send(EventNSClientStatus("Disabled"))
} else if (nsURL != "" && (nsURL.lowercase(Locale.getDefault()).startsWith("https://"))) {
try {
@ -243,7 +243,7 @@ class NSClientService : DaggerService() {
socket = IO.socket(nsURL, opt).also { socket ->
socket.on(Socket.EVENT_CONNECT, onConnect)
socket.on(Socket.EVENT_DISCONNECT, onDisconnect)
rxBus.send(EventNSClientNewLog("NSCLIENT", "do connect"))
rxBus.send(EventNSClientNewLog("NSCLIENT", "do connect"))
socket.connect()
socket.on("dataUpdate", onDataUpdate)
socket.on("announcement", onAnnouncement)
@ -252,17 +252,17 @@ class NSClientService : DaggerService() {
socket.on("clear_alarm", onClearAlarm)
}
} catch (e: URISyntaxException) {
rxBus.send(EventNSClientNewLog("NSCLIENT", "Wrong URL syntax"))
rxBus.send(EventNSClientNewLog("NSCLIENT", "Wrong URL syntax"))
rxBus.send(EventNSClientStatus("Wrong URL syntax"))
} catch (e: RuntimeException) {
rxBus.send(EventNSClientNewLog("NSCLIENT", "Wrong URL syntax"))
rxBus.send(EventNSClientNewLog("NSCLIENT", "Wrong URL syntax"))
rxBus.send(EventNSClientStatus("Wrong URL syntax"))
}
} else if (nsURL.lowercase(Locale.getDefault()).startsWith("http://")) {
rxBus.send(EventNSClientNewLog("NSCLIENT", "NS URL not encrypted"))
rxBus.send(EventNSClientNewLog("NSCLIENT", "NS URL not encrypted"))
rxBus.send(EventNSClientStatus("Not encrypted"))
} else {
rxBus.send(EventNSClientNewLog("NSCLIENT", "No NS URL specified"))
rxBus.send(EventNSClientNewLog("NSCLIENT", "No NS URL specified"))
rxBus.send(EventNSClientStatus("Not configured"))
}
}
@ -270,7 +270,7 @@ class NSClientService : DaggerService() {
private val onConnect = Emitter.Listener {
connectCounter++
val socketId = socket?.id() ?: "NULL"
rxBus.send(EventNSClientNewLog("NSCLIENT", "connect #$connectCounter event. ID: $socketId"))
rxBus.send(EventNSClientNewLog("NSCLIENT", "connect #$connectCounter event. ID: $socketId"))
if (socket != null) sendAuthMessage(NSAuthAck(rxBus))
watchdog()
}
@ -284,16 +284,16 @@ class NSClientService : DaggerService() {
reconnections.remove(r)
}
}
rxBus.send(EventNSClientNewLog("WATCHDOG", "connections in last " + WATCHDOG_INTERVAL_MINUTES + " minutes: " + reconnections.size + "/" + WATCHDOG_MAX_CONNECTIONS))
rxBus.send(EventNSClientNewLog("WATCHDOG", "connections in last " + WATCHDOG_INTERVAL_MINUTES + " minutes: " + reconnections.size + "/" + WATCHDOG_MAX_CONNECTIONS))
if (reconnections.size >= WATCHDOG_MAX_CONNECTIONS) {
val n = Notification(Notification.NS_MALFUNCTION, rh.gs(R.string.ns_malfunction), Notification.URGENT)
rxBus.send(EventNewNotification(n))
rxBus.send(EventNSClientNewLog("WATCHDOG", "pausing for $WATCHDOG_RECONNECT_IN minutes"))
rxBus.send(EventNSClientNewLog("WATCHDOG", "pausing for $WATCHDOG_RECONNECT_IN minutes"))
nsClientPlugin.pause(true)
rxBus.send(EventNSClientUpdateGUI())
rxBus.send(EventNSClientUpdateGuiStatus())
Thread {
SystemClock.sleep(mins(WATCHDOG_RECONNECT_IN.toLong()).msecs())
rxBus.send(EventNSClientNewLog("WATCHDOG", "re-enabling NSClient"))
rxBus.send(EventNSClientNewLog("WATCHDOG", "re-enabling NSClient"))
nsClientPlugin.pause(false)
}.start()
}
@ -302,7 +302,7 @@ class NSClientService : DaggerService() {
private val onDisconnect = Emitter.Listener { args ->
aapsLogger.debug(LTag.NSCLIENT, "disconnect reason: {}", *args)
rxBus.send(EventNSClientNewLog("NSCLIENT", "disconnect event"))
rxBus.send(EventNSClientNewLog("NSCLIENT", "disconnect event"))
}
@Synchronized fun destroy() {
@ -313,7 +313,7 @@ class NSClientService : DaggerService() {
socket?.off("alarm")
socket?.off("urgent_alarm")
socket?.off("clear_alarm")
rxBus.send(EventNSClientNewLog("NSCLIENT", "destroy"))
rxBus.send(EventNSClientNewLog("NSCLIENT", "destroy"))
isConnected = false
hasWriteAuth = false
socket?.disconnect()
@ -332,7 +332,7 @@ class NSClientService : DaggerService() {
aapsLogger.error("Unhandled exception", e)
return
}
rxBus.send(EventNSClientNewLog("AUTH", "requesting auth"))
rxBus.send(EventNSClientNewLog("AUTH", "requesting auth"))
socket?.emit("authorize", authMessage, ack)
}
@ -409,7 +409,7 @@ class NSClientService : DaggerService() {
val data: JSONObject
try {
data = args[0] as JSONObject
rxBus.send(EventNSClientNewLog("CLEARALARM", "received"))
rxBus.send(EventNSClientNewLog("CLEARALARM", "received"))
rxBus.send(EventDismissNotification(Notification.NS_ALARM))
rxBus.send(EventDismissNotification(Notification.NS_URGENT_ALARM))
aapsLogger.debug(LTag.NSCLIENT, data.toString())
@ -428,30 +428,26 @@ class NSClientService : DaggerService() {
try {
// delta means only increment/changes are coming
val isDelta = data.has("delta")
rxBus.send(EventNSClientNewLog("DATA", "Data packet #" + dataCounter++ + if (isDelta) " delta" else " full"))
rxBus.send(EventNSClientNewLog("DATA", "Data packet #" + dataCounter++ + if (isDelta) " delta" else " full"))
if (data.has("status")) {
val status = data.getJSONObject("status")
nsSettingsStatus.handleNewData(status)
} else if (!isDelta) {
rxBus.send(EventNSClientNewLog("ERROR", "Unsupported Nightscout version "))
rxBus.send(EventNSClientNewLog("ERROR", "Unsupported Nightscout version "))
}
if (data.has("profiles")) {
val profiles = data.getJSONArray("profiles")
if (profiles.length() > 0) {
// take the newest
val profileStoreJson = profiles[profiles.length() - 1] as JSONObject
rxBus.send(EventNSClientNewLog("PROFILE", "profile received"))
dataWorkerStorage.enqueue(
OneTimeWorkRequest.Builder(workerClasses.nsProfileWorker)
.setInputData(dataWorkerStorage.storeInputData(profileStoreJson))
.build()
)
rxBus.send(EventNSClientNewLog("◄ PROFILE", "profile received"))
nsIncomingDataProcessor.processProfile(profileStoreJson)
}
}
if (data.has("treatments")) {
val treatments = data.getJSONArray("treatments")
val addedOrUpdatedTreatments = JSONArray()
if (treatments.length() > 0) rxBus.send(EventNSClientNewLog("DATA", "received " + treatments.length() + " treatments"))
if (treatments.length() > 0) rxBus.send(EventNSClientNewLog("DATA", "received " + treatments.length() + " treatments"))
for (index in 0 until treatments.length()) {
val jsonTreatment = treatments.getJSONObject(index)
val action = safeGetStringAllowNull(jsonTreatment, "action", null)
@ -507,7 +503,7 @@ class NSClientService : DaggerService() {
val devicestatuses = gson.fromJson(data.getString("devicestatus"), Array<NSDeviceStatus>::class.java)
if (devicestatuses.isNotEmpty()) {
rxBus.send(EventNSClientNewLog("DATA", "received " + devicestatuses.size + " device statuses"))
rxBus.send(EventNSClientNewLog("DATA", "received " + devicestatuses.size + " device statuses"))
nsDeviceStatusHandler.handleNewData(devicestatuses)
}
} catch (e: JSONException) {
@ -516,19 +512,13 @@ class NSClientService : DaggerService() {
}
if (data.has("food")) {
val foods = data.getJSONArray("food")
if (foods.length() > 0) rxBus.send(EventNSClientNewLog("DATA", "received " + foods.length() + " foods"))
dataWorkerStorage
.beginUniqueWork(
"ProcessFoods",
OneTimeWorkRequest.Builder(ProcessFoodWorker::class.java)
.setInputData(dataWorkerStorage.storeInputData(foods))
.build()
).then(OneTimeWorkRequest.Builder(StoreDataForDbImpl.StoreFoodWorker::class.java).build())
.enqueue()
if (foods.length() > 0) rxBus.send(EventNSClientNewLog("◄ DATA", "received " + foods.length() + " foods"))
nsIncomingDataProcessor.processFood(foods)
storeDataForDb.storeFoodsToDb()
}
if (data.has("mbgs")) {
val mbgArray = data.getJSONArray("mbgs")
if (mbgArray.length() > 0) rxBus.send(EventNSClientNewLog("DATA", "received " + mbgArray.length() + " mbgs"))
if (mbgArray.length() > 0) rxBus.send(EventNSClientNewLog("◄ DATA", "received " + mbgArray.length() + " mbgs"))
dataWorkerStorage.enqueue(
OneTimeWorkRequest.Builder(NSClientMbgWorker::class.java)
.setInputData(dataWorkerStorage.storeInputData(mbgArray))
@ -537,26 +527,20 @@ class NSClientService : DaggerService() {
}
if (data.has("cals")) {
val cals = data.getJSONArray("cals")
if (cals.length() > 0) rxBus.send(EventNSClientNewLog("DATA", "received " + cals.length() + " cals"))
if (cals.length() > 0) rxBus.send(EventNSClientNewLog("DATA", "received " + cals.length() + " cals"))
// Calibrations ignored
}
if (data.has("sgvs")) {
val sgvs = data.getJSONArray("sgvs")
if (sgvs.length() > 0) {
rxBus.send(EventNSClientNewLog("DATA", "received " + sgvs.length() + " sgvs"))
rxBus.send(EventNSClientNewLog("DATA", "received " + sgvs.length() + " sgvs"))
// Objective0
sp.putBoolean(info.nightscout.core.utils.R.string.key_objectives_bg_is_available_in_ns, true)
dataWorkerStorage
.beginUniqueWork(
"ProcessBg",
OneTimeWorkRequest.Builder(workerClasses.nsClientSourceWorker)
.setInputData(dataWorkerStorage.storeInputData(sgvs))
.build()
).then(OneTimeWorkRequest.Builder(StoreDataForDbImpl.StoreBgWorker::class.java).build())
.enqueue()
nsIncomingDataProcessor.processSgvs(sgvs)
storeDataForDb.storeGlucoseValuesToDb()
}
}
rxBus.send(EventNSClientNewLog("LAST", dateUtil.dateAndTimeString(latestDateInReceivedData)))
rxBus.send(EventNSClientNewLog("◄ LAST", dateUtil.dateAndTimeString(latestDateInReceivedData)))
} catch (e: JSONException) {
aapsLogger.error("Unhandled exception", e)
}
@ -578,7 +562,7 @@ class NSClientService : DaggerService() {
socket?.emit("dbUpdate", message, NSUpdateAck("dbUpdate", _id, aapsLogger, rxBus, this, dateUtil, dataWorkerStorage, originalObject))
rxBus.send(
EventNSClientNewLog(
"UPDATE $collection", "Sent " + originalObject.javaClass.simpleName + " " +
"UPDATE $collection", "Sent " + originalObject.javaClass.simpleName + " " +
"" + _id + " " + data + progress
)
)
@ -594,7 +578,7 @@ class NSClientService : DaggerService() {
message.put("collection", collection)
message.put("data", data)
socket?.emit("dbAdd", message, NSAddAck(aapsLogger, rxBus, this, dateUtil, dataWorkerStorage, originalObject))
rxBus.send(EventNSClientNewLog("ADD $collection", "Sent " + originalObject.javaClass.simpleName + " " + data + " " + progress))
rxBus.send(EventNSClientNewLog("ADD $collection", "Sent " + originalObject.javaClass.simpleName + " " + data + " " + progress))
} catch (e: JSONException) {
aapsLogger.error("Unhandled exception", e)
}
@ -603,29 +587,22 @@ class NSClientService : DaggerService() {
fun sendAlarmAck(alarmAck: AlarmAck) {
if (!isConnected || !hasWriteAuth) return
socket?.emit("ack", alarmAck.level, alarmAck.group, alarmAck.silenceTime)
rxBus.send(EventNSClientNewLog("ALARMACK ", alarmAck.level.toString() + " " + alarmAck.group + " " + alarmAck.silenceTime))
rxBus.send(EventNSClientNewLog("ALARMACK ", alarmAck.level.toString() + " " + alarmAck.group + " " + alarmAck.silenceTime))
}
fun resend(reason: String) {
if (!isConnected || !hasWriteAuth) return
handler.post {
if (socket?.connected() != true) return@post
@Synchronized
fun resend(reason: String) = runBlocking {
if (!isConnected || !hasWriteAuth) return@runBlocking
scope.async {
if (socket?.connected() != true) return@async
if (lastAckTime > System.currentTimeMillis() - 10 * 1000L) {
aapsLogger.debug(LTag.NSCLIENT, "Skipping resend by lastAckTime: " + (System.currentTimeMillis() - lastAckTime) / 1000L + " sec")
return@post
}
// val powerManager = getSystemService(POWER_SERVICE) as PowerManager
// val wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
// "AndroidAPS:NSClientService_onDataUpdate")
// wakeLock.acquire(mins(10).msecs())
try {
rxBus.send(EventNSClientNewLog("QUEUE", "Resend started: $reason"))
dataSyncSelector.doUpload()
rxBus.send(EventNSClientNewLog("QUEUE", "Resend ended: $reason"))
} finally {
// if (wakeLock.isHeld) wakeLock.release()
}
return@async
}
rxBus.send(EventNSClientNewLog("● QUEUE", "Resend started: $reason"))
dataSyncSelectorV1.doUpload()
rxBus.send(EventNSClientNewLog("● QUEUE", "Resend ended: $reason"))
}.join()
}
fun restart() {
@ -638,7 +615,7 @@ class NSClientService : DaggerService() {
if (sp.getBoolean(info.nightscout.core.utils.R.string.key_ns_announcements, defaultVal)) {
val nsAlarm = NSAlarm(announcement)
uiInteraction.addNotificationWithAction(injector, nsAlarm)
rxBus.send(EventNSClientNewLog("ANNOUNCEMENT", safeGetString(announcement, "message", "received")))
rxBus.send(EventNSClientNewLog("ANNOUNCEMENT", safeGetString(announcement, "message", "received")))
aapsLogger.debug(LTag.NSCLIENT, announcement.toString())
}
}
@ -651,7 +628,7 @@ class NSClientService : DaggerService() {
val nsAlarm = NSAlarm(alarm)
uiInteraction.addNotificationWithAction(injector, nsAlarm)
}
rxBus.send(EventNSClientNewLog("ALARM", safeGetString(alarm, "message", "received")))
rxBus.send(EventNSClientNewLog("ALARM", safeGetString(alarm, "message", "received")))
aapsLogger.debug(LTag.NSCLIENT, alarm.toString())
}
}
@ -664,7 +641,7 @@ class NSClientService : DaggerService() {
val nsAlarm = NSAlarm(alarm)
uiInteraction.addNotificationWithAction(injector, nsAlarm)
}
rxBus.send(EventNSClientNewLog("URGENTALARM", safeGetString(alarm, "message", "received")))
rxBus.send(EventNSClientNewLog("URGENTALARM", safeGetString(alarm, "message", "received")))
aapsLogger.debug(LTag.NSCLIENT, alarm.toString())
}
}

View file

@ -22,6 +22,7 @@ import info.nightscout.interfaces.sync.DataSyncSelector.PairProfileSwitch
import info.nightscout.interfaces.sync.DataSyncSelector.PairTemporaryBasal
import info.nightscout.interfaces.sync.DataSyncSelector.PairTemporaryTarget
import info.nightscout.interfaces.sync.DataSyncSelector.PairTherapyEvent
import info.nightscout.interfaces.sync.DataSyncSelectorV1
import info.nightscout.plugins.sync.R
import info.nightscout.plugins.sync.nsclient.acks.NSAddAck
import info.nightscout.rx.AapsSchedulers
@ -39,7 +40,7 @@ class NSClientAddAckWorker(
@Inject lateinit var dataWorkerStorage: DataWorkerStorage
@Inject lateinit var repository: AppRepository
@Inject lateinit var rxBus: RxBus
@Inject lateinit var dataSyncSelector: DataSyncSelector
@Inject lateinit var dataSyncSelectorV1: DataSyncSelectorV1
@Inject lateinit var aapsSchedulers: AapsSchedulers
@Inject lateinit var sp: SP
@Inject lateinit var storeDataForDb: StoreDataForDb
@ -60,148 +61,148 @@ class NSClientAddAckWorker(
val pair = ack.originalObject
pair.value.interfaceIDs.nightscoutId = ack.id
storeDataForDb.nsIdTemporaryTargets.add(pair.value)
dataSyncSelector.confirmLastTempTargetsIdIfGreater(pair.id)
dataSyncSelectorV1.confirmLastTempTargetsIdIfGreater(pair.id)
storeDataForDb.scheduleNsIdUpdate()
rxBus.send(EventNSClientNewLog("DBADD", "Acked TemporaryTarget " + pair.value.interfaceIDs.nightscoutId))
rxBus.send(EventNSClientNewLog("DBADD", "Acked TemporaryTarget " + pair.value.interfaceIDs.nightscoutId))
// Send new if waiting
dataSyncSelector.processChangedTempTargets()
dataSyncSelectorV1.processChangedTempTargets()
}
is PairGlucoseValue -> {
val pair = ack.originalObject
pair.value.interfaceIDs.nightscoutId = ack.id
storeDataForDb.nsIdGlucoseValues.add(pair.value)
dataSyncSelector.confirmLastGlucoseValueIdIfGreater(pair.id)
dataSyncSelectorV1.confirmLastGlucoseValueIdIfGreater(pair.id)
storeDataForDb.scheduleNsIdUpdate()
rxBus.send(EventNSClientNewLog("DBADD", "Acked GlucoseValue " + pair.value.interfaceIDs.nightscoutId))
rxBus.send(EventNSClientNewLog("DBADD", "Acked GlucoseValue " + pair.value.interfaceIDs.nightscoutId))
// Send new if waiting
dataSyncSelector.processChangedGlucoseValues()
dataSyncSelectorV1.processChangedGlucoseValues()
}
is PairFood -> {
val pair = ack.originalObject
pair.value.interfaceIDs.nightscoutId = ack.id
storeDataForDb.nsIdFoods.add(pair.value)
dataSyncSelector.confirmLastFoodIdIfGreater(pair.id)
dataSyncSelectorV1.confirmLastFoodIdIfGreater(pair.id)
storeDataForDb.scheduleNsIdUpdate()
rxBus.send(EventNSClientNewLog("DBADD", "Acked Food " + pair.value.interfaceIDs.nightscoutId))
rxBus.send(EventNSClientNewLog("DBADD", "Acked Food " + pair.value.interfaceIDs.nightscoutId))
// Send new if waiting
dataSyncSelector.processChangedFoods()
dataSyncSelectorV1.processChangedFoods()
}
is PairTherapyEvent -> {
val pair = ack.originalObject
pair.value.interfaceIDs.nightscoutId = ack.id
storeDataForDb.nsIdTherapyEvents.add(pair.value)
dataSyncSelector.confirmLastTherapyEventIdIfGreater(pair.id)
dataSyncSelectorV1.confirmLastTherapyEventIdIfGreater(pair.id)
storeDataForDb.scheduleNsIdUpdate()
rxBus.send(EventNSClientNewLog("DBADD", "Acked TherapyEvent " + pair.value.interfaceIDs.nightscoutId))
rxBus.send(EventNSClientNewLog("DBADD", "Acked TherapyEvent " + pair.value.interfaceIDs.nightscoutId))
// Send new if waiting
dataSyncSelector.processChangedTherapyEvents()
dataSyncSelectorV1.processChangedTherapyEvents()
}
is PairBolus -> {
val pair = ack.originalObject
pair.value.interfaceIDs.nightscoutId = ack.id
storeDataForDb.nsIdBoluses.add(pair.value)
dataSyncSelector.confirmLastBolusIdIfGreater(pair.id)
dataSyncSelectorV1.confirmLastBolusIdIfGreater(pair.id)
storeDataForDb.scheduleNsIdUpdate()
rxBus.send(EventNSClientNewLog("DBADD", "Acked Bolus " + pair.value.interfaceIDs.nightscoutId))
rxBus.send(EventNSClientNewLog("DBADD", "Acked Bolus " + pair.value.interfaceIDs.nightscoutId))
// Send new if waiting
dataSyncSelector.processChangedBoluses()
dataSyncSelectorV1.processChangedBoluses()
}
is PairCarbs -> {
val pair = ack.originalObject
pair.value.interfaceIDs.nightscoutId = ack.id
storeDataForDb.nsIdCarbs.add(pair.value)
dataSyncSelector.confirmLastCarbsIdIfGreater(pair.id)
dataSyncSelectorV1.confirmLastCarbsIdIfGreater(pair.id)
storeDataForDb.scheduleNsIdUpdate()
rxBus.send(EventNSClientNewLog("DBADD", "Acked Carbs " + pair.value.interfaceIDs.nightscoutId))
rxBus.send(EventNSClientNewLog("DBADD", "Acked Carbs " + pair.value.interfaceIDs.nightscoutId))
// Send new if waiting
dataSyncSelector.processChangedCarbs()
dataSyncSelectorV1.processChangedCarbs()
}
is PairBolusCalculatorResult -> {
val pair = ack.originalObject
pair.value.interfaceIDs.nightscoutId = ack.id
storeDataForDb.nsIdBolusCalculatorResults.add(pair.value)
dataSyncSelector.confirmLastBolusCalculatorResultsIdIfGreater(pair.id)
dataSyncSelectorV1.confirmLastBolusCalculatorResultsIdIfGreater(pair.id)
storeDataForDb.scheduleNsIdUpdate()
rxBus.send(EventNSClientNewLog("DBADD", "Acked BolusCalculatorResult " + pair.value.interfaceIDs.nightscoutId))
rxBus.send(EventNSClientNewLog("DBADD", "Acked BolusCalculatorResult " + pair.value.interfaceIDs.nightscoutId))
// Send new if waiting
dataSyncSelector.processChangedBolusCalculatorResults()
dataSyncSelectorV1.processChangedBolusCalculatorResults()
}
is PairTemporaryBasal -> {
val pair = ack.originalObject
pair.value.interfaceIDs.nightscoutId = ack.id
storeDataForDb.nsIdTemporaryBasals.add(pair.value)
dataSyncSelector.confirmLastTemporaryBasalIdIfGreater(pair.id)
dataSyncSelectorV1.confirmLastTemporaryBasalIdIfGreater(pair.id)
storeDataForDb.scheduleNsIdUpdate()
rxBus.send(EventNSClientNewLog("DBADD", "Acked TemporaryBasal " + pair.value.interfaceIDs.nightscoutId))
rxBus.send(EventNSClientNewLog("DBADD", "Acked TemporaryBasal " + pair.value.interfaceIDs.nightscoutId))
// Send new if waiting
dataSyncSelector.processChangedTemporaryBasals()
dataSyncSelectorV1.processChangedTemporaryBasals()
}
is PairExtendedBolus -> {
val pair = ack.originalObject
pair.value.interfaceIDs.nightscoutId = ack.id
storeDataForDb.nsIdExtendedBoluses.add(pair.value)
dataSyncSelector.confirmLastExtendedBolusIdIfGreater(pair.id)
dataSyncSelectorV1.confirmLastExtendedBolusIdIfGreater(pair.id)
storeDataForDb.scheduleNsIdUpdate()
rxBus.send(EventNSClientNewLog("DBADD", "Acked ExtendedBolus " + pair.value.interfaceIDs.nightscoutId))
rxBus.send(EventNSClientNewLog("DBADD", "Acked ExtendedBolus " + pair.value.interfaceIDs.nightscoutId))
// Send new if waiting
dataSyncSelector.processChangedExtendedBoluses()
dataSyncSelectorV1.processChangedExtendedBoluses()
}
is PairProfileSwitch -> {
val pair = ack.originalObject
pair.value.interfaceIDs.nightscoutId = ack.id
storeDataForDb.nsIdProfileSwitches.add(pair.value)
dataSyncSelector.confirmLastProfileSwitchIdIfGreater(pair.id)
dataSyncSelectorV1.confirmLastProfileSwitchIdIfGreater(pair.id)
storeDataForDb.scheduleNsIdUpdate()
rxBus.send(EventNSClientNewLog("DBADD", "Acked ProfileSwitch " + pair.value.interfaceIDs.nightscoutId))
rxBus.send(EventNSClientNewLog("DBADD", "Acked ProfileSwitch " + pair.value.interfaceIDs.nightscoutId))
// Send new if waiting
dataSyncSelector.processChangedProfileSwitches()
dataSyncSelectorV1.processChangedProfileSwitches()
}
is PairEffectiveProfileSwitch -> {
val pair = ack.originalObject
pair.value.interfaceIDs.nightscoutId = ack.id
storeDataForDb.nsIdEffectiveProfileSwitches.add(pair.value)
dataSyncSelector.confirmLastEffectiveProfileSwitchIdIfGreater(pair.id)
dataSyncSelectorV1.confirmLastEffectiveProfileSwitchIdIfGreater(pair.id)
storeDataForDb.scheduleNsIdUpdate()
rxBus.send(EventNSClientNewLog("DBADD", "Acked EffectiveProfileSwitch " + pair.value.interfaceIDs.nightscoutId))
rxBus.send(EventNSClientNewLog("DBADD", "Acked EffectiveProfileSwitch " + pair.value.interfaceIDs.nightscoutId))
// Send new if waiting
dataSyncSelector.processChangedEffectiveProfileSwitches()
dataSyncSelectorV1.processChangedEffectiveProfileSwitches()
}
is DataSyncSelector.PairDeviceStatus -> {
val pair = ack.originalObject
pair.value.interfaceIDs.nightscoutId = ack.id
storeDataForDb.nsIdDeviceStatuses.add(pair.value)
dataSyncSelector.confirmLastDeviceStatusIdIfGreater(pair.value.id)
dataSyncSelectorV1.confirmLastDeviceStatusIdIfGreater(pair.value.id)
storeDataForDb.scheduleNsIdUpdate()
rxBus.send(EventNSClientNewLog("DBADD", "Acked DeviceStatus " + pair.value.interfaceIDs.nightscoutId))
rxBus.send(EventNSClientNewLog("DBADD", "Acked DeviceStatus " + pair.value.interfaceIDs.nightscoutId))
// Send new if waiting
dataSyncSelector.processChangedDeviceStatuses()
dataSyncSelectorV1.processChangedDeviceStatuses()
}
is PairProfileStore -> {
dataSyncSelector.confirmLastProfileStore(ack.originalObject.id)
rxBus.send(EventNSClientNewLog("DBADD", "Acked ProfileStore " + ack.id))
dataSyncSelectorV1.confirmLastProfileStore(ack.originalObject.id)
rxBus.send(EventNSClientNewLog("DBADD", "Acked ProfileStore " + ack.id))
}
is PairOfflineEvent -> {
val pair = ack.originalObject
pair.value.interfaceIDs.nightscoutId = ack.id
storeDataForDb.nsIdOfflineEvents.add(pair.value)
dataSyncSelector.confirmLastOfflineEventIdIfGreater(pair.id)
dataSyncSelectorV1.confirmLastOfflineEventIdIfGreater(pair.id)
storeDataForDb.scheduleNsIdUpdate()
rxBus.send(EventNSClientNewLog("DBADD", "Acked OfflineEvent " + pair.value.interfaceIDs.nightscoutId))
rxBus.send(EventNSClientNewLog("DBADD", "Acked OfflineEvent " + pair.value.interfaceIDs.nightscoutId))
// Send new if waiting
dataSyncSelector.processChangedOfflineEvents()
dataSyncSelectorV1.processChangedOfflineEvents()
}
}

View file

@ -6,7 +6,6 @@ import androidx.work.workDataOf
import info.nightscout.core.utils.receivers.DataWorkerStorage
import info.nightscout.core.utils.worker.LoggingWorker
import info.nightscout.database.impl.AppRepository
import info.nightscout.interfaces.sync.DataSyncSelector
import info.nightscout.interfaces.sync.DataSyncSelector.PairBolus
import info.nightscout.interfaces.sync.DataSyncSelector.PairBolusCalculatorResult
import info.nightscout.interfaces.sync.DataSyncSelector.PairCarbs
@ -19,6 +18,7 @@ import info.nightscout.interfaces.sync.DataSyncSelector.PairProfileSwitch
import info.nightscout.interfaces.sync.DataSyncSelector.PairTemporaryBasal
import info.nightscout.interfaces.sync.DataSyncSelector.PairTemporaryTarget
import info.nightscout.interfaces.sync.DataSyncSelector.PairTherapyEvent
import info.nightscout.interfaces.sync.DataSyncSelectorV1
import info.nightscout.plugins.sync.nsclient.acks.NSUpdateAck
import info.nightscout.rx.AapsSchedulers
import info.nightscout.rx.bus.RxBus
@ -34,7 +34,7 @@ class NSClientUpdateRemoveAckWorker(
@Inject lateinit var dataWorkerStorage: DataWorkerStorage
@Inject lateinit var repository: AppRepository
@Inject lateinit var rxBus: RxBus
@Inject lateinit var dataSyncSelector: DataSyncSelector
@Inject lateinit var dataSyncSelectorV1: DataSyncSelectorV1
@Inject lateinit var aapsSchedulers: AapsSchedulers
override suspend fun doWorkAndLog(): Result {
@ -47,109 +47,109 @@ class NSClientUpdateRemoveAckWorker(
when (ack.originalObject) {
is PairTemporaryTarget -> {
val pair = ack.originalObject
dataSyncSelector.confirmLastTempTargetsIdIfGreater(pair.id)
rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked TemporaryTarget" + ack._id))
dataSyncSelectorV1.confirmLastTempTargetsIdIfGreater(pair.id)
rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked TemporaryTarget" + ack._id))
// Send new if waiting
dataSyncSelector.processChangedTempTargets()
dataSyncSelectorV1.processChangedTempTargets()
ret = Result.success(workDataOf("ProcessedData" to pair.toString()))
}
is PairGlucoseValue -> {
val pair = ack.originalObject
dataSyncSelector.confirmLastGlucoseValueIdIfGreater(pair.id)
rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked GlucoseValue " + ack._id))
dataSyncSelectorV1.confirmLastGlucoseValueIdIfGreater(pair.id)
rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked GlucoseValue " + ack._id))
// Send new if waiting
dataSyncSelector.processChangedGlucoseValues()
dataSyncSelectorV1.processChangedGlucoseValues()
ret = Result.success(workDataOf("ProcessedData" to pair.toString()))
}
is PairFood -> {
val pair = ack.originalObject
dataSyncSelector.confirmLastFoodIdIfGreater(pair.id)
rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked Food " + ack._id))
dataSyncSelectorV1.confirmLastFoodIdIfGreater(pair.id)
rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked Food " + ack._id))
// Send new if waiting
dataSyncSelector.processChangedFoods()
dataSyncSelectorV1.processChangedFoods()
ret = Result.success(workDataOf("ProcessedData" to pair.toString()))
}
is PairTherapyEvent -> {
val pair = ack.originalObject
dataSyncSelector.confirmLastTherapyEventIdIfGreater(pair.id)
rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked TherapyEvent " + ack._id))
dataSyncSelectorV1.confirmLastTherapyEventIdIfGreater(pair.id)
rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked TherapyEvent " + ack._id))
// Send new if waiting
dataSyncSelector.processChangedTherapyEvents()
dataSyncSelectorV1.processChangedTherapyEvents()
ret = Result.success(workDataOf("ProcessedData" to pair.toString()))
}
is PairBolus -> {
val pair = ack.originalObject
dataSyncSelector.confirmLastBolusIdIfGreater(pair.id)
rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked Bolus " + ack._id))
dataSyncSelectorV1.confirmLastBolusIdIfGreater(pair.id)
rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked Bolus " + ack._id))
// Send new if waiting
dataSyncSelector.processChangedBoluses()
dataSyncSelectorV1.processChangedBoluses()
ret = Result.success(workDataOf("ProcessedData" to pair.toString()))
}
is PairCarbs -> {
val pair = ack.originalObject
dataSyncSelector.confirmLastCarbsIdIfGreater(pair.id)
rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked Carbs " + ack._id))
dataSyncSelectorV1.confirmLastCarbsIdIfGreater(pair.id)
rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked Carbs " + ack._id))
// Send new if waiting
dataSyncSelector.processChangedCarbs()
dataSyncSelectorV1.processChangedCarbs()
ret = Result.success(workDataOf("ProcessedData" to pair.toString()))
}
is PairBolusCalculatorResult -> {
val pair = ack.originalObject
dataSyncSelector.confirmLastBolusCalculatorResultsIdIfGreater(pair.id)
rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked BolusCalculatorResult " + ack._id))
dataSyncSelectorV1.confirmLastBolusCalculatorResultsIdIfGreater(pair.id)
rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked BolusCalculatorResult " + ack._id))
// Send new if waiting
dataSyncSelector.processChangedBolusCalculatorResults()
dataSyncSelectorV1.processChangedBolusCalculatorResults()
ret = Result.success(workDataOf("ProcessedData" to pair.toString()))
}
is PairTemporaryBasal -> {
val pair = ack.originalObject
dataSyncSelector.confirmLastTemporaryBasalIdIfGreater(pair.id)
rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked TemporaryBasal " + ack._id))
dataSyncSelectorV1.confirmLastTemporaryBasalIdIfGreater(pair.id)
rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked TemporaryBasal " + ack._id))
// Send new if waiting
dataSyncSelector.processChangedTemporaryBasals()
dataSyncSelectorV1.processChangedTemporaryBasals()
ret = Result.success(workDataOf("ProcessedData" to pair.toString()))
}
is PairExtendedBolus -> {
val pair = ack.originalObject
dataSyncSelector.confirmLastExtendedBolusIdIfGreater(pair.id)
rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked ExtendedBolus " + ack._id))
dataSyncSelectorV1.confirmLastExtendedBolusIdIfGreater(pair.id)
rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked ExtendedBolus " + ack._id))
// Send new if waiting
dataSyncSelector.processChangedExtendedBoluses()
dataSyncSelectorV1.processChangedExtendedBoluses()
ret = Result.success(workDataOf("ProcessedData" to pair.toString()))
}
is PairProfileSwitch -> {
val pair = ack.originalObject
dataSyncSelector.confirmLastProfileSwitchIdIfGreater(pair.id)
rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked ProfileSwitch " + ack._id))
dataSyncSelectorV1.confirmLastProfileSwitchIdIfGreater(pair.id)
rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked ProfileSwitch " + ack._id))
// Send new if waiting
dataSyncSelector.processChangedProfileSwitches()
dataSyncSelectorV1.processChangedProfileSwitches()
ret = Result.success(workDataOf("ProcessedData" to pair.toString()))
}
is PairEffectiveProfileSwitch -> {
val pair = ack.originalObject
dataSyncSelector.confirmLastEffectiveProfileSwitchIdIfGreater(pair.id)
rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked EffectiveProfileSwitch " + ack._id))
dataSyncSelectorV1.confirmLastEffectiveProfileSwitchIdIfGreater(pair.id)
rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked EffectiveProfileSwitch " + ack._id))
// Send new if waiting
dataSyncSelector.processChangedEffectiveProfileSwitches()
dataSyncSelectorV1.processChangedEffectiveProfileSwitches()
ret = Result.success(workDataOf("ProcessedData" to pair.toString()))
}
is PairOfflineEvent -> {
val pair = ack.originalObject
dataSyncSelector.confirmLastOfflineEventIdIfGreater(pair.id)
rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked OfflineEvent" + ack._id))
dataSyncSelectorV1.confirmLastOfflineEventIdIfGreater(pair.id)
rxBus.send(EventNSClientNewLog("DBUPDATE", "Acked OfflineEvent" + ack._id))
// Send new if waiting
dataSyncSelector.processChangedOfflineEvents()
dataSyncSelectorV1.processChangedOfflineEvents()
ret = Result.success(workDataOf("ProcessedData" to pair.toString()))
}
}

View file

@ -0,0 +1,695 @@
package info.nightscout.plugins.sync.nsclientV3
import info.nightscout.androidaps.annotations.OpenForTesting
import info.nightscout.database.impl.AppRepository
import info.nightscout.interfaces.nsclient.StoreDataForDb
import info.nightscout.interfaces.plugin.ActivePlugin
import info.nightscout.interfaces.profile.ProfileFunction
import info.nightscout.interfaces.sync.DataSyncSelector
import info.nightscout.interfaces.sync.DataSyncSelectorV3
import info.nightscout.interfaces.utils.JsonHelper
import info.nightscout.plugins.sync.R
import info.nightscout.plugins.sync.nsShared.events.EventNSClientUpdateGuiQueue
import info.nightscout.plugins.sync.nsShared.events.EventNSClientUpdateGuiStatus
import info.nightscout.rx.bus.RxBus
import info.nightscout.rx.logging.AAPSLogger
import info.nightscout.rx.logging.LTag
import info.nightscout.shared.sharedPreferences.SP
import info.nightscout.shared.utils.DateUtil
import javax.inject.Inject
import javax.inject.Singleton
@OpenForTesting
@Singleton
class DataSyncSelectorV3Impl @Inject constructor(
private val sp: SP,
private val aapsLogger: AAPSLogger,
private val dateUtil: DateUtil,
private val profileFunction: ProfileFunction,
private val activePlugin: ActivePlugin,
private val appRepository: AppRepository,
private val rxBus: RxBus,
private val storeDataForDb: StoreDataForDb
) : DataSyncSelectorV3 {
class QueueCounter(
var bolusesRemaining: Long = -1L,
var carbsRemaining: Long = -1L,
var bcrRemaining: Long = -1L,
var ttsRemaining: Long = -1L,
var foodsRemaining: Long = -1L,
var gvsRemaining: Long = -1L,
var tesRemaining: Long = -1L,
var dssRemaining: Long = -1L,
var tbrsRemaining: Long = -1L,
var ebsRemaining: Long = -1L,
var pssRemaining: Long = -1L,
var epssRemaining: Long = -1L,
var oesRemaining: Long = -1L
) {
fun size(): Long =
bolusesRemaining +
carbsRemaining +
bcrRemaining +
ttsRemaining +
foodsRemaining +
gvsRemaining +
tesRemaining +
dssRemaining +
tbrsRemaining +
ebsRemaining +
pssRemaining +
epssRemaining +
oesRemaining
}
private val queueCounter = QueueCounter()
private val isPaused get() = sp.getBoolean(R.string.key_ns_paused, false)
override fun queueSize(): Long = queueCounter.size()
override suspend fun doUpload() {
rxBus.send(EventNSClientUpdateGuiStatus())
if (sp.getBoolean(R.string.key_ns_upload, true) && !isPaused) {
queueCounter.bolusesRemaining = (appRepository.getLastBolusId() ?: 0L) - sp.getLong(R.string.key_ns_bolus_last_synced_id, 0)
queueCounter.carbsRemaining = (appRepository.getLastCarbsId() ?: 0L) - sp.getLong(R.string.key_ns_carbs_last_synced_id, 0)
queueCounter.bcrRemaining = (appRepository.getLastBolusCalculatorResultId() ?: 0L) - sp.getLong(R.string.key_ns_bolus_calculator_result_last_synced_id, 0)
queueCounter.ttsRemaining = (appRepository.getLastTempTargetId() ?: 0L) - sp.getLong(R.string.key_ns_temporary_target_last_synced_id, 0)
queueCounter.foodsRemaining = (appRepository.getLastFoodId() ?: 0L) - sp.getLong(R.string.key_ns_food_last_synced_id, 0)
queueCounter.gvsRemaining = (appRepository.getLastGlucoseValueId() ?: 0L) - sp.getLong(R.string.key_ns_glucose_value_last_synced_id, 0)
queueCounter.tesRemaining = (appRepository.getLastTherapyEventId() ?: 0L) - sp.getLong(R.string.key_ns_therapy_event_last_synced_id, 0)
queueCounter.dssRemaining = (appRepository.getLastDeviceStatusId() ?: 0L) - sp.getLong(R.string.key_ns_device_status_last_synced_id, 0)
queueCounter.tbrsRemaining = (appRepository.getLastTemporaryBasalId() ?: 0L) - sp.getLong(R.string.key_ns_temporary_basal_last_synced_id, 0)
queueCounter.ebsRemaining = (appRepository.getLastExtendedBolusId() ?: 0L) - sp.getLong(R.string.key_ns_extended_bolus_last_synced_id, 0)
queueCounter.pssRemaining = (appRepository.getLastProfileSwitchId() ?: 0L) - sp.getLong(R.string.key_ns_profile_switch_last_synced_id, 0)
queueCounter.epssRemaining = (appRepository.getLastEffectiveProfileSwitchId() ?: 0L) - sp.getLong(R.string.key_ns_effective_profile_switch_last_synced_id, 0)
queueCounter.oesRemaining = (appRepository.getLastOfflineEventId() ?: 0L) - sp.getLong(R.string.key_ns_offline_event_last_synced_id, 0)
rxBus.send(EventNSClientUpdateGuiQueue())
processChangedGlucoseValues()
processChangedBoluses()
processChangedCarbs()
processChangedBolusCalculatorResults()
processChangedTemporaryBasals()
processChangedExtendedBoluses()
processChangedProfileSwitches()
processChangedEffectiveProfileSwitches()
processChangedTempTargets()
processChangedFoods()
processChangedTherapyEvents()
processChangedDeviceStatuses()
processChangedOfflineEvents()
processChangedProfileStore()
storeDataForDb.updateNsIds()
}
rxBus.send(EventNSClientUpdateGuiStatus())
}
override fun resetToNextFullSync() {
sp.remove(R.string.key_ns_glucose_value_last_synced_id)
sp.remove(R.string.key_ns_temporary_basal_last_synced_id)
sp.remove(R.string.key_ns_temporary_target_last_synced_id)
sp.remove(R.string.key_ns_extended_bolus_last_synced_id)
sp.remove(R.string.key_ns_food_last_synced_id)
sp.remove(R.string.key_ns_bolus_last_synced_id)
sp.remove(R.string.key_ns_carbs_last_synced_id)
sp.remove(R.string.key_ns_bolus_calculator_result_last_synced_id)
sp.remove(R.string.key_ns_therapy_event_last_synced_id)
sp.remove(R.string.key_ns_profile_switch_last_synced_id)
sp.remove(R.string.key_ns_effective_profile_switch_last_synced_id)
sp.remove(R.string.key_ns_offline_event_last_synced_id)
sp.remove(R.string.key_ns_profile_store_last_synced_timestamp)
val lastDeviceStatusDbId = appRepository.getLastDeviceStatusId()
if (lastDeviceStatusDbId != null) sp.putLong(R.string.key_ns_device_status_last_synced_id, lastDeviceStatusDbId)
else sp.remove(R.string.key_ns_device_status_last_synced_id)
}
fun confirmLastBolusIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_bolus_last_synced_id, 0)) {
//aapsLogger.debug(LTag.NSCLIENT, "Setting Bolus data sync from $lastSynced")
sp.putLong(R.string.key_ns_bolus_last_synced_id, lastSynced)
}
}
suspend fun processChangedBoluses() {
val lastDbId = appRepository.getLastBolusId() ?: 0L
var cont = true
while (cont) {
if (isPaused) return
var startId = sp.getLong(R.string.key_ns_bolus_last_synced_id, 0)
if (startId > lastDbId) {
sp.putLong(R.string.key_ns_bolus_last_synced_id, 0)
startId = 0
}
queueCounter.bolusesRemaining = lastDbId - startId
rxBus.send(EventNSClientUpdateGuiQueue())
appRepository.getNextSyncElementBolus(startId).blockingGet()?.let { bolus ->
//aapsLogger.info(LTag.NSCLIENT, "Loading Bolus data Start: $startId ${bolus.first} forID: ${bolus.second.id} ")
when {
// new record with existing NS id => must be coming from NS => ignore
bolus.first.id == bolus.second.id && bolus.first.interfaceIDs.nightscoutId != null ->
aapsLogger.info(LTag.NSCLIENT, "Ignoring Bolus. Loaded from NS: ${bolus.second.id} ")
// only NsId changed, no need to upload
bolus.first.onlyNsIdAdded(bolus.second) ->
aapsLogger.info(LTag.NSCLIENT, "Ignoring Bolus. Only NS id changed: ${bolus.second.id} ")
// without nsId = create new
bolus.first.interfaceIDs.nightscoutId == null ->
cont = activePlugin.activeNsClient?.nsAdd("treatments", DataSyncSelector.PairBolus(bolus.first, bolus.second.id), " $startId/$lastDbId") ?: false
// with nsId = update if it's modified record
bolus.first.interfaceIDs.nightscoutId != null && bolus.first.id != bolus.second.id ->
cont = activePlugin.activeNsClient?.nsUpdate("treatments", DataSyncSelector.PairBolus(bolus.first, bolus.second.id), "$startId/$lastDbId") ?: false
}
confirmLastBolusIdIfGreater(bolus.second.id)
} ?: run {
cont = false
}
}
}
fun confirmLastCarbsIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_carbs_last_synced_id, 0)) {
//aapsLogger.debug(LTag.NSCLIENT, "Setting Carbs data sync from $lastSynced")
sp.putLong(R.string.key_ns_carbs_last_synced_id, lastSynced)
}
}
suspend fun processChangedCarbs() {
val lastDbId = appRepository.getLastCarbsId() ?: 0L
var cont = true
while (cont) {
if (isPaused) return
var startId = sp.getLong(R.string.key_ns_carbs_last_synced_id, 0)
if (startId > lastDbId) {
sp.putLong(R.string.key_ns_carbs_last_synced_id, 0)
startId = 0
}
queueCounter.carbsRemaining = lastDbId - startId
rxBus.send(EventNSClientUpdateGuiQueue())
appRepository.getNextSyncElementCarbs(startId).blockingGet()?.let { carb ->
//aapsLogger.info(LTag.NSCLIENT, "Loading Carbs data Start: $startId ${carb.first} forID: ${carb.second.id} ")
when {
// new record with existing NS id => must be coming from NS => ignore
carb.first.id == carb.second.id && carb.first.interfaceIDs.nightscoutId != null ->
aapsLogger.info(LTag.NSCLIENT, "Ignoring Carbs. Loaded from NS: ${carb.second.id} ")
// only NsId changed, no need to upload
carb.first.onlyNsIdAdded(carb.second) ->
aapsLogger.info(LTag.NSCLIENT, "Ignoring Carbs. Only NS id changed ID: ${carb.second.id} ")
// without nsId = create new
carb.first.interfaceIDs.nightscoutId == null ->
cont = activePlugin.activeNsClient?.nsAdd("treatments", DataSyncSelector.PairCarbs(carb.first, carb.second.id), "$startId/$lastDbId") ?: false
// with nsId = update if it's modified record
carb.first.interfaceIDs.nightscoutId != null && carb.first.id != carb.second.id ->
cont = activePlugin.activeNsClient?.nsUpdate("treatments", DataSyncSelector.PairCarbs(carb.first, carb.second.id), "$startId/$lastDbId") ?: false
}
confirmLastCarbsIdIfGreater(carb.second.id)
} ?: run {
cont = false
}
}
}
fun confirmLastBolusCalculatorResultsIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_bolus_calculator_result_last_synced_id, 0)) {
//aapsLogger.debug(LTag.NSCLIENT, "Setting BolusCalculatorResult data sync from $lastSynced")
sp.putLong(R.string.key_ns_bolus_calculator_result_last_synced_id, lastSynced)
}
}
suspend fun processChangedBolusCalculatorResults() {
val lastDbId = appRepository.getLastBolusCalculatorResultId() ?: 0L
var cont = true
while (cont) {
if (isPaused) return
var startId = sp.getLong(R.string.key_ns_bolus_calculator_result_last_synced_id, 0)
if (startId > lastDbId) {
sp.putLong(R.string.key_ns_bolus_calculator_result_last_synced_id, 0)
startId = 0
}
queueCounter.bcrRemaining = lastDbId - startId
rxBus.send(EventNSClientUpdateGuiQueue())
appRepository.getNextSyncElementBolusCalculatorResult(startId).blockingGet()?.let { bolusCalculatorResult ->
//aapsLogger.info(LTag.NSCLIENT, "Loading BolusCalculatorResult data Start: $startId ${bolusCalculatorResult.first} forID: ${bolusCalculatorResult.second.id} ")
when {
// new record with existing NS id => must be coming from NS => ignore
bolusCalculatorResult.first.id == bolusCalculatorResult.second.id && bolusCalculatorResult.first.interfaceIDs.nightscoutId != null ->
aapsLogger.info(LTag.NSCLIENT, "Ignoring BolusCalculatorResult. Loaded from NS: ${bolusCalculatorResult.second.id} ")
// only NsId changed, no need to upload
bolusCalculatorResult.first.onlyNsIdAdded(bolusCalculatorResult.second) ->
aapsLogger.info(LTag.NSCLIENT, "Ignoring BolusCalculatorResult. Only NS id changed ID: ${bolusCalculatorResult.second.id} ")
// without nsId = create new
bolusCalculatorResult.first.interfaceIDs.nightscoutId == null ->
cont = activePlugin.activeNsClient?.nsAdd(
"treatments",
DataSyncSelector.PairBolusCalculatorResult(bolusCalculatorResult.first, bolusCalculatorResult.second.id),
"$startId/$lastDbId"
) ?: false
// with nsId = update if it's modified record
bolusCalculatorResult.first.interfaceIDs.nightscoutId != null && bolusCalculatorResult.first.id != bolusCalculatorResult.second.id ->
cont = activePlugin.activeNsClient?.nsUpdate(
"treatments",
DataSyncSelector.PairBolusCalculatorResult(bolusCalculatorResult.first, bolusCalculatorResult.second.id),
"$startId/$lastDbId"
) ?: false
}
confirmLastBolusCalculatorResultsIdIfGreater(bolusCalculatorResult.second.id)
} ?: run {
cont = false
}
}
}
fun confirmLastTempTargetsIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_temporary_target_last_synced_id, 0)) {
//aapsLogger.debug(LTag.NSCLIENT, "Setting TemporaryTarget data sync from $lastSynced")
sp.putLong(R.string.key_ns_temporary_target_last_synced_id, lastSynced)
}
}
suspend fun processChangedTempTargets() {
val lastDbId = appRepository.getLastTempTargetId() ?: 0L
var cont = true
while (cont) {
if (isPaused) return
var startId = sp.getLong(R.string.key_ns_temporary_target_last_synced_id, 0)
if (startId > lastDbId) {
sp.putLong(R.string.key_ns_temporary_target_last_synced_id, 0)
startId = 0
}
queueCounter.ttsRemaining = lastDbId - startId
rxBus.send(EventNSClientUpdateGuiQueue())
appRepository.getNextSyncElementTemporaryTarget(startId).blockingGet()?.let { tt ->
//aapsLogger.info(LTag.NSCLIENT, "Loading TemporaryTarget data Start: $startId ${tt.first} forID: ${tt.second.id} ")
when {
// new record with existing NS id => must be coming from NS => ignore
tt.first.id == tt.second.id && tt.first.interfaceIDs.nightscoutId != null ->
aapsLogger.info(LTag.NSCLIENT, "Ignoring TemporaryTarget. Loaded from NS: ${tt.second.id} ")
// only NsId changed, no need to upload
tt.first.onlyNsIdAdded(tt.second) ->
aapsLogger.info(LTag.NSCLIENT, "Ignoring TemporaryTarget. Only NS id changed ID: ${tt.second.id} ")
// without nsId = create new
tt.first.interfaceIDs.nightscoutId == null ->
cont = activePlugin.activeNsClient?.nsAdd("treatments", DataSyncSelector.PairTemporaryTarget(tt.first, tt.second.id), "$startId/$lastDbId") ?: false
// existing with nsId = update
tt.first.interfaceIDs.nightscoutId != null ->
cont = activePlugin.activeNsClient?.nsUpdate("treatments", DataSyncSelector.PairTemporaryTarget(tt.first, tt.second.id), "$startId/$lastDbId") ?: false
}
confirmLastTempTargetsIdIfGreater(tt.second.id)
} ?: run {
cont = false
}
}
}
fun confirmLastFoodIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_food_last_synced_id, 0)) {
//aapsLogger.debug(LTag.NSCLIENT, "Setting Food data sync from $lastSynced")
sp.putLong(R.string.key_ns_food_last_synced_id, lastSynced)
}
}
suspend fun processChangedFoods() {
val lastDbId = appRepository.getLastFoodId() ?: 0L
var cont = true
while (cont) {
if (isPaused) return
var startId = sp.getLong(R.string.key_ns_food_last_synced_id, 0)
if (startId > lastDbId) {
sp.putLong(R.string.key_ns_food_last_synced_id, 0)
startId = 0
}
queueCounter.foodsRemaining = lastDbId - startId
rxBus.send(EventNSClientUpdateGuiQueue())
appRepository.getNextSyncElementFood(startId).blockingGet()?.let { food ->
//aapsLogger.info(LTag.NSCLIENT, "Loading Food data Start: $startId ${food.first} forID: ${food.second.id} ")
when {
// new record with existing NS id => must be coming from NS => ignore
food.first.id == food.second.id && food.first.interfaceIDs.nightscoutId != null ->
aapsLogger.info(LTag.NSCLIENT, "Ignoring Food. Loaded from NS: ${food.second.id} ")
// only NsId changed, no need to upload
food.first.onlyNsIdAdded(food.second) ->
aapsLogger.info(LTag.NSCLIENT, "Ignoring Food. Only NS id changed ID: ${food.second.id} ")
// without nsId = create new
food.first.interfaceIDs.nightscoutId == null ->
cont = activePlugin.activeNsClient?.nsAdd("food", DataSyncSelector.PairFood(food.first, food.second.id), "$startId/$lastDbId") ?: false
// with nsId = update
food.first.interfaceIDs.nightscoutId != null ->
cont = activePlugin.activeNsClient?.nsUpdate("food", DataSyncSelector.PairFood(food.first, food.second.id), "$startId/$lastDbId") ?: false
}
confirmLastFoodIdIfGreater(food.second.id)
} ?: run {
cont = false
}
}
}
fun confirmLastGlucoseValueIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_glucose_value_last_synced_id, 0)) {
//aapsLogger.debug(LTag.NSCLIENT, "Setting GlucoseValue data sync from $lastSynced")
sp.putLong(R.string.key_ns_glucose_value_last_synced_id, lastSynced)
}
}
suspend fun processChangedGlucoseValues() {
val lastDbId = appRepository.getLastGlucoseValueId() ?: 0L
var cont = true
while (cont) {
if (isPaused) return
var startId = sp.getLong(R.string.key_ns_glucose_value_last_synced_id, 0)
if (startId > lastDbId) {
sp.putLong(R.string.key_ns_glucose_value_last_synced_id, 0)
startId = 0
}
queueCounter.gvsRemaining = lastDbId - startId
rxBus.send(EventNSClientUpdateGuiQueue())
appRepository.getNextSyncElementGlucoseValue(startId).blockingGet()?.let { gv ->
//aapsLogger.info(LTag.NSCLIENT, "Loading GlucoseValue data Start: $startId ${gv.first} forID: ${gv.second.id} ")
if (activePlugin.activeBgSource.shouldUploadToNs(gv.first)) {
when {
// new record with existing NS id => must be coming from NS => ignore
gv.first.id == gv.second.id && gv.first.interfaceIDs.nightscoutId != null ->
aapsLogger.info(LTag.NSCLIENT, "Ignoring GlucoseValue. Loaded from NS: ${gv.second.id} ")
// only NsId changed, no need to upload
gv.first.onlyNsIdAdded(gv.second) ->
aapsLogger.info(LTag.NSCLIENT, "Ignoring GlucoseValue. Only NS id changed ID: ${gv.second.id} ")
// without nsId = create new
gv.first.interfaceIDs.nightscoutId == null ->
cont = activePlugin.activeNsClient?.nsAdd("entries", DataSyncSelector.PairGlucoseValue(gv.first, gv.second.id), "$startId/$lastDbId") ?: false
// with nsId = update
else -> // gv.first.interfaceIDs.nightscoutId != null
cont = activePlugin.activeNsClient?.nsUpdate("entries", DataSyncSelector.PairGlucoseValue(gv.first, gv.second.id), "$startId/$lastDbId") ?: false
}
}
confirmLastGlucoseValueIdIfGreater(gv.second.id)
} ?: run {
cont = false
}
}
}
fun confirmLastTherapyEventIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_therapy_event_last_synced_id, 0)) {
//aapsLogger.debug(LTag.NSCLIENT, "Setting TherapyEvents data sync from $lastSynced")
sp.putLong(R.string.key_ns_therapy_event_last_synced_id, lastSynced)
}
}
suspend fun processChangedTherapyEvents() {
val lastDbId = appRepository.getLastTherapyEventId() ?: 0L
var cont = true
while (cont) {
if (isPaused) return
var startId = sp.getLong(R.string.key_ns_therapy_event_last_synced_id, 0)
if (startId > lastDbId) {
sp.putLong(R.string.key_ns_therapy_event_last_synced_id, 0)
startId = 0
}
queueCounter.tesRemaining = lastDbId - startId
rxBus.send(EventNSClientUpdateGuiQueue())
appRepository.getNextSyncElementTherapyEvent(startId).blockingGet()?.let { te ->
//aapsLogger.info(LTag.NSCLIENT, "Loading TherapyEvents data Start: $startId ${te.first} forID: ${te.second.id} ")
when {
// new record with existing NS id => must be coming from NS => ignore
te.first.id == te.second.id && te.first.interfaceIDs.nightscoutId != null ->
aapsLogger.info(LTag.NSCLIENT, "Ignoring TherapyEvent. Loaded from NS: ${te.second.id} ")
// only NsId changed, no need to upload
te.first.onlyNsIdAdded(te.second) ->
aapsLogger.info(LTag.NSCLIENT, "Ignoring TherapyEvent. Only NS id changed ID: ${te.second.id} ")
// without nsId = create new
te.first.interfaceIDs.nightscoutId == null ->
cont = activePlugin.activeNsClient?.nsAdd("treatments", DataSyncSelector.PairTherapyEvent(te.first, te.second.id), "$startId/$lastDbId") ?: false
// nsId = update
te.first.interfaceIDs.nightscoutId != null ->
cont = activePlugin.activeNsClient?.nsUpdate("treatments", DataSyncSelector.PairTherapyEvent(te.first, te.second.id), "$startId/$lastDbId") ?: false
}
confirmLastTherapyEventIdIfGreater(te.second.id)
} ?: run {
cont = false
}
}
}
fun confirmLastDeviceStatusIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_device_status_last_synced_id, 0)) {
//aapsLogger.debug(LTag.NSCLIENT, "Setting DeviceStatus data sync from $lastSynced")
sp.putLong(R.string.key_ns_device_status_last_synced_id, lastSynced)
}
}
suspend fun processChangedDeviceStatuses() {
if (isPaused) return
val lastDbId = appRepository.getLastDeviceStatusId() ?: 0L
var startId = sp.getLong(R.string.key_ns_device_status_last_synced_id, 0)
if (startId > lastDbId) {
sp.putLong(R.string.key_ns_device_status_last_synced_id, 0)
startId = 0
}
queueCounter.dssRemaining = lastDbId - startId
rxBus.send(EventNSClientUpdateGuiQueue())
appRepository.getNextSyncElementDeviceStatus(startId).blockingGet()?.let { deviceStatus ->
//aapsLogger.info(LTag.NSCLIENT, "Loading DeviceStatus data Start: $startId $deviceStatus")
// without nsId = create new
if (deviceStatus.interfaceIDs.nightscoutId == null) {
if (activePlugin.activeNsClient?.nsAdd("devicestatus", DataSyncSelector.PairDeviceStatus(deviceStatus, lastDbId), "$startId/$lastDbId") == true)
confirmLastDeviceStatusIdIfGreater(lastDbId)
}
// with nsId = ignore
}
queueCounter.dssRemaining = 0
}
fun confirmLastTemporaryBasalIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_temporary_basal_last_synced_id, 0)) {
//aapsLogger.debug(LTag.NSCLIENT, "Setting TemporaryBasal data sync from $lastSynced")
sp.putLong(R.string.key_ns_temporary_basal_last_synced_id, lastSynced)
}
}
suspend fun processChangedTemporaryBasals() {
val lastDbId = appRepository.getLastTemporaryBasalId() ?: 0L
var cont = true
while (cont) {
if (isPaused) return
var startId = sp.getLong(R.string.key_ns_temporary_basal_last_synced_id, 0)
if (startId > lastDbId) {
sp.putLong(R.string.key_ns_temporary_basal_last_synced_id, 0)
startId = 0
}
queueCounter.tbrsRemaining = lastDbId - startId
rxBus.send(EventNSClientUpdateGuiQueue())
appRepository.getNextSyncElementTemporaryBasal(startId).blockingGet()?.let { tb ->
//aapsLogger.info(LTag.NSCLIENT, "Loading TemporaryBasal data Start: $startId ${tb.first} forID: ${tb.second.id} ")
when {
// new record with existing NS id => must be coming from NS => ignore
tb.first.id == tb.second.id && tb.first.interfaceIDs.nightscoutId != null ->
aapsLogger.info(LTag.NSCLIENT, "Ignoring TemporaryBasal. Loaded from NS: ${tb.second.id} ")
// only NsId changed, no need to upload
tb.first.onlyNsIdAdded(tb.second) ->
aapsLogger.info(LTag.NSCLIENT, "Ignoring TemporaryBasal. Only NS id changed ID: ${tb.second.id} ")
// without nsId = create new
tb.first.interfaceIDs.nightscoutId == null ->
cont = activePlugin.activeNsClient?.nsAdd("treatments", DataSyncSelector.PairTemporaryBasal(tb.first, tb.second.id), "$startId/$lastDbId") ?: false
// with nsId = update
tb.first.interfaceIDs.nightscoutId != null ->
cont = activePlugin.activeNsClient?.nsUpdate("treatments", DataSyncSelector.PairTemporaryBasal(tb.first, tb.second.id), "$startId/$lastDbId") ?: false
}
confirmLastTemporaryBasalIdIfGreater(tb.second.id)
} ?: run {
cont = false
}
}
}
fun confirmLastExtendedBolusIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_extended_bolus_last_synced_id, 0)) {
//aapsLogger.debug(LTag.NSCLIENT, "Setting ExtendedBolus data sync from $lastSynced")
sp.putLong(R.string.key_ns_extended_bolus_last_synced_id, lastSynced)
}
}
suspend fun processChangedExtendedBoluses() {
val lastDbId = appRepository.getLastExtendedBolusId() ?: 0L
var cont = true
while (cont) {
if (isPaused) return
var startId = sp.getLong(R.string.key_ns_extended_bolus_last_synced_id, 0)
if (startId > lastDbId) {
sp.putLong(R.string.key_ns_extended_bolus_last_synced_id, 0)
startId = 0
}
queueCounter.ebsRemaining = lastDbId - startId
rxBus.send(EventNSClientUpdateGuiQueue())
appRepository.getNextSyncElementExtendedBolus(startId).blockingGet()?.let { eb ->
//aapsLogger.info(LTag.NSCLIENT, "Loading ExtendedBolus data Start: $startId ${eb.first} forID: ${eb.second.id} ")
val profile = profileFunction.getProfile(eb.first.timestamp)
if (profile != null) {
when {
// new record with existing NS id => must be coming from NS => ignore
eb.first.id == eb.second.id && eb.first.interfaceIDs.nightscoutId != null ->
aapsLogger.info(LTag.NSCLIENT, "Ignoring ExtendedBolus. Loaded from NS: ${eb.second.id} ")
// only NsId changed, no need to upload
eb.first.onlyNsIdAdded(eb.second) ->
aapsLogger.info(LTag.NSCLIENT, "Ignoring ExtendedBolus. Only NS id changed ID: ${eb.second.id} ")
// without nsId = create new
eb.first.interfaceIDs.nightscoutId == null ->
cont = activePlugin.activeNsClient?.nsAdd("treatments", DataSyncSelector.PairExtendedBolus(eb.first, eb.second.id), "$startId/$lastDbId") ?: false
// with nsId = update
eb.first.interfaceIDs.nightscoutId != null ->
cont = activePlugin.activeNsClient?.nsUpdate("treatments", DataSyncSelector.PairExtendedBolus(eb.first, eb.second.id), "$startId/$lastDbId") ?: false
}
} else aapsLogger.info(LTag.NSCLIENT, "Ignoring ExtendedBolus. No profile: ${eb.second.id} ")
confirmLastExtendedBolusIdIfGreater(eb.second.id)
} ?: run {
cont = false
}
}
}
fun confirmLastProfileSwitchIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_profile_switch_last_synced_id, 0)) {
//aapsLogger.debug(LTag.NSCLIENT, "Setting ProfileSwitch data sync from $lastSynced")
sp.putLong(R.string.key_ns_profile_switch_last_synced_id, lastSynced)
}
}
suspend fun processChangedProfileSwitches() {
val lastDbId = appRepository.getLastProfileSwitchId() ?: 0L
var cont = true
while (cont) {
if (isPaused) return
var startId = sp.getLong(R.string.key_ns_profile_switch_last_synced_id, 0)
if (startId > lastDbId) {
sp.putLong(R.string.key_ns_profile_switch_last_synced_id, 0)
startId = 0
}
queueCounter.pssRemaining = lastDbId - startId
rxBus.send(EventNSClientUpdateGuiQueue())
appRepository.getNextSyncElementProfileSwitch(startId).blockingGet()?.let { ps ->
//aapsLogger.info(LTag.NSCLIENT, "Loading ProfileSwitch data Start: $startId ${ps.first} forID: ${ps.second.id} ")
when {
// new record with existing NS id => must be coming from NS => ignore
ps.first.id == ps.second.id && ps.first.interfaceIDs.nightscoutId != null ->
aapsLogger.info(LTag.NSCLIENT, "Ignoring ProfileSwitch. Loaded from NS: ${ps.second.id} ")
// only NsId changed, no need to upload
ps.first.onlyNsIdAdded(ps.second) ->
aapsLogger.info(LTag.NSCLIENT, "Ignoring ProfileSwitch. Only NS id changed ID: ${ps.second.id} ")
// without nsId = create new
ps.first.interfaceIDs.nightscoutId == null ->
cont = activePlugin.activeNsClient?.nsAdd("treatments", DataSyncSelector.PairProfileSwitch(ps.first, ps.second.id), "$startId/$lastDbId") ?: false
// with nsId = update
ps.first.interfaceIDs.nightscoutId != null ->
cont = activePlugin.activeNsClient?.nsUpdate("treatments", DataSyncSelector.PairProfileSwitch(ps.first, ps.second.id), "$startId/$lastDbId") ?: false
}
confirmLastProfileSwitchIdIfGreater(ps.second.id)
} ?: run {
cont = false
}
}
}
fun confirmLastEffectiveProfileSwitchIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_effective_profile_switch_last_synced_id, 0)) {
//aapsLogger.debug(LTag.NSCLIENT, "Setting EffectiveProfileSwitch data sync from $lastSynced")
sp.putLong(R.string.key_ns_effective_profile_switch_last_synced_id, lastSynced)
}
}
suspend fun processChangedEffectiveProfileSwitches() {
val lastDbId = appRepository.getLastEffectiveProfileSwitchId() ?: 0L
var cont = true
while (cont) {
if (isPaused) return
var startId = sp.getLong(R.string.key_ns_effective_profile_switch_last_synced_id, 0)
if (startId > lastDbId) {
sp.putLong(R.string.key_ns_effective_profile_switch_last_synced_id, 0)
startId = 0
}
queueCounter.epssRemaining = lastDbId - startId
rxBus.send(EventNSClientUpdateGuiQueue())
appRepository.getNextSyncElementEffectiveProfileSwitch(startId).blockingGet()?.let { ps ->
//aapsLogger.info(LTag.NSCLIENT, "Loading EffectiveProfileSwitch data Start: $startId ${ps.first} forID: ${ps.second.id} ")
when {
// new record with existing NS id => must be coming from NS => ignore
ps.first.id == ps.second.id && ps.first.interfaceIDs.nightscoutId != null ->
aapsLogger.info(LTag.NSCLIENT, "Ignoring EffectiveProfileSwitch. Loaded from NS: ${ps.second.id} ")
// only NsId changed, no need to upload
ps.first.onlyNsIdAdded(ps.second) ->
aapsLogger.info(LTag.NSCLIENT, "Ignoring EffectiveProfileSwitch. Only NS id changed ID: ${ps.second.id} ")
// without nsId = create new
ps.first.interfaceIDs.nightscoutId == null ->
cont = activePlugin.activeNsClient?.nsAdd("treatments", DataSyncSelector.PairEffectiveProfileSwitch(ps.first, ps.second.id), "$startId/$lastDbId") ?: false
// with nsId = update
ps.first.interfaceIDs.nightscoutId != null ->
cont = activePlugin.activeNsClient?.nsUpdate("treatments", DataSyncSelector.PairEffectiveProfileSwitch(ps.first, ps.second.id), "$startId/$lastDbId") ?: false
}
confirmLastEffectiveProfileSwitchIdIfGreater(ps.second.id)
} ?: run {
cont = false
}
}
}
fun confirmLastOfflineEventIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_ns_offline_event_last_synced_id, 0)) {
//aapsLogger.debug(LTag.NSCLIENT, "Setting OfflineEvent data sync from $lastSynced")
sp.putLong(R.string.key_ns_offline_event_last_synced_id, lastSynced)
}
}
suspend fun processChangedOfflineEvents() {
val lastDbId = appRepository.getLastOfflineEventId() ?: 0L
var cont = true
while (cont) {
if (isPaused) return
var startId = sp.getLong(R.string.key_ns_offline_event_last_synced_id, 0)
if (startId > lastDbId) {
sp.putLong(R.string.key_ns_offline_event_last_synced_id, 0)
startId = 0
}
queueCounter.oesRemaining = lastDbId - startId
rxBus.send(EventNSClientUpdateGuiQueue())
appRepository.getNextSyncElementOfflineEvent(startId).blockingGet()?.let { oe ->
//aapsLogger.info(LTag.NSCLIENT, "Loading OfflineEvent data Start: $startId ${oe.first} forID: ${oe.second.id} ")
when {
// new record with existing NS id => must be coming from NS => ignore
oe.first.id == oe.second.id && oe.first.interfaceIDs.nightscoutId != null ->
aapsLogger.info(LTag.NSCLIENT, "Ignoring OfflineEvent. Loaded from NS: ${oe.second.id} ")
// only NsId changed, no need to upload
oe.first.onlyNsIdAdded(oe.second) ->
aapsLogger.info(LTag.NSCLIENT, "Ignoring OfflineEvent. Only NS id changed ID: ${oe.second.id} ")
// without nsId = create new
oe.first.interfaceIDs.nightscoutId == null ->
cont = activePlugin.activeNsClient?.nsAdd("treatments", DataSyncSelector.PairOfflineEvent(oe.first, oe.second.id), "$startId/$lastDbId") ?: false
// existing with nsId = update
oe.first.interfaceIDs.nightscoutId != null ->
cont = activePlugin.activeNsClient?.nsUpdate("treatments", DataSyncSelector.PairOfflineEvent(oe.first, oe.second.id), "$startId/$lastDbId") ?: false
}
confirmLastOfflineEventIdIfGreater(oe.second.id)
} ?: run {
cont = false
}
}
}
fun confirmLastProfileStore(lastSynced: Long) {
sp.putLong(R.string.key_ns_profile_store_last_synced_timestamp, lastSynced)
}
suspend fun processChangedProfileStore() {
if (isPaused) return
val lastSync = sp.getLong(R.string.key_ns_profile_store_last_synced_timestamp, 0)
val lastChange = sp.getLong(info.nightscout.core.utils.R.string.key_local_profile_last_change, 0)
if (lastChange == 0L) return
if (lastChange > lastSync) {
if (activePlugin.activeProfileSource.profile?.allProfilesValid != true) return
val profileStore = activePlugin.activeProfileSource.profile
val profileJson = profileStore?.data ?: return
// add for v3
if (JsonHelper.safeGetLongAllowNull(profileJson, "date") == null)
profileJson.put("date", profileStore.getStartDate())
val now = dateUtil.now()
if (activePlugin.activeNsClient?.nsAdd("profile", DataSyncSelector.PairProfileStore(profileJson, now), "") == true)
confirmLastProfileStore(now)
}
}
}

View file

@ -4,7 +4,6 @@ import android.content.Context
import android.os.Handler
import android.os.HandlerThread
import android.os.SystemClock
import android.text.Spanned
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceScreen
import androidx.preference.SwitchPreference
@ -17,7 +16,6 @@ import com.google.gson.GsonBuilder
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.annotations.OpenForTesting
import info.nightscout.core.utils.fabric.FabricPrivacy
import info.nightscout.core.utils.receivers.DataWorkerStorage
import info.nightscout.database.ValueWrapper
import info.nightscout.database.entities.interfaces.TraceableDBEntry
import info.nightscout.database.impl.AppRepository
@ -25,23 +23,24 @@ import info.nightscout.interfaces.Config
import info.nightscout.interfaces.Constants
import info.nightscout.interfaces.notifications.Notification
import info.nightscout.interfaces.nsclient.NSAlarm
import info.nightscout.interfaces.nsclient.StoreDataForDb
import info.nightscout.interfaces.plugin.PluginBase
import info.nightscout.interfaces.plugin.PluginDescription
import info.nightscout.interfaces.plugin.PluginType
import info.nightscout.interfaces.profile.ProfileFunction
import info.nightscout.interfaces.source.NSClientSource
import info.nightscout.interfaces.sync.DataSyncSelector
import info.nightscout.interfaces.sync.DataSyncSelectorV3
import info.nightscout.interfaces.sync.NsClient
import info.nightscout.interfaces.sync.Sync
import info.nightscout.interfaces.ui.UiInteraction
import info.nightscout.interfaces.utils.HtmlHelper
import info.nightscout.interfaces.workflow.WorkerClasses
import info.nightscout.plugins.sync.R
import info.nightscout.plugins.sync.nsShared.NSClientFragment
import info.nightscout.plugins.sync.nsShared.StoreDataForDbImpl
import info.nightscout.plugins.sync.nsShared.NsIncomingDataProcessor
import info.nightscout.plugins.sync.nsShared.events.EventConnectivityOptionChanged
import info.nightscout.plugins.sync.nsShared.events.EventNSClientResend
import info.nightscout.plugins.sync.nsShared.events.EventNSClientUpdateGUI
import info.nightscout.plugins.sync.nsShared.events.EventNSClientUpdateGuiInsert
import info.nightscout.plugins.sync.nsShared.events.EventNSClientUpdateGuiStatus
import info.nightscout.plugins.sync.nsclient.ReceiverDelegate
import info.nightscout.plugins.sync.nsclient.data.NSDeviceStatusHandler
import info.nightscout.plugins.sync.nsclientV3.extensions.toNSBolus
@ -59,10 +58,12 @@ import info.nightscout.plugins.sync.nsclientV3.extensions.toNSTemporaryTarget
import info.nightscout.plugins.sync.nsclientV3.extensions.toNSTherapyEvent
import info.nightscout.plugins.sync.nsclientV3.workers.DataSyncWorker
import info.nightscout.plugins.sync.nsclientV3.workers.LoadBgWorker
import info.nightscout.plugins.sync.nsclientV3.workers.LoadDeviceStatusWorker
import info.nightscout.plugins.sync.nsclientV3.workers.LoadFoodsWorker
import info.nightscout.plugins.sync.nsclientV3.workers.LoadLastModificationWorker
import info.nightscout.plugins.sync.nsclientV3.workers.LoadProfileStoreWorker
import info.nightscout.plugins.sync.nsclientV3.workers.LoadStatusWorker
import info.nightscout.plugins.sync.nsclientV3.workers.ProcessFoodWorker
import info.nightscout.plugins.sync.nsclientV3.workers.ProcessTreatmentsWorker
import info.nightscout.plugins.sync.nsclientV3.workers.LoadTreatmentsWorker
import info.nightscout.rx.AapsSchedulers
import info.nightscout.rx.bus.RxBus
import info.nightscout.rx.events.EventAppExit
@ -90,18 +91,16 @@ import io.socket.client.Ack
import io.socket.client.IO
import io.socket.client.Socket
import io.socket.emitter.Emitter
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import org.json.JSONArray
import org.json.JSONObject
import java.net.URISyntaxException
import java.security.InvalidParameterException
import javax.inject.Inject
import javax.inject.Singleton
@Suppress("SpellCheckingInspection")
@OpenForTesting
@Singleton
class NSClientV3Plugin @Inject constructor(
@ -117,14 +116,13 @@ class NSClientV3Plugin @Inject constructor(
private val config: Config,
private val dateUtil: DateUtil,
private val uiInteraction: UiInteraction,
private val dataSyncSelector: DataSyncSelector,
private val dataSyncSelectorV3: DataSyncSelectorV3,
private val profileFunction: ProfileFunction,
private val repository: AppRepository,
private val nsDeviceStatusHandler: NSDeviceStatusHandler,
private val workManager: WorkManager,
private val workerClasses: WorkerClasses,
private val dataWorkerStorage: DataWorkerStorage,
private val nsClientSource: NSClientSource
private val nsClientSource: NSClientSource,
private val nsIncomingDataProcessor: NsIncomingDataProcessor,
private val storeDataForDb: StoreDataForDb
) : NsClient, Sync, PluginBase(
PluginDescription()
.mainType(PluginType.SYNC)
@ -146,10 +144,10 @@ class NSClientV3Plugin @Inject constructor(
}
private val disposable = CompositeDisposable()
var scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
private lateinit var runLoop: Runnable
private val handler = Handler(HandlerThread(this::class.simpleName + "Handler").also { it.start() }.looper)
private val listLog: MutableList<EventNSClientNewLog> = ArrayList()
override val listLog: MutableList<EventNSClientNewLog> = ArrayList()
override val dataSyncSelector: DataSyncSelector get() = dataSyncSelectorV3
override val status
get() =
when {
@ -159,7 +157,7 @@ class NSClientV3Plugin @Inject constructor(
sp.getBoolean(info.nightscout.core.utils.R.string.key_ns_use_ws, true) && !wsConnected -> "WS: " + rh.gs(R.string.not_connected)
lastOperationError != null -> rh.gs(info.nightscout.core.ui.R.string.error)
nsAndroidClient?.lastStatus == null -> rh.gs(R.string.not_connected)
workIsRunning(arrayOf(JOB_NAME)) -> rh.gs(R.string.working)
workIsRunning() -> rh.gs(R.string.working)
nsAndroidClient?.lastStatus?.apiPermissions?.isFull() == true -> rh.gs(info.nightscout.shared.R.string.connected)
nsAndroidClient?.lastStatus?.apiPermissions?.isRead() == true -> rh.gs(R.string.read_only)
else -> rh.gs(info.nightscout.core.ui.R.string.unknown)
@ -196,6 +194,7 @@ class NSClientV3Plugin @Inject constructor(
rxBus.send(EventNSClientNewLog("● CONNECTIVITY", ev.blockingReason))
setClient("CONNECTIVITY")
if (isAllowed) executeLoop("CONNECTIVITY", forceNew = false)
rxBus.send(EventNSClientUpdateGuiStatus())
}, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventPreferenceChange::class.java)
@ -290,10 +289,14 @@ class NSClientV3Plugin @Inject constructor(
override val hasWritePermission: Boolean get() = nsAndroidClient?.lastStatus?.apiPermissions?.isFull() ?: false
override val connected: Boolean get() = nsAndroidClient?.lastStatus != null
override fun clearLog() {
handler.post {
synchronized(listLog) { listLog.clear() }
rxBus.send(EventNSClientUpdateGUI())
private fun addToLog(ev: EventNSClientNewLog) {
synchronized(listLog) {
listLog.add(0, ev)
rxBus.send(EventNSClientUpdateGuiInsert(0))
// remove the first line if log is too large
if (listLog.size >= Constants.MAX_LOG_LINES) {
listLog.removeAt(listLog.size - 1)
}
}
}
@ -433,40 +436,21 @@ class NSClientV3Plugin @Inject constructor(
when (collection) {
"devicestatus" -> docString.toNSDeviceStatus().let { nsDeviceStatusHandler.handleNewData(arrayOf(it)) }
"entries" -> docString.toNSSgvV3()?.let {
workManager.beginUniqueWork(
JOB_NAME + collection,
ExistingWorkPolicy.APPEND_OR_REPLACE,
OneTimeWorkRequest.Builder(workerClasses.nsClientSourceWorker).setInputData(dataWorkerStorage.storeInputData(listOf(it))).build()
)
.then(OneTimeWorkRequest.Builder(StoreDataForDbImpl.StoreBgWorker::class.java).build())
.enqueue()
nsIncomingDataProcessor.processSgvs(listOf(it))
storeDataForDb.storeGlucoseValuesToDb()
}
"profile" ->
workManager.enqueueUniqueWork(
JOB_NAME + collection,
ExistingWorkPolicy.APPEND_OR_REPLACE,
OneTimeWorkRequest.Builder(workerClasses.nsProfileWorker).setInputData(dataWorkerStorage.storeInputData(docJson)).build()
)
nsIncomingDataProcessor.processProfile(docJson)
"treatments" -> docString.toNSTreatment()?.let {
workManager.beginUniqueWork(
JOB_NAME + collection,
ExistingWorkPolicy.APPEND_OR_REPLACE,
OneTimeWorkRequest.Builder(ProcessTreatmentsWorker::class.java).setInputData(dataWorkerStorage.storeInputData(listOf(it))).build()
)
.then(OneTimeWorkRequest.Builder(StoreDataForDbImpl.StoreTreatmentsWorker::class.java).build())
.enqueue()
nsIncomingDataProcessor.processTreatments(listOf(it))
storeDataForDb.storeTreatmentsToDb()
}
"foods" -> docString.toNSFood()?.let {
workManager.beginUniqueWork(
JOB_NAME + collection,
ExistingWorkPolicy.APPEND_OR_REPLACE,
OneTimeWorkRequest.Builder(ProcessFoodWorker::class.java).setInputData(dataWorkerStorage.storeInputData(listOf(it))).build()
)
.then(OneTimeWorkRequest.Builder(StoreDataForDbImpl.StoreFoodWorker::class.java).build())
.enqueue()
nsIncomingDataProcessor.processFood(listOf(it))
storeDataForDb.storeFoodsToDb()
}
"settings" -> {}
@ -565,30 +549,6 @@ class NSClientV3Plugin @Inject constructor(
WS code end
**********************/
private fun addToLog(ev: EventNSClientNewLog) {
synchronized(listLog) {
listLog.add(ev)
// remove the first line if log is too large
if (listLog.size >= Constants.MAX_LOG_LINES) {
listLog.removeAt(0)
}
}
rxBus.send(EventNSClientUpdateGUI())
}
override fun textLog(): Spanned {
try {
val newTextLog = StringBuilder()
synchronized(listLog) {
for (log in listLog) newTextLog.append(log.toPreparedHtml())
}
return HtmlHelper.fromHtml(newTextLog.toString())
} catch (e: OutOfMemoryError) {
uiInteraction.showToastAndNotification(context, "Out of memory!\nStop using this phone !!!", info.nightscout.core.ui.R.raw.error)
}
return HtmlHelper.fromHtml("")
}
override fun resend(reason: String) {
if (sp.getBoolean(info.nightscout.core.utils.R.string.key_ns_use_ws, true))
executeUpload("RESEND", forceNew = false)
@ -626,23 +586,26 @@ class NSClientV3Plugin @Inject constructor(
lastLoadedSrvModified = LastModified(LastModified.Collections())
initialLoadFinished = false
storeLastLoadedSrvModified()
dataSyncSelector.resetToNextFullSync()
dataSyncSelectorV3.resetToNextFullSync()
}
override fun nsAdd(collection: String, dataPair: DataSyncSelector.DataPair, progress: String) {
override suspend fun nsAdd(collection: String, dataPair: DataSyncSelector.DataPair, progress: String): Boolean =
dbOperation(collection, dataPair, progress, Operation.CREATE)
}
override fun nsUpdate(collection: String, dataPair: DataSyncSelector.DataPair, progress: String) {
override suspend fun nsUpdate(collection: String, dataPair: DataSyncSelector.DataPair, progress: String): Boolean =
dbOperation(collection, dataPair, progress, Operation.UPDATE)
}
enum class Operation { CREATE, UPDATE }
private val gson: Gson = GsonBuilder().create()
private fun dbOperationProfileStore(collection: String = "profile", dataPair: DataSyncSelector.DataPair, progress: String) {
private suspend fun slowDown() {
if (sp.getBoolean(R.string.key_ns_sync_slow, false)) SystemClock.sleep(250)
else SystemClock.sleep(10)
}
private suspend fun dbOperationProfileStore(collection: String = "profile", dataPair: DataSyncSelector.DataPair, progress: String): Boolean {
val data = (dataPair as DataSyncSelector.PairProfileStore).value
scope.launch {
try {
rxBus.send(EventNSClientNewLog("► ADD $collection", "Sent ${dataPair.javaClass.simpleName} <i>$data</i> $progress"))
nsAndroidClient?.createProfileStore(data)?.let { result ->
@ -653,27 +616,21 @@ class NSClientV3Plugin @Inject constructor(
else -> {
rxBus.send(EventNSClientNewLog("◄ ERROR", "ProfileStore"))
return@launch
return true
}
}
// if (result.response == 201) { // created
// dataPair.value.interfaceIDs.nightscoutId = result.identifier
// storeDataForDb.nsIdDeviceStatuses.add(dataPair.value)
// storeDataForDb.scheduleNsIdUpdate()
// }
dataSyncSelector.confirmLastProfileStore(dataPair.id)
dataSyncSelector.processChangedProfileStore()
slowDown()
}
} catch (e: Exception) {
aapsLogger.error(LTag.NSCLIENT, "Upload exception", e)
return false
}
}
return true
}
private fun dbOperationDeviceStatus(collection: String = "devicestatus", dataPair: DataSyncSelector.DataPair, progress: String) {
val data = (dataPair as DataSyncSelector.PairDeviceStatus).value.toNSDeviceStatus()
scope.launch {
private suspend fun dbOperationDeviceStatus(collection: String = "devicestatus", dataPair: DataSyncSelector.PairDeviceStatus, progress: String): Boolean {
try {
val data = dataPair.value.toNSDeviceStatus()
rxBus.send(EventNSClientNewLog("► ADD $collection", "Sent ${dataPair.javaClass.simpleName} <i>${gson.toJson(data)}</i> $progress"))
nsAndroidClient?.createDeviceStatus(data)?.let { result ->
when (result.response) {
@ -683,35 +640,28 @@ class NSClientV3Plugin @Inject constructor(
else -> {
rxBus.send(EventNSClientNewLog("◄ ERROR", "${dataPair.value.javaClass.simpleName} "))
return@launch
return true
}
}
// if (result.response == 201) { // created
// dataPair.value.interfaceIDs.nightscoutId = result.identifier
// storeDataForDb.nsIdDeviceStatuses.add(dataPair.value)
// storeDataForDb.scheduleNsIdUpdate()
// }
dataSyncSelector.confirmLastDeviceStatusIdIfGreater(dataPair.id)
dataSyncSelector.processChangedDeviceStatuses()
result.identifier?.let {
dataPair.value.interfaceIDs.nightscoutId = it
storeDataForDb.nsIdDeviceStatuses.add(dataPair.value)
}
}
} catch (e: Exception) {
aapsLogger.error(LTag.NSCLIENT, "Upload exception", e)
}
}
return true
}
private fun dbOperationEntries(collection: String = "entries", dataPair: DataSyncSelector.DataPair, progress: String, operation: Operation) {
private suspend fun dbOperationEntries(collection: String = "entries", dataPair: DataSyncSelector.PairGlucoseValue, progress: String, operation: Operation): Boolean {
val call = when (operation) {
Operation.CREATE -> nsAndroidClient?.let { return@let it::createSvg }
Operation.CREATE -> nsAndroidClient?.let { return@let it::createSgv }
Operation.UPDATE -> nsAndroidClient?.let { return@let it::updateSvg }
}
when (dataPair) {
is DataSyncSelector.PairGlucoseValue -> dataPair.value.toNSSvgV3()
else -> null
}?.let { data ->
scope.launch {
try {
val id = if (dataPair.value is TraceableDBEntry) (dataPair.value as TraceableDBEntry).interfaceIDs.nightscoutId else ""
val data = dataPair.value.toNSSvgV3()
val id = dataPair.value.interfaceIDs.nightscoutId
rxBus.send(
EventNSClientNewLog(
when (operation) {
@ -733,40 +683,30 @@ class NSClientV3Plugin @Inject constructor(
else -> {
rxBus.send(EventNSClientNewLog("◄ ERROR", "${dataPair.value.javaClass.simpleName} "))
return@launch
return true
}
}
when (dataPair) {
is DataSyncSelector.PairGlucoseValue -> {
// if (result.response == 201) { // created
// dataPair.value.interfaceIDs.nightscoutId = result.identifier
// storeDataForDb.nsIdGlucoseValues.add(dataPair.value)
// storeDataForDb.scheduleNsIdUpdate()
// }
dataSyncSelector.confirmLastGlucoseValueIdIfGreater(dataPair.id)
dataSyncSelector.processChangedGlucoseValues()
}
result.identifier?.let {
dataPair.value.interfaceIDs.nightscoutId = it
storeDataForDb.nsIdGlucoseValues.add(dataPair.value)
}
slowDown()
}
} catch (e: Exception) {
aapsLogger.error(LTag.NSCLIENT, "Upload exception", e)
return false
}
}
}
return true
}
private fun dbOperationFood(collection: String = "food", dataPair: DataSyncSelector.DataPair, progress: String, operation: Operation) {
private suspend fun dbOperationFood(collection: String = "food", dataPair: DataSyncSelector.PairFood, progress: String, operation: Operation): Boolean {
val call = when (operation) {
Operation.CREATE -> nsAndroidClient?.let { return@let it::createFood }
Operation.UPDATE -> nsAndroidClient?.let { return@let it::updateFood }
}
when (dataPair) {
is DataSyncSelector.PairFood -> dataPair.value.toNSFood()
else -> null
}?.let { data ->
scope.launch {
try {
val id = if (dataPair.value is TraceableDBEntry) (dataPair.value as TraceableDBEntry).interfaceIDs.nightscoutId else ""
val data = dataPair.value.toNSFood()
val id = dataPair.value.interfaceIDs.nightscoutId
rxBus.send(
EventNSClientNewLog(
when (operation) {
@ -788,29 +728,23 @@ class NSClientV3Plugin @Inject constructor(
else -> {
rxBus.send(EventNSClientNewLog("◄ ERROR", "${dataPair.value.javaClass.simpleName} "))
return@launch
return true
}
}
when (dataPair) {
is DataSyncSelector.PairFood -> {
// if (result.response == 201) { // created
// dataPair.value.interfaceIDs.nightscoutId = result.identifier
// storeDataForDb.nsIdFoods.add(dataPair.value)
// storeDataForDb.scheduleNsIdUpdate()
// }
dataSyncSelector.confirmLastFoodIdIfGreater(dataPair.id)
dataSyncSelector.processChangedFoods()
}
result.identifier?.let {
dataPair.value.interfaceIDs.nightscoutId = it
storeDataForDb.nsIdFoods.add(dataPair.value)
}
slowDown()
}
} catch (e: Exception) {
aapsLogger.error(LTag.NSCLIENT, "Upload exception", e)
return false
}
}
}
return true
}
private fun dbOperationTreatments(collection: String = "treatments", dataPair: DataSyncSelector.DataPair, progress: String, operation: Operation) {
private suspend fun dbOperationTreatments(collection: String = "treatments", dataPair: DataSyncSelector.DataPair, progress: String, operation: Operation): Boolean {
val call = when (operation) {
Operation.CREATE -> nsAndroidClient?.let { return@let it::createTreatment }
Operation.UPDATE -> nsAndroidClient?.let { return@let it::updateTreatment }
@ -823,22 +757,12 @@ class NSClientV3Plugin @Inject constructor(
is DataSyncSelector.PairTherapyEvent -> dataPair.value.toNSTherapyEvent()
is DataSyncSelector.PairTemporaryBasal -> {
val profile = profileFunction.getProfile(dataPair.value.timestamp)
if (profile == null) {
dataSyncSelector.confirmLastTemporaryBasalIdIfGreater(dataPair.id)
dataSyncSelector.processChangedTemporaryBasals()
return
}
val profile = profileFunction.getProfile(dataPair.value.timestamp) ?: return true
dataPair.value.toNSTemporaryBasal(profile)
}
is DataSyncSelector.PairExtendedBolus -> {
val profile = profileFunction.getProfile(dataPair.value.timestamp)
if (profile == null) {
dataSyncSelector.confirmLastExtendedBolusIdIfGreater(dataPair.id)
dataSyncSelector.processChangedExtendedBoluses()
return
}
val profile = profileFunction.getProfile(dataPair.value.timestamp) ?: return true
dataPair.value.toNSExtendedBolus(profile)
}
@ -847,7 +771,6 @@ class NSClientV3Plugin @Inject constructor(
is DataSyncSelector.PairOfflineEvent -> dataPair.value.toNSOfflineEvent()
else -> null
}?.let { data ->
scope.launch {
try {
val id = if (dataPair.value is TraceableDBEntry) (dataPair.value as TraceableDBEntry).interfaceIDs.nightscoutId else ""
rxBus.send(
@ -871,126 +794,85 @@ class NSClientV3Plugin @Inject constructor(
else -> {
rxBus.send(EventNSClientNewLog("◄ ERROR", "${dataPair.value.javaClass.simpleName} "))
return@launch
return true
}
}
result.identifier?.let {
when (dataPair) {
is DataSyncSelector.PairBolus -> {
// if (result.response == 201) { // created
// dataPair.value.interfaceIDs.nightscoutId = result.identifier
// storeDataForDb.nsIdBoluses.add(dataPair.value)
// storeDataForDb.scheduleNsIdUpdate()
// }
dataSyncSelector.confirmLastBolusIdIfGreater(dataPair.id)
dataSyncSelector.processChangedBoluses()
dataPair.value.interfaceIDs.nightscoutId = it
storeDataForDb.nsIdBoluses.add(dataPair.value)
}
is DataSyncSelector.PairCarbs -> {
// if (result.response == 201) { // created
// dataPair.value.interfaceIDs.nightscoutId = result.identifier
// storeDataForDb.nsIdCarbs.add(dataPair.value)
// storeDataForDb.scheduleNsIdUpdate()
// }
dataSyncSelector.confirmLastCarbsIdIfGreater(dataPair.id)
dataSyncSelector.processChangedCarbs()
dataPair.value.interfaceIDs.nightscoutId = it
storeDataForDb.nsIdCarbs.add(dataPair.value)
}
is DataSyncSelector.PairBolusCalculatorResult -> {
// if (result.response == 201) { // created
// dataPair.value.interfaceIDs.nightscoutId = result.identifier
// storeDataForDb.nsIdBolusCalculatorResults.add(dataPair.value)
// storeDataForDb.scheduleNsIdUpdate()
// }
dataSyncSelector.confirmLastBolusCalculatorResultsIdIfGreater(dataPair.id)
dataSyncSelector.processChangedBolusCalculatorResults()
dataPair.value.interfaceIDs.nightscoutId = it
storeDataForDb.nsIdBolusCalculatorResults.add(dataPair.value)
}
is DataSyncSelector.PairTemporaryTarget -> {
// if (result.response == 201) { // created
// dataPair.value.interfaceIDs.nightscoutId = result.identifier
// storeDataForDb.nsIdTemporaryTargets.add(dataPair.value)
// storeDataForDb.scheduleNsIdUpdate()
// }
dataSyncSelector.confirmLastTempTargetsIdIfGreater(dataPair.id)
dataSyncSelector.processChangedTempTargets()
dataPair.value.interfaceIDs.nightscoutId = it
storeDataForDb.nsIdTemporaryTargets.add(dataPair.value)
}
is DataSyncSelector.PairTherapyEvent -> {
// if (result.response == 201) { // created
// dataPair.value.interfaceIDs.nightscoutId = result.identifier
// storeDataForDb.nsIdTherapyEvents.add(dataPair.value)
// storeDataForDb.scheduleNsIdUpdate()
// }
dataSyncSelector.confirmLastTherapyEventIdIfGreater(dataPair.id)
dataSyncSelector.processChangedTherapyEvents()
dataPair.value.interfaceIDs.nightscoutId = it
storeDataForDb.nsIdTherapyEvents.add(dataPair.value)
}
is DataSyncSelector.PairTemporaryBasal -> {
// if (result.response == 201) { // created
// dataPair.value.interfaceIDs.nightscoutId = result.identifier
// storeDataForDb.nsIdTemporaryBasals.add(dataPair.value)
// storeDataForDb.scheduleNsIdUpdate()
// }
dataSyncSelector.confirmLastTemporaryBasalIdIfGreater(dataPair.id)
dataSyncSelector.processChangedTemporaryBasals()
dataPair.value.interfaceIDs.nightscoutId = it
storeDataForDb.nsIdTemporaryBasals.add(dataPair.value)
}
is DataSyncSelector.PairExtendedBolus -> {
// if (result.response == 201) { // created
// dataPair.value.interfaceIDs.nightscoutId = result.identifier
// storeDataForDb.nsIdExtendedBoluses.add(dataPair.value)
// storeDataForDb.scheduleNsIdUpdate()
// }
dataSyncSelector.confirmLastExtendedBolusIdIfGreater(dataPair.id)
dataSyncSelector.processChangedExtendedBoluses()
dataPair.value.interfaceIDs.nightscoutId = it
storeDataForDb.nsIdExtendedBoluses.add(dataPair.value)
}
is DataSyncSelector.PairProfileSwitch -> {
// if (result.response == 201) { // created
// dataPair.value.interfaceIDs.nightscoutId = result.identifier
// storeDataForDb.nsIdProfileSwitches.add(dataPair.value)
// storeDataForDb.scheduleNsIdUpdate()
// }
dataSyncSelector.confirmLastProfileSwitchIdIfGreater(dataPair.id)
dataSyncSelector.processChangedProfileSwitches()
dataPair.value.interfaceIDs.nightscoutId = it
storeDataForDb.nsIdProfileSwitches.add(dataPair.value)
}
is DataSyncSelector.PairEffectiveProfileSwitch -> {
// if (result.response == 201) { // created
// dataPair.value.interfaceIDs.nightscoutId = result.identifier
// storeDataForDb.nsIdEffectiveProfileSwitches.add(dataPair.value)
// storeDataForDb.scheduleNsIdUpdate()
// }
dataSyncSelector.confirmLastEffectiveProfileSwitchIdIfGreater(dataPair.id)
dataSyncSelector.processChangedEffectiveProfileSwitches()
dataPair.value.interfaceIDs.nightscoutId = it
storeDataForDb.nsIdEffectiveProfileSwitches.add(dataPair.value)
}
is DataSyncSelector.PairOfflineEvent -> {
// if (result.response == 201) { // created
// dataPair.value.interfaceIDs.nightscoutId = result.identifier
// storeDataForDb.nsIdOfflineEvents.add(dataPair.value)
// storeDataForDb.scheduleNsIdUpdate()
// }
dataSyncSelector.confirmLastOfflineEventIdIfGreater(dataPair.id)
dataSyncSelector.processChangedOfflineEvents()
dataPair.value.interfaceIDs.nightscoutId = it
storeDataForDb.nsIdOfflineEvents.add(dataPair.value)
}
else -> {
throw InvalidParameterException()
}
}
}
slowDown()
}
} catch (e: Exception) {
aapsLogger.error(LTag.NSCLIENT, "Upload exception", e)
return false
}
}
}
return true
}
private fun dbOperation(collection: String, dataPair: DataSyncSelector.DataPair, progress: String, operation: Operation) {
private suspend fun dbOperation(collection: String, dataPair: DataSyncSelector.DataPair, progress: String, operation: Operation): Boolean =
when (collection) {
"profile" -> dbOperationProfileStore(dataPair = dataPair, progress = progress)
"devicestatus" -> dbOperationDeviceStatus(dataPair = dataPair, progress = progress)
"entries" -> dbOperationEntries(dataPair = dataPair, progress = progress, operation = operation)
"food" -> dbOperationFood(dataPair = dataPair, progress = progress, operation = operation)
"devicestatus" -> dbOperationDeviceStatus(dataPair = dataPair as DataSyncSelector.PairDeviceStatus, progress = progress)
"entries" -> dbOperationEntries(dataPair = dataPair as DataSyncSelector.PairGlucoseValue, progress = progress, operation = operation)
"food" -> dbOperationFood(dataPair = dataPair as DataSyncSelector.PairFood, progress = progress, operation = operation)
"treatments" -> dbOperationTreatments(dataPair = dataPair, progress = progress, operation = operation)
}
else -> false
}
fun storeLastLoadedSrvModified() {
@ -1007,13 +889,14 @@ class NSClientV3Plugin @Inject constructor(
rxBus.send(EventNSClientNewLog("● RUN", "$blockingReason $origin"))
return
}
if (workIsRunning(arrayOf(JOB_NAME))) {
if (workIsRunning()) {
rxBus.send(EventNSClientNewLog("● RUN", "Already running $origin"))
if (!forceNew) return
// Wait for end and start new cycle
while (workIsRunning(arrayOf(JOB_NAME))) Thread.sleep(5000)
while (workIsRunning()) Thread.sleep(5000)
}
rxBus.send(EventNSClientNewLog("● RUN", "Starting next round $origin"))
rxBus.send(EventNSClientUpdateGuiStatus())
WorkManager.getInstance(context)
.beginUniqueWork(
JOB_NAME,
@ -1022,12 +905,11 @@ class NSClientV3Plugin @Inject constructor(
)
.then(OneTimeWorkRequest.Builder(LoadLastModificationWorker::class.java).build())
.then(OneTimeWorkRequest.Builder(LoadBgWorker::class.java).build())
// Other Workers are enqueued after BG finish
// LoadTreatmentsWorker
// LoadFoodsWorker
// LoadProfileStoreWorker
// LoadDeviceStatusWorker
// DataSyncWorker
.then(OneTimeWorkRequest.Builder(LoadTreatmentsWorker::class.java).build())
.then(OneTimeWorkRequest.Builder(LoadFoodsWorker::class.java).build())
.then(OneTimeWorkRequest.Builder(LoadProfileStoreWorker::class.java).build())
.then(OneTimeWorkRequest.Builder(LoadDeviceStatusWorker::class.java).build())
.then(OneTimeWorkRequest.Builder(DataSyncWorker::class.java).build())
.enqueue()
}
@ -1040,11 +922,11 @@ class NSClientV3Plugin @Inject constructor(
rxBus.send(EventNSClientNewLog("● RUN", blockingReason))
return
}
if (workIsRunning(arrayOf(JOB_NAME))) {
if (workIsRunning()) {
rxBus.send(EventNSClientNewLog("● RUN", "Already running $origin"))
if (!forceNew) return
// Wait for end and start new cycle
while (workIsRunning(arrayOf(JOB_NAME))) Thread.sleep(5000)
while (workIsRunning()) Thread.sleep(5000)
}
rxBus.send(EventNSClientNewLog("● RUN", "Starting upload $origin"))
WorkManager.getInstance(context)
@ -1055,8 +937,7 @@ class NSClientV3Plugin @Inject constructor(
)
}
private fun workIsRunning(workNames: Array<String>): Boolean {
for (workName in workNames)
private fun workIsRunning(workName: String = JOB_NAME): Boolean {
for (workInfo in WorkManager.getInstance(context).getWorkInfosForUniqueWork(workName).get())
if (workInfo.state == WorkInfo.State.BLOCKED || workInfo.state == WorkInfo.State.ENQUEUED || workInfo.state == WorkInfo.State.RUNNING)
return true

View file

@ -8,7 +8,6 @@ import info.nightscout.sdk.localmodel.entry.NsUnits
import info.nightscout.shared.utils.T
import java.security.InvalidParameterException
// copy of NSClientSourcePlugin for testing
fun NSSgvV3.toTransactionGlucoseValue(): TransactionGlucoseValue {
return TransactionGlucoseValue(
timestamp = date ?: throw InvalidParameterException(),

View file

@ -5,8 +5,7 @@ import androidx.work.WorkerParameters
import info.nightscout.androidaps.annotations.OpenForTesting
import info.nightscout.core.utils.worker.LoggingWorker
import info.nightscout.interfaces.plugin.ActivePlugin
import info.nightscout.interfaces.sync.DataSyncSelector
import info.nightscout.plugins.sync.nsShared.events.EventNSClientUpdateGUI
import info.nightscout.interfaces.sync.DataSyncSelectorV3
import info.nightscout.plugins.sync.nsclientV3.NSClientV3Plugin
import info.nightscout.rx.bus.RxBus
import info.nightscout.rx.events.EventNSClientNewLog
@ -18,7 +17,7 @@ class DataSyncWorker(
context: Context, params: WorkerParameters
) : LoggingWorker(context, params, Dispatchers.IO) {
@Inject lateinit var dataSyncSelector: DataSyncSelector
@Inject lateinit var dataSyncSelectorV3: DataSyncSelectorV3
@Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var rxBus: RxBus
@Inject lateinit var nsClientV3Plugin: NSClientV3Plugin
@ -26,14 +25,13 @@ class DataSyncWorker(
override suspend fun doWorkAndLog(): Result {
if (activePlugin.activeNsClient?.hasWritePermission == true || nsClientV3Plugin.wsConnected) {
rxBus.send(EventNSClientNewLog("► UPL", "Start"))
dataSyncSelector.doUpload()
dataSyncSelectorV3.doUpload()
rxBus.send(EventNSClientNewLog("► UPL", "End"))
} else {
rxBus.send(EventNSClientNewLog("► ERROR", "Not connected or write permission"))
// refresh token
nsClientV3Plugin.scheduleIrregularExecution(refreshToken = true)
}
rxBus.send(EventNSClientUpdateGUI())
return Result.success()
}
}

View file

@ -1,18 +1,13 @@
package info.nightscout.plugins.sync.nsclientV3.workers
import android.content.Context
import androidx.work.ExistingWorkPolicy
import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager
import androidx.work.WorkerParameters
import androidx.work.workDataOf
import info.nightscout.core.utils.receivers.DataWorkerStorage
import info.nightscout.core.utils.worker.LoggingWorker
import info.nightscout.core.utils.worker.then
import info.nightscout.interfaces.nsclient.StoreDataForDb
import info.nightscout.interfaces.source.NSClientSource
import info.nightscout.interfaces.sync.NsClient
import info.nightscout.interfaces.workflow.WorkerClasses
import info.nightscout.plugins.sync.nsShared.StoreDataForDbImpl
import info.nightscout.plugins.sync.nsShared.NsIncomingDataProcessor
import info.nightscout.plugins.sync.nsclientV3.NSClientV3Plugin
import info.nightscout.rx.bus.RxBus
import info.nightscout.rx.events.EventNSClientNewLog
@ -29,29 +24,23 @@ class LoadBgWorker(
context: Context, params: WorkerParameters
) : LoggingWorker(context, params, Dispatchers.IO) {
@Inject lateinit var dataWorkerStorage: DataWorkerStorage
@Inject lateinit var rxBus: RxBus
@Inject lateinit var sp: SP
@Inject lateinit var context: Context
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var nsClientV3Plugin: NSClientV3Plugin
@Inject lateinit var nsClientSource: NSClientSource
@Inject lateinit var workerClasses: WorkerClasses
@Inject lateinit var workManager: WorkManager
@Inject lateinit var nsIncomingDataProcessor: NsIncomingDataProcessor
@Inject lateinit var storeDataForDb: StoreDataForDb
override suspend fun doWorkAndLog(): Result {
if (!nsClientSource.isEnabled() && !sp.getBoolean(info.nightscout.core.utils.R.string.key_ns_receive_cgm, false)) {
workManager
.enqueueUniqueWork(
nsClientV3Plugin.JOB_NAME,
ExistingWorkPolicy.APPEND_OR_REPLACE,
OneTimeWorkRequest.Builder(LoadTreatmentsWorker::class.java).build()
)
if (!nsClientSource.isEnabled() && !sp.getBoolean(info.nightscout.core.utils.R.string.key_ns_receive_cgm, false))
return Result.success(workDataOf("Result" to "Load not enabled"))
}
try {
val nsAndroidClient = nsClientV3Plugin.nsAndroidClient ?: return Result.failure(workDataOf("Error" to "AndroidClient is null"))
var continueLoading = true
try {
while (continueLoading) {
val isFirstLoad = nsClientV3Plugin.isFirstLoad(NsClient.Collection.ENTRIES)
val lastLoaded =
if (isFirstLoad) max(nsClientV3Plugin.firstLoadContinueTimestamp.collections.entries, dateUtil.now() - nsClientV3Plugin.maxAge)
@ -70,22 +59,13 @@ class LoadBgWorker(
sgvs = response.values
aapsLogger.debug(LTag.NSCLIENT, "SGVS: $sgvs")
if (sgvs.isNotEmpty()) {
val action = if (isFirstLoad) "RCV-FIRST" else "RCV"
val action = if (isFirstLoad) "RCV-F" else "RCV"
rxBus.send(EventNSClientNewLog("$action", "${sgvs.size} SVGs from ${dateUtil.dateAndTimeAndSecondsString(lastLoaded)}"))
// Objective0
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
val stopLoading = sgvs.size != NSClientV3Plugin.RECORDS_TO_LOAD || response.code == 304
workManager
.beginUniqueWork(
nsClientV3Plugin.JOB_NAME,
ExistingWorkPolicy.APPEND_OR_REPLACE,
OneTimeWorkRequest.Builder(workerClasses.nsClientSourceWorker).setInputData(dataWorkerStorage.storeInputData(sgvs)).build()
)
// response 304 == Not modified (happens when date > srvModified => bad time on phone or server during upload
.then(!stopLoading, OneTimeWorkRequest.Builder(LoadBgWorker::class.java).build())
.then(stopLoading, OneTimeWorkRequest.Builder(LoadTreatmentsWorker::class.java).build())
.enqueue()
continueLoading = !(sgvs.size != NSClientV3Plugin.RECORDS_TO_LOAD || response.code == 304)
nsIncomingDataProcessor.processSgvs(sgvs)
} else {
// End first load
if (isFirstLoad) {
@ -93,14 +73,8 @@ class LoadBgWorker(
nsClientV3Plugin.storeLastLoadedSrvModified()
}
rxBus.send(EventNSClientNewLog("◄ RCV BG END", "No data from ${dateUtil.dateAndTimeAndSecondsString(lastLoaded)}"))
workManager
.beginUniqueWork(
nsClientV3Plugin.JOB_NAME,
ExistingWorkPolicy.APPEND_OR_REPLACE,
OneTimeWorkRequest.Builder(StoreDataForDbImpl.StoreBgWorker::class.java).build()
)
.then(OneTimeWorkRequest.Builder(LoadTreatmentsWorker::class.java).build())
.enqueue()
storeDataForDb.storeGlucoseValuesToDb()
continueLoading = false
}
} else {
// End first load
@ -109,14 +83,9 @@ class LoadBgWorker(
nsClientV3Plugin.storeLastLoadedSrvModified()
}
rxBus.send(EventNSClientNewLog("◄ RCV BG END", "No new data from ${dateUtil.dateAndTimeAndSecondsString(lastLoaded)}"))
workManager
.beginUniqueWork(
nsClientV3Plugin.JOB_NAME,
ExistingWorkPolicy.APPEND_OR_REPLACE,
OneTimeWorkRequest.Builder(StoreDataForDbImpl.StoreBgWorker::class.java).build()
)
.then(OneTimeWorkRequest.Builder(LoadTreatmentsWorker::class.java).build())
.enqueue()
storeDataForDb.storeGlucoseValuesToDb()
continueLoading = false
}
}
} catch (error: Exception) {
aapsLogger.error("Error: ", error)

View file

@ -1,9 +1,6 @@
package info.nightscout.plugins.sync.nsclientV3.workers
import android.content.Context
import androidx.work.ExistingWorkPolicy
import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager
import androidx.work.WorkerParameters
import androidx.work.workDataOf
import info.nightscout.core.utils.receivers.DataWorkerStorage
@ -46,12 +43,6 @@ class LoadDeviceStatusWorker(
} else {
rxBus.send(EventNSClientNewLog("◄ RCV DS END", "No data from ${dateUtil.dateAndTimeAndSecondsString(from)}"))
}
WorkManager.getInstance(context)
.enqueueUniqueWork(
nsClientV3Plugin.JOB_NAME,
ExistingWorkPolicy.APPEND_OR_REPLACE,
OneTimeWorkRequest.Builder(DataSyncWorker::class.java).build()
)
} catch (error: Exception) {
aapsLogger.error("Error: ", error)
rxBus.send(EventNSClientNewLog("◄ ERROR", error.localizedMessage))

View file

@ -1,15 +1,11 @@
package info.nightscout.plugins.sync.nsclientV3.workers
import android.content.Context
import androidx.work.ExistingWorkPolicy
import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager
import androidx.work.WorkerParameters
import androidx.work.workDataOf
import info.nightscout.core.utils.receivers.DataWorkerStorage
import info.nightscout.core.utils.worker.LoggingWorker
import info.nightscout.interfaces.nsclient.StoreDataForDb
import info.nightscout.plugins.sync.nsShared.StoreDataForDbImpl
import info.nightscout.plugins.sync.nsShared.NsIncomingDataProcessor
import info.nightscout.plugins.sync.nsclientV3.NSClientV3Plugin
import info.nightscout.rx.bus.RxBus
import info.nightscout.rx.events.EventNSClientNewLog
@ -24,12 +20,12 @@ class LoadFoodsWorker(
params: WorkerParameters
) : LoggingWorker(context, params, Dispatchers.IO) {
@Inject lateinit var dataWorkerStorage: DataWorkerStorage
@Inject lateinit var rxBus: RxBus
@Inject lateinit var context: Context
@Inject lateinit var nsClientV3Plugin: NSClientV3Plugin
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var storeDataForDb: StoreDataForDb
@Inject lateinit var nsIncomingDataProcessor: NsIncomingDataProcessor
override suspend fun doWorkAndLog(): Result {
val nsAndroidClient = nsClientV3Plugin.nsAndroidClient ?: return Result.failure(workDataOf("Error" to "AndroidClient is null"))
@ -42,24 +38,10 @@ class LoadFoodsWorker(
aapsLogger.debug(LTag.NSCLIENT, "FOODS: $foods")
rxBus.send(EventNSClientNewLog("◄ RCV", "${foods.size} FOODs"))
// Schedule processing of fetched data
WorkManager.getInstance(context)
.beginUniqueWork(
nsClientV3Plugin.JOB_NAME,
ExistingWorkPolicy.APPEND_OR_REPLACE,
OneTimeWorkRequest.Builder(ProcessFoodWorker::class.java)
.setInputData(dataWorkerStorage.storeInputData(foods))
.build()
).then(OneTimeWorkRequest.Builder(StoreDataForDbImpl.StoreFoodWorker::class.java).build())
.then(OneTimeWorkRequest.Builder(LoadProfileStoreWorker::class.java).build())
.enqueue()
nsIncomingDataProcessor.processFood(foods)
storeDataForDb.storeFoodsToDb()
} else {
rxBus.send(EventNSClientNewLog("● RCV FOOD", "skipped"))
WorkManager.getInstance(context)
.enqueueUniqueWork(
nsClientV3Plugin.JOB_NAME,
ExistingWorkPolicy.APPEND_OR_REPLACE,
OneTimeWorkRequest.Builder(LoadProfileStoreWorker::class.java).build()
)
}
} catch (error: Exception) {
aapsLogger.error("Error: ", error)

View file

@ -1,16 +1,13 @@
package info.nightscout.plugins.sync.nsclientV3.workers
import android.content.Context
import androidx.work.ExistingWorkPolicy
import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager
import androidx.work.WorkerParameters
import androidx.work.workDataOf
import info.nightscout.core.utils.receivers.DataWorkerStorage
import info.nightscout.core.utils.worker.LoggingWorker
import info.nightscout.interfaces.sync.NsClient
import info.nightscout.interfaces.utils.JsonHelper
import info.nightscout.interfaces.workflow.WorkerClasses
import info.nightscout.plugins.sync.nsShared.NsIncomingDataProcessor
import info.nightscout.plugins.sync.nsclientV3.NSClientV3Plugin
import info.nightscout.rx.bus.RxBus
import info.nightscout.rx.events.EventNSClientNewLog
@ -32,7 +29,7 @@ class LoadProfileStoreWorker(
@Inject lateinit var context: Context
@Inject lateinit var nsClientV3Plugin: NSClientV3Plugin
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var workerClasses: WorkerClasses
@Inject lateinit var nsIncomingDataProcessor: NsIncomingDataProcessor
override suspend fun doWorkAndLog(): Result {
val nsAndroidClient = nsClientV3Plugin.nsAndroidClient ?: return Result.failure(workDataOf("Error" to "AndroidClient is null"))
@ -59,32 +56,12 @@ class LoadProfileStoreWorker(
nsClientV3Plugin.storeLastLoadedSrvModified()
aapsLogger.debug(LTag.NSCLIENT, "PROFILE: $profile")
rxBus.send(EventNSClientNewLog("◄ RCV", "1 PROFILE from ${dateUtil.dateAndTimeAndSecondsString(lastLoaded)}"))
WorkManager.getInstance(context)
.beginUniqueWork(
nsClientV3Plugin.JOB_NAME,
ExistingWorkPolicy.APPEND_OR_REPLACE,
OneTimeWorkRequest.Builder((workerClasses.nsProfileWorker))
.setInputData(dataWorkerStorage.storeInputData(profile))
.build()
).then(OneTimeWorkRequest.Builder(LoadDeviceStatusWorker::class.java).build())
.enqueue()
nsIncomingDataProcessor.processProfile(profile)
} else {
rxBus.send(EventNSClientNewLog("◄ RCV PROFILE END", "No new data from ${dateUtil.dateAndTimeAndSecondsString(lastLoaded)}"))
WorkManager.getInstance(context)
.enqueueUniqueWork(
nsClientV3Plugin.JOB_NAME,
ExistingWorkPolicy.APPEND_OR_REPLACE,
OneTimeWorkRequest.Builder(LoadDeviceStatusWorker::class.java).build()
)
}
} else {
rxBus.send(EventNSClientNewLog("◄ RCV PROFILE END", "No data from ${dateUtil.dateAndTimeAndSecondsString(lastLoaded)}"))
WorkManager.getInstance(context)
.enqueueUniqueWork(
nsClientV3Plugin.JOB_NAME,
ExistingWorkPolicy.APPEND_OR_REPLACE,
OneTimeWorkRequest.Builder(LoadDeviceStatusWorker::class.java).build()
)
}
} catch (error: Exception) {
aapsLogger.error("Error: ", error)
@ -94,4 +71,5 @@ class LoadProfileStoreWorker(
return Result.success()
}
}

View file

@ -4,6 +4,7 @@ import android.content.Context
import androidx.work.WorkerParameters
import androidx.work.workDataOf
import info.nightscout.core.utils.worker.LoggingWorker
import info.nightscout.plugins.sync.nsShared.events.EventNSClientUpdateGuiStatus
import info.nightscout.plugins.sync.nsclientV3.NSClientV3Plugin
import info.nightscout.rx.bus.RxBus
import info.nightscout.rx.events.EventNSClientNewLog
@ -28,9 +29,11 @@ class LoadStatusWorker(
aapsLogger.error("Error: ", error)
rxBus.send(EventNSClientNewLog("◄ ERROR", error.localizedMessage))
nsClientV3Plugin.lastOperationError = error.localizedMessage
rxBus.send(EventNSClientUpdateGuiStatus())
return Result.failure(workDataOf("Error" to error.localizedMessage))
}
nsClientV3Plugin.lastOperationError = null
rxBus.send(EventNSClientUpdateGuiStatus())
return Result.success()
}
}

View file

@ -1,17 +1,12 @@
package info.nightscout.plugins.sync.nsclientV3.workers
import android.content.Context
import androidx.work.ExistingWorkPolicy
import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager
import androidx.work.WorkerParameters
import androidx.work.workDataOf
import info.nightscout.core.utils.receivers.DataWorkerStorage
import info.nightscout.core.utils.worker.LoggingWorker
import info.nightscout.core.utils.worker.then
import info.nightscout.interfaces.nsclient.StoreDataForDb
import info.nightscout.interfaces.sync.NsClient
import info.nightscout.plugins.sync.nsShared.StoreDataForDbImpl
import info.nightscout.plugins.sync.nsShared.NsIncomingDataProcessor
import info.nightscout.plugins.sync.nsclientV3.NSClientV3Plugin
import info.nightscout.rx.bus.RxBus
import info.nightscout.rx.events.EventNSClientNewLog
@ -28,17 +23,19 @@ class LoadTreatmentsWorker(
params: WorkerParameters
) : LoggingWorker(context, params, Dispatchers.IO) {
@Inject lateinit var dataWorkerStorage: DataWorkerStorage
@Inject lateinit var rxBus: RxBus
@Inject lateinit var context: Context
@Inject lateinit var nsClientV3Plugin: NSClientV3Plugin
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var storeDataForDb: StoreDataForDb
@Inject lateinit var nsIncomingDataProcessor: NsIncomingDataProcessor
override suspend fun doWorkAndLog(): Result {
val nsAndroidClient = nsClientV3Plugin.nsAndroidClient ?: return Result.failure(workDataOf("Error" to "AndroidClient is null"))
var continueLoading = true
try {
while (continueLoading) {
val isFirstLoad = nsClientV3Plugin.isFirstLoad(NsClient.Collection.TREATMENTS)
val lastLoaded =
if (isFirstLoad) max(nsClientV3Plugin.firstLoadContinueTimestamp.collections.treatments, dateUtil.now() - nsClientV3Plugin.maxAge)
@ -58,22 +55,11 @@ class LoadTreatmentsWorker(
treatments = response.values
aapsLogger.debug(LTag.NSCLIENT, "TREATMENTS: $treatments")
if (treatments.isNotEmpty()) {
val action = if (isFirstLoad) "RCV-FIRST" else "RCV"
val action = if (isFirstLoad) "RCV-F" else "RCV"
rxBus.send(EventNSClientNewLog("$action", "${treatments.size} TRs from ${dateUtil.dateAndTimeAndSecondsString(lastLoaded)}"))
// Schedule processing of fetched data and continue of loading
val stopLoading = treatments.size != NSClientV3Plugin.RECORDS_TO_LOAD || response.code == 304
WorkManager.getInstance(context)
.beginUniqueWork(
nsClientV3Plugin.JOB_NAME,
ExistingWorkPolicy.APPEND_OR_REPLACE,
OneTimeWorkRequest.Builder(ProcessTreatmentsWorker::class.java)
.setInputData(dataWorkerStorage.storeInputData(response.values))
.build()
)
// response 304 == Not modified (happens when date > srvModified => bad time on phone or server during upload
.then(!stopLoading, OneTimeWorkRequest.Builder(LoadTreatmentsWorker::class.java).build())
.then(stopLoading, OneTimeWorkRequest.Builder(LoadFoodsWorker::class.java).build())
.enqueue()
continueLoading = !(treatments.size != NSClientV3Plugin.RECORDS_TO_LOAD || response.code == 304)
nsIncomingDataProcessor.processTreatments(response.values)
} else {
// End first load
if (isFirstLoad) {
@ -81,14 +67,8 @@ class LoadTreatmentsWorker(
nsClientV3Plugin.storeLastLoadedSrvModified()
}
rxBus.send(EventNSClientNewLog("◄ RCV TR END", "No data from ${dateUtil.dateAndTimeAndSecondsString(lastLoaded)}"))
WorkManager.getInstance(context)
.beginUniqueWork(
nsClientV3Plugin.JOB_NAME,
ExistingWorkPolicy.APPEND_OR_REPLACE,
OneTimeWorkRequest.Builder(StoreDataForDbImpl.StoreTreatmentsWorker::class.java).build()
)
.then(OneTimeWorkRequest.Builder(LoadFoodsWorker::class.java).build())
.enqueue()
storeDataForDb.storeTreatmentsToDb()
continueLoading = false
}
} else {
// End first load
@ -97,14 +77,9 @@ class LoadTreatmentsWorker(
nsClientV3Plugin.storeLastLoadedSrvModified()
}
rxBus.send(EventNSClientNewLog("◄ RCV TR END", "No new data from ${dateUtil.dateAndTimeAndSecondsString(lastLoaded)}"))
WorkManager.getInstance(context)
.beginUniqueWork(
nsClientV3Plugin.JOB_NAME,
ExistingWorkPolicy.APPEND_OR_REPLACE,
OneTimeWorkRequest.Builder(StoreDataForDbImpl.StoreTreatmentsWorker::class.java).build()
)
.then(OneTimeWorkRequest.Builder(LoadFoodsWorker::class.java).build())
.enqueue()
storeDataForDb.storeTreatmentsToDb()
continueLoading = false
}
}
} catch (error: Exception) {
aapsLogger.error("Error: ", error)

View file

@ -1,81 +0,0 @@
package info.nightscout.plugins.sync.nsclientV3.workers
import android.content.Context
import androidx.work.WorkerParameters
import androidx.work.workDataOf
import dagger.android.HasAndroidInjector
import info.nightscout.core.extensions.foodFromJson
import info.nightscout.core.utils.receivers.DataWorkerStorage
import info.nightscout.core.utils.worker.LoggingWorker
import info.nightscout.database.entities.Food
import info.nightscout.database.impl.AppRepository
import info.nightscout.interfaces.nsclient.StoreDataForDb
import info.nightscout.interfaces.utils.JsonHelper
import info.nightscout.plugins.sync.nsclientV3.extensions.toFood
import info.nightscout.rx.bus.RxBus
import info.nightscout.rx.events.EventNSClientNewLog
import info.nightscout.rx.logging.LTag
import info.nightscout.sdk.localmodel.food.NSFood
import info.nightscout.shared.sharedPreferences.SP
import kotlinx.coroutines.Dispatchers
import org.json.JSONArray
import org.json.JSONObject
import javax.inject.Inject
class ProcessFoodWorker(
context: Context,
params: WorkerParameters
) : LoggingWorker(context, params, Dispatchers.Default) {
@Inject lateinit var injector: HasAndroidInjector
@Inject lateinit var repository: AppRepository
@Inject lateinit var sp: SP
@Inject lateinit var dataWorkerStorage: DataWorkerStorage
@Inject lateinit var storeDataForDb: StoreDataForDb
@Inject lateinit var rxBus: RxBus
override suspend fun doWorkAndLog(): Result {
val data = dataWorkerStorage.pickupObject(inputData.getLong(DataWorkerStorage.STORE_KEY, -1))
?: return Result.failure(workDataOf("Error" to "missing input data"))
aapsLogger.debug(LTag.DATABASE, "Received Food Data: $data")
try {
val foods = mutableListOf<Food>()
if (data is JSONArray) {
for (index in 0 until data.length()) {
val jsonFood: JSONObject = data.getJSONObject(index)
if (JsonHelper.safeGetString(jsonFood, "type") != "food") continue
when (JsonHelper.safeGetString(jsonFood, "action")) {
"remove" -> {
val delFood = Food(
name = "",
portion = 0.0,
carbs = 0,
isValid = false
).also { it.interfaceIDs.nightscoutId = JsonHelper.safeGetString(jsonFood, "_id") }
foods += delFood
}
else -> {
val food = foodFromJson(jsonFood)
if (food != null) foods += food
else aapsLogger.error(LTag.DATABASE, "Error parsing food", jsonFood.toString())
}
}
}
} else if (data is List<*>) {
for (i in 0 until data.size)
foods += (data[i] as NSFood).toFood()
}
storeDataForDb.foods.addAll(foods)
} catch (error: Exception) {
aapsLogger.error("Error: ", error)
rxBus.send(EventNSClientNewLog("◄ ERROR", error.localizedMessage))
return Result.failure(workDataOf("Error" to error.localizedMessage))
}
return Result.success()
}
}

View file

@ -1,146 +0,0 @@
package info.nightscout.plugins.sync.nsclientV3.workers
import android.content.Context
import androidx.work.WorkerParameters
import androidx.work.workDataOf
import info.nightscout.core.utils.receivers.DataWorkerStorage
import info.nightscout.core.utils.worker.LoggingWorker
import info.nightscout.database.impl.AppRepository
import info.nightscout.interfaces.Config
import info.nightscout.interfaces.Constants
import info.nightscout.interfaces.nsclient.StoreDataForDb
import info.nightscout.interfaces.plugin.ActivePlugin
import info.nightscout.plugins.sync.R
import info.nightscout.plugins.sync.nsclientV3.extensions.toBolus
import info.nightscout.plugins.sync.nsclientV3.extensions.toBolusCalculatorResult
import info.nightscout.plugins.sync.nsclientV3.extensions.toCarbs
import info.nightscout.plugins.sync.nsclientV3.extensions.toEffectiveProfileSwitch
import info.nightscout.plugins.sync.nsclientV3.extensions.toExtendedBolus
import info.nightscout.plugins.sync.nsclientV3.extensions.toOfflineEvent
import info.nightscout.plugins.sync.nsclientV3.extensions.toProfileSwitch
import info.nightscout.plugins.sync.nsclientV3.extensions.toTemporaryBasal
import info.nightscout.plugins.sync.nsclientV3.extensions.toTemporaryTarget
import info.nightscout.plugins.sync.nsclientV3.extensions.toTherapyEvent
import info.nightscout.rx.bus.RxBus
import info.nightscout.rx.events.EventNSClientNewLog
import info.nightscout.rx.logging.LTag
import info.nightscout.sdk.localmodel.treatment.NSBolus
import info.nightscout.sdk.localmodel.treatment.NSBolusWizard
import info.nightscout.sdk.localmodel.treatment.NSCarbs
import info.nightscout.sdk.localmodel.treatment.NSEffectiveProfileSwitch
import info.nightscout.sdk.localmodel.treatment.NSExtendedBolus
import info.nightscout.sdk.localmodel.treatment.NSOfflineEvent
import info.nightscout.sdk.localmodel.treatment.NSProfileSwitch
import info.nightscout.sdk.localmodel.treatment.NSTemporaryBasal
import info.nightscout.sdk.localmodel.treatment.NSTemporaryTarget
import info.nightscout.sdk.localmodel.treatment.NSTherapyEvent
import info.nightscout.sdk.localmodel.treatment.NSTreatment
import info.nightscout.shared.sharedPreferences.SP
import info.nightscout.shared.utils.DateUtil
import kotlinx.coroutines.Dispatchers
import javax.inject.Inject
class ProcessTreatmentsWorker(
context: Context,
params: WorkerParameters
) : LoggingWorker(context, params, Dispatchers.Default) {
@Inject lateinit var dataWorkerStorage: DataWorkerStorage
@Inject lateinit var config: Config
@Inject lateinit var sp: SP
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var repository: AppRepository
@Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var rxBus: RxBus
@Inject lateinit var storeDataForDb: StoreDataForDb
override suspend fun doWorkAndLog(): Result {
@Suppress("UNCHECKED_CAST")
val treatments = dataWorkerStorage.pickupObject(inputData.getLong(DataWorkerStorage.STORE_KEY, -1)) as List<NSTreatment>?
?: return Result.failure(workDataOf("Error" to "missing input data"))
try {
var latestDateInReceivedData: Long = 0
for (treatment in treatments) {
aapsLogger.debug(LTag.DATABASE, "Received NS treatment: $treatment")
val date = treatment.date ?: continue
if (date > latestDateInReceivedData) latestDateInReceivedData = date
when (treatment) {
is NSBolus ->
if (sp.getBoolean(info.nightscout.core.utils.R.string.key_ns_receive_insulin, false) || config.NSCLIENT)
storeDataForDb.boluses.add(treatment.toBolus())
is NSCarbs ->
if (sp.getBoolean(info.nightscout.core.utils.R.string.key_ns_receive_carbs, false) || config.NSCLIENT)
storeDataForDb.carbs.add(treatment.toCarbs())
is NSTemporaryTarget ->
if (sp.getBoolean(info.nightscout.core.utils.R.string.key_ns_receive_temp_target, false) || config.NSCLIENT) {
if (treatment.duration > 0L) {
// not ending event
if (treatment.targetBottomAsMgdl() < Constants.MIN_TT_MGDL
|| treatment.targetBottomAsMgdl() > Constants.MAX_TT_MGDL
|| treatment.targetTopAsMgdl() < Constants.MIN_TT_MGDL
|| treatment.targetTopAsMgdl() > Constants.MAX_TT_MGDL
|| treatment.targetBottomAsMgdl() > treatment.targetTopAsMgdl()
) {
aapsLogger.debug(LTag.DATABASE, "Ignored TemporaryTarget $treatment")
continue
}
}
storeDataForDb.temporaryTargets.add(treatment.toTemporaryTarget())
}
is NSTemporaryBasal ->
if (config.isEngineeringMode() && sp.getBoolean(R.string.key_ns_receive_tbr_eb, false) || config.NSCLIENT)
storeDataForDb.temporaryBasals.add(treatment.toTemporaryBasal())
is NSEffectiveProfileSwitch ->
if (sp.getBoolean(info.nightscout.core.utils.R.string.key_ns_receive_profile_switch, false) || config.NSCLIENT) {
treatment.toEffectiveProfileSwitch(dateUtil)?.let { effectiveProfileSwitch ->
storeDataForDb.effectiveProfileSwitches.add(effectiveProfileSwitch)
}
}
is NSProfileSwitch ->
if (sp.getBoolean(info.nightscout.core.utils.R.string.key_ns_receive_profile_switch, false) || config.NSCLIENT) {
treatment.toProfileSwitch(activePlugin, dateUtil)?.let { profileSwitch ->
storeDataForDb.profileSwitches.add(profileSwitch)
}
}
is NSBolusWizard ->
treatment.toBolusCalculatorResult()?.let { bolusCalculatorResult ->
storeDataForDb.bolusCalculatorResults.add(bolusCalculatorResult)
}
is NSTherapyEvent ->
if (sp.getBoolean(info.nightscout.core.utils.R.string.key_ns_receive_therapy_events, false) || config.NSCLIENT)
treatment.toTherapyEvent().let { therapyEvent ->
storeDataForDb.therapyEvents.add(therapyEvent)
}
is NSOfflineEvent ->
if (sp.getBoolean(info.nightscout.core.utils.R.string.key_ns_receive_offline_event, false) && config.isEngineeringMode() || config.NSCLIENT)
treatment.toOfflineEvent().let { offlineEvent ->
storeDataForDb.offlineEvents.add(offlineEvent)
}
is NSExtendedBolus ->
if (config.isEngineeringMode() && sp.getBoolean(R.string.key_ns_receive_tbr_eb, false) || config.NSCLIENT)
treatment.toExtendedBolus().let { extendedBolus ->
storeDataForDb.extendedBoluses.add(extendedBolus)
}
}
}
activePlugin.activeNsClient?.updateLatestTreatmentReceivedIfNewer(latestDateInReceivedData)
// xDripBroadcast.sendTreatments(treatments)
} catch (error: Exception) {
aapsLogger.error("Error: ", error)
rxBus.send(EventNSClientNewLog("◄ ERROR", error.localizedMessage))
return Result.failure(workDataOf("Error" to error.localizedMessage))
}
return Result.success()
}
}

View file

@ -1,7 +1,6 @@
package info.nightscout.plugins.sync.tidepool.utils
import info.nightscout.rx.logging.AAPSLogger
import info.nightscout.rx.logging.LTag
import info.nightscout.shared.utils.DateUtil
import info.nightscout.shared.utils.T
import javax.inject.Inject
@ -21,7 +20,7 @@ class RateLimit @Inject constructor(
// check if over limit
rateLimits[name]?.let {
if (dateUtil.now() - it < T.secs(seconds.toLong()).msecs()) {
aapsLogger.debug(LTag.TIDEPOOL, "$name rate limited: $seconds seconds")
//aapsLogger.debug(LTag.TIDEPOOL, "$name rate limited: $seconds seconds")
return false
}
}

View file

@ -1,12 +1,12 @@
package info.nightscout.plugins.sync.xdrip
import dagger.Lazy
import info.nightscout.database.ValueWrapper
import info.nightscout.database.impl.AppRepository
import info.nightscout.interfaces.XDripBroadcast
import info.nightscout.interfaces.plugin.ActivePlugin
import info.nightscout.interfaces.profile.ProfileFunction
import info.nightscout.interfaces.sync.DataSyncSelector
import info.nightscout.interfaces.sync.DataSyncSelectorXdrip
import info.nightscout.interfaces.utils.JsonHelper
import info.nightscout.plugins.sync.R
import info.nightscout.rx.logging.AAPSLogger
@ -18,7 +18,7 @@ import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class XdripDataSyncSelectorImplementation @Inject constructor(
class DataSyncSelectorXdripImpl @Inject constructor(
private val sp: SP,
private val aapsLogger: AAPSLogger,
private val dateUtil: DateUtil,
@ -26,7 +26,7 @@ class XdripDataSyncSelectorImplementation @Inject constructor(
private val activePlugin: ActivePlugin,
private val xdripBroadcast: Lazy<XDripBroadcast>,
private val appRepository: AppRepository
) : DataSyncSelector {
) : DataSyncSelectorXdrip {
class QueueCounter(
var bolusesRemaining: Long = -1L,
@ -72,7 +72,7 @@ class XdripDataSyncSelectorImplementation @Inject constructor(
override fun queueSize(): Long = queueCounter.size()
override fun doUpload() {
override suspend fun doUpload() {
if (isEnabled) {
processChangedGlucoseValues()
processChangedBoluses()
@ -113,22 +113,21 @@ class XdripDataSyncSelectorImplementation @Inject constructor(
sp.remove(R.string.key_xdrip_offline_event_last_synced_id)
sp.remove(R.string.key_xdrip_profile_store_last_synced_timestamp)
val lastDeviceStatusDbIdWrapped = appRepository.getLastDeviceStatusIdWrapped().blockingGet()
if (lastDeviceStatusDbIdWrapped is ValueWrapper.Existing) sp.putLong(R.string.key_xdrip_device_status_last_synced_id, lastDeviceStatusDbIdWrapped.value)
val lastDeviceStatusDbId = appRepository.getLastDeviceStatusId()
if (lastDeviceStatusDbId != null) sp.putLong(R.string.key_xdrip_device_status_last_synced_id, lastDeviceStatusDbId)
else sp.remove(R.string.key_xdrip_device_status_last_synced_id)
}
override fun confirmLastGlucoseValueIdIfGreater(lastSynced: Long) {
fun confirmLastGlucoseValueIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_xdrip_glucose_value_last_synced_id, 0)) {
//aapsLogger.debug(LTag.XDRIP, "Setting GlucoseValue data sync from $lastSynced")
sp.putLong(R.string.key_xdrip_glucose_value_last_synced_id, lastSynced)
}
}
override fun processChangedGlucoseValues() {
private fun processChangedGlucoseValues() {
var progress: String
val lastDbIdWrapped = appRepository.getLastGlucoseValueIdWrapped().blockingGet()
val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L
val lastDbId = appRepository.getLastGlucoseValueId() ?: 0L
while (true) {
if (!isEnabled) return
var startId = sp.getLong(R.string.key_xdrip_glucose_value_last_synced_id, 0)
@ -170,17 +169,16 @@ class XdripDataSyncSelectorImplementation @Inject constructor(
}
}
override fun confirmLastBolusIdIfGreater(lastSynced: Long) {
fun confirmLastBolusIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_xdrip_bolus_last_synced_id, 0)) {
//aapsLogger.debug(LTag.XDRIP, "Setting Bolus data sync from $lastSynced")
sp.putLong(R.string.key_xdrip_bolus_last_synced_id, lastSynced)
}
}
override fun processChangedBoluses() {
private fun processChangedBoluses() {
var progress: String
val lastDbIdWrapped = appRepository.getLastBolusIdWrapped().blockingGet()
val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L
val lastDbId = appRepository.getLastBolusId() ?: 0L
while (true) {
if (!isEnabled) return
var startId = sp.getLong(R.string.key_xdrip_bolus_last_synced_id, 0)
@ -201,17 +199,16 @@ class XdripDataSyncSelectorImplementation @Inject constructor(
sendTreatments(force = true, progress)
}
override fun confirmLastCarbsIdIfGreater(lastSynced: Long) {
fun confirmLastCarbsIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_xdrip_carbs_last_synced_id, 0)) {
//aapsLogger.debug(LTag.XDRIP, "Setting Carbs data sync from $lastSynced")
sp.putLong(R.string.key_xdrip_carbs_last_synced_id, lastSynced)
}
}
override fun processChangedCarbs() {
private fun processChangedCarbs() {
var progress: String
val lastDbIdWrapped = appRepository.getLastCarbsIdWrapped().blockingGet()
val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L
val lastDbId = appRepository.getLastCarbsId() ?: 0L
while (true) {
if (!isEnabled) return
var startId = sp.getLong(R.string.key_xdrip_carbs_last_synced_id, 0)
@ -232,17 +229,16 @@ class XdripDataSyncSelectorImplementation @Inject constructor(
sendTreatments(force = true, progress)
}
override fun confirmLastBolusCalculatorResultsIdIfGreater(lastSynced: Long) {
fun confirmLastBolusCalculatorResultsIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_xdrip_bolus_calculator_result_last_synced_id, 0)) {
//aapsLogger.debug(LTag.XDRIP, "Setting BolusCalculatorResult data sync from $lastSynced")
sp.putLong(R.string.key_xdrip_bolus_calculator_result_last_synced_id, lastSynced)
}
}
override fun processChangedBolusCalculatorResults() {
private fun processChangedBolusCalculatorResults() {
var progress: String
val lastDbIdWrapped = appRepository.getLastBolusCalculatorResultIdWrapped().blockingGet()
val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L
val lastDbId = appRepository.getLastBolusCalculatorResultId() ?: 0L
while (true) {
if (!isEnabled) return
var startId = sp.getLong(R.string.key_xdrip_bolus_calculator_result_last_synced_id, 0)
@ -263,17 +259,16 @@ class XdripDataSyncSelectorImplementation @Inject constructor(
sendTreatments(force = true, progress)
}
override fun confirmLastTempTargetsIdIfGreater(lastSynced: Long) {
fun confirmLastTempTargetsIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_xdrip_temporary_target_last_synced_id, 0)) {
//aapsLogger.debug(LTag.XDRIP, "Setting TemporaryTarget data sync from $lastSynced")
sp.putLong(R.string.key_xdrip_temporary_target_last_synced_id, lastSynced)
}
}
override fun processChangedTempTargets() {
private fun processChangedTempTargets() {
var progress: String
val lastDbIdWrapped = appRepository.getLastTempTargetIdWrapped().blockingGet()
val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L
val lastDbId = appRepository.getLastTempTargetId() ?: 0L
while (true) {
if (!isEnabled) return
var startId = sp.getLong(R.string.key_xdrip_temporary_target_last_synced_id, 0)
@ -294,17 +289,16 @@ class XdripDataSyncSelectorImplementation @Inject constructor(
sendTreatments(force = true, progress)
}
override fun confirmLastFoodIdIfGreater(lastSynced: Long) {
fun confirmLastFoodIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_xdrip_food_last_synced_id, 0)) {
//aapsLogger.debug(LTag.XDRIP, "Setting Food data sync from $lastSynced")
sp.putLong(R.string.key_xdrip_food_last_synced_id, lastSynced)
}
}
override fun processChangedFoods() {
private fun processChangedFoods() {
var progress: String
val lastDbIdWrapped = appRepository.getLastFoodIdWrapped().blockingGet()
val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L
val lastDbId = appRepository.getLastFoodId() ?: 0L
while (true) {
if (!isEnabled) return
var startId = sp.getLong(R.string.key_xdrip_food_last_synced_id, 0)
@ -324,17 +318,16 @@ class XdripDataSyncSelectorImplementation @Inject constructor(
sendFoods(force = true, progress)
}
override fun confirmLastTherapyEventIdIfGreater(lastSynced: Long) {
fun confirmLastTherapyEventIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_xdrip_therapy_event_last_synced_id, 0)) {
//aapsLogger.debug(LTag.XDRIP, "Setting TherapyEvents data sync from $lastSynced")
sp.putLong(R.string.key_xdrip_therapy_event_last_synced_id, lastSynced)
}
}
override fun processChangedTherapyEvents() {
private fun processChangedTherapyEvents() {
var progress: String
val lastDbIdWrapped = appRepository.getLastTherapyEventIdWrapped().blockingGet()
val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L
val lastDbId = appRepository.getLastTherapyEventId() ?: 0L
while (true) {
if (!isEnabled) return
var startId = sp.getLong(R.string.key_xdrip_therapy_event_last_synced_id, 0)
@ -355,16 +348,15 @@ class XdripDataSyncSelectorImplementation @Inject constructor(
sendTreatments(force = true, progress)
}
override fun confirmLastDeviceStatusIdIfGreater(lastSynced: Long) {
fun confirmLastDeviceStatusIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_xdrip_device_status_last_synced_id, 0)) {
//aapsLogger.debug(LTag.XDRIP, "Setting DeviceStatus data sync from $lastSynced")
sp.putLong(R.string.key_xdrip_device_status_last_synced_id, lastSynced)
}
}
override fun processChangedDeviceStatuses() {
val lastDbIdWrapped = appRepository.getLastDeviceStatusIdWrapped().blockingGet()
val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L
private fun processChangedDeviceStatuses() {
val lastDbId = appRepository.getLastDeviceStatusId() ?: 0L
while (true) {
if (!isEnabled) return
var startId = sp.getLong(R.string.key_xdrip_device_status_last_synced_id, 0)
@ -382,17 +374,16 @@ class XdripDataSyncSelectorImplementation @Inject constructor(
}
}
override fun confirmLastTemporaryBasalIdIfGreater(lastSynced: Long) {
fun confirmLastTemporaryBasalIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_xdrip_temporary_basal_last_synced_id, 0)) {
//aapsLogger.debug(LTag.XDRIP, "Setting TemporaryBasal data sync from $lastSynced")
sp.putLong(R.string.key_xdrip_temporary_basal_last_synced_id, lastSynced)
}
}
override fun processChangedTemporaryBasals() {
private fun processChangedTemporaryBasals() {
var progress: String
val lastDbIdWrapped = appRepository.getLastTemporaryBasalIdWrapped().blockingGet()
val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L
val lastDbId = appRepository.getLastTemporaryBasalId() ?: 0L
while (true) {
if (!isEnabled) return
var startId = sp.getLong(R.string.key_xdrip_temporary_basal_last_synced_id, 0)
@ -413,17 +404,16 @@ class XdripDataSyncSelectorImplementation @Inject constructor(
sendTreatments(force = true, progress)
}
override fun confirmLastExtendedBolusIdIfGreater(lastSynced: Long) {
fun confirmLastExtendedBolusIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_xdrip_extended_bolus_last_synced_id, 0)) {
//aapsLogger.debug(LTag.XDRIP, "Setting ExtendedBolus data sync from $lastSynced")
sp.putLong(R.string.key_xdrip_extended_bolus_last_synced_id, lastSynced)
}
}
override fun processChangedExtendedBoluses() {
private fun processChangedExtendedBoluses() {
var progress: String
val lastDbIdWrapped = appRepository.getLastExtendedBolusIdWrapped().blockingGet()
val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L
val lastDbId = appRepository.getLastExtendedBolusId() ?: 0L
while (true) {
if (!isEnabled) return
var startId = sp.getLong(R.string.key_xdrip_extended_bolus_last_synced_id, 0)
@ -447,17 +437,16 @@ class XdripDataSyncSelectorImplementation @Inject constructor(
sendTreatments(force = true, progress)
}
override fun confirmLastProfileSwitchIdIfGreater(lastSynced: Long) {
fun confirmLastProfileSwitchIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_xdrip_profile_switch_last_synced_id, 0)) {
//aapsLogger.debug(LTag.XDRIP, "Setting ProfileSwitch data sync from $lastSynced")
sp.putLong(R.string.key_xdrip_profile_switch_last_synced_id, lastSynced)
}
}
override fun processChangedProfileSwitches() {
private fun processChangedProfileSwitches() {
var progress: String
val lastDbIdWrapped = appRepository.getLastProfileSwitchIdWrapped().blockingGet()
val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L
val lastDbId = appRepository.getLastProfileSwitchId() ?: 0L
while (true) {
if (!isEnabled) return
var startId = sp.getLong(R.string.key_xdrip_profile_switch_last_synced_id, 0)
@ -478,17 +467,16 @@ class XdripDataSyncSelectorImplementation @Inject constructor(
sendTreatments(force = true, progress)
}
override fun confirmLastEffectiveProfileSwitchIdIfGreater(lastSynced: Long) {
fun confirmLastEffectiveProfileSwitchIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_xdrip_effective_profile_switch_last_synced_id, 0)) {
//aapsLogger.debug(LTag.XDRIP, "Setting EffectiveProfileSwitch data sync from $lastSynced")
sp.putLong(R.string.key_xdrip_effective_profile_switch_last_synced_id, lastSynced)
}
}
override fun processChangedEffectiveProfileSwitches() {
private fun processChangedEffectiveProfileSwitches() {
var progress: String
val lastDbIdWrapped = appRepository.getLastEffectiveProfileSwitchIdWrapped().blockingGet()
val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L
val lastDbId = appRepository.getLastEffectiveProfileSwitchId() ?: 0L
while (true) {
if (!isEnabled) return
var startId = sp.getLong(R.string.key_xdrip_effective_profile_switch_last_synced_id, 0)
@ -509,17 +497,16 @@ class XdripDataSyncSelectorImplementation @Inject constructor(
sendTreatments(force = true, progress)
}
override fun confirmLastOfflineEventIdIfGreater(lastSynced: Long) {
fun confirmLastOfflineEventIdIfGreater(lastSynced: Long) {
if (lastSynced > sp.getLong(R.string.key_xdrip_offline_event_last_synced_id, 0)) {
//aapsLogger.debug(LTag.XDRIP, "Setting OfflineEvent data sync from $lastSynced")
sp.putLong(R.string.key_xdrip_offline_event_last_synced_id, lastSynced)
}
}
override fun processChangedOfflineEvents() {
private fun processChangedOfflineEvents() {
var progress: String
val lastDbIdWrapped = appRepository.getLastOfflineEventIdWrapped().blockingGet()
val lastDbId = if (lastDbIdWrapped is ValueWrapper.Existing) lastDbIdWrapped.value else 0L
val lastDbId = appRepository.getLastOfflineEventId() ?: 0L
while (true) {
if (!isEnabled) return
var startId = sp.getLong(R.string.key_xdrip_offline_event_last_synced_id, 0)
@ -540,11 +527,11 @@ class XdripDataSyncSelectorImplementation @Inject constructor(
sendTreatments(force = true, progress)
}
override fun confirmLastProfileStore(lastSynced: Long) {
fun confirmLastProfileStore(lastSynced: Long) {
sp.putLong(R.string.key_xdrip_profile_store_last_synced_timestamp, lastSynced)
}
override fun processChangedProfileStore() {
private fun processChangedProfileStore() {
if (!isEnabled) return
val lastSync = sp.getLong(R.string.key_xdrip_profile_store_last_synced_timestamp, 0)
val lastChange = sp.getLong(info.nightscout.core.utils.R.string.key_local_profile_last_change, 0)
@ -556,7 +543,10 @@ class XdripDataSyncSelectorImplementation @Inject constructor(
// add for v3
if (JsonHelper.safeGetLongAllowNull(profileJson, "date") == null)
profileJson.put("date", profileStore.getStartDate())
xdripPlugin.sendToXdrip("profile", DataSyncSelector.PairProfileStore(profileJson, dateUtil.now()), "")
val now = dateUtil.now()
xdripPlugin.sendToXdrip("profile", DataSyncSelector.PairProfileStore(profileJson, now), "")
confirmLastProfileStore(now)
processChangedProfileStore()
}
}
}

View file

@ -35,7 +35,7 @@ class XdripFragment : DaggerFragment(), MenuProvider, PluginFragment {
@Inject lateinit var rxBus: RxBus
@Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var aapsSchedulers: AapsSchedulers
@Inject lateinit var dataSyncSelector: XdripDataSyncSelectorImplementation
@Inject lateinit var dataSyncSelector: DataSyncSelectorXdripImpl
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var xdripPlugin: XdripPlugin
@Inject lateinit var config: Config

View file

@ -29,6 +29,7 @@ import info.nightscout.interfaces.profile.Profile
import info.nightscout.interfaces.profile.ProfileFunction
import info.nightscout.interfaces.receivers.Intents
import info.nightscout.interfaces.sync.DataSyncSelector
import info.nightscout.interfaces.sync.DataSyncSelectorXdrip
import info.nightscout.interfaces.sync.Sync
import info.nightscout.interfaces.ui.UiInteraction
import info.nightscout.interfaces.utils.DecimalFormatter
@ -54,6 +55,10 @@ import info.nightscout.shared.sharedPreferences.SP
import info.nightscout.shared.utils.DateUtil
import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
import org.json.JSONArray
import java.util.concurrent.Executors
import java.util.concurrent.ScheduledFuture
@ -74,7 +79,7 @@ class XdripPlugin @Inject constructor(
private val iobCobCalculator: IobCobCalculator,
private val rxBus: RxBus,
private val uiInteraction: UiInteraction,
private val dataSyncSelector: XdripDataSyncSelectorImplementation,
private val dataSyncSelector: DataSyncSelectorXdrip,
private val dateUtil: DateUtil,
aapsLogger: AAPSLogger
) : XDripBroadcast, Sync, PluginBase(
@ -92,6 +97,7 @@ class XdripPlugin @Inject constructor(
@Suppress("PrivatePropertyName")
private val XDRIP_JOB_NAME: String = this::class.java.simpleName
private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
private val disposable = CompositeDisposable()
private val handler = Handler(HandlerThread(this::class.simpleName + "Handler").also { it.start() }.looper)
private val listLog: MutableList<EventXdripNewLog> = ArrayList()
@ -316,14 +322,17 @@ class XdripPlugin @Inject constructor(
*/
override fun sendToXdrip(collection: String, dataPair: DataSyncSelector.DataPair, progress: String) {
scope.launch {
when (collection) {
"profile" -> sendProfileStore(dataPair = dataPair, progress = progress)
"devicestatus" -> sendDeviceStatus(dataPair = dataPair, progress = progress)
else -> throw IllegalStateException()
}
}
}
override fun sendToXdrip(collection: String, dataPairs: List<DataSyncSelector.DataPair>, progress: String) {
scope.launch {
when (collection) {
"entries" -> sendEntries(dataPairs = dataPairs, progress = progress)
"food" -> sendFood(dataPairs = dataPairs, progress = progress)
@ -331,8 +340,9 @@ class XdripPlugin @Inject constructor(
else -> throw IllegalStateException()
}
}
}
private fun sendProfileStore(dataPair: DataSyncSelector.DataPair, progress: String) {
private suspend fun sendProfileStore(dataPair: DataSyncSelector.DataPair, progress: String) {
val data = (dataPair as DataSyncSelector.PairProfileStore).value
rxBus.send(EventXdripNewLog("SENDING", "Sent 1 PROFILE ($progress)"))
broadcast(
@ -340,8 +350,6 @@ class XdripPlugin @Inject constructor(
.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES)
.putExtras(Bundle().apply { putString("profile", data.toString()) })
)
dataSyncSelector.confirmLastProfileStore(dataPair.id)
dataSyncSelector.processChangedProfileStore()
}
private fun sendDeviceStatus(dataPair: DataSyncSelector.DataPair, progress: String) {

View file

@ -5,7 +5,7 @@ import androidx.work.WorkerParameters
import info.nightscout.androidaps.annotations.OpenForTesting
import info.nightscout.core.utils.worker.LoggingWorker
import info.nightscout.interfaces.plugin.ActivePlugin
import info.nightscout.plugins.sync.xdrip.XdripDataSyncSelectorImplementation
import info.nightscout.plugins.sync.xdrip.DataSyncSelectorXdripImpl
import info.nightscout.plugins.sync.xdrip.events.EventXdripUpdateGUI
import info.nightscout.rx.bus.RxBus
import info.nightscout.rx.events.EventXdripNewLog
@ -17,7 +17,7 @@ class XdripDataSyncWorker(
context: Context, params: WorkerParameters
) : LoggingWorker(context, params, Dispatchers.IO) {
@Inject lateinit var dataSyncSelector: XdripDataSyncSelectorImplementation
@Inject lateinit var dataSyncSelector: DataSyncSelectorXdripImpl
@Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var rxBus: RxBus

View file

@ -96,18 +96,14 @@
</LinearLayout>
<ScrollView
android:id="@+id/log_scrollview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_marginStart="5dp"
android:layout_marginEnd="5dp">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fadeScrollbars="true"
android:scrollbarStyle="outsideOverlay"
android:scrollbars="vertical">
<TextView
android:id="@+id/log"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="" />
</ScrollView>
</androidx.recyclerview.widget.RecyclerView>
</LinearLayout>

View file

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/log_card"
style="@style/Widget.MaterialComponents.CardView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
app:cardCornerRadius="4dp"
app:cardElevation="1dp"
app:cardUseCompatPadding="true"
app:contentPadding="2dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:baselineAligned="false"
android:orientation="horizontal">
<TextView
android:id="@+id/log_text"
android:layout_width="wrap_content"
android:layout_height="match_parent"
tools:text="LogText" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>

View file

@ -1,11 +1,9 @@
package info.nightscout.plugins.sync.nsclientV3
import androidx.work.WorkManager
import dagger.android.AndroidInjector
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.TestBaseWithProfile
import info.nightscout.core.extensions.fromConstant
import info.nightscout.core.utils.receivers.DataWorkerStorage
import info.nightscout.database.entities.Bolus
import info.nightscout.database.entities.BolusCalculatorResult
import info.nightscout.database.entities.Carbs
@ -20,7 +18,6 @@ import info.nightscout.database.entities.TemporaryBasal
import info.nightscout.database.entities.TemporaryTarget
import info.nightscout.database.entities.TherapyEvent
import info.nightscout.database.entities.embedments.InterfaceIDs
import info.nightscout.interfaces.XDripBroadcast
import info.nightscout.interfaces.logging.UserEntryLogger
import info.nightscout.interfaces.nsclient.StoreDataForDb
import info.nightscout.interfaces.profile.ProfileFunction
@ -28,16 +25,15 @@ import info.nightscout.interfaces.pump.VirtualPump
import info.nightscout.interfaces.source.NSClientSource
import info.nightscout.interfaces.sync.DataSyncSelector
import info.nightscout.interfaces.ui.UiInteraction
import info.nightscout.interfaces.workflow.WorkerClasses
import info.nightscout.plugins.sync.nsShared.NsIncomingDataProcessor
import info.nightscout.plugins.sync.nsShared.StoreDataForDbImpl
import info.nightscout.plugins.sync.nsclient.ReceiverDelegate
import info.nightscout.plugins.sync.nsclient.data.NSDeviceStatusHandler
import info.nightscout.plugins.sync.nsclient.extensions.fromConstant
import info.nightscout.sdk.interfaces.NSAndroidClient
import info.nightscout.sdk.localmodel.treatment.CreateUpdateResponse
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.mockito.Mock
@ -51,17 +47,14 @@ internal class NSClientV3PluginTest : TestBaseWithProfile() {
@Mock lateinit var receiverDelegate: ReceiverDelegate
@Mock lateinit var uiInteraction: UiInteraction
@Mock lateinit var dataSyncSelector: DataSyncSelector
@Mock lateinit var dataSyncSelectorV3: DataSyncSelectorV3Impl
@Mock lateinit var nsAndroidClient: NSAndroidClient
@Mock lateinit var uel: UserEntryLogger
@Mock lateinit var nsClientSource: NSClientSource
@Mock lateinit var xDripBroadcast: XDripBroadcast
@Mock lateinit var virtualPump: VirtualPump
@Mock lateinit var mockedProfileFunction: ProfileFunction
@Mock lateinit var nsDeviceStatusHandler: NSDeviceStatusHandler
@Mock lateinit var workManager: WorkManager
@Mock lateinit var workerClasses: WorkerClasses
@Mock lateinit var dataWorkerStorage: DataWorkerStorage
@Mock lateinit var nsIncomingDataProcessor: NsIncomingDataProcessor
private lateinit var storeDataForDb: StoreDataForDb
private lateinit var sut: NSClientV3Plugin
@ -77,8 +70,8 @@ internal class NSClientV3PluginTest : TestBaseWithProfile() {
sut =
NSClientV3Plugin(
injector, aapsLogger, aapsSchedulers, rxBus, rh, context, fabricPrivacy,
sp, receiverDelegate, config, dateUtil, uiInteraction, dataSyncSelector, mockedProfileFunction, repository,
nsDeviceStatusHandler, workManager, workerClasses, dataWorkerStorage, nsClientSource
sp, receiverDelegate, config, dateUtil, uiInteraction, dataSyncSelectorV3, mockedProfileFunction, repository,
nsDeviceStatusHandler, nsClientSource, nsIncomingDataProcessor, storeDataForDb
)
sut.nsAndroidClient = nsAndroidClient
`when`(mockedProfileFunction.getProfile(anyLong())).thenReturn(validProfile)
@ -87,7 +80,6 @@ internal class NSClientV3PluginTest : TestBaseWithProfile() {
@Test
@OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
fun nsAddDeviceStatus() = runTest {
sut.scope = CoroutineScope(UnconfinedTestDispatcher(testScheduler))
val deviceStatus = DeviceStatus(
timestamp = 10000,
suggested = "{\"temp\":\"absolute\",\"bg\":133,\"tick\":-6,\"eventualBG\":67,\"targetBG\":99,\"insulinReq\":0,\"deliverAt\":\"2023-01-02T15:29:33.374Z\",\"sensitivityRatio\":1,\"variable_sens\":97.5,\"predBGs\":{\"IOB\":[133,127,121,116,111,106,101,97,93,89,85,81,78,75,72,69,67,65,62,60,58,57,55,54,52,51,50,49,48,47,46,45,45,44,43,43,42,42,41,41,41,41,40,40,40,40,39],\"ZT\":[133,127,121,115,110,105,101,96,92,88,84,81,77,74,71,69,66,64,62,59,58,56,54,53,51,50,49,48,47,46,45,44,44,43,42,42,41,41,40,40,40,39,39,39,39,39,39,39],\"UAM\":[133,127,121,115,110,105,101,96,92,88,84,81,77,74,71,69,66,64,62,59,58,56,54,53,51,50,49,48,47,46,45,44,44,43,42,42,41,41,40,40,40,39]},\"reason\":\"COB: 0, Dev: 0.1, BGI: -0.3, ISF: 5.4, CR: 13, Target: 5.5, minPredBG 2.2, minGuardBG 2.1, IOBpredBG 2.2, UAMpredBG 2.2; minGuardBG 2.1<4.0\",\"COB\":0,\"IOB\":0.692,\"duration\":90,\"rate\":0,\"timestamp\":\"2023-01-02T15:29:39.460Z\"}",
@ -103,23 +95,18 @@ internal class NSClientV3PluginTest : TestBaseWithProfile() {
)
val dataPair = DataSyncSelector.PairDeviceStatus(deviceStatus, 1000)
// create
`when`(nsAndroidClient.createDeviceStatus(anyObject())).thenReturn(CreateUpdateResponse(201, null))
`when`(nsAndroidClient.createDeviceStatus(anyObject())).thenReturn(CreateUpdateResponse(201, "aaa"))
sut.nsAdd("devicestatus", dataPair, "1/3")
// Assertions.assertEquals(1, storeDataForDb.nsIdDeviceStatuses.size)
verify(dataSyncSelector, Times(1)).confirmLastDeviceStatusIdIfGreater(1000)
verify(dataSyncSelector, Times(1)).processChangedDeviceStatuses()
Assertions.assertEquals(1, storeDataForDb.nsIdDeviceStatuses.size)
// update
`when`(nsAndroidClient.createDeviceStatus(anyObject())).thenReturn(CreateUpdateResponse(200, null))
`when`(nsAndroidClient.createDeviceStatus(anyObject())).thenReturn(CreateUpdateResponse(200, "aaa"))
sut.nsAdd("devicestatus", dataPair, "1/3")
// Assertions.assertEquals(1, storeDataForDb.nsIdDeviceStatuses.size) // still only 1
verify(dataSyncSelector, Times(2)).confirmLastDeviceStatusIdIfGreater(1000)
verify(dataSyncSelector, Times(2)).processChangedDeviceStatuses()
Assertions.assertEquals(2, storeDataForDb.nsIdDeviceStatuses.size) // still only 1
}
@Test
@OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
fun nsAddEntries() = runTest {
sut.scope = CoroutineScope(UnconfinedTestDispatcher(testScheduler))
val glucoseValue = GlucoseValue(
timestamp = 10000,
isValid = true,
@ -134,21 +121,18 @@ internal class NSClientV3PluginTest : TestBaseWithProfile() {
)
val dataPair = DataSyncSelector.PairGlucoseValue(glucoseValue, 1000)
// create
`when`(nsAndroidClient.createSvg(anyObject())).thenReturn(CreateUpdateResponse(201, null))
`when`(nsAndroidClient.createSgv(anyObject())).thenReturn(CreateUpdateResponse(201, "aaa"))
sut.nsAdd("entries", dataPair, "1/3")
verify(dataSyncSelector, Times(1)).confirmLastGlucoseValueIdIfGreater(1000)
verify(dataSyncSelector, Times(1)).processChangedGlucoseValues()
Assertions.assertEquals(1, storeDataForDb.nsIdGlucoseValues.size)
// update
`when`(nsAndroidClient.updateSvg(anyObject())).thenReturn(CreateUpdateResponse(200, null))
`when`(nsAndroidClient.updateSvg(anyObject())).thenReturn(CreateUpdateResponse(200, "aaa"))
sut.nsUpdate("entries", dataPair, "1/3")
verify(dataSyncSelector, Times(2)).confirmLastGlucoseValueIdIfGreater(1000)
verify(dataSyncSelector, Times(2)).processChangedGlucoseValues()
Assertions.assertEquals(2, storeDataForDb.nsIdGlucoseValues.size)
}
@Test
@OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
fun nsAddFood() = runTest {
sut.scope = CoroutineScope(UnconfinedTestDispatcher(testScheduler))
val food = Food(
isValid = true,
name = "name",
@ -167,21 +151,18 @@ internal class NSClientV3PluginTest : TestBaseWithProfile() {
)
val dataPair = DataSyncSelector.PairFood(food, 1000)
// create
`when`(nsAndroidClient.createFood(anyObject())).thenReturn(CreateUpdateResponse(201, null))
`when`(nsAndroidClient.createFood(anyObject())).thenReturn(CreateUpdateResponse(201, "aaa"))
sut.nsAdd("food", dataPair, "1/3")
verify(dataSyncSelector, Times(1)).confirmLastFoodIdIfGreater(1000)
verify(dataSyncSelector, Times(1)).processChangedFoods()
Assertions.assertEquals(1, storeDataForDb.nsIdFoods.size)
// update
`when`(nsAndroidClient.updateFood(anyObject())).thenReturn(CreateUpdateResponse(200, null))
`when`(nsAndroidClient.updateFood(anyObject())).thenReturn(CreateUpdateResponse(200, "aaa"))
sut.nsUpdate("food", dataPair, "1/3")
verify(dataSyncSelector, Times(2)).confirmLastFoodIdIfGreater(1000)
verify(dataSyncSelector, Times(2)).processChangedFoods()
Assertions.assertEquals(2, storeDataForDb.nsIdFoods.size)
}
@Test
@OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
fun nsAddBolus() = runTest {
sut.scope = CoroutineScope(UnconfinedTestDispatcher(testScheduler))
val bolus = Bolus(
timestamp = 10000,
isValid = true,
@ -198,21 +179,18 @@ internal class NSClientV3PluginTest : TestBaseWithProfile() {
)
val dataPair = DataSyncSelector.PairBolus(bolus, 1000)
// create
`when`(nsAndroidClient.createTreatment(anyObject())).thenReturn(CreateUpdateResponse(201, null))
`when`(nsAndroidClient.createTreatment(anyObject())).thenReturn(CreateUpdateResponse(201, "aaa"))
sut.nsAdd("treatments", dataPair, "1/3")
verify(dataSyncSelector, Times(1)).confirmLastBolusIdIfGreater(1000)
verify(dataSyncSelector, Times(1)).processChangedBoluses()
Assertions.assertEquals(1, storeDataForDb.nsIdBoluses.size)
// update
`when`(nsAndroidClient.updateTreatment(anyObject())).thenReturn(CreateUpdateResponse(200, null))
`when`(nsAndroidClient.updateTreatment(anyObject())).thenReturn(CreateUpdateResponse(200, "aaa"))
sut.nsUpdate("treatments", dataPair, "1/3")
verify(dataSyncSelector, Times(2)).confirmLastBolusIdIfGreater(1000)
verify(dataSyncSelector, Times(2)).processChangedBoluses()
Assertions.assertEquals(2, storeDataForDb.nsIdBoluses.size)
}
@Test
@OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
fun nsAddCarbs() = runTest {
sut.scope = CoroutineScope(UnconfinedTestDispatcher(testScheduler))
val carbs = Carbs(
timestamp = 10000,
isValid = true,
@ -228,21 +206,18 @@ internal class NSClientV3PluginTest : TestBaseWithProfile() {
)
val dataPair = DataSyncSelector.PairCarbs(carbs, 1000)
// create
`when`(nsAndroidClient.createTreatment(anyObject())).thenReturn(CreateUpdateResponse(201, null))
`when`(nsAndroidClient.createTreatment(anyObject())).thenReturn(CreateUpdateResponse(201, "aaa"))
sut.nsAdd("treatments", dataPair, "1/3")
verify(dataSyncSelector, Times(1)).confirmLastCarbsIdIfGreater(1000)
verify(dataSyncSelector, Times(1)).processChangedCarbs()
Assertions.assertEquals(1, storeDataForDb.nsIdCarbs.size)
// update
`when`(nsAndroidClient.updateTreatment(anyObject())).thenReturn(CreateUpdateResponse(200, null))
`when`(nsAndroidClient.updateTreatment(anyObject())).thenReturn(CreateUpdateResponse(200, "aaa"))
sut.nsUpdate("treatments", dataPair, "1/3")
verify(dataSyncSelector, Times(2)).confirmLastCarbsIdIfGreater(1000)
verify(dataSyncSelector, Times(2)).processChangedCarbs()
Assertions.assertEquals(2, storeDataForDb.nsIdCarbs.size)
}
@Test
@OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
fun nsAddBolusCalculatorResult() = runTest {
sut.scope = CoroutineScope(UnconfinedTestDispatcher(testScheduler))
val bolus = BolusCalculatorResult(
timestamp = 10000,
isValid = true,
@ -284,21 +259,18 @@ internal class NSClientV3PluginTest : TestBaseWithProfile() {
)
val dataPair = DataSyncSelector.PairBolusCalculatorResult(bolus, 1000)
// create
`when`(nsAndroidClient.createTreatment(anyObject())).thenReturn(CreateUpdateResponse(201, null))
`when`(nsAndroidClient.createTreatment(anyObject())).thenReturn(CreateUpdateResponse(201, "aaa"))
sut.nsAdd("treatments", dataPair, "1/3")
verify(dataSyncSelector, Times(1)).confirmLastBolusCalculatorResultsIdIfGreater(1000)
verify(dataSyncSelector, Times(1)).processChangedBolusCalculatorResults()
Assertions.assertEquals(1, storeDataForDb.nsIdBolusCalculatorResults.size)
// update
`when`(nsAndroidClient.updateTreatment(anyObject())).thenReturn(CreateUpdateResponse(200, null))
`when`(nsAndroidClient.updateTreatment(anyObject())).thenReturn(CreateUpdateResponse(200, "aaa"))
sut.nsUpdate("treatments", dataPair, "1/3")
verify(dataSyncSelector, Times(2)).confirmLastBolusCalculatorResultsIdIfGreater(1000)
verify(dataSyncSelector, Times(2)).processChangedBolusCalculatorResults()
Assertions.assertEquals(2, storeDataForDb.nsIdBolusCalculatorResults.size)
}
@Test
@OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
fun nsAddEffectiveProfileSwitch() = runTest {
sut.scope = CoroutineScope(UnconfinedTestDispatcher(testScheduler))
val profileSwitch = EffectiveProfileSwitch(
timestamp = 10000,
isValid = true,
@ -325,21 +297,18 @@ internal class NSClientV3PluginTest : TestBaseWithProfile() {
)
val dataPair = DataSyncSelector.PairEffectiveProfileSwitch(profileSwitch, 1000)
// create
`when`(nsAndroidClient.createTreatment(anyObject())).thenReturn(CreateUpdateResponse(201, null))
`when`(nsAndroidClient.createTreatment(anyObject())).thenReturn(CreateUpdateResponse(201, "aaa"))
sut.nsAdd("treatments", dataPair, "1/3")
verify(dataSyncSelector, Times(1)).confirmLastEffectiveProfileSwitchIdIfGreater(1000)
verify(dataSyncSelector, Times(1)).processChangedEffectiveProfileSwitches()
Assertions.assertEquals(1, storeDataForDb.nsIdEffectiveProfileSwitches.size)
// update
`when`(nsAndroidClient.updateTreatment(anyObject())).thenReturn(CreateUpdateResponse(200, null))
`when`(nsAndroidClient.updateTreatment(anyObject())).thenReturn(CreateUpdateResponse(200, "aaa"))
sut.nsUpdate("treatments", dataPair, "1/3")
verify(dataSyncSelector, Times(2)).confirmLastEffectiveProfileSwitchIdIfGreater(1000)
verify(dataSyncSelector, Times(2)).processChangedEffectiveProfileSwitches()
Assertions.assertEquals(2, storeDataForDb.nsIdEffectiveProfileSwitches.size)
}
@Test
@OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
fun nsAddProfileSwitch() = runTest {
sut.scope = CoroutineScope(UnconfinedTestDispatcher(testScheduler))
val profileSwitch = ProfileSwitch(
timestamp = 10000,
isValid = true,
@ -364,21 +333,18 @@ internal class NSClientV3PluginTest : TestBaseWithProfile() {
)
val dataPair = DataSyncSelector.PairProfileSwitch(profileSwitch, 1000)
// create
`when`(nsAndroidClient.createTreatment(anyObject())).thenReturn(CreateUpdateResponse(201, null))
`when`(nsAndroidClient.createTreatment(anyObject())).thenReturn(CreateUpdateResponse(201, "aaa"))
sut.nsAdd("treatments", dataPair, "1/3")
verify(dataSyncSelector, Times(1)).confirmLastProfileSwitchIdIfGreater(1000)
verify(dataSyncSelector, Times(1)).processChangedProfileSwitches()
Assertions.assertEquals(1, storeDataForDb.nsIdProfileSwitches.size)
// update
`when`(nsAndroidClient.updateTreatment(anyObject())).thenReturn(CreateUpdateResponse(200, null))
`when`(nsAndroidClient.updateTreatment(anyObject())).thenReturn(CreateUpdateResponse(200, "aaa"))
sut.nsUpdate("treatments", dataPair, "1/3")
verify(dataSyncSelector, Times(2)).confirmLastProfileSwitchIdIfGreater(1000)
verify(dataSyncSelector, Times(2)).processChangedProfileSwitches()
Assertions.assertEquals(2, storeDataForDb.nsIdProfileSwitches.size)
}
@Test
@OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
fun nsAddExtendedBolus() = runTest {
sut.scope = CoroutineScope(UnconfinedTestDispatcher(testScheduler))
val extendedBolus = ExtendedBolus(
timestamp = 10000,
isValid = true,
@ -394,21 +360,18 @@ internal class NSClientV3PluginTest : TestBaseWithProfile() {
)
val dataPair = DataSyncSelector.PairExtendedBolus(extendedBolus, 1000)
// create
`when`(nsAndroidClient.createTreatment(anyObject())).thenReturn(CreateUpdateResponse(201, null))
`when`(nsAndroidClient.createTreatment(anyObject())).thenReturn(CreateUpdateResponse(201, "aaa"))
sut.nsAdd("treatments", dataPair, "1/3")
verify(dataSyncSelector, Times(1)).confirmLastExtendedBolusIdIfGreater(1000)
verify(dataSyncSelector, Times(1)).processChangedExtendedBoluses()
Assertions.assertEquals(1, storeDataForDb.nsIdExtendedBoluses.size)
// update
`when`(nsAndroidClient.updateTreatment(anyObject())).thenReturn(CreateUpdateResponse(200, null))
`when`(nsAndroidClient.updateTreatment(anyObject())).thenReturn(CreateUpdateResponse(200, "aaa"))
sut.nsUpdate("treatments", dataPair, "1/3")
verify(dataSyncSelector, Times(2)).confirmLastExtendedBolusIdIfGreater(1000)
verify(dataSyncSelector, Times(2)).processChangedExtendedBoluses()
Assertions.assertEquals(2, storeDataForDb.nsIdExtendedBoluses.size)
}
@Test
@OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
fun nsAddOffilineEvent() = runTest {
sut.scope = CoroutineScope(UnconfinedTestDispatcher(testScheduler))
val offlineEvent = OfflineEvent(
timestamp = 10000,
isValid = true,
@ -423,21 +386,18 @@ internal class NSClientV3PluginTest : TestBaseWithProfile() {
)
val dataPair = DataSyncSelector.PairOfflineEvent(offlineEvent, 1000)
// create
`when`(nsAndroidClient.createTreatment(anyObject())).thenReturn(CreateUpdateResponse(201, null))
`when`(nsAndroidClient.createTreatment(anyObject())).thenReturn(CreateUpdateResponse(201, "aaa"))
sut.nsAdd("treatments", dataPair, "1/3")
verify(dataSyncSelector, Times(1)).confirmLastOfflineEventIdIfGreater(1000)
verify(dataSyncSelector, Times(1)).processChangedOfflineEvents()
Assertions.assertEquals(1, storeDataForDb.nsIdOfflineEvents.size)
// update
`when`(nsAndroidClient.updateTreatment(anyObject())).thenReturn(CreateUpdateResponse(200, null))
`when`(nsAndroidClient.updateTreatment(anyObject())).thenReturn(CreateUpdateResponse(200, "aaa"))
sut.nsUpdate("treatments", dataPair, "1/3")
verify(dataSyncSelector, Times(2)).confirmLastOfflineEventIdIfGreater(1000)
verify(dataSyncSelector, Times(2)).processChangedOfflineEvents()
Assertions.assertEquals(2, storeDataForDb.nsIdOfflineEvents.size)
}
@Test
@OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
fun nsAddTemporaryBasal() = runTest {
sut.scope = CoroutineScope(UnconfinedTestDispatcher(testScheduler))
val temporaryBasal = TemporaryBasal(
timestamp = 10000,
isValid = true,
@ -454,21 +414,18 @@ internal class NSClientV3PluginTest : TestBaseWithProfile() {
)
val dataPair = DataSyncSelector.PairTemporaryBasal(temporaryBasal, 1000)
// create
`when`(nsAndroidClient.createTreatment(anyObject())).thenReturn(CreateUpdateResponse(201, null))
`when`(nsAndroidClient.createTreatment(anyObject())).thenReturn(CreateUpdateResponse(201, "aaa"))
sut.nsAdd("treatments", dataPair, "1/3")
verify(dataSyncSelector, Times(1)).confirmLastTemporaryBasalIdIfGreater(1000)
verify(dataSyncSelector, Times(1)).processChangedTemporaryBasals()
Assertions.assertEquals(1, storeDataForDb.nsIdTemporaryBasals.size)
// update
`when`(nsAndroidClient.updateTreatment(anyObject())).thenReturn(CreateUpdateResponse(200, null))
`when`(nsAndroidClient.updateTreatment(anyObject())).thenReturn(CreateUpdateResponse(200, "aaa"))
sut.nsUpdate("treatments", dataPair, "1/3")
verify(dataSyncSelector, Times(2)).confirmLastTemporaryBasalIdIfGreater(1000)
verify(dataSyncSelector, Times(2)).processChangedTemporaryBasals()
Assertions.assertEquals(2, storeDataForDb.nsIdTemporaryBasals.size)
}
@Test
@OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
fun nsAddTemporaryTarget() = runTest {
sut.scope = CoroutineScope(UnconfinedTestDispatcher(testScheduler))
val temporaryTarget = TemporaryTarget(
timestamp = 10000,
isValid = true,
@ -485,21 +442,18 @@ internal class NSClientV3PluginTest : TestBaseWithProfile() {
)
val dataPair = DataSyncSelector.PairTemporaryTarget(temporaryTarget, 1000)
// create
`when`(nsAndroidClient.createTreatment(anyObject())).thenReturn(CreateUpdateResponse(201, null))
`when`(nsAndroidClient.createTreatment(anyObject())).thenReturn(CreateUpdateResponse(201, "aaa"))
sut.nsAdd("treatments", dataPair, "1/3")
verify(dataSyncSelector, Times(1)).confirmLastTempTargetsIdIfGreater(1000)
verify(dataSyncSelector, Times(1)).processChangedTempTargets()
Assertions.assertEquals(1, storeDataForDb.nsIdTemporaryTargets.size)
// update
`when`(nsAndroidClient.updateTreatment(anyObject())).thenReturn(CreateUpdateResponse(200, null))
`when`(nsAndroidClient.updateTreatment(anyObject())).thenReturn(CreateUpdateResponse(200, "aaa"))
sut.nsUpdate("treatments", dataPair, "1/3")
verify(dataSyncSelector, Times(2)).confirmLastTempTargetsIdIfGreater(1000)
verify(dataSyncSelector, Times(2)).processChangedTempTargets()
Assertions.assertEquals(2, storeDataForDb.nsIdTemporaryTargets.size)
}
@Test
@OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
fun nsAddTherapyEvent() = runTest {
sut.scope = CoroutineScope(UnconfinedTestDispatcher(testScheduler))
val therapyEvent = TherapyEvent(
timestamp = 10000,
isValid = true,
@ -519,32 +473,29 @@ internal class NSClientV3PluginTest : TestBaseWithProfile() {
)
val dataPair = DataSyncSelector.PairTherapyEvent(therapyEvent, 1000)
// create
`when`(nsAndroidClient.createTreatment(anyObject())).thenReturn(CreateUpdateResponse(201, null))
`when`(nsAndroidClient.createTreatment(anyObject())).thenReturn(CreateUpdateResponse(201, "aaa"))
sut.nsAdd("treatments", dataPair, "1/3")
verify(dataSyncSelector, Times(1)).confirmLastTherapyEventIdIfGreater(1000)
verify(dataSyncSelector, Times(1)).processChangedTherapyEvents()
Assertions.assertEquals(1, storeDataForDb.nsIdTherapyEvents.size)
// update
`when`(nsAndroidClient.updateTreatment(anyObject())).thenReturn(CreateUpdateResponse(200, null))
`when`(nsAndroidClient.updateTreatment(anyObject())).thenReturn(CreateUpdateResponse(200, "aaa"))
sut.nsUpdate("treatments", dataPair, "1/3")
verify(dataSyncSelector, Times(2)).confirmLastTherapyEventIdIfGreater(1000)
verify(dataSyncSelector, Times(2)).processChangedTherapyEvents()
Assertions.assertEquals(2, storeDataForDb.nsIdTherapyEvents.size)
}
@Test
@OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
fun nsAddProfile() = runTest {
sut.scope = CoroutineScope(UnconfinedTestDispatcher(testScheduler))
val dataPair = DataSyncSelector.PairProfileStore(getValidProfileStore().data, 1000)
// create
`when`(nsAndroidClient.createProfileStore(anyObject())).thenReturn(CreateUpdateResponse(201, null))
`when`(nsAndroidClient.createProfileStore(anyObject())).thenReturn(CreateUpdateResponse(201, "aaa"))
sut.nsAdd("profile", dataPair, "1/3")
verify(dataSyncSelector, Times(1)).confirmLastProfileStore(1000)
verify(dataSyncSelector, Times(1)).processChangedProfileStore()
// verify(dataSyncSelectorV3, Times(1)).confirmLastProfileStore(1000)
// verify(dataSyncSelectorV3, Times(1)).processChangedProfileStore()
// update
`when`(nsAndroidClient.updateTreatment(anyObject())).thenReturn(CreateUpdateResponse(200, null))
`when`(nsAndroidClient.updateTreatment(anyObject())).thenReturn(CreateUpdateResponse(200, "aaa"))
sut.nsUpdate("profile", dataPair, "1/3")
verify(dataSyncSelector, Times(2)).confirmLastProfileStore(1000)
verify(dataSyncSelector, Times(2)).processChangedProfileStore()
// verify(dataSyncSelectorV3, Times(2)).confirmLastProfileStore(1000)
// verify(dataSyncSelectorV3, Times(2)).processChangedProfileStore()
}
}

View file

@ -7,8 +7,8 @@ import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.TestBase
import info.nightscout.core.utils.fabric.FabricPrivacy
import info.nightscout.interfaces.plugin.ActivePlugin
import info.nightscout.interfaces.sync.DataSyncSelector
import info.nightscout.interfaces.sync.NsClient
import info.nightscout.plugins.sync.nsclientV3.DataSyncSelectorV3Impl
import info.nightscout.plugins.sync.nsclientV3.NSClientV3Plugin
import info.nightscout.rx.bus.RxBus
import kotlinx.coroutines.ExperimentalCoroutinesApi
@ -24,7 +24,7 @@ import org.mockito.Mockito.`when`
internal class DataSyncWorkerTest : TestBase() {
@Mock lateinit var fabricPrivacy: FabricPrivacy
@Mock lateinit var dataSyncSelector: DataSyncSelector
@Mock lateinit var dataSyncSelectorV3: DataSyncSelectorV3Impl
@Mock lateinit var activePlugin: ActivePlugin
@Mock lateinit var nsClient: NsClient
@Mock lateinit var rxBus: RxBus
@ -37,7 +37,7 @@ internal class DataSyncWorkerTest : TestBase() {
if (it is DataSyncWorker) {
it.aapsLogger = aapsLogger
it.fabricPrivacy = fabricPrivacy
it.dataSyncSelector = dataSyncSelector
it.dataSyncSelectorV3 = dataSyncSelectorV3
it.activePlugin = activePlugin
it.rxBus = rxBus
it.nsClientV3Plugin = nsClientV3Plugin
@ -57,11 +57,11 @@ internal class DataSyncWorkerTest : TestBase() {
sut = TestListenableWorkerBuilder<DataSyncWorker>(context).build()
`when`(nsClient.hasWritePermission).thenReturn(false)
sut.doWorkAndLog()
Mockito.verify(dataSyncSelector, Mockito.times(0)).doUpload()
Mockito.verify(dataSyncSelectorV3, Mockito.times(0)).doUpload()
`when`(nsClient.hasWritePermission).thenReturn(true)
val result = sut.doWorkAndLog()
Mockito.verify(dataSyncSelector, Mockito.times(1)).doUpload()
Mockito.verify(dataSyncSelectorV3, Mockito.times(1)).doUpload()
Assertions.assertTrue(result is Success)
}
}

View file

@ -1,6 +1,5 @@
package info.nightscout.plugins.sync.nsclientV3.workers
import androidx.work.ExistingWorkPolicy
import androidx.work.ListenableWorker
import androidx.work.OneTimeWorkRequest
import androidx.work.WorkContinuation
@ -11,17 +10,17 @@ import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.TestBase
import info.nightscout.core.utils.fabric.FabricPrivacy
import info.nightscout.core.utils.receivers.DataWorkerStorage
import info.nightscout.core.utils.worker.LoggingWorker
import info.nightscout.database.entities.GlucoseValue
import info.nightscout.database.entities.embedments.InterfaceIDs
import info.nightscout.database.impl.AppRepository
import info.nightscout.interfaces.Config
import info.nightscout.interfaces.nsclient.StoreDataForDb
import info.nightscout.interfaces.profile.ProfileFunction
import info.nightscout.interfaces.receivers.ReceiverStatusStore
import info.nightscout.interfaces.source.NSClientSource
import info.nightscout.interfaces.sync.DataSyncSelector
import info.nightscout.interfaces.sync.DataSyncSelectorV3
import info.nightscout.interfaces.ui.UiInteraction
import info.nightscout.interfaces.workflow.WorkerClasses
import info.nightscout.plugins.sync.nsShared.NsIncomingDataProcessor
import info.nightscout.plugins.sync.nsclient.ReceiverDelegate
import info.nightscout.plugins.sync.nsclient.data.NSDeviceStatusHandler
import info.nightscout.plugins.sync.nsclientV3.NSClientV3Plugin
@ -41,14 +40,12 @@ import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.anyLong
import org.mockito.ArgumentMatchers.anyString
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
import org.mockito.Mockito
@OptIn(ExperimentalCoroutinesApi::class)
internal class LoadBgWorkerTest : TestBase() {
@Mock lateinit var workerClasses: WorkerClasses
@Mock lateinit var sp: SP
@Mock lateinit var fabricPrivacy: FabricPrivacy
@Mock lateinit var dateUtil: DateUtil
@ -57,13 +54,15 @@ internal class LoadBgWorkerTest : TestBase() {
@Mock lateinit var profileFunction: ProfileFunction
@Mock lateinit var config: Config
@Mock lateinit var uiInteraction: UiInteraction
@Mock lateinit var dataSyncSelector: DataSyncSelector
@Mock lateinit var dataSyncSelectorV3: DataSyncSelectorV3
@Mock lateinit var repository: AppRepository
@Mock lateinit var receiverStatusStore: ReceiverStatusStore
@Mock lateinit var nsClientSource: NSClientSource
@Mock lateinit var workManager: WorkManager
@Mock lateinit var workContinuation: WorkContinuation
@Mock lateinit var nsDeviceStatusHandler: NSDeviceStatusHandler
@Mock lateinit var storeDataForDb: StoreDataForDb
@Mock lateinit var nsIncomingDataProcessor: NsIncomingDataProcessor
private val rxBus: RxBus = RxBus(aapsSchedulers, aapsLogger)
private lateinit var nsClientV3Plugin: NSClientV3Plugin
@ -78,15 +77,13 @@ internal class LoadBgWorkerTest : TestBase() {
if (it is LoadBgWorker) {
it.aapsLogger = aapsLogger
it.fabricPrivacy = fabricPrivacy
it.dataWorkerStorage = dataWorkerStorage
it.sp = sp
it.rxBus = rxBus
it.context = context
it.dateUtil = dateUtil
it.nsClientV3Plugin = nsClientV3Plugin
it.workerClasses = workerClasses
it.nsClientSource = nsClientSource
it.workManager = workManager
it.storeDataForDb = storeDataForDb
}
}
}
@ -101,8 +98,8 @@ internal class LoadBgWorkerTest : TestBase() {
receiverDelegate = ReceiverDelegate(rxBus, rh, sp, receiverStatusStore, aapsSchedulers, fabricPrivacy)
nsClientV3Plugin = NSClientV3Plugin(
injector, aapsLogger, aapsSchedulers, rxBus, rh, context, fabricPrivacy,
sp, receiverDelegate, config, dateUtil, uiInteraction, dataSyncSelector, profileFunction, repository,
nsDeviceStatusHandler, workManager, workerClasses, dataWorkerStorage, nsClientSource
sp, receiverDelegate, config, dateUtil, uiInteraction, dataSyncSelectorV3, profileFunction, repository,
nsDeviceStatusHandler, nsClientSource, nsIncomingDataProcessor, storeDataForDb
)
nsClientV3Plugin.newestDataOnServer = LastModified(LastModified.Collections())
}
@ -124,11 +121,6 @@ internal class LoadBgWorkerTest : TestBase() {
val result = sut.doWorkAndLog()
Assertions.assertTrue(result is ListenableWorker.Result.Success)
Assertions.assertTrue(result.outputData.getString("Result") == "Load not enabled")
Mockito.verify(workManager, Mockito.times(1)).enqueueUniqueWork(
eq(nsClientV3Plugin.JOB_NAME),
eq(ExistingWorkPolicy.APPEND_OR_REPLACE),
any<OneTimeWorkRequest>()
)
}
@Test
@ -144,13 +136,6 @@ internal class LoadBgWorkerTest : TestBase() {
val result = sut.doWorkAndLog()
Assertions.assertEquals(now - 1000, nsClientV3Plugin.lastLoadedSrvModified.collections.entries)
Assertions.assertTrue(result is ListenableWorker.Result.Success)
Mockito.verify(workManager, Mockito.times(1)).beginUniqueWork(
eq(nsClientV3Plugin.JOB_NAME),
eq(ExistingWorkPolicy.APPEND_OR_REPLACE),
any<OneTimeWorkRequest>()
)
Mockito.verify(workContinuation, Mockito.times(1)).then(any<OneTimeWorkRequest>())
Mockito.verify(workContinuation, Mockito.times(1)).enqueue()
}
@Test
@ -171,7 +156,6 @@ internal class LoadBgWorkerTest : TestBase() {
Mockito.`when`(workManager.beginUniqueWork(anyString(), any(), any<OneTimeWorkRequest>())).thenReturn(workContinuation)
Mockito.`when`(workContinuation.then(any<OneTimeWorkRequest>())).thenReturn(workContinuation)
Mockito.`when`(workerClasses.nsClientSourceWorker).thenReturn(LoggingWorker::class.java)
nsClientV3Plugin.nsAndroidClient = nsAndroidClient
nsClientV3Plugin.lastLoadedSrvModified.collections.entries = 0L // first load
nsClientV3Plugin.firstLoadContinueTimestamp.collections.entries = now - 1000
@ -180,13 +164,6 @@ internal class LoadBgWorkerTest : TestBase() {
val result = sut.doWorkAndLog()
Assertions.assertTrue(result is ListenableWorker.Result.Success)
Mockito.verify(workManager, Mockito.times(1)).beginUniqueWork(
eq(nsClientV3Plugin.JOB_NAME),
eq(ExistingWorkPolicy.APPEND_OR_REPLACE),
any<OneTimeWorkRequest>()
)
Mockito.verify(workContinuation, Mockito.times(1)).then(any<OneTimeWorkRequest>())
Mockito.verify(workContinuation, Mockito.times(1)).enqueue()
}
@ -203,12 +180,5 @@ internal class LoadBgWorkerTest : TestBase() {
val result = sut.doWorkAndLog()
Assertions.assertEquals(now - 1000, nsClientV3Plugin.lastLoadedSrvModified.collections.entries)
Assertions.assertTrue(result is ListenableWorker.Result.Success)
Mockito.verify(workManager, Mockito.times(1)).beginUniqueWork(
eq(nsClientV3Plugin.JOB_NAME),
eq(ExistingWorkPolicy.APPEND_OR_REPLACE),
any<OneTimeWorkRequest>()
)
Mockito.verify(workContinuation, Mockito.times(1)).then(any<OneTimeWorkRequest>())
Mockito.verify(workContinuation, Mockito.times(1)).enqueue()
}
}