Merge pull request #742 from andyrozman/mdt_syncstorage

Medtronic - reafctoring sync storage and OL
This commit is contained in:
Milos Kozak 2021-10-17 14:49:22 +02:00 committed by GitHub
commit 3c7a8225c0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 520 additions and 320 deletions

View file

@ -1006,24 +1006,23 @@ class MedtronicPumpPlugin @Inject constructor(
if (medtronicHistoryData.isTBRActive(runningTBR)) { if (medtronicHistoryData.isTBRActive(runningTBR)) {
val differenceTime = System.currentTimeMillis() - runningTBR.date val differenceTime = System.currentTimeMillis() - runningTBR.date
val tbrData = runningTBR.tbrData!! //val tbrData = runningTBR
val result = pumpSync.syncTemporaryBasalWithPumpId( val result = pumpSync.syncTemporaryBasalWithPumpId(
runningTBR.date, runningTBR.date,
tbrData.rate, runningTBR.rate,
differenceTime, differenceTime,
tbrData.isAbsolute, runningTBR.isAbsolute,
tbrData.tbrType, runningTBR.tbrType,
runningTBR.pumpId!!, runningTBR.pumpId!!,
runningTBR.pumpType, runningTBR.pumpType,
runningTBR.serialNumber) runningTBR.serialNumber)
val differenceTimeMin = Math.floor(differenceTime / (60.0 * 1000.0)) val differenceTimeMin = Math.floor(differenceTime / (60.0 * 1000.0))
aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "canceling running TBR - syncTemporaryBasalWithPumpId [date=%d, pumpId=%d, rate=%.2f U, duration=%d, pumpSerial=%s] - Result: %b", aapsLogger.debug(LTag.PUMP, "canceling running TBR - syncTemporaryBasalWithPumpId [date=${runningTBR.date}, " +
runningTBR.date, runningTBR.pumpId, "pumpId=${runningTBR.pumpId}, rate=${runningTBR.rate} U, duration=${differenceTimeMin.toInt()}, " +
tbrData.rate, differenceTimeMin.toInt(), "pumpSerial=${medtronicPumpStatus.serialNumber}] - Result: $result")
medtronicPumpStatus.serialNumber, result))
} }
} }

View file

@ -10,6 +10,7 @@ import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.pump.common.defs.PumpType import info.nightscout.androidaps.plugins.pump.common.defs.PumpType
import info.nightscout.androidaps.plugins.pump.common.sync.PumpDbEntry import info.nightscout.androidaps.plugins.pump.common.sync.PumpDbEntry
import info.nightscout.androidaps.plugins.pump.common.sync.PumpDbEntryBolus
import info.nightscout.androidaps.plugins.pump.common.sync.PumpDbEntryTBR import info.nightscout.androidaps.plugins.pump.common.sync.PumpDbEntryTBR
import info.nightscout.androidaps.plugins.pump.common.utils.DateTimeUtil import info.nightscout.androidaps.plugins.pump.common.utils.DateTimeUtil
import info.nightscout.androidaps.plugins.pump.common.utils.StringUtil import info.nightscout.androidaps.plugins.pump.common.utils.StringUtil
@ -490,17 +491,17 @@ class MedtronicHistoryData @Inject constructor(
var temporaryId: Long? = null var temporaryId: Long? = null
if (!multiwave) { if (!multiwave) {
val entryWithTempId = findDbEntry(bolus, boluses) val entryWithTempId = findDbEntry(bolus, boluses as MutableList<PumpDbEntry>) as PumpDbEntryBolus?
aapsLogger.debug(LTag.PUMP, "DD: entryWithTempId=$entryWithTempId") aapsLogger.debug(LTag.PUMP, "DD: entryWithTempId=$entryWithTempId")
if (entryWithTempId != null) { if (entryWithTempId != null) {
aapsLogger.debug(LTag.PUMP, String.format("DD: entryWithTempId.bolusData=%s", if (entryWithTempId.bolusData == null) "null" else entryWithTempId.bolusData)) //aapsLogger.debug(LTag.PUMP, String.format("DD: entryWithTempId.bolusData=%s", if (entryWithTempId.bolusData == null) "null" else entryWithTempId.bolusData))
temporaryId = entryWithTempId.temporaryId temporaryId = entryWithTempId.temporaryId
pumpSyncStorage.removeBolusWithTemporaryId(temporaryId) pumpSyncStorage.removeBolusWithTemporaryId(temporaryId)
boluses.remove(entryWithTempId) boluses.remove(entryWithTempId)
type = entryWithTempId.bolusData!!.bolusType type = entryWithTempId.bolusType
} }
} }
@ -619,7 +620,7 @@ class MedtronicHistoryData @Inject constructor(
aapsLogger.debug(LTag.PUMP, "DD: tempBasalProcessDTO.itemOne: " + gson.toJson(tempBasalProcessDTO.itemOne)) aapsLogger.debug(LTag.PUMP, "DD: tempBasalProcessDTO.itemOne: " + gson.toJson(tempBasalProcessDTO.itemOne))
aapsLogger.debug(LTag.PUMP, "DD: tempBasalProcessDTO.itemTwo: " + (if (tempBasalProcessDTO.itemTwo == null) "null" else gson.toJson(tempBasalProcessDTO.itemTwo!!))) aapsLogger.debug(LTag.PUMP, "DD: tempBasalProcessDTO.itemTwo: " + (if (tempBasalProcessDTO.itemTwo == null) "null" else gson.toJson(tempBasalProcessDTO.itemTwo!!)))
val entryWithTempId = findDbEntry(tempBasalProcessDTO.itemOne, tbrRecords) val entryWithTempId = findDbEntry(tempBasalProcessDTO.itemOne, tbrRecords as MutableList<PumpDbEntry>) as PumpDbEntryTBR?
aapsLogger.debug(LTag.PUMP, "DD: entryWithTempId: " + (if (entryWithTempId == null) "null" else entryWithTempId.toString())) aapsLogger.debug(LTag.PUMP, "DD: entryWithTempId: " + (if (entryWithTempId == null) "null" else entryWithTempId.toString()))
@ -697,16 +698,16 @@ class MedtronicHistoryData @Inject constructor(
if (isTBRActive(startTimestamp = tryToGetByLocalTime(tempBasalProcessDTO.atechDateTime), if (isTBRActive(startTimestamp = tryToGetByLocalTime(tempBasalProcessDTO.atechDateTime),
durationSeconds = tempBasalProcessDTO.durationAsSeconds)) { durationSeconds = tempBasalProcessDTO.durationAsSeconds)) {
if (medtronicPumpStatus.runningTBR == null) { if (medtronicPumpStatus.runningTBR == null) {
medtronicPumpStatus.runningTBR = info.nightscout.androidaps.plugins.pump.common.sync.PumpDbEntry(0L, medtronicPumpStatus.runningTBR = PumpDbEntryTBR(
tryToGetByLocalTime(tempBasalProcessDTO.atechDateTime), temporaryId = 0L,
medtronicPumpStatus.pumpType, date = tryToGetByLocalTime(tempBasalProcessDTO.atechDateTime),
medtronicPumpStatus.serialNumber, pumpType = medtronicPumpStatus.pumpType,
null, serialNumber = medtronicPumpStatus.serialNumber,
PumpDbEntryTBR(rate = tbrEntry.insulinRate, entry = PumpDbEntryTBR(rate = tbrEntry.insulinRate,
isAbsolute = !tbrEntry.isPercent, isAbsolute = !tbrEntry.isPercent,
durationInSeconds = tempBasalProcessDTO.durationAsSeconds, durationInSeconds = tempBasalProcessDTO.durationAsSeconds,
tbrType = PumpSync.TemporaryBasalType.NORMAL), tbrType = PumpSync.TemporaryBasalType.NORMAL),
tempBasalProcessDTO.pumpId) pumpId = tempBasalProcessDTO.pumpId)
} }
} }
} else { } else {
@ -717,10 +718,12 @@ class MedtronicHistoryData @Inject constructor(
} // collection } // collection
} }
fun isTBRActive(dbEntry: PumpDbEntry): Boolean {
fun isTBRActive(dbEntry: PumpDbEntryTBR): Boolean {
return isTBRActive( return isTBRActive(
startTimestamp = dbEntry.date, startTimestamp = dbEntry.date,
durationSeconds = dbEntry.tbrData!!.durationInSeconds) durationSeconds = dbEntry.durationInSeconds)
} }
fun isTBRActive(startTimestamp: Long, durationSeconds: Int): Boolean { fun isTBRActive(startTimestamp: Long, durationSeconds: Int): Boolean {

View file

@ -7,6 +7,7 @@ import info.nightscout.androidaps.plugins.pump.common.events.EventRileyLinkDevic
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkUtil import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkUtil
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.data.RLHistoryItem import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.data.RLHistoryItem
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkTargetDevice import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkTargetDevice
import info.nightscout.androidaps.plugins.pump.common.sync.PumpDbEntryTBR
import info.nightscout.androidaps.plugins.pump.medtronic.defs.BasalProfileStatus import info.nightscout.androidaps.plugins.pump.medtronic.defs.BasalProfileStatus
import info.nightscout.androidaps.plugins.pump.medtronic.defs.BatteryType import info.nightscout.androidaps.plugins.pump.medtronic.defs.BatteryType
import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicDeviceType import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicDeviceType
@ -32,7 +33,7 @@ class MedtronicPumpStatus @Inject constructor(private val resourceHelper: Resour
var pumpFrequency: String? = null var pumpFrequency: String? = null
var maxBolus: Double? = null var maxBolus: Double? = null
var maxBasal: Double? = null var maxBasal: Double? = null
var runningTBR: info.nightscout.androidaps.plugins.pump.common.sync.PumpDbEntry? = null var runningTBR: PumpDbEntryTBR? = null
// statuses // statuses
var pumpDeviceState = PumpDeviceState.NeverContacted var pumpDeviceState = PumpDeviceState.NeverContacted

View file

@ -4,45 +4,75 @@ import info.nightscout.androidaps.data.DetailedBolusInfo
import info.nightscout.androidaps.interfaces.PumpSync import info.nightscout.androidaps.interfaces.PumpSync
import info.nightscout.androidaps.plugins.pump.common.defs.PumpType import info.nightscout.androidaps.plugins.pump.common.defs.PumpType
data class PumpDbEntry constructor(var temporaryId: Long, // data class PumpDbEntry constructor(var temporaryId: Long,
var date: Long, // var date: Long,
var pumpType: PumpType, // var pumpType: PumpType,
var serialNumber: String, // var serialNumber: String,
var bolusData: PumpDbEntryBolus? = null, // var bolusData: PumpDbEntryBolus? = null,
var tbrData: PumpDbEntryTBR? = null, // var tbrData: PumpDbEntryTBR? = null,
var pumpId: Long? = null) { // var pumpId: Long? = null) {
//
// constructor(temporaryId: Long,
// date: Long,
// pumpType: PumpType,
// serialNumber: String,
// detailedBolusInfo: DetailedBolusInfo) : this(temporaryId, date, pumpType, serialNumber) {
// this.bolusData = PumpDbEntryBolus(
// detailedBolusInfo.insulin,
// detailedBolusInfo.carbs,
// detailedBolusInfo.bolusType)
// }
//
// constructor(temporaryId: Long,
// date: Long,
// pumpType: PumpType,
// serialNumber: String,
// rate: Double,
// isAbsolute: Boolean,
// durationInMinutes: Int,
// tbrType: PumpSync.TemporaryBasalType) : this(temporaryId, date, pumpType, serialNumber) {
// this.tbrData = PumpDbEntryTBR(
// rate,
// isAbsolute,
// durationInMinutes,
// tbrType)
// }
//
// }
constructor(temporaryId: Long,
date: Long,
pumpType: PumpType,
serialNumber: String,
detailedBolusInfo: DetailedBolusInfo) : this(temporaryId, date, pumpType, serialNumber) {
this.bolusData = PumpDbEntryBolus(
detailedBolusInfo.insulin,
detailedBolusInfo.carbs,
detailedBolusInfo.bolusType)
}
constructor(temporaryId: Long,
date: Long,
pumpType: PumpType,
serialNumber: String,
rate: Double,
isAbsolute: Boolean,
durationInMinutes: Int,
tbrType: PumpSync.TemporaryBasalType) : this(temporaryId, date, pumpType, serialNumber) {
this.tbrData = PumpDbEntryTBR(
rate,
isAbsolute,
durationInMinutes,
tbrType)
}
interface PumpDbEntry {
var temporaryId: Long
var date: Long
var pumpType: PumpType
var serialNumber: String
var pumpId: Long?
} }
data class PumpDbEntryBolus(var insulin: Double,
data class PumpDbEntryBolus(override var temporaryId: Long,
override var date: Long,
override var pumpType: PumpType,
override var serialNumber: String,
override var pumpId: Long? = null,
var insulin: Double,
var carbs: Double, var carbs: Double,
var bolusType: DetailedBolusInfo.BolusType) var bolusType: DetailedBolusInfo.BolusType) : PumpDbEntry {
constructor(temporaryId: Long,
date: Long,
pumpType: PumpType,
serialNumber: String,
detailedBolusInfo: DetailedBolusInfo) : this(temporaryId, date, pumpType, serialNumber, null,
detailedBolusInfo.insulin,
detailedBolusInfo.carbs,
detailedBolusInfo.bolusType) {
}
}
data class PumpDbEntryCarbs(var date: Long, data class PumpDbEntryCarbs(var date: Long,
var carbs: Double, var carbs: Double,
@ -57,7 +87,29 @@ data class PumpDbEntryCarbs(var date: Long,
creator.serialNumber()) creator.serialNumber())
} }
data class PumpDbEntryTBR(var rate: Double, data class PumpDbEntryTBR(override var temporaryId: Long,
override var date: Long,
override var pumpType: PumpType,
override var serialNumber: String,
override var pumpId: Long? = null,
var rate: Double,
var isAbsolute: Boolean, var isAbsolute: Boolean,
var durationInSeconds: Int, var durationInSeconds: Int,
var tbrType: PumpSync.TemporaryBasalType) var tbrType: PumpSync.TemporaryBasalType) : PumpDbEntry {
constructor(rate: Double,
isAbsolute: Boolean,
durationInSeconds: Int,
tbrType: PumpSync.TemporaryBasalType) : this(0, 0, PumpType.GENERIC_AAPS, "", null,
rate, isAbsolute, durationInSeconds, tbrType)
constructor(temporaryId: Long,
date: Long,
pumpType: PumpType,
serialNumber: String,
entry: PumpDbEntryTBR,
pumpId: Long?
) : this(temporaryId, date, pumpType, serialNumber, pumpId,
entry.rate, entry.isAbsolute, entry.durationInSeconds, entry.tbrType)
}

View file

@ -22,13 +22,13 @@ class PumpSyncStorage @Inject constructor(
) { ) {
companion object { companion object {
const val pumpSyncStorageBolusKey: String = "pump_sync_storage_bolus"
const val pumpSyncStorageKey: String = "pump_sync_storage_xstream_v2" const val pumpSyncStorageTBRKey: String = "pump_sync_storage_tbr"
const val TBR: String = "TBR"
const val BOLUS: String = "BOLUS"
} }
var pumpSyncStorage: MutableMap<String, MutableList<PumpDbEntry>> = mutableMapOf() var pumpSyncStorageBolus: MutableList<PumpDbEntryBolus> = mutableListOf()
var pumpSyncStorageTBR: MutableList<PumpDbEntryTBR> = mutableListOf()
private var storageInitialized: Boolean = false private var storageInitialized: Boolean = false
private var xstream: XStream = XStream() private var xstream: XStream = XStream()
@ -41,38 +41,58 @@ class PumpSyncStorage @Inject constructor(
if (storageInitialized) if (storageInitialized)
return return
var loaded = false xstream.addPermission(AnyTypePermission.ANY)
if (sp.contains(pumpSyncStorageKey)) { if (sp.contains(pumpSyncStorageBolusKey)) {
val jsonData: String = sp.getString(pumpSyncStorageKey, "") val jsonData: String = sp.getString(pumpSyncStorageBolusKey, "")
if (jsonData.isNotBlank()) { if (jsonData.isNotBlank()) {
xstream.addPermission(AnyTypePermission.ANY)
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
pumpSyncStorage = xstream.fromXML(jsonData, MutableMap::class.java) as MutableMap<String, MutableList<PumpDbEntry>> pumpSyncStorageBolus = xstream.fromXML(jsonData, MutableList::class.java) as
MutableList<PumpDbEntryBolus>
aapsLogger.debug(LTag.PUMP, String.format("Loading Pump Sync Storage: boluses=%d, tbrs=%d.", pumpSyncStorage[BOLUS]!!.size, pumpSyncStorage[TBR]!!.size)) aapsLogger.debug(LTag.PUMP, "Loading Pump Sync Storage Bolus: boluses=${pumpSyncStorageBolus.size}")
aapsLogger.debug(LTag.PUMP, "DD: PumpSyncStorage=$pumpSyncStorage") aapsLogger.debug(LTag.PUMP, "DD: PumpSyncStorageBolus=$pumpSyncStorageBolus")
loaded = true
} }
} }
if (!loaded) { if (sp.contains(pumpSyncStorageTBRKey)) {
pumpSyncStorage[BOLUS] = mutableListOf() val jsonData: String = sp.getString(pumpSyncStorageTBRKey, "")
pumpSyncStorage[TBR] = mutableListOf()
if (jsonData.isNotBlank()) {
@Suppress("UNCHECKED_CAST")
pumpSyncStorageTBR = xstream.fromXML(jsonData, MutableList::class.java) as
MutableList<PumpDbEntryTBR>
aapsLogger.debug(LTag.PUMP, "Loading Pump Sync Storage: tbrs=${pumpSyncStorageTBR.size}.")
aapsLogger.debug(LTag.PUMP, "DD: PumpSyncStorageTBR=$pumpSyncStorageTBR")
}
}
storageInitialized = true
}
fun saveStorageBolus() {
if (!pumpSyncStorageBolus.isEmpty()) {
sp.putString(pumpSyncStorageBolusKey, xstream.toXML(pumpSyncStorageBolus))
aapsLogger.debug(LTag.PUMP,"Saving Pump Sync Storage: boluses=${pumpSyncStorageBolus.size}")
} else {
if (sp.contains(pumpSyncStorageBolusKey))
sp.remove(pumpSyncStorageBolusKey)
} }
} }
fun saveStorage() { fun saveStorageTBR() {
if (!isStorageEmpty()) { if (!pumpSyncStorageTBR.isEmpty()) {
sp.putString(pumpSyncStorageKey, xstream.toXML(pumpSyncStorage)) sp.putString(pumpSyncStorageTBRKey, xstream.toXML(pumpSyncStorageTBR))
aapsLogger.debug(String.format("Saving Pump Sync Storage: boluses=%d, tbrs=%d.", pumpSyncStorage[BOLUS]!!.size, pumpSyncStorage[TBR]!!.size)) aapsLogger.debug(LTag.PUMP, "Saving Pump Sync Storage: tbr=${pumpSyncStorageTBR.size}")
} else {
if (sp.contains(pumpSyncStorageTBRKey))
sp.remove(pumpSyncStorageTBRKey)
} }
} }
private fun cleanOldStorage() { private fun cleanOldStorage() {
val oldSpKeys = setOf("pump_sync_storage", "pump_sync_storage_xstream") val oldSpKeys = setOf("pump_sync_storage", "pump_sync_storage_xstream", "pump_sync_storage_xstream_v2")
for (oldSpKey in oldSpKeys) { for (oldSpKey in oldSpKeys) {
if (sp.contains(oldSpKey)) if (sp.contains(oldSpKey))
@ -80,16 +100,12 @@ class PumpSyncStorage @Inject constructor(
} }
} }
private fun isStorageEmpty(): Boolean { fun getBoluses(): MutableList<PumpDbEntryBolus> {
return pumpSyncStorage[BOLUS]!!.isEmpty() && pumpSyncStorage[TBR]!!.isEmpty() return pumpSyncStorageBolus
} }
fun getBoluses(): MutableList<PumpDbEntry> { fun getTBRs(): MutableList<PumpDbEntryTBR> {
return pumpSyncStorage[BOLUS]!! return pumpSyncStorageTBR
}
fun getTBRs(): MutableList<PumpDbEntry> {
return pumpSyncStorage[TBR]!!
} }
fun addBolusWithTempId(detailedBolusInfo: DetailedBolusInfo, writeToInternalHistory: Boolean, creator: PumpSyncEntriesCreator): Boolean { fun addBolusWithTempId(detailedBolusInfo: DetailedBolusInfo, writeToInternalHistory: Boolean, creator: PumpSyncEntriesCreator): Boolean {
@ -102,24 +118,25 @@ class PumpSyncStorage @Inject constructor(
creator.model(), creator.model(),
creator.serialNumber()) creator.serialNumber())
aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "addBolusWithTempId [date=%d, temporaryId=%d, insulin=%.2f, type=%s, pumpSerial=%s] - Result: %b", aapsLogger.debug(LTag.PUMP, "addBolusWithTempId [date=${detailedBolusInfo.timestamp}, temporaryId=$temporaryId, " +
detailedBolusInfo.timestamp, temporaryId, detailedBolusInfo.insulin, detailedBolusInfo.bolusType, "insulin=${detailedBolusInfo.insulin}, type=${detailedBolusInfo.bolusType}, pumpSerial=${creator.serialNumber()}] - " +
creator.serialNumber(), result)) "Result: $result")
if (detailedBolusInfo.carbs > 0.0) { if (detailedBolusInfo.carbs > 0.0) {
addCarbs(PumpDbEntryCarbs(detailedBolusInfo, creator)) addCarbs(PumpDbEntryCarbs(detailedBolusInfo, creator))
} }
if (result && writeToInternalHistory) { if (result && writeToInternalHistory) {
val innerList: MutableList<PumpDbEntry> = pumpSyncStorage[BOLUS]!! val dbEntry = PumpDbEntryBolus(temporaryId = temporaryId,
date = detailedBolusInfo.timestamp,
pumpType = creator.model(),
serialNumber = creator.serialNumber(),
detailedBolusInfo = detailedBolusInfo)
val dbEntry = PumpDbEntry(temporaryId, detailedBolusInfo.timestamp, creator.model(), creator.serialNumber(), detailedBolusInfo) aapsLogger.debug("PumpDbEntryBolus: $dbEntry")
aapsLogger.debug("PumpDbEntry: $dbEntry") pumpSyncStorageBolus.add(dbEntry)
saveStorageBolus()
innerList.add(dbEntry)
pumpSyncStorage[BOLUS] = innerList
saveStorage()
} }
return result return result
} }
@ -132,8 +149,8 @@ class PumpSyncStorage @Inject constructor(
carbsDto.pumpType, carbsDto.pumpType,
carbsDto.serialNumber) carbsDto.serialNumber)
aapsLogger.debug(LTag.PUMP, String.format(Locale.ENGLISH, "syncCarbsWithTimestamp [date=%d, carbs=%.2f, pumpSerial=%s] - Result: %b", aapsLogger.debug(LTag.PUMP, "syncCarbsWithTimestamp [date=${carbsDto.date}, " +
carbsDto.date, carbsDto.carbs, carbsDto.serialNumber, result)) "carbs=${carbsDto.carbs}, pumpSerial=${carbsDto.serialNumber}] - Result: $result")
} }
fun addTemporaryBasalRateWithTempId(temporaryBasal: PumpDbEntryTBR, writeToInternalHistory: Boolean, creator: PumpSyncEntriesCreator): Boolean { fun addTemporaryBasalRateWithTempId(temporaryBasal: PumpDbEntryTBR, writeToInternalHistory: Boolean, creator: PumpSyncEntriesCreator): Boolean {
@ -151,42 +168,52 @@ class PumpSyncStorage @Inject constructor(
creator.serialNumber()) creator.serialNumber())
if (response && writeToInternalHistory) { if (response && writeToInternalHistory) {
val innerList: MutableList<PumpDbEntry> = pumpSyncStorage[TBR]!! val dbEntry = PumpDbEntryTBR(temporaryId = temporaryId,
date = timeNow,
pumpType = creator.model(),
serialNumber = creator.serialNumber(),
entry = temporaryBasal,
pumpId=null)
innerList.add(PumpDbEntry(temporaryId, timeNow, creator.model(), creator.serialNumber(), null, temporaryBasal)) aapsLogger.debug("PumpDbEntryTBR: $dbEntry")
pumpSyncStorage[BOLUS] = innerList
saveStorage() pumpSyncStorageTBR.add(dbEntry)
saveStorageTBR()
} }
return response return response
} }
fun removeBolusWithTemporaryId(temporaryId: Long) { fun removeBolusWithTemporaryId(temporaryId: Long) {
val bolusList = removeTemporaryId(temporaryId, pumpSyncStorage[BOLUS]!!) var dbEntry: PumpDbEntryBolus? = null
pumpSyncStorage[BOLUS] = bolusList
saveStorage()
}
fun removeTemporaryBasalWithTemporaryId(temporaryId: Long) { for (pumpDbEntry in pumpSyncStorageBolus) {
val tbrList = removeTemporaryId(temporaryId, pumpSyncStorage[TBR]!!)
pumpSyncStorage[TBR] = tbrList
saveStorage()
}
private fun removeTemporaryId(temporaryId: Long, list: MutableList<PumpDbEntry>): MutableList<PumpDbEntry> {
var dbEntry: PumpDbEntry? = null
for (pumpDbEntry in list) {
if (pumpDbEntry.temporaryId == temporaryId) { if (pumpDbEntry.temporaryId == temporaryId) {
dbEntry = pumpDbEntry dbEntry = pumpDbEntry
} }
} }
if (dbEntry != null) { if (dbEntry != null) {
list.remove(dbEntry) pumpSyncStorageBolus.remove(dbEntry)
} }
return list saveStorageBolus()
}
fun removeTemporaryBasalWithTemporaryId(temporaryId: Long) {
var dbEntry: PumpDbEntryTBR? = null
for (pumpDbEntry in pumpSyncStorageTBR) {
if (pumpDbEntry.temporaryId == temporaryId) {
dbEntry = pumpDbEntry
}
}
if (dbEntry != null) {
pumpSyncStorageTBR.remove(dbEntry)
}
saveStorageTBR()
} }
} }

View file

@ -9,6 +9,7 @@ import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.command.S
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.command.SetPreamble import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.command.SetPreamble
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data.RadioPacket import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data.RadioPacket
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data.RadioResponse import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data.RadioResponse
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.device.OrangeLinkImpl
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.dialog.RileyLinkStatusActivity import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.dialog.RileyLinkStatusActivity
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.dialog.RileyLinkStatusGeneralFragment import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.dialog.RileyLinkStatusGeneralFragment
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.dialog.RileyLinkStatusHistoryFragment import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.dialog.RileyLinkStatusHistoryFragment
@ -30,6 +31,7 @@ abstract class RileyLinkModule {
@ContributesAndroidInjector abstract fun sendAndListenProvider(): SendAndListen @ContributesAndroidInjector abstract fun sendAndListenProvider(): SendAndListen
@ContributesAndroidInjector abstract fun setPreambleProvider(): SetPreamble @ContributesAndroidInjector abstract fun setPreambleProvider(): SetPreamble
@ContributesAndroidInjector abstract fun radioPacketProvider(): RadioPacket @ContributesAndroidInjector abstract fun radioPacketProvider(): RadioPacket
@ContributesAndroidInjector abstract fun orangeLinkDeviceProvider(): OrangeLinkImpl
@ContributesAndroidInjector abstract fun contributesRileyLinkStatusGeneral(): RileyLinkStatusGeneralFragment @ContributesAndroidInjector abstract fun contributesRileyLinkStatusGeneral(): RileyLinkStatusGeneralFragment
@ContributesAndroidInjector abstract fun contributesRileyLinkStatusHistoryFragment(): RileyLinkStatusHistoryFragment @ContributesAndroidInjector abstract fun contributesRileyLinkStatusHistoryFragment(): RileyLinkStatusHistoryFragment

View file

@ -68,7 +68,7 @@ public class RFSpy {
private final UUID radioDataUUID = UUID.fromString(GattAttributes.CHARA_RADIO_DATA); private final UUID radioDataUUID = UUID.fromString(GattAttributes.CHARA_RADIO_DATA);
private final UUID radioVersionUUID = UUID.fromString(GattAttributes.CHARA_RADIO_VERSION); private final UUID radioVersionUUID = UUID.fromString(GattAttributes.CHARA_RADIO_VERSION);
private final UUID batteryServiceUUID = UUID.fromString(GattAttributes.SERVICE_BATTERY); private final UUID batteryServiceUUID = UUID.fromString(GattAttributes.SERVICE_BATTERY);
private final UUID batteryLevelUUID = UUID.fromString(GattAttributes.CHARA_BATTERY_UNK); private final UUID batteryLevelUUID = UUID.fromString(GattAttributes.CHARA_BATTERY_LEVEL);
private String bleVersion; // We don't use it so no need of sofisticated logic private String bleVersion; // We don't use it so no need of sofisticated logic
private Double currentFrequencyMHz; private Double currentFrequencyMHz;
private long nextBatteryCheck = 0; private long nextBatteryCheck = 0;
@ -103,6 +103,10 @@ public class RFSpy {
String cc1110Version = getCC1110Version(); String cc1110Version = getCC1110Version();
rileyLinkServiceData.versionCC110 = cc1110Version; rileyLinkServiceData.versionCC110 = cc1110Version;
rileyLinkServiceData.firmwareVersion = getFirmwareVersion(aapsLogger, bleVersion, cc1110Version); rileyLinkServiceData.firmwareVersion = getFirmwareVersion(aapsLogger, bleVersion, cc1110Version);
aapsLogger.debug(LTag.PUMPBTCOMM,
String.format("RileyLink - BLE Version: %s, CC1110 Version: %s, Firmware Version: %s",
bleVersion, cc1110Version, rileyLinkServiceData.firmwareVersion));
} }
// Call this from the "response count" notification handler. // Call this from the "response count" notification handler.

View file

@ -8,19 +8,11 @@ import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor; import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService; import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothProfile;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanFilter;
import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
import android.content.Context; import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock; import android.os.SystemClock;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.UUID; import java.util.UUID;
@ -34,6 +26,7 @@ import info.nightscout.androidaps.logging.LTag;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkConst; import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkConst;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkUtil; import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkUtil;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data.GattAttributes; import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data.GattAttributes;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.device.OrangeLinkImpl;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.operations.BLECommOperation; import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.operations.BLECommOperation;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.operations.BLECommOperationResult; import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.operations.BLECommOperationResult;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.operations.CharacteristicReadOperation; import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.operations.CharacteristicReadOperation;
@ -57,6 +50,7 @@ public class RileyLinkBLE {
@Inject RileyLinkServiceData rileyLinkServiceData; @Inject RileyLinkServiceData rileyLinkServiceData;
@Inject RileyLinkUtil rileyLinkUtil; @Inject RileyLinkUtil rileyLinkUtil;
@Inject SP sp; @Inject SP sp;
@Inject OrangeLinkImpl orangeLink;
private final Context context; private final Context context;
private final boolean gattDebugEnabled = true; private final boolean gattDebugEnabled = true;
@ -75,6 +69,8 @@ public class RileyLinkBLE {
this.context = context; this.context = context;
this.bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); this.bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
//orangeLink.rileyLinkBLE = this;
bluetoothGattCallback = new BluetoothGattCallback() { bluetoothGattCallback = new BluetoothGattCallback() {
@Override @Override
@ -93,16 +89,7 @@ public class RileyLinkBLE {
radioResponseCountNotified.run(); radioResponseCountNotified.run();
} }
if (characteristic.getUuid().toString().equals(GattAttributes.UUID_NOTIF_CHARACTER.toString())) { orangeLink.onCharacteristicChanged(characteristic);
final byte[] data = characteristic.getValue();
int first = 0xff & data[0];
aapsLogger.info(LTag.PUMPBTCOMM,
"onCharacteristicChanged " + ByteUtil.shortHexString(characteristic.getValue()) + "=====" + first);
String fv = data[3] + "." + data[4];
String hv = data[5] + "." + data[6];
rileyLinkServiceData.versionOrangeFV = fv;
rileyLinkServiceData.versionOrangeHV = hv;
}
} }
@ -242,6 +229,9 @@ public class RileyLinkBLE {
final List<BluetoothGattService> services = gatt.getServices(); final List<BluetoothGattService> services = gatt.getServices();
boolean rileyLinkFound = false; boolean rileyLinkFound = false;
orangeLink.resetOrangeLinkData();
StringBuilder stringBuilder = new StringBuilder("RileyLink Device Debug\n");
for (BluetoothGattService service : services) { for (BluetoothGattService service : services) {
final UUID uuidService = service.getUuid(); final UUID uuidService = service.getUuid();
@ -251,14 +241,14 @@ public class RileyLinkBLE {
} }
if (gattDebugEnabled) { if (gattDebugEnabled) {
debugService(service, 0); debugService(service, 0, stringBuilder);
}
if (GattAttributes.isOrange(uuidService)) {
rileyLinkServiceData.isOrange = true;
} }
orangeLink.checkIsOrange(uuidService);
} }
if (gattDebugEnabled) { if (gattDebugEnabled) {
aapsLogger.warn(LTag.PUMPBTCOMM, stringBuilder.toString());
aapsLogger.warn(LTag.PUMPBTCOMM, "onServicesDiscovered " + getGattStatusMessage(status)); aapsLogger.warn(LTag.PUMPBTCOMM, "onServicesDiscovered " + getGattStatusMessage(status));
} }
@ -284,6 +274,7 @@ public class RileyLinkBLE {
@Inject @Inject
public void onInit() { public void onInit() {
aapsLogger.debug(LTag.PUMPBTCOMM, "BT Adapter: " + this.bluetoothAdapter); aapsLogger.debug(LTag.PUMPBTCOMM, "BT Adapter: " + this.bluetoothAdapter);
this.orangeLink.rileyLinkBLE = this;
} }
@ -300,6 +291,7 @@ public class RileyLinkBLE {
if (isAnyRileyLinkServiceFound(serviceI)) { if (isAnyRileyLinkServiceFound(serviceI)) {
return true; return true;
} }
orangeLink.checkIsOrange(serviceI.getUuid());
} }
} }
@ -312,7 +304,7 @@ public class RileyLinkBLE {
} }
public void debugService(BluetoothGattService service, int indentCount) { public void debugService(BluetoothGattService service, int indentCount, StringBuilder stringBuilder) {
String indentString = StringUtils.repeat(' ', indentCount); String indentString = StringUtils.repeat(' ', indentCount);
@ -321,7 +313,7 @@ public class RileyLinkBLE {
if (gattDebugEnabled) { if (gattDebugEnabled) {
final String uuidServiceString = uuidService.toString(); final String uuidServiceString = uuidService.toString();
StringBuilder stringBuilder = new StringBuilder(); //StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(indentString); stringBuilder.append(indentString);
stringBuilder.append(GattAttributes.lookup(uuidServiceString, "Unknown service")); stringBuilder.append(GattAttributes.lookup(uuidServiceString, "Unknown service"));
@ -338,12 +330,12 @@ public class RileyLinkBLE {
stringBuilder.append("\n\n"); stringBuilder.append("\n\n");
aapsLogger.warn(LTag.PUMPBTCOMM, stringBuilder.toString()); //aapsLogger.warn(LTag.PUMPBTCOMM, stringBuilder.toString());
List<BluetoothGattService> includedServices = service.getIncludedServices(); List<BluetoothGattService> includedServices = service.getIncludedServices();
for (BluetoothGattService serviceI : includedServices) { for (BluetoothGattService serviceI : includedServices) {
debugService(serviceI, indentCount + 4); debugService(serviceI, indentCount + 4, stringBuilder);
} }
} }
} }
@ -383,53 +375,46 @@ public class RileyLinkBLE {
aapsLogger.error(LTag.PUMPBTCOMM, "Error setting response count notification"); aapsLogger.error(LTag.PUMPBTCOMM, "Error setting response count notification");
return false; return false;
} }
if(rileyLinkServiceData.isOrange){
enableNotificationsOrange();
}
return true;
}
public boolean enableNotificationsOrange() {
aapsLogger.error(LTag.PUMPBTCOMM, "enableNotificationsORG");
BLECommOperationResult result = setNotification_blocking(GattAttributes.UUID_NOTIF_SERVICE, //
GattAttributes.UUID_NOTIF_CHARACTER);
if (result.resultCode != BLECommOperationResult.RESULT_SUCCESS) {
aapsLogger.error(LTag.PUMPBTCOMM, "Error setting response count notification");
return false;
}
return true;
}
String macAddress;
public void findRileyLink(String RileyLinkAddress) { if (rileyLinkServiceData.isOrange) {
aapsLogger.debug(LTag.PUMPBTCOMM, "RileyLink address: " + RileyLinkAddress); return orangeLink.enableNotifications();
}
return true;
}
public void findRileyLink(String rileyLinkAddress) {
aapsLogger.debug(LTag.PUMPBTCOMM, "RileyLink address: " + rileyLinkAddress);
// Must verify that this is a valid MAC, or crash. // Must verify that this is a valid MAC, or crash.
macAddress = RileyLinkAddress; //macAddress = RileyLinkAddress;
boolean useScanning = sp.getBoolean(RileyLinkConst.Prefs.OrangeUseScanning, false); boolean useScanning = sp.getBoolean(RileyLinkConst.Prefs.OrangeUseScanning, false);
if (useScanning) { if (useScanning) {
startScan(); aapsLogger.debug(LTag.PUMPBTCOMM, "Start scan for OrangeLink device.");
orangeLink.startScan();
} else { } else {
rileyLinkDevice = bluetoothAdapter.getRemoteDevice(RileyLinkAddress); rileyLinkDevice = bluetoothAdapter.getRemoteDevice(rileyLinkAddress);
// if this succeeds, we get a connection state change callback? // if this succeeds, we get a connection state change callback?
if (rileyLinkDevice != null) { if (rileyLinkDevice != null) {
connectGatt(); connectGattInternal();
} else { } else {
aapsLogger.error(LTag.PUMPBTCOMM, "RileyLink device not found with address: " + RileyLinkAddress); aapsLogger.error(LTag.PUMPBTCOMM, "RileyLink device not found with address: " + rileyLinkAddress);
} }
} }
} }
public void connectGattCheckOrange() { public void connectGatt() {
boolean useScanning = sp.getBoolean(RileyLinkConst.Prefs.OrangeUseScanning, false); boolean useScanning = sp.getBoolean(RileyLinkConst.Prefs.OrangeUseScanning, false);
if (useScanning) { if (useScanning) {
startScan(); aapsLogger.debug(LTag.PUMPBTCOMM, "Start scan for OrangeLink device.");
orangeLink.startScan();
} else { } else {
connectGatt(); connectGattInternal();
} }
} }
// This function must be run on UI thread. // This function must be run on UI thread.
public void connectGatt() { public void connectGattInternal() {
if (this.rileyLinkDevice == null) { if (this.rileyLinkDevice == null) {
aapsLogger.error(LTag.PUMPBTCOMM, "RileyLink device is null, can't do connectGatt."); aapsLogger.error(LTag.PUMPBTCOMM, "RileyLink device is null, can't do connectGatt.");
return; return;
@ -478,7 +463,7 @@ public class RileyLinkBLE {
} }
private BLECommOperationResult setNotification_blocking(UUID serviceUUID, UUID charaUUID) { public BLECommOperationResult setNotification_blocking(UUID serviceUUID, UUID charaUUID) {
BLECommOperationResult rval = new BLECommOperationResult(); BLECommOperationResult rval = new BLECommOperationResult();
if (bluetoothConnectionGatt != null) { if (bluetoothConnectionGatt != null) {
@ -643,116 +628,11 @@ public class RileyLinkBLE {
return statusMessage; return statusMessage;
} }
private List<ScanFilter> buildScanFilters() { public void setRileyLinkDevice(BluetoothDevice device) {
ArrayList scanFilterList = new ArrayList<>(); this.rileyLinkDevice = device;
ScanFilter.Builder scanFilterBuilder = new ScanFilter.Builder();
scanFilterBuilder.setDeviceAddress(macAddress);
scanFilterList.add(scanFilterBuilder.build());
return scanFilterList;
} }
private ScanSettings buildScanSettings() { public BluetoothAdapter getBluetoothAdapter() {
ScanSettings.Builder scanSettingBuilder = new ScanSettings.Builder(); return bluetoothAdapter;
scanSettingBuilder.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY);
scanSettingBuilder.setMatchMode(ScanSettings.MATCH_MODE_AGGRESSIVE);
scanSettingBuilder.setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES);
return scanSettingBuilder.build();
}
public void startScan() {
try {
stopScan();
aapsLogger.debug(LTag.PUMPBTCOMM, "startScan");
handler.sendEmptyMessageDelayed(TIME_OUT_WHAT, TIME_OUT);
BluetoothLeScanner bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner();
if (bluetoothLeScanner == null) {
bluetoothAdapter.startLeScan(mLeScanCallback);
return;
}
bluetoothLeScanner.startScan(buildScanFilters(), buildScanSettings(), scanCallback);
} catch (Exception e) {
e.printStackTrace();
aapsLogger.error(LTag.PUMPBTCOMM, e.getMessage());
}
}
ScanCallback scanCallback = new ScanCallback() {
@Override
public void onScanResult(int callbackType, ScanResult result) {
super.onScanResult(callbackType, result);
String name = result.getDevice().getName();
String address = result.getDevice().getAddress();
if (macAddress.equals(address)) {
stopScan();
rileyLinkDevice = result.getDevice();
connectGatt();
}
}
@Override
public void onBatchScanResults(List<ScanResult> results) {
super.onBatchScanResults(results);
}
@Override
public void onScanFailed(int errorCode) {
super.onScanFailed(errorCode);
stopScan();
}
};
private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
public void onLeScan(final BluetoothDevice device, final int rssi,
final byte[] scanRecord) {
if (macAddress.equals(device.getAddress())) {
stopScan();
rileyLinkDevice = device;
connectGatt();
}
}
};
public static final int TIME_OUT = 90 * 1000;
public static final int TIME_OUT_WHAT = 0x12;
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case TIME_OUT_WHAT:
stopScan();
break;
}
}
};
public void stopScan() {
handler.removeMessages(TIME_OUT_WHAT);
if (bluetoothAdapter == null) {
return;
}
try {
BluetoothLeScanner bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner();
if (bluetoothLeScanner == null) {
if (isBluetoothAvailable()) {
bluetoothAdapter.stopLeScan(mLeScanCallback);
}
return;
}
if (isBluetoothAvailable()) {
bluetoothLeScanner.stopScan(scanCallback);
}
} catch (Exception e) {
e.printStackTrace();
aapsLogger.error(LTag.PUMPBTCOMM, e.getMessage());
}
}
public boolean isBluetoothAvailable() {
return (bluetoothAdapter != null &&
bluetoothAdapter.isEnabled() &&
bluetoothAdapter.getState() == BluetoothAdapter.STATE_ON);
} }
} }

View file

@ -13,13 +13,20 @@ public class GattAttributes {
public static String PREFIX = "0000"; public static String PREFIX = "0000";
public static String SUFFIX = "-0000-1000-8000-00805f9b34fb"; public static String SUFFIX = "-0000-1000-8000-00805f9b34fb";
public static String SERVICE_GAP = PREFIX + "1800" + SUFFIX;
public static String CHARA_GAP_NAME = PREFIX + "2a00" + SUFFIX; // RileyLink RFSpy
public static String CHARA_GAP_NUM = PREFIX + "2a01" + SUFFIX; // 0000
public static String CHARA_GAP_UNK = PREFIX + "2a01" + SUFFIX; // a
// Generic Access
public static String SERVICE_GA = PREFIX + "1800" + SUFFIX;
public static String CHARA_GA_NAME = PREFIX + "2a00" + SUFFIX; // RileyLink RFSpy
public static String CHARA_GA_APPEARANCE = PREFIX + "2a01" + SUFFIX; // 0000
public static String CHARA_GA_PPCP = PREFIX + "2a04" + SUFFIX; // 0000
public static String CHARA_GA_CAR = PREFIX + "2aa6" + SUFFIX; // 0000
// Generic Attribute
public static String SERVICE_G_ATTR = PREFIX + "1801" + SUFFIX;
// Battery Service
public static String SERVICE_BATTERY = PREFIX + "180f" + SUFFIX; // Battery public static String SERVICE_BATTERY = PREFIX + "180f" + SUFFIX; // Battery
public static String CHARA_BATTERY_UNK = PREFIX + "2a19" + SUFFIX; public static String CHARA_BATTERY_LEVEL = PREFIX + "2a19" + SUFFIX;
// RileyLink Radio Service // RileyLink Radio Service
public static String SERVICE_RADIO = "0235733b-99c5-4197-b856-69219c2a3845"; public static String SERVICE_RADIO = "0235733b-99c5-4197-b856-69219c2a3845";
@ -29,9 +36,20 @@ public class GattAttributes {
public static String CHARA_RADIO_CUSTOM_NAME = "d93b2af0-1e28-11e4-8c21-0800200c9a66"; public static String CHARA_RADIO_CUSTOM_NAME = "d93b2af0-1e28-11e4-8c21-0800200c9a66";
public static String CHARA_RADIO_VERSION = "30d99dc9-7c91-4295-a051-0a104d238cf2"; public static String CHARA_RADIO_VERSION = "30d99dc9-7c91-4295-a051-0a104d238cf2";
public static String CHARA_RADIO_LED_MODE = "c6d84241-f1a7-4f9c-a25f-fce16732f14e"; public static String CHARA_RADIO_LED_MODE = "c6d84241-f1a7-4f9c-a25f-fce16732f14e";
//Orange Radio Service
public static UUID UUID_NOTIF_SERVICE = UUID.fromString("6e400001-b5a3-f393-e0a9-e50e24dcca9e"); // Secure DFU Service (Orange 1.5 - 3.2)
public static UUID UUID_NOTIF_CHARACTER = UUID.fromString("6e400003-b5a3-f393-e0a9-e50e24dcca9e"); public static String SERVICE_DFU = "0000fe59-0000-1000-8000-00805f9b34fb";
public static String CHARA_BUTTONLESS_DFU = "8ec90003-f315-4f60-9fb8-838830daea50";
// Nordic UART Service (Orange 2.1 - 3.2)
public static String SERVICE_NORDIC_UART = "6e400001-b5a3-f393-e0a9-e50e24dcca9e";
public static String CHARA_NORDIC_RX = "6e400002-b5a3-f393-e0a9-e50e24dcca9e";
public static String CHARA_NORDIC_TX = "6e400003-b5a3-f393-e0a9-e50e24dcca9e";
// Orange Radio Service
public static String SERVICE_RADIO_ORANGE = "6e400001-b5a3-f393-e0a9-e50e24dcca9e";
public static String CHARA_NOTIFICATION_ORANGE = "6e400003-b5a3-f393-e0a9-e50e24dcca9e";
private static final Map<String, String> attributes; private static final Map<String, String> attributes;
private static final Map<String, String> attributesRileyLinkSpecific; private static final Map<String, String> attributesRileyLinkSpecific;
@ -40,13 +58,18 @@ public class GattAttributes {
static { static {
attributes = new HashMap<>(); attributes = new HashMap<>();
attributes.put(SERVICE_GAP, "Device Information Service"); attributes.put(SERVICE_GA, "Generic Access");
attributes.put(CHARA_GAP_NAME, "Name"); // attributes.put(CHARA_GA_NAME, "Device Name"); //
attributes.put(CHARA_GAP_NUM, "Number"); // attributes.put(CHARA_GA_APPEARANCE, "Appearance"); //
attributes.put(CHARA_GA_PPCP, "Peripheral Preffered Connection Parameters");
attributes.put(CHARA_GA_CAR, "Central Address Resolution");
attributes.put(SERVICE_G_ATTR, "Generic Attribute");
attributes.put(SERVICE_BATTERY, "Battery Service"); attributes.put(SERVICE_BATTERY, "Battery Service");
attributes.put(CHARA_BATTERY_LEVEL, "Battery Level");
attributes.put(SERVICE_RADIO, "Radio Interface"); // a attributes.put(SERVICE_RADIO, "Radio Interface Service");
attributes.put(CHARA_RADIO_CUSTOM_NAME, "Custom Name"); attributes.put(CHARA_RADIO_CUSTOM_NAME, "Custom Name");
attributes.put(CHARA_RADIO_DATA, "Data"); attributes.put(CHARA_RADIO_DATA, "Data");
attributes.put(CHARA_RADIO_RESPONSE_COUNT, "Response Count"); attributes.put(CHARA_RADIO_RESPONSE_COUNT, "Response Count");
@ -54,6 +77,13 @@ public class GattAttributes {
attributes.put(CHARA_RADIO_VERSION, "Version"); // firmwareVersion attributes.put(CHARA_RADIO_VERSION, "Version"); // firmwareVersion
attributes.put(CHARA_RADIO_LED_MODE, "Led Mode"); attributes.put(CHARA_RADIO_LED_MODE, "Led Mode");
attributes.put(SERVICE_DFU, "Secure DFU Service");
attributes.put(CHARA_BUTTONLESS_DFU, "Buttonless DFU");
attributes.put(SERVICE_NORDIC_UART, "Nordic UART Service");
attributes.put(CHARA_NORDIC_RX, "RX Characteristic");
attributes.put(CHARA_NORDIC_TX, "TX Characteristic");
attributesRileyLinkSpecific = new HashMap<>(); attributesRileyLinkSpecific = new HashMap<>();
attributesRileyLinkSpecific.put(SERVICE_RADIO, "Radio Interface"); // a attributesRileyLinkSpecific.put(SERVICE_RADIO, "Radio Interface"); // a
@ -63,6 +93,9 @@ public class GattAttributes {
attributesRileyLinkSpecific.put(CHARA_RADIO_TIMER_TICK, "Timer Tick"); attributesRileyLinkSpecific.put(CHARA_RADIO_TIMER_TICK, "Timer Tick");
attributesRileyLinkSpecific.put(CHARA_RADIO_VERSION, "Version"); // firmwareVersion attributesRileyLinkSpecific.put(CHARA_RADIO_VERSION, "Version"); // firmwareVersion
attributesRileyLinkSpecific.put(CHARA_RADIO_LED_MODE, "Led Mode"); attributesRileyLinkSpecific.put(CHARA_RADIO_LED_MODE, "Led Mode");
attributesRileyLinkSpecific.put(SERVICE_RADIO_ORANGE, "Orange Radio Interface");
attributesRileyLinkSpecific.put(CHARA_NOTIFICATION_ORANGE, "Orange Notification");
} }
@ -86,8 +119,10 @@ public class GattAttributes {
public static boolean isRileyLink(UUID uuid) { public static boolean isRileyLink(UUID uuid) {
return attributesRileyLinkSpecific.containsKey(uuid.toString()); return attributesRileyLinkSpecific.containsKey(uuid.toString());
} }
public static boolean isOrange(UUID uuid) { public static boolean isOrange(UUID uuid) {
return UUID_NOTIF_SERVICE.equals(uuid.toString()); return SERVICE_RADIO_ORANGE.equals(uuid.toString());
} }
} }

View file

@ -0,0 +1,195 @@
package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.device
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothAdapter.LeScanCallback
import android.bluetooth.BluetoothGattCharacteristic
import android.bluetooth.le.BluetoothLeScanner
import android.bluetooth.le.ScanCallback
import android.bluetooth.le.ScanFilter
import android.bluetooth.le.ScanResult
import android.bluetooth.le.ScanSettings
import android.os.Handler
import android.os.Message
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkUtil
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.RileyLinkBLE
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data.GattAttributes
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.operations.BLECommOperationResult
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.RileyLinkServiceData
import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil
import info.nightscout.androidaps.utils.sharedPreferences.SP
import java.lang.Exception
import java.util.*
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class OrangeLinkImpl @Inject constructor(
var aapsLogger: AAPSLogger,
var rileyLinkServiceData: RileyLinkServiceData,
var rileyLinkUtil: RileyLinkUtil,
var sp: SP) {
lateinit var rileyLinkBLE: RileyLinkBLE
fun onCharacteristicChanged(characteristic: BluetoothGattCharacteristic) {
if (characteristic.uuid.toString().equals(GattAttributes.CHARA_NOTIFICATION_ORANGE)) {
val data = characteristic.value
val first = 0xff and data[0].toInt()
aapsLogger.info(LTag.PUMPBTCOMM,
"OrangeLinkImpl: onCharacteristicChanged " + ByteUtil.shortHexString(characteristic.value) + "=====" + first)
val fv = data[3].toString() + "." + data[4]
val hv = data[5].toString() + "." + data[6]
rileyLinkServiceData.versionOrangeFirmware = fv
rileyLinkServiceData.versionOrangeHardware = hv
aapsLogger.info(LTag.PUMPBTCOMM, "OrangeLink: Firmware: ${fv}, Hardware: ${hv}")
}
}
fun resetOrangeLinkData() {
rileyLinkServiceData.isOrange = false
rileyLinkServiceData.versionOrangeFirmware = null
rileyLinkServiceData.versionOrangeHardware = null
}
/**
* We are checking if this is special Orange (with ORANGE_NOTIFICTION_SERVICE)
*/
fun checkIsOrange(uuidService: UUID) {
if (GattAttributes.isOrange(uuidService)) {
rileyLinkServiceData.isOrange = true
}
}
fun enableNotifications(): Boolean {
aapsLogger.info(LTag.PUMPBTCOMM, "OrangeLinkImpl::enableNotifications")
val result: BLECommOperationResult = rileyLinkBLE.setNotification_blocking(
UUID.fromString(GattAttributes.SERVICE_RADIO_ORANGE), //
UUID.fromString(GattAttributes.CHARA_NOTIFICATION_ORANGE)
)
if (result.resultCode != BLECommOperationResult.RESULT_SUCCESS) {
aapsLogger.error(LTag.PUMPBTCOMM, "Error setting response count notification")
return false
}
return true
}
private fun buildScanFilters(): List<ScanFilter> {
val scanFilterList: MutableList<ScanFilter> = mutableListOf() //ArrayList<*> = ArrayList<Any>()
val scanFilterBuilder = ScanFilter.Builder()
scanFilterBuilder.setDeviceAddress(rileyLinkServiceData.rileyLinkAddress)
scanFilterList.add(scanFilterBuilder.build())
return scanFilterList
}
private fun buildScanSettings(): ScanSettings? {
val scanSettingBuilder = ScanSettings.Builder()
scanSettingBuilder.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
scanSettingBuilder.setMatchMode(ScanSettings.MATCH_MODE_AGGRESSIVE)
scanSettingBuilder.setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
return scanSettingBuilder.build()
}
fun startScan() {
try {
stopScan()
val bluetoothAdapter = rileyLinkBLE.getBluetoothAdapter()
aapsLogger.debug(LTag.PUMPBTCOMM, "startScan")
handler.sendEmptyMessageDelayed(TIME_OUT_WHAT, TIME_OUT.toLong())
val bluetoothLeScanner: BluetoothLeScanner = bluetoothAdapter.bluetoothLeScanner
// if (bluetoothLeScanner == null) {
// bluetoothAdapter.startLeScan(mLeScanCallback)
// return
// }
bluetoothLeScanner.startScan(buildScanFilters(), buildScanSettings(), scanCallback)
} catch (e: Exception) {
e.printStackTrace()
aapsLogger.error(LTag.PUMPBTCOMM, "Start scan: ${e.message}", e)
}
}
var scanCallback: ScanCallback = object : ScanCallback() {
override fun onScanResult(callbackType: Int, result: ScanResult) {
super.onScanResult(callbackType, result)
//val name = result.device.name
val address = result.device.address
if (rileyLinkServiceData.rileyLinkAddress.equals(address)) {
stopScan()
rileyLinkBLE.rileyLinkDevice = result.device
rileyLinkBLE.connectGattInternal()
}
}
override fun onBatchScanResults(results: List<ScanResult>) {
super.onBatchScanResults(results)
}
override fun onScanFailed(errorCode: Int) {
super.onScanFailed(errorCode)
stopScan()
}
}
private val mLeScanCallback = LeScanCallback { device, _, _ ->
if (rileyLinkServiceData.rileyLinkAddress.equals(device.address)) {
stopScan()
rileyLinkBLE.rileyLinkDevice = device
rileyLinkBLE.connectGattInternal()
}
}
val TIME_OUT = 90 * 1000
val TIME_OUT_WHAT = 0x12
var handler: Handler = object : Handler() {
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
when (msg.what) {
TIME_OUT_WHAT -> stopScan()
}
}
}
fun stopScan() {
handler.removeMessages(TIME_OUT_WHAT)
val bluetoothAdapter = rileyLinkBLE.getBluetoothAdapter() ?: return
try {
val bluetoothLeScanner: BluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner()
if (isBluetoothAvailable()) {
bluetoothLeScanner.stopScan(scanCallback)
}
return
// if (bluetoothLeScanner == null) {
// if (isBluetoothAvailable()) {
// bluetoothAdapter.stopLeScan(mLeScanCallback)
// }
// return
// }
// if (isBluetoothAvailable()) {
// bluetoothLeScanner.stopScan(scanCallback)
// }
} catch (e: Exception) {
aapsLogger.error(LTag.PUMPBTCOMM, "Stop scan: ${e.message}", e)
}
}
fun isBluetoothAvailable(): Boolean {
val bluetoothAdapter = rileyLinkBLE.getBluetoothAdapter()
return bluetoothAdapter != null &&
bluetoothAdapter.isEnabled() &&
bluetoothAdapter.getState() == BluetoothAdapter.STATE_ON
}
}

View file

@ -91,30 +91,29 @@ public class RileyLinkStatusGeneralFragment extends DaggerFragment implements Re
this.connectionStatus.setText(resourceHelper.gs(rileyLinkServiceData.rileyLinkServiceState.getResourceId())); this.connectionStatus.setText(resourceHelper.gs(rileyLinkServiceData.rileyLinkServiceState.getResourceId()));
// BS FIXME rileyLinkServiceData is injected so I suppose it cannot be null? this.configuredRileyLinkAddress.setText(Optional.ofNullable(rileyLinkServiceData.rileyLinkAddress).orElse(PLACEHOLDER));
if (rileyLinkServiceData != null) { this.configuredRileyLinkName.setText(Optional.ofNullable(rileyLinkServiceData.rileyLinkName).orElse(PLACEHOLDER));
this.configuredRileyLinkAddress.setText(Optional.ofNullable(rileyLinkServiceData.rileyLinkAddress).orElse(PLACEHOLDER));
this.configuredRileyLinkName.setText(Optional.ofNullable(rileyLinkServiceData.rileyLinkName).orElse(PLACEHOLDER));
if (sp.getBoolean(resourceHelper.gs(R.string.key_riley_link_show_battery_level), false)) { if (sp.getBoolean(resourceHelper.gs(R.string.key_riley_link_show_battery_level), false)) {
batteryLevelRow.setVisibility(View.VISIBLE); batteryLevelRow.setVisibility(View.VISIBLE);
Integer batteryLevel = rileyLinkServiceData.batteryLevel; Integer batteryLevel = rileyLinkServiceData.batteryLevel;
this.batteryLevel.setText(batteryLevel == null ? PLACEHOLDER : resourceHelper.gs(R.string.rileylink_battery_level_value, batteryLevel)); this.batteryLevel.setText(batteryLevel == null ? PLACEHOLDER : resourceHelper.gs(R.string.rileylink_battery_level_value, batteryLevel));
} else { } else {
batteryLevelRow.setVisibility(View.GONE); batteryLevelRow.setVisibility(View.GONE);
} }
RileyLinkError rileyLinkError = rileyLinkServiceData.rileyLinkError; RileyLinkError rileyLinkError = rileyLinkServiceData.rileyLinkError;
this.connectionError.setText(rileyLinkError == null ? PLACEHOLDER : resourceHelper.gs(rileyLinkError.getResourceId(targetDevice))); this.connectionError.setText(rileyLinkError == null ? PLACEHOLDER : resourceHelper.gs(rileyLinkError.getResourceId(targetDevice)));
if(rileyLinkServiceData.isOrange){ if (rileyLinkServiceData.isOrange && rileyLinkServiceData.versionOrangeFirmware!=null) {
this.firmwareVersion.setText("FV:"+Optional.ofNullable(rileyLinkServiceData.versionOrangeFV).orElse(PLACEHOLDER)+"\nHV:"+Optional.ofNullable(rileyLinkServiceData.versionOrangeHV).orElse(PLACEHOLDER)); this.firmwareVersion.setText(resourceHelper.gs(R.string.rileylink_firmware_version_value_orange,
}else{ rileyLinkServiceData.versionOrangeFirmware,
this.firmwareVersion.setText(resourceHelper.gs(R.string.rileylink_firmware_version_value, Optional.ofNullable(rileyLinkServiceData.versionOrangeHardware).orElse(PLACEHOLDER)));
Optional.ofNullable(rileyLinkServiceData.versionBLE113).orElse(PLACEHOLDER), Optional.ofNullable(rileyLinkServiceData.versionCC110).orElse(PLACEHOLDER))); } else {
this.firmwareVersion.setText(resourceHelper.gs(R.string.rileylink_firmware_version_value,
} Optional.ofNullable(rileyLinkServiceData.versionBLE113).orElse(PLACEHOLDER),
Optional.ofNullable(rileyLinkServiceData.versionCC110).orElse(PLACEHOLDER)));
} }
RileyLinkPumpDevice rileyLinkPumpDevice = (RileyLinkPumpDevice) activePlugin.getActivePump(); RileyLinkPumpDevice rileyLinkPumpDevice = (RileyLinkPumpDevice) activePlugin.getActivePump();

View file

@ -48,9 +48,11 @@ public class RileyLinkServiceData {
public String versionBLE113; public String versionBLE113;
// radio version // radio version
public String versionCC110; public String versionCC110;
// orangeLink
public boolean isOrange; public boolean isOrange;
public String versionOrangeFV; public String versionOrangeFirmware;
public String versionOrangeHV; public String versionOrangeHardware;
public RileyLinkTargetDevice targetDevice; public RileyLinkTargetDevice targetDevice;

View file

@ -38,7 +38,7 @@ public class DiscoverGattServicesTask extends ServiceTask {
RileyLinkPumpDevice pumpDevice = (RileyLinkPumpDevice) activePlugin.getActivePump(); RileyLinkPumpDevice pumpDevice = (RileyLinkPumpDevice) activePlugin.getActivePump();
if (needToConnect) { if (needToConnect) {
pumpDevice.getRileyLinkService().getRileyLinkBLE().connectGattCheckOrange(); pumpDevice.getRileyLinkService().getRileyLinkBLE().connectGatt();
} }
pumpDevice.getRileyLinkService().getRileyLinkBLE().discoverServices(); pumpDevice.getRileyLinkService().getRileyLinkBLE().discoverServices();

View file

@ -45,6 +45,7 @@
<string name="rileylink_last_device_contact">Last Device Contact:</string> <string name="rileylink_last_device_contact">Last Device Contact:</string>
<string name="rileylink_firmware_version">Firmware Version:</string> <string name="rileylink_firmware_version">Firmware Version:</string>
<string name="rileylink_firmware_version_value">BLE113: %1$s\nCC110: %2$s</string> <string name="rileylink_firmware_version_value">BLE113: %1$s\nCC110: %2$s</string>
<string name="rileylink_firmware_version_value_orange">Fw: %1$s\nHw: %2$s</string>
<string name="rileylink_pump_serial_number">Pump Serial Number:</string> <string name="rileylink_pump_serial_number">Pump Serial Number:</string>
<string name="rileylink_pump_frequency">Pump Frequency:</string> <string name="rileylink_pump_frequency">Pump Frequency:</string>
<string name="rileylink_pump_frequency_value">%1$.2f MHz</string> <string name="rileylink_pump_frequency_value">%1$.2f MHz</string>