From 6b7efdcb95abee1240098a6a6487e233700cd654 Mon Sep 17 00:00:00 2001 From: Andy Rozman Date: Tue, 20 Apr 2021 16:45:29 +0100 Subject: [PATCH] - more kotlinize, MedtronicHistoryActivity not yet working --- app/build.gradle | 2 +- .../pump/common/PumpPluginAbstract.java | 529 ------ .../plugins/pump/common/PumpPluginAbstract.kt | 431 +++++ .../plugins/pump/common/data/PumpDbEntry.java | 21 - .../plugins/pump/common/data/PumpDbEntry.kt | 9 + .../pump/medtronic/MedtronicPumpPlugin.java | 1627 ----------------- .../pump/medtronic/MedtronicPumpPlugin.kt | 1179 ++++++++++++ .../comm/MedtronicCommunicationManager.java | 38 + .../pump/medtronic/data/dto/BasalProfile.kt | 25 +- .../dialog/MedtronicHistoryActivity.java | 250 --- .../dialog/MedtronicHistoryActivity.kt | 200 ++ .../RileyLinkStatusDeviceMedtronic.java | 166 -- .../dialog/RileyLinkStatusDeviceMedtronic.kt | 137 ++ .../service/RileyLinkMedtronicService.kt | 11 +- .../pump/common/defs/PumpHistoryEntryGroup.kt | 4 +- .../res/layout/rileylink_status_device.xml | 2 +- 16 files changed, 2016 insertions(+), 2615 deletions(-) delete mode 100644 medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/common/PumpPluginAbstract.java create mode 100644 medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/common/PumpPluginAbstract.kt delete mode 100644 medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/common/data/PumpDbEntry.java create mode 100644 medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/common/data/PumpDbEntry.kt delete mode 100644 medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicPumpPlugin.java create mode 100644 medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicPumpPlugin.kt delete mode 100644 medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/dialog/MedtronicHistoryActivity.java create mode 100644 medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/dialog/MedtronicHistoryActivity.kt delete mode 100644 medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/dialog/RileyLinkStatusDeviceMedtronic.java create mode 100644 medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/dialog/RileyLinkStatusDeviceMedtronic.kt diff --git a/app/build.gradle b/app/build.gradle index e4d4c5be8b..ce044fe703 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -111,7 +111,7 @@ android { defaultConfig { multiDexEnabled true versionCode 1500 - version "2.8.2.1-dev-e3" + version "2.8.2.1-meallink-mdt" buildConfigField "String", "VERSION", '"' + version + '"' buildConfigField "String", "BUILDVERSION", '"' + generateGitBuild() + '-' + generateDate() + '"' buildConfigField "String", "REMOTE", '"' + generateGitRemote() + '"' diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/common/PumpPluginAbstract.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/common/PumpPluginAbstract.java deleted file mode 100644 index ca9100a086..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/common/PumpPluginAbstract.java +++ /dev/null @@ -1,529 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.common; - -import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; - -import androidx.annotation.NonNull; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; - -import org.json.JSONException; -import org.json.JSONObject; - -import java.util.HashMap; -import java.util.Map; - -import dagger.android.HasAndroidInjector; -import info.nightscout.androidaps.core.R; -import info.nightscout.androidaps.data.DetailedBolusInfo; -import info.nightscout.androidaps.data.Profile; -import info.nightscout.androidaps.data.PumpEnactResult; -import info.nightscout.androidaps.db.TemporaryBasal; -import info.nightscout.androidaps.events.EventAppExit; -import info.nightscout.androidaps.events.EventCustomActionsChanged; -import info.nightscout.androidaps.extensions.PumpStateExtensionKt; -import info.nightscout.androidaps.interfaces.ActivePlugin; -import info.nightscout.androidaps.interfaces.CommandQueueProvider; -import info.nightscout.androidaps.interfaces.Constraints; -import info.nightscout.androidaps.interfaces.PluginDescription; -import info.nightscout.androidaps.interfaces.PumpDescription; -import info.nightscout.androidaps.interfaces.Pump; -import info.nightscout.androidaps.interfaces.PumpPluginBase; -import info.nightscout.androidaps.interfaces.PumpSync; -import info.nightscout.androidaps.logging.AAPSLogger; -import info.nightscout.androidaps.logging.LTag; -import info.nightscout.androidaps.plugins.bus.RxBusWrapper; -import info.nightscout.androidaps.plugins.common.ManufacturerType; -import info.nightscout.androidaps.plugins.general.overview.events.EventOverviewBolusProgress; -import info.nightscout.androidaps.plugins.pump.common.data.PumpDbEntry; -import info.nightscout.androidaps.plugins.pump.common.data.PumpStatus; -import info.nightscout.androidaps.plugins.pump.common.defs.PumpDriverState; -import info.nightscout.androidaps.plugins.pump.common.defs.PumpType; -import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicConst; -import info.nightscout.androidaps.utils.DateUtil; -import info.nightscout.androidaps.utils.DecimalFormatter; -import info.nightscout.androidaps.utils.FabricPrivacy; -import info.nightscout.androidaps.utils.resources.ResourceHelper; -import info.nightscout.androidaps.utils.rx.AapsSchedulers; -import info.nightscout.androidaps.utils.sharedPreferences.SP; -import io.reactivex.disposables.CompositeDisposable; - -import static info.nightscout.androidaps.extensions.PumpStateExtensionKt.convertedToAbsolute; -import static info.nightscout.androidaps.extensions.PumpStateExtensionKt.getPlannedRemainingMinutes; - -/** - * Created by andy on 23.04.18. - */ - -// When using this class, make sure that your first step is to create mConnection (see MedtronicPumpPlugin) - -public abstract class PumpPluginAbstract extends PumpPluginBase implements Pump, Constraints { - private final CompositeDisposable disposable = new CompositeDisposable(); - - protected HasAndroidInjector injector; - protected AAPSLogger aapsLogger; - protected RxBusWrapper rxBus; - protected ActivePlugin activePlugin; - protected Context context; - protected FabricPrivacy fabricPrivacy; - protected ResourceHelper resourceHelper; - protected CommandQueueProvider commandQueue; - protected SP sp; - protected DateUtil dateUtil; - protected PumpDescription pumpDescription = new PumpDescription(); - protected ServiceConnection serviceConnection; - protected boolean serviceRunning = false; - protected PumpDriverState pumpState = PumpDriverState.NotInitialized; - protected boolean displayConnectionMessages = false; - protected PumpType pumpType; - protected AapsSchedulers aapsSchedulers; - protected PumpSync pumpSync; - protected Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create(); - - protected PumpPluginAbstract( - PluginDescription pluginDescription, - PumpType pumpType, - HasAndroidInjector injector, - ResourceHelper resourceHelper, - AAPSLogger aapsLogger, - CommandQueueProvider commandQueue, - RxBusWrapper rxBus, - ActivePlugin activePlugin, - SP sp, - Context context, - FabricPrivacy fabricPrivacy, - DateUtil dateUtil, - AapsSchedulers aapsSchedulers, - PumpSync pumpSync - ) { - - super(pluginDescription, injector, aapsLogger, resourceHelper, commandQueue); - this.aapsLogger = aapsLogger; - this.rxBus = rxBus; - this.activePlugin = activePlugin; - this.context = context; - this.fabricPrivacy = fabricPrivacy; - this.resourceHelper = resourceHelper; - this.sp = sp; - this.commandQueue = commandQueue; - - pumpDescription.setPumpDescription(pumpType); - this.pumpType = pumpType; - this.dateUtil = dateUtil; - this.aapsSchedulers = aapsSchedulers; - this.pumpSync = pumpSync; - } - - - public abstract void initPumpStatusData(); - - - @Override - protected void onStart() { - super.onStart(); - - initPumpStatusData(); - - Intent intent = new Intent(context, getServiceClass()); - context.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE); - - serviceRunning = true; - - disposable.add(rxBus - .toObservable(EventAppExit.class) - .observeOn(aapsSchedulers.getIo()) - .subscribe(event -> context.unbindService(serviceConnection), fabricPrivacy::logException) - ); - onStartCustomActions(); - } - - - @Override - protected void onStop() { - aapsLogger.debug(LTag.PUMP, this.deviceID() + " onStop()"); - - context.unbindService(serviceConnection); - - serviceRunning = false; - - disposable.clear(); - super.onStop(); - } - - - /** - * If we need to run any custom actions in onStart (triggering events, etc) - */ - public abstract void onStartCustomActions(); - - /** - * Service class (same one you did serviceConnection for) - * - * @return Class - */ - public abstract Class getServiceClass(); - - public abstract PumpStatus getPumpStatusData(); - - - public boolean isInitialized() { - return pumpState.isInitialized(); - } - - - public boolean isSuspended() { - return pumpState == PumpDriverState.Suspended; - } - - - public boolean isBusy() { - return pumpState == PumpDriverState.Busy; - } - - - public boolean isConnected() { - if (displayConnectionMessages) - aapsLogger.debug(LTag.PUMP, "isConnected [PumpPluginAbstract]."); - return pumpState.isConnected(); - } - - - public boolean isConnecting() { - if (displayConnectionMessages) - aapsLogger.debug(LTag.PUMP, "isConnecting [PumpPluginAbstract]."); - return pumpState == PumpDriverState.Connecting; - } - - - public void connect(@NonNull String reason) { - if (displayConnectionMessages) - aapsLogger.debug(LTag.PUMP, "connect (reason={}) [PumpPluginAbstract] - default (empty) implementation." + reason); - } - - - public void disconnect(@NonNull String reason) { - if (displayConnectionMessages) - aapsLogger.debug(LTag.PUMP, "disconnect (reason={}) [PumpPluginAbstract] - default (empty) implementation." + reason); - } - - - public void stopConnecting() { - if (displayConnectionMessages) - aapsLogger.debug(LTag.PUMP, "stopConnecting [PumpPluginAbstract] - default (empty) implementation."); - } - - - @Override - public boolean isHandshakeInProgress() { - if (displayConnectionMessages) - aapsLogger.debug(LTag.PUMP, "isHandshakeInProgress [PumpPluginAbstract] - default (empty) implementation."); - return false; - } - - - @Override - public void finishHandshaking() { - if (displayConnectionMessages) - aapsLogger.debug(LTag.PUMP, "finishHandshaking [PumpPluginAbstract] - default (empty) implementation."); - } - - // Upload to pump new basal profile - @NonNull public PumpEnactResult setNewBasalProfile(@NonNull Profile profile) { - aapsLogger.debug(LTag.PUMP, "setNewBasalProfile [PumpPluginAbstract] - Not implemented."); - return getOperationNotSupportedWithCustomText(R.string.pump_operation_not_supported_by_pump_driver); - } - - - public boolean isThisProfileSet(@NonNull Profile profile) { - aapsLogger.debug(LTag.PUMP, "isThisProfileSet [PumpPluginAbstract] - Not implemented."); - return true; - } - - - public long lastDataTime() { - aapsLogger.debug(LTag.PUMP, "lastDataTime [PumpPluginAbstract]."); - return getPumpStatusData().getLastConnection(); - } - - - public double getBaseBasalRate() { - aapsLogger.debug(LTag.PUMP, "getBaseBasalRate [PumpPluginAbstract] - Not implemented."); - return 0.0d; - } // base basal rate, not temp basal - - - public void stopBolusDelivering() { - aapsLogger.debug(LTag.PUMP, "stopBolusDelivering [PumpPluginAbstract] - Not implemented."); - } - - - @NonNull @Override - public PumpEnactResult setTempBasalAbsolute(double absoluteRate, int durationInMinutes, @NonNull Profile profile, boolean enforceNew, @NonNull PumpSync.TemporaryBasalType tbrType) { - aapsLogger.debug(LTag.PUMP, "setTempBasalAbsolute [PumpPluginAbstract] - Not implemented."); - return getOperationNotSupportedWithCustomText(R.string.pump_operation_not_supported_by_pump_driver); - } - - - @NonNull @Override - public PumpEnactResult setTempBasalPercent(int percent, int durationInMinutes, @NonNull Profile profile, boolean enforceNew, @NonNull PumpSync.TemporaryBasalType tbrType) { - aapsLogger.debug(LTag.PUMP, "setTempBasalPercent [PumpPluginAbstract] - Not implemented."); - return getOperationNotSupportedWithCustomText(R.string.pump_operation_not_supported_by_pump_driver); - } - - - @NonNull public PumpEnactResult setExtendedBolus(double insulin, int durationInMinutes) { - aapsLogger.debug(LTag.PUMP, "setExtendedBolus [PumpPluginAbstract] - Not implemented."); - return getOperationNotSupportedWithCustomText(R.string.pump_operation_not_supported_by_pump_driver); - } - - - // some pumps might set a very short temp close to 100% as cancelling a temp can be noisy - // when the cancel request is requested by the user (forced), the pump should always do a real cancel - - @NonNull public PumpEnactResult cancelTempBasal(boolean enforceNew) { - aapsLogger.debug(LTag.PUMP, "cancelTempBasal [PumpPluginAbstract] - Not implemented."); - return getOperationNotSupportedWithCustomText(R.string.pump_operation_not_supported_by_pump_driver); - } - - - @NonNull public PumpEnactResult cancelExtendedBolus() { - aapsLogger.debug(LTag.PUMP, "cancelExtendedBolus [PumpPluginAbstract] - Not implemented."); - return getOperationNotSupportedWithCustomText(R.string.pump_operation_not_supported_by_pump_driver); - } - - - // Status to be passed to NS - - // public JSONObject getJSONStatus(Profile profile, String profileName) { - // return pumpDriver.getJSONStatus(profile, profileName); - // } - - public String deviceID() { - aapsLogger.debug(LTag.PUMP, "deviceID [PumpPluginAbstract] - Not implemented."); - return "FakeDevice"; - } - - - // Pump capabilities - - - public PumpDescription getPumpDescription() { - return pumpDescription; - } - - - // Short info for SMS, Wear etc - - public boolean isFakingTempsByExtendedBoluses() { - aapsLogger.debug(LTag.PUMP, "isFakingTempsByExtendedBoluses [PumpPluginAbstract] - Not implemented."); - return false; - } - - - @NonNull @Override - public PumpEnactResult loadTDDs() { - aapsLogger.debug(LTag.PUMP, "loadTDDs [PumpPluginAbstract] - Not implemented."); - return getOperationNotSupportedWithCustomText(R.string.pump_operation_not_supported_by_pump_driver); - } - - - @NonNull @Override - public JSONObject getJSONStatus(@NonNull Profile profile, @NonNull String profileName, @NonNull String version) { - - if ((getPumpStatusData().getLastConnection() + 60 * 60 * 1000L) < System.currentTimeMillis()) { - return new JSONObject(); - } - - long now = System.currentTimeMillis(); - JSONObject pump = new JSONObject(); - JSONObject battery = new JSONObject(); - JSONObject status = new JSONObject(); - JSONObject extended = new JSONObject(); - try { - battery.put("percent", getPumpStatusData().getBatteryRemaining()); - status.put("status", getPumpStatusData().getPumpStatusType() != null ? getPumpStatusData().getPumpStatusType().getStatus() : "normal"); - extended.put("Version", version); - try { - extended.put("ActiveProfile", profileName); - } catch (Exception ignored) { - } - - PumpSync.PumpState.TemporaryBasal tb = pumpSync.expectedPumpState().getTemporaryBasal(); - if (tb != null) { - extended.put("TempBasalAbsoluteRate", convertedToAbsolute(tb, now, profile)); - extended.put("TempBasalStart", dateUtil.dateAndTimeString(tb.getTimestamp())); - extended.put("TempBasalRemaining", getPlannedRemainingMinutes(tb)); - } - - PumpSync.PumpState.ExtendedBolus eb = pumpSync.expectedPumpState().getExtendedBolus(); - if (eb != null) { - extended.put("ExtendedBolusAbsoluteRate", eb.getRate()); - extended.put("ExtendedBolusStart", dateUtil.dateAndTimeString(eb.getTimestamp())); - extended.put("ExtendedBolusRemaining", getPlannedRemainingMinutes(eb)); - } - - status.put("timestamp", dateUtil.toISOString(dateUtil.now())); - - pump.put("battery", battery); - pump.put("status", status); - pump.put("extended", extended); - pump.put("reservoir", getPumpStatusData().getReservoirRemainingUnits()); - pump.put("clock", dateUtil.toISOString(dateUtil.now())); - } catch (JSONException e) { - aapsLogger.error("Unhandled exception", e); - } - return pump; - } - - - // FIXME i18n, null checks: iob, TDD - @NonNull @Override - public String shortStatus(boolean veryShort) { - String ret = ""; - if (getPumpStatusData().getLastConnection() != 0) { - long agoMsec = System.currentTimeMillis() - getPumpStatusData().getLastConnection(); - int agoMin = (int) (agoMsec / 60d / 1000d); - ret += "LastConn: " + agoMin + " min ago\n"; - } - if (getPumpStatusData().getLastBolusTime() != null && getPumpStatusData().getLastBolusTime().getTime() != 0) { - ret += "LastBolus: " + DecimalFormatter.INSTANCE.to2Decimal(getPumpStatusData().getLastBolusAmount()) + "U @" + // - android.text.format.DateFormat.format("HH:mm", getPumpStatusData().getLastBolusTime()) + "\n"; - } - PumpSync.PumpState.TemporaryBasal activeTemp = pumpSync.expectedPumpState().getTemporaryBasal(); - if (activeTemp != null) { - ret += "Temp: " + PumpStateExtensionKt.toStringFull(activeTemp, dateUtil) + "\n"; - } - PumpSync.PumpState.ExtendedBolus activeExtendedBolus = pumpSync.expectedPumpState().getExtendedBolus(); - if (activeExtendedBolus != null) { - ret += "Extended: " + PumpStateExtensionKt.toStringFull(activeExtendedBolus, dateUtil) + "\n"; - } - // if (!veryShort) { - // ret += "TDD: " + DecimalFormatter.to0Decimal(pumpStatus.dailyTotalUnits) + " / " - // + pumpStatus.maxDailyTotalUnits + " U\n"; - // } - ret += "IOB: " + getPumpStatusData().getIob() + "U\n"; - ret += "Reserv: " + DecimalFormatter.INSTANCE.to0Decimal(getPumpStatusData().getReservoirRemainingUnits()) + "U\n"; - ret += "Batt: " + getPumpStatusData().getBatteryRemaining() + "\n"; - return ret; - } - - - @NonNull @Override - public PumpEnactResult deliverTreatment(DetailedBolusInfo detailedBolusInfo) { - - try { - if (detailedBolusInfo.insulin == 0 && detailedBolusInfo.carbs == 0) { - // neither carbs nor bolus requested - aapsLogger.error("deliverTreatment: Invalid input"); - return new PumpEnactResult(getInjector()).success(false).enacted(false).bolusDelivered(0d).carbsDelivered(0d) - .comment(R.string.invalidinput); - } else if (detailedBolusInfo.insulin > 0) { - // bolus needed, ask pump to deliver it - return deliverBolus(detailedBolusInfo); - } else { - //if (MedtronicHistoryData.doubleBolusDebug) - // aapsLogger.debug("DoubleBolusDebug: deliverTreatment::(carb only entry)"); - - // TODO fix - // no bolus required, carb only treatment - activePlugin.getActiveTreatments().addToHistoryTreatment(detailedBolusInfo, true); - - EventOverviewBolusProgress bolusingEvent = EventOverviewBolusProgress.INSTANCE; - bolusingEvent.setT(new EventOverviewBolusProgress.Treatment(0, 0, detailedBolusInfo.getBolusType() == DetailedBolusInfo.BolusType.SMB)); - bolusingEvent.setPercent(100); - rxBus.send(bolusingEvent); - - aapsLogger.debug(LTag.PUMP, "deliverTreatment: Carb only treatment."); - - return new PumpEnactResult(getInjector()).success(true).enacted(true).bolusDelivered(0d) - .carbsDelivered(detailedBolusInfo.carbs).comment(R.string.common_resultok); - } - } finally { - triggerUIChange(); - } - - } - - - protected void refreshCustomActionsList() { - rxBus.send(new EventCustomActionsChanged()); - } - - - @NonNull public ManufacturerType manufacturer() { - return pumpType.getManufacturer(); - } - - @NonNull - public PumpType model() { - return pumpType; - } - - - public PumpType getPumpType() { - return pumpType; - } - - - public void setPumpType(PumpType pumpType) { - this.pumpType = pumpType; - this.pumpDescription.setPumpDescription(pumpType); - } - - - public boolean canHandleDST() { - return false; - } - - - protected abstract PumpEnactResult deliverBolus(DetailedBolusInfo detailedBolusInfo); - - protected abstract void triggerUIChange(); - - private PumpEnactResult getOperationNotSupportedWithCustomText(int resourceId) { - return new PumpEnactResult(getInjector()).success(false).enacted(false).comment(resourceId); - } - - // PumpSync - - Map driverHistory = new HashMap<>(); - - public abstract long generateTempId(long timeMillis); - - protected boolean addBolusWithTempId(DetailedBolusInfo detailedBolusInfo, boolean writeToInternalHistory) { - long temporaryId = generateTempId(detailedBolusInfo.timestamp); - boolean response = pumpSync.addBolusWithTempId(detailedBolusInfo.timestamp, detailedBolusInfo.insulin, - temporaryId, detailedBolusInfo.getBolusType(), - getPumpType(), serialNumber()); - - if (response && writeToInternalHistory) { - driverHistory.put(temporaryId, new PumpDbEntry(temporaryId, model(), serialNumber(), detailedBolusInfo)); - sp.putString(MedtronicConst.Statistics.InternalTemporaryDatabase, gson.toJson(driverHistory)); - } - - return response; - } - - protected void addTemporaryBasalRateWithTempId(TemporaryBasal temporaryBasal, boolean b) { -// long temporaryId = generateTempId(temporaryBasal.timestamp); -// boolean response = pumpSync.addBolusWithTempId(temporaryBasal.timestamp, detailedBolusInfo.insulin, -// generateTempId(detailedBolusInfo.timestamp), detailedBolusInfo.getBolusType(), -// getPumpType(), serialNumber()); -// -// if (response && writeToInternalHistory) { -// driverHistory.put(temporaryId, new PumpDbEntry(temporaryId, model(), serialNumber(), detailedBolusInfo)); -// sp.putString(MedtronicConst.Statistics.InternalTemporaryDatabase, gson.toJson(driverHistory)); -// } -// -// return response; - } - - - public void removeTemporaryId(long temporaryId) { - driverHistory.remove(temporaryId); - } - - -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/common/PumpPluginAbstract.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/common/PumpPluginAbstract.kt new file mode 100644 index 0000000000..26660c4936 --- /dev/null +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/common/PumpPluginAbstract.kt @@ -0,0 +1,431 @@ +package info.nightscout.androidaps.plugins.pump.common + +import android.content.Context +import android.content.Intent +import android.content.ServiceConnection +import android.text.format.DateFormat +import com.google.gson.GsonBuilder +import dagger.android.HasAndroidInjector +import info.nightscout.androidaps.core.R +import info.nightscout.androidaps.data.DetailedBolusInfo +import info.nightscout.androidaps.data.Profile +import info.nightscout.androidaps.data.PumpEnactResult +import info.nightscout.androidaps.db.TemporaryBasal +import info.nightscout.androidaps.events.EventAppExit +import info.nightscout.androidaps.events.EventCustomActionsChanged +import info.nightscout.androidaps.extensions.convertedToAbsolute +import info.nightscout.androidaps.extensions.plannedRemainingMinutes +import info.nightscout.androidaps.extensions.toStringFull +import info.nightscout.androidaps.interfaces.* +import info.nightscout.androidaps.interfaces.PumpSync.TemporaryBasalType +import info.nightscout.androidaps.logging.AAPSLogger +import info.nightscout.androidaps.logging.LTag +import info.nightscout.androidaps.plugins.bus.RxBusWrapper +import info.nightscout.androidaps.plugins.common.ManufacturerType +import info.nightscout.androidaps.plugins.general.overview.events.EventOverviewBolusProgress +import info.nightscout.androidaps.plugins.pump.common.data.PumpDbEntry +import info.nightscout.androidaps.plugins.pump.common.data.PumpStatus +import info.nightscout.androidaps.plugins.pump.common.defs.PumpDriverState +import info.nightscout.androidaps.plugins.pump.common.defs.PumpType +import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicConst +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.DecimalFormatter.to0Decimal +import info.nightscout.androidaps.utils.DecimalFormatter.to2Decimal +import info.nightscout.androidaps.utils.FabricPrivacy +import info.nightscout.androidaps.utils.resources.ResourceHelper +import info.nightscout.androidaps.utils.rx.AapsSchedulers +import info.nightscout.androidaps.utils.sharedPreferences.SP +import io.reactivex.disposables.CompositeDisposable +import org.json.JSONException +import org.json.JSONObject +import java.util.* + +/** + * Created by andy on 23.04.18. + */ +// When using this class, make sure that your first step is to create mConnection (see MedtronicPumpPlugin) +abstract class PumpPluginAbstract protected constructor( + pluginDescription: PluginDescription?, + pumpType: PumpType, + injector: HasAndroidInjector?, + resourceHelper: ResourceHelper, + aapsLogger: AAPSLogger, + commandQueue: CommandQueueProvider, + var rxBus: RxBusWrapper, + var activePlugin: ActivePlugin, + var sp: SP, + var context: Context, + var fabricPrivacy: FabricPrivacy, + dateUtil: DateUtil, + aapsSchedulers: AapsSchedulers, + pumpSync: PumpSync +) : PumpPluginBase(pluginDescription!!, injector!!, aapsLogger, resourceHelper, commandQueue), Pump, Constraints { + + private val disposable = CompositeDisposable() + //protected override var injector: HasAndroidInjector? = null + protected var dateUtil: DateUtil + + // Pump capabilities + final override var pumpDescription = PumpDescription() + //protected set + + @JvmField protected var serviceConnection: ServiceConnection? = null + @JvmField protected var serviceRunning = false + @JvmField protected var pumpState = PumpDriverState.NotInitialized + @JvmField protected var displayConnectionMessages = false + + var pumpType: PumpType? = null + get() = field + set(value) { + field = value + pumpDescription.setPumpDescription(value!!) + } + + + protected var aapsSchedulers: AapsSchedulers + protected var pumpSync: PumpSync + protected var gson = GsonBuilder().excludeFieldsWithoutExposeAnnotation().create() + + abstract fun initPumpStatusData() + + override fun onStart() { + super.onStart() + initPumpStatusData() + val intent = Intent(context, serviceClass) + context.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE) + serviceRunning = true + disposable.add(rxBus + .toObservable(EventAppExit::class.java) + .observeOn(aapsSchedulers.io) + .subscribe({ event: EventAppExit? -> context.unbindService(serviceConnection) }) { throwable: Throwable? -> fabricPrivacy.logException(throwable!!) } + ) + onStartCustomActions() + } + + override fun onStop() { + aapsLogger.debug(LTag.PUMP, deviceID() + " onStop()") + context.unbindService(serviceConnection) + serviceRunning = false + disposable.clear() + super.onStop() + } + + /** + * If we need to run any custom actions in onStart (triggering events, etc) + */ + abstract fun onStartCustomActions() + + /** + * Service class (same one you did serviceConnection for) + * + * @return Class + */ + abstract val serviceClass: Class<*>? + abstract val pumpStatusData: PumpStatus + + override fun isInitialized(): Boolean { + return pumpState.isInitialized() + } + + override fun isSuspended(): Boolean { + return pumpState === PumpDriverState.Suspended + } + + override fun isBusy(): Boolean { + return pumpState === PumpDriverState.Busy + } + + override fun isConnected(): Boolean { + if (displayConnectionMessages) aapsLogger.debug(LTag.PUMP, "isConnected [PumpPluginAbstract].") + return pumpState.isConnected() + } + + override fun isConnecting(): Boolean { + if (displayConnectionMessages) aapsLogger.debug(LTag.PUMP, "isConnecting [PumpPluginAbstract].") + return pumpState === PumpDriverState.Connecting + } + + override fun connect(reason: String) { + if (displayConnectionMessages) aapsLogger.debug(LTag.PUMP, "connect (reason={}) [PumpPluginAbstract] - default (empty) implementation.$reason") + } + + override fun disconnect(reason: String) { + if (displayConnectionMessages) aapsLogger.debug(LTag.PUMP, "disconnect (reason={}) [PumpPluginAbstract] - default (empty) implementation.$reason") + } + + override fun stopConnecting() { + if (displayConnectionMessages) aapsLogger.debug(LTag.PUMP, "stopConnecting [PumpPluginAbstract] - default (empty) implementation.") + } + + override fun isHandshakeInProgress(): Boolean { + if (displayConnectionMessages) aapsLogger.debug(LTag.PUMP, "isHandshakeInProgress [PumpPluginAbstract] - default (empty) implementation.") + return false + } + + override fun finishHandshaking() { + if (displayConnectionMessages) aapsLogger.debug(LTag.PUMP, "finishHandshaking [PumpPluginAbstract] - default (empty) implementation.") + } + + // Upload to pump new basal profile + override fun setNewBasalProfile(profile: Profile): PumpEnactResult { + aapsLogger.debug(LTag.PUMP, "setNewBasalProfile [PumpPluginAbstract] - Not implemented.") + return getOperationNotSupportedWithCustomText(R.string.pump_operation_not_supported_by_pump_driver) + } + + override fun isThisProfileSet(profile: Profile): Boolean { + aapsLogger.debug(LTag.PUMP, "isThisProfileSet [PumpPluginAbstract] - Not implemented.") + return true + } + + override fun lastDataTime(): Long { + aapsLogger.debug(LTag.PUMP, "lastDataTime [PumpPluginAbstract].") + return pumpStatusData.lastConnection + } + + // base basal rate, not temp basal + override val baseBasalRate: Double + get() { + aapsLogger.debug(LTag.PUMP, "getBaseBasalRate [PumpPluginAbstract] - Not implemented.") + return 0.0 + } + + override fun stopBolusDelivering() { + aapsLogger.debug(LTag.PUMP, "stopBolusDelivering [PumpPluginAbstract] - Not implemented.") + } + + override fun setTempBasalAbsolute(absoluteRate: Double, durationInMinutes: Int, profile: Profile, enforceNew: Boolean, tbrType: TemporaryBasalType): PumpEnactResult { + aapsLogger.debug(LTag.PUMP, "setTempBasalAbsolute [PumpPluginAbstract] - Not implemented.") + return getOperationNotSupportedWithCustomText(R.string.pump_operation_not_supported_by_pump_driver) + } + + override fun setTempBasalPercent(percent: Int, durationInMinutes: Int, profile: Profile, enforceNew: Boolean, tbrType: TemporaryBasalType): PumpEnactResult { + aapsLogger.debug(LTag.PUMP, "setTempBasalPercent [PumpPluginAbstract] - Not implemented.") + return getOperationNotSupportedWithCustomText(R.string.pump_operation_not_supported_by_pump_driver) + } + + override fun setExtendedBolus(insulin: Double, durationInMinutes: Int): PumpEnactResult { + aapsLogger.debug(LTag.PUMP, "setExtendedBolus [PumpPluginAbstract] - Not implemented.") + return getOperationNotSupportedWithCustomText(R.string.pump_operation_not_supported_by_pump_driver) + } + + // some pumps might set a very short temp close to 100% as cancelling a temp can be noisy + // when the cancel request is requested by the user (forced), the pump should always do a real cancel + override fun cancelTempBasal(enforceNew: Boolean): PumpEnactResult { + aapsLogger.debug(LTag.PUMP, "cancelTempBasal [PumpPluginAbstract] - Not implemented.") + return getOperationNotSupportedWithCustomText(R.string.pump_operation_not_supported_by_pump_driver) + } + + override fun cancelExtendedBolus(): PumpEnactResult { + aapsLogger.debug(LTag.PUMP, "cancelExtendedBolus [PumpPluginAbstract] - Not implemented.") + return getOperationNotSupportedWithCustomText(R.string.pump_operation_not_supported_by_pump_driver) + } + + // Status to be passed to NS + // public JSONObject getJSONStatus(Profile profile, String profileName) { + // return pumpDriver.getJSONStatus(profile, profileName); + // } + open fun deviceID(): String { + aapsLogger.debug(LTag.PUMP, "deviceID [PumpPluginAbstract] - Not implemented.") + return "FakeDevice" + } + + // Short info for SMS, Wear etc + override val isFakingTempsByExtendedBoluses: Boolean + get() { + aapsLogger.debug(LTag.PUMP, "isFakingTempsByExtendedBoluses [PumpPluginAbstract] - Not implemented.") + return false + } + + override fun loadTDDs(): PumpEnactResult { + aapsLogger.debug(LTag.PUMP, "loadTDDs [PumpPluginAbstract] - Not implemented.") + return getOperationNotSupportedWithCustomText(R.string.pump_operation_not_supported_by_pump_driver) + } + + override fun getJSONStatus(profile: Profile, profileName: String, version: String): JSONObject { + if (pumpStatusData.lastConnection + 60 * 60 * 1000L < System.currentTimeMillis()) { + return JSONObject() + } + val now = System.currentTimeMillis() + val pump = JSONObject() + val battery = JSONObject() + val status = JSONObject() + val extended = JSONObject() + try { + battery.put("percent", pumpStatusData.batteryRemaining) + status.put("status", if (pumpStatusData.pumpStatusType != null) pumpStatusData.pumpStatusType.status else "normal") + extended.put("Version", version) + try { + extended.put("ActiveProfile", profileName) + } catch (ignored: Exception) { + } + val tb = pumpSync.expectedPumpState().temporaryBasal + if (tb != null) { + extended.put("TempBasalAbsoluteRate", tb.convertedToAbsolute(now, profile)) + extended.put("TempBasalStart", dateUtil.dateAndTimeString(tb.timestamp)) + extended.put("TempBasalRemaining", tb.plannedRemainingMinutes) + } + val eb = pumpSync.expectedPumpState().extendedBolus + if (eb != null) { + extended.put("ExtendedBolusAbsoluteRate", eb.rate) + extended.put("ExtendedBolusStart", dateUtil.dateAndTimeString(eb.timestamp)) + extended.put("ExtendedBolusRemaining", eb.plannedRemainingMinutes) + } + status.put("timestamp", dateUtil.toISOString(dateUtil.now())) + pump.put("battery", battery) + pump.put("status", status) + pump.put("extended", extended) + pump.put("reservoir", pumpStatusData.reservoirRemainingUnits) + pump.put("clock", dateUtil.toISOString(dateUtil.now())) + } catch (e: JSONException) { + aapsLogger.error("Unhandled exception", e) + } + return pump + } + + // FIXME i18n, null checks: iob, TDD + override fun shortStatus(veryShort: Boolean): String { + var ret = "" + + if (pumpStatusData.lastConnection==0L) { + ret += "LastConn: never\n" + } else { + val agoMsec = System.currentTimeMillis() - pumpStatusData.lastConnection + val agoMin = (agoMsec / 60.0 / 1000.0).toInt() + ret += "LastConn: $agoMin min ago\n" + } + + if (pumpStatusData.lastBolusTime != null && pumpStatusData.lastBolusTime!!.time != 0L) { + ret += """ + LastBolus: ${to2Decimal(pumpStatusData.lastBolusAmount!!)}U @${DateFormat.format("HH:mm", pumpStatusData.lastBolusTime)} + + """.trimIndent() + } + val activeTemp = pumpSync.expectedPumpState().temporaryBasal + if (activeTemp != null) { + ret += """ + Temp: ${activeTemp.toStringFull(dateUtil)} + + """.trimIndent() + } + val activeExtendedBolus = pumpSync.expectedPumpState().extendedBolus + if (activeExtendedBolus != null) { + ret += """ + Extended: ${activeExtendedBolus.toStringFull(dateUtil)} + + """.trimIndent() + } + // if (!veryShort) { + // ret += "TDD: " + DecimalFormatter.to0Decimal(pumpStatus.dailyTotalUnits) + " / " + // + pumpStatus.maxDailyTotalUnits + " U\n"; + // } + ret += """ + IOB: ${pumpStatusData.iob}U + + """.trimIndent() + ret += """ + Reserv: ${to0Decimal(pumpStatusData.reservoirRemainingUnits)}U + + """.trimIndent() + ret += """ + Batt: ${pumpStatusData.batteryRemaining} + + """.trimIndent() + return ret + } + + override fun deliverTreatment(detailedBolusInfo: DetailedBolusInfo): PumpEnactResult { + return try { + if (detailedBolusInfo.insulin == 0.0 && detailedBolusInfo.carbs == 0.0) { + // neither carbs nor bolus requested + aapsLogger.error("deliverTreatment: Invalid input") + PumpEnactResult(injector).success(false).enacted(false).bolusDelivered(0.0).carbsDelivered(0.0) + .comment(R.string.invalidinput) + } else if (detailedBolusInfo.insulin > 0) { + // bolus needed, ask pump to deliver it + deliverBolus(detailedBolusInfo) + } else { + + // TODO fix + // no bolus required, carb only treatment + activePlugin.activeTreatments.addToHistoryTreatment(detailedBolusInfo, true) + val bolusingEvent = EventOverviewBolusProgress + bolusingEvent.t = EventOverviewBolusProgress.Treatment(0.0, 0, detailedBolusInfo.bolusType === DetailedBolusInfo.BolusType.SMB) + bolusingEvent.percent = 100 + rxBus.send(bolusingEvent) + aapsLogger.debug(LTag.PUMP, "deliverTreatment: Carb only treatment.") + PumpEnactResult(injector).success(true).enacted(true).bolusDelivered(0.0) + .carbsDelivered(detailedBolusInfo.carbs).comment(R.string.common_resultok) + } + } finally { + triggerUIChange() + } + } + + protected fun refreshCustomActionsList() { + rxBus.send(EventCustomActionsChanged()) + } + + override fun manufacturer(): ManufacturerType { + return pumpType!!.manufacturer!! + } + + override fun model(): PumpType { + return pumpType!! + } + + + override fun canHandleDST(): Boolean { + return false + } + + protected abstract fun deliverBolus(detailedBolusInfo: DetailedBolusInfo?): PumpEnactResult + + protected abstract fun triggerUIChange() + + private fun getOperationNotSupportedWithCustomText(resourceId: Int): PumpEnactResult { + return PumpEnactResult(injector).success(false).enacted(false).comment(resourceId) + } + + // PumpSync + var driverHistory: MutableMap = HashMap() + + abstract fun generateTempId(timeMillis: Long): Long + + protected fun addBolusWithTempId(detailedBolusInfo: DetailedBolusInfo, writeToInternalHistory: Boolean): Boolean { + val temporaryId = generateTempId(detailedBolusInfo.timestamp) + val response = pumpSync.addBolusWithTempId(detailedBolusInfo.timestamp, detailedBolusInfo.insulin, + temporaryId, detailedBolusInfo.bolusType, + pumpType!!, serialNumber()) + if (response && writeToInternalHistory) { + driverHistory[temporaryId] = PumpDbEntry(temporaryId, model(), serialNumber(), detailedBolusInfo) + sp.putString(MedtronicConst.Statistics.InternalTemporaryDatabase, gson.toJson(driverHistory)) + } + return response + } + + protected fun addTemporaryBasalRateWithTempId(temporaryBasal: TemporaryBasal?, b: Boolean) { +// long temporaryId = generateTempId(temporaryBasal.timestamp); +// boolean response = pumpSync.addBolusWithTempId(temporaryBasal.timestamp, detailedBolusInfo.insulin, +// generateTempId(detailedBolusInfo.timestamp), detailedBolusInfo.getBolusType(), +// getPumpType(), serialNumber()); +// +// if (response && writeToInternalHistory) { +// driverHistory.put(temporaryId, new PumpDbEntry(temporaryId, model(), serialNumber(), detailedBolusInfo)); +// sp.putString(MedtronicConst.Statistics.InternalTemporaryDatabase, gson.toJson(driverHistory)); +// } +// +// return response; + } + + fun removeTemporaryId(temporaryId: Long) { + driverHistory.remove(temporaryId) + } + + init { + pumpDescription.setPumpDescription(pumpType) + this.pumpType = pumpType + this.dateUtil = dateUtil + this.aapsSchedulers = aapsSchedulers + this.pumpSync = pumpSync + } +} \ No newline at end of file diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/common/data/PumpDbEntry.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/common/data/PumpDbEntry.java deleted file mode 100644 index f67a9741f8..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/common/data/PumpDbEntry.java +++ /dev/null @@ -1,21 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.common.data; - -import info.nightscout.androidaps.data.DetailedBolusInfo; -import info.nightscout.androidaps.plugins.pump.common.defs.PumpType; - -public class PumpDbEntry { - - long temporaryId; - PumpType pumpType; - String serialNumber; - DetailedBolusInfo detailedBolusInfo; - - public PumpDbEntry(long temporaryId, PumpType pumpType, String serialNumber, DetailedBolusInfo detailedBolusInfo) { - this.temporaryId = temporaryId; - this.pumpType = pumpType; - this.serialNumber = serialNumber; - this.detailedBolusInfo = detailedBolusInfo; - } - - -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/common/data/PumpDbEntry.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/common/data/PumpDbEntry.kt new file mode 100644 index 0000000000..6dfe38133a --- /dev/null +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/common/data/PumpDbEntry.kt @@ -0,0 +1,9 @@ +package info.nightscout.androidaps.plugins.pump.common.data + +import info.nightscout.androidaps.data.DetailedBolusInfo +import info.nightscout.androidaps.plugins.pump.common.defs.PumpType + +data class PumpDbEntry(var temporaryId: Long, + var pumpType: PumpType, + var serialNumber: String, + var detailedBolusInfo: DetailedBolusInfo) \ No newline at end of file diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicPumpPlugin.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicPumpPlugin.java deleted file mode 100644 index c86e7c95a3..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicPumpPlugin.java +++ /dev/null @@ -1,1627 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.medtronic; - -import android.content.ComponentName; -import android.content.Context; -import android.content.ServiceConnection; -import android.os.IBinder; -import android.os.SystemClock; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.preference.Preference; - -import org.joda.time.LocalDateTime; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Calendar; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; - -import javax.inject.Inject; -import javax.inject.Singleton; - -import dagger.android.HasAndroidInjector; -import info.nightscout.androidaps.activities.ErrorHelperActivity; -import info.nightscout.androidaps.data.DetailedBolusInfo; -import info.nightscout.androidaps.data.Profile; -import info.nightscout.androidaps.data.PumpEnactResult; -import info.nightscout.androidaps.db.Source; -import info.nightscout.androidaps.db.TemporaryBasal; -import info.nightscout.androidaps.events.EventRefreshOverview; -import info.nightscout.androidaps.interfaces.ActivePlugin; -import info.nightscout.androidaps.interfaces.CommandQueueProvider; -import info.nightscout.androidaps.interfaces.PluginDescription; -import info.nightscout.androidaps.interfaces.PluginType; -import info.nightscout.androidaps.interfaces.Pump; -import info.nightscout.androidaps.interfaces.PumpDescription; -import info.nightscout.androidaps.interfaces.PumpSync; -import info.nightscout.androidaps.logging.AAPSLogger; -import info.nightscout.androidaps.logging.LTag; -import info.nightscout.androidaps.plugins.bus.RxBusWrapper; -import info.nightscout.androidaps.plugins.common.ManufacturerType; -import info.nightscout.androidaps.plugins.general.actions.defs.CustomAction; -import info.nightscout.androidaps.plugins.general.actions.defs.CustomActionType; -import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification; -import info.nightscout.androidaps.plugins.general.overview.notifications.Notification; -import info.nightscout.androidaps.plugins.pump.common.PumpPluginAbstract; -import info.nightscout.androidaps.plugins.pump.common.data.PumpStatus; -import info.nightscout.androidaps.plugins.pump.common.defs.PumpDriverState; -import info.nightscout.androidaps.plugins.pump.common.defs.PumpType; -import info.nightscout.androidaps.plugins.pump.common.events.EventRefreshButtonState; -import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkConst; -import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkPumpDevice; -import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkPumpInfo; -import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkServiceState; -import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.RileyLinkServiceData; -import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.tasks.ResetRileyLinkConfigurationTask; -import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.tasks.ServiceTaskExecutor; -import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.tasks.WakeAndTuneTask; -import info.nightscout.androidaps.plugins.pump.common.utils.DateTimeUtil; -import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.PumpHistoryEntry; -import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.PumpHistoryResult; -import info.nightscout.androidaps.plugins.pump.medtronic.comm.ui.MedtronicUITask; -import info.nightscout.androidaps.plugins.pump.medtronic.data.MedtronicHistoryData; -import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.BasalProfile; -import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.BasalProfileEntry; -import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.ClockDTO; -import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.TempBasalPair; -import info.nightscout.androidaps.plugins.pump.medtronic.defs.BasalProfileStatus; -import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicCommandType; -import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicCustomActionType; -import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicNotificationType; -import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicStatusRefreshType; -import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicUIResponseType; -import info.nightscout.androidaps.plugins.pump.medtronic.driver.MedtronicPumpStatus; -import info.nightscout.androidaps.plugins.pump.medtronic.events.EventMedtronicPumpConfigurationChanged; -import info.nightscout.androidaps.plugins.pump.medtronic.events.EventMedtronicPumpValuesChanged; -import info.nightscout.androidaps.plugins.pump.medtronic.service.RileyLinkMedtronicService; -import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicConst; -import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil; -import info.nightscout.androidaps.utils.DateUtil; -import info.nightscout.androidaps.utils.FabricPrivacy; -import info.nightscout.androidaps.utils.TimeChangeType; -import info.nightscout.androidaps.utils.resources.ResourceHelper; -import info.nightscout.androidaps.utils.rx.AapsSchedulers; -import info.nightscout.androidaps.utils.sharedPreferences.SP; - - -/** - * Created by andy on 23.04.18. - * - * @author Andy Rozman (andy.rozman@gmail.com) - */ -@Singleton -public class MedtronicPumpPlugin extends PumpPluginAbstract implements Pump, RileyLinkPumpDevice { - - private final MedtronicUtil medtronicUtil; - private final MedtronicPumpStatus medtronicPumpStatus; - private final MedtronicHistoryData medtronicHistoryData; - private final RileyLinkServiceData rileyLinkServiceData; - private final ServiceTaskExecutor serviceTaskExecutor; - - private RileyLinkMedtronicService rileyLinkMedtronicService; - - // variables for handling statuses and history - private boolean firstRun = true; - private boolean isRefresh = false; - private final Map statusRefreshMap = new HashMap<>(); - private boolean isInitialized = false; - private PumpHistoryEntry lastPumpHistoryEntry; - - public static boolean isBusy = false; - private final List busyTimestamps = new ArrayList<>(); - private boolean hasTimeDateOrTimeZoneChanged = false; - private boolean usePumpSync = false; - - @Inject - public MedtronicPumpPlugin( - HasAndroidInjector injector, - AAPSLogger aapsLogger, - RxBusWrapper rxBus, - Context context, - ResourceHelper resourceHelper, - ActivePlugin activePlugin, - SP sp, - CommandQueueProvider commandQueue, - FabricPrivacy fabricPrivacy, - MedtronicUtil medtronicUtil, - MedtronicPumpStatus medtronicPumpStatus, - MedtronicHistoryData medtronicHistoryData, - RileyLinkServiceData rileyLinkServiceData, - ServiceTaskExecutor serviceTaskExecutor, - DateUtil dateUtil, - AapsSchedulers aapsSchedulers, - PumpSync pumpSync - ) { - - super(new PluginDescription() // - .mainType(PluginType.PUMP) // - .fragmentClass(MedtronicFragment.class.getName()) // - .pluginIcon(R.drawable.ic_veo_128) - .pluginName(R.string.medtronic_name) // - .shortName(R.string.medtronic_name_short) // - .preferencesId(R.xml.pref_medtronic) - .description(R.string.description_pump_medtronic), // - PumpType.MEDTRONIC_522_722, // we default to most basic model, correct model from config is loaded later - injector, resourceHelper, aapsLogger, commandQueue, rxBus, activePlugin, sp, context, fabricPrivacy, dateUtil, aapsSchedulers, pumpSync - ); - - this.medtronicUtil = medtronicUtil; - this.medtronicPumpStatus = medtronicPumpStatus; - this.medtronicHistoryData = medtronicHistoryData; - this.rileyLinkServiceData = rileyLinkServiceData; - this.serviceTaskExecutor = serviceTaskExecutor; - - displayConnectionMessages = false; - } - - - @Override - protected void onStart() { - aapsLogger.debug(LTag.PUMP, this.deviceID() + " started."); - - serviceConnection = new ServiceConnection() { - - public void onServiceDisconnected(ComponentName name) { - aapsLogger.debug(LTag.PUMP, "RileyLinkMedtronicService is disconnected"); - rileyLinkMedtronicService = null; - } - - public void onServiceConnected(ComponentName name, IBinder service) { - aapsLogger.debug(LTag.PUMP, "RileyLinkMedtronicService is connected"); - RileyLinkMedtronicService.LocalBinder mLocalBinder = (RileyLinkMedtronicService.LocalBinder) service; - rileyLinkMedtronicService = mLocalBinder.getServiceInstance(); - rileyLinkMedtronicService.verifyConfiguration(); - - new Thread(() -> { - - for (int i = 0; i < 20; i++) { - SystemClock.sleep(5000); - - aapsLogger.debug(LTag.PUMP, "Starting Medtronic-RileyLink service"); - if (rileyLinkMedtronicService.setNotInPreInit()) { - break; - } - } - }).start(); - } - }; - - super.onStart(); - } - - @Override - public void updatePreferenceSummary(@NonNull Preference pref) { - super.updatePreferenceSummary(pref); - - if (pref.getKey().equals(getResourceHelper().gs(R.string.key_rileylink_mac_address))) { - String value = sp.getStringOrNull(R.string.key_rileylink_mac_address, null); - pref.setSummary(value == null ? getResourceHelper().gs(R.string.not_set_short) : value); - } - } - - private String getLogPrefix() { - return "MedtronicPumpPlugin::"; - } - -// public PumpDescription getPumpDescription() { -// return super.pumpDescription; -// } - - - @Override - public void initPumpStatusData() { - - medtronicPumpStatus.setLastConnection(sp.getLong(RileyLinkConst.Prefs.LastGoodDeviceCommunicationTime, 0L)); - medtronicPumpStatus.setLastDataTime(medtronicPumpStatus.getLastConnection()); - medtronicPumpStatus.setPreviousConnection( medtronicPumpStatus.getLastConnection()); - - //if (rileyLinkMedtronicService != null) rileyLinkMedtronicService.verifyConfiguration(); - - aapsLogger.debug(LTag.PUMP, "initPumpStatusData: " + this.medtronicPumpStatus); - - // this is only thing that can change, by being configured - pumpDescription.setMaxTempAbsolute((medtronicPumpStatus.getMaxBasal() != null) ? medtronicPumpStatus.getMaxBasal() : 35.0d); - - // set first Medtronic Pump Start - if (!sp.contains(MedtronicConst.Statistics.FirstPumpStart)) { - sp.putLong(MedtronicConst.Statistics.FirstPumpStart, System.currentTimeMillis()); - } - - migrateSettings(); - - } - - @Override - public void triggerPumpConfigurationChangedEvent() { - rxBus.send(new EventMedtronicPumpConfigurationChanged()); - } - - private void migrateSettings() { - - if ("US (916 MHz)".equals(sp.getString(MedtronicConst.Prefs.PumpFrequency, "US (916 MHz)"))) { - sp.putString(MedtronicConst.Prefs.PumpFrequency, getResourceHelper().gs(R.string.key_medtronic_pump_frequency_us_ca)); - } - - String encoding = sp.getString(MedtronicConst.Prefs.Encoding, "RileyLink 4b6b Encoding"); - - if ("RileyLink 4b6b Encoding".equals(encoding)) { - sp.putString(MedtronicConst.Prefs.Encoding, getResourceHelper().gs(R.string.key_medtronic_pump_encoding_4b6b_rileylink)); - } - - if ("Local 4b6b Encoding".equals(encoding)) { - sp.putString(MedtronicConst.Prefs.Encoding, getResourceHelper().gs(R.string.key_medtronic_pump_encoding_4b6b_local)); - } - } - - - public void onStartCustomActions() { - - // check status every minute (if any status needs refresh we send readStatus command) - new Thread(() -> { - - do { - SystemClock.sleep(60000); - - if (this.isInitialized) { - - Map statusRefresh = workWithStatusRefresh( - StatusRefreshAction.GetData, null, null); - - if (doWeHaveAnyStatusNeededRefereshing(statusRefresh)) { - if (!getCommandQueue().statusInQueue()) { - getCommandQueue().readStatus("Scheduled Status Refresh", null); - } - } - - clearBusyQueue(); - } - - } while (serviceRunning); - - }).start(); - } - - - public Class getServiceClass() { - return RileyLinkMedtronicService.class; - } - - @Override public PumpStatus getPumpStatusData() { - return medtronicPumpStatus; - } - - @Override - public String deviceID() { - return "Medtronic"; - } - - - @Override - public boolean isFakingTempsByExtendedBoluses() { - return false; - } - - - @Override - public boolean canHandleDST() { - return false; - } - - - // Pump Plugin - - private boolean isServiceSet() { - return rileyLinkMedtronicService != null; - } - - @Nullable - public RileyLinkMedtronicService getRileyLinkService() { - return rileyLinkMedtronicService; - } - - @Override - public RileyLinkPumpInfo getPumpInfo() { - String frequency = resourceHelper.gs(medtronicPumpStatus.getPumpFrequency().equals("medtronic_pump_frequency_us_ca") ? R.string.medtronic_pump_frequency_us_ca : R.string.medtronic_pump_frequency_worldwide); - String model = medtronicPumpStatus.getMedtronicDeviceType() == null ? "???" : "Medtronic " + medtronicPumpStatus.getMedtronicDeviceType().getPumpModel(); - String serialNumber = medtronicPumpStatus.getSerialNumber(); - return new RileyLinkPumpInfo(frequency, model, serialNumber); - } - - @Override public long getLastConnectionTimeMillis() { - return medtronicPumpStatus.getLastConnection(); - } - - @Override public void setLastCommunicationToNow() { - medtronicPumpStatus.setLastCommunicationToNow(); - } - - @Override - public boolean isInitialized() { - if (displayConnectionMessages) - aapsLogger.debug(LTag.PUMP, "MedtronicPumpPlugin::isInitialized"); - return isServiceSet() && isInitialized; - } - - - @Override - public void setBusy(boolean busy) { - isBusy = busy; - } - - @Override - public boolean isBusy() { - if (displayConnectionMessages) - aapsLogger.debug(LTag.PUMP, "MedtronicPumpPlugin::isBusy"); - - if (isServiceSet()) { - - if (isBusy) - return true; - - if (busyTimestamps.size() > 0) { - - clearBusyQueue(); - - return busyTimestamps.size() > 0; - } - } - - return false; - } - - - private synchronized void clearBusyQueue() { - - if (busyTimestamps.size() == 0) { - return; - } - - Set deleteFromQueue = new HashSet<>(); - - for (Long busyTimestamp : busyTimestamps) { - - if (System.currentTimeMillis() > busyTimestamp) { - deleteFromQueue.add(busyTimestamp); - } - } - - if (deleteFromQueue.size() == busyTimestamps.size()) { - busyTimestamps.clear(); - setEnableCustomAction(MedtronicCustomActionType.ClearBolusBlock, false); - } - - if (deleteFromQueue.size() > 0) { - busyTimestamps.removeAll(deleteFromQueue); - } - - } - - - @Override - public boolean isConnected() { - if (displayConnectionMessages) - aapsLogger.debug(LTag.PUMP, "MedtronicPumpPlugin::isConnected"); - return isServiceSet() && rileyLinkMedtronicService.isInitialized(); - } - - - @Override - public boolean isConnecting() { - if (displayConnectionMessages) - aapsLogger.debug(LTag.PUMP, "MedtronicPumpPlugin::isConnecting"); - return !isServiceSet() || !rileyLinkMedtronicService.isInitialized(); - } - - - @Override - public void getPumpStatus(String reason) { - boolean needRefresh = true; - - if (firstRun) { - needRefresh = initializePump(!isRefresh); - } else { - refreshAnyStatusThatNeedsToBeRefreshed(); - } - - if (needRefresh) - rxBus.send(new EventMedtronicPumpValuesChanged()); - } - - - void resetStatusState() { - firstRun = true; - isRefresh = true; - } - - - private boolean isPumpNotReachable() { - - RileyLinkServiceState rileyLinkServiceState = rileyLinkServiceData.rileyLinkServiceState; - - if (rileyLinkServiceState == null) { - aapsLogger.debug(LTag.PUMP, "RileyLink unreachable. RileyLinkServiceState is null."); - return false; - } - - if (rileyLinkServiceState != RileyLinkServiceState.PumpConnectorReady // - && rileyLinkServiceState != RileyLinkServiceState.RileyLinkReady // - && rileyLinkServiceState != RileyLinkServiceState.TuneUpDevice) { - aapsLogger.debug(LTag.PUMP, "RileyLink unreachable."); - return false; - } - - return (!rileyLinkMedtronicService.getDeviceCommunicationManager().isDeviceReachable()); - } - - - private void refreshAnyStatusThatNeedsToBeRefreshed() { - - Map statusRefresh = workWithStatusRefresh(StatusRefreshAction.GetData, null, - null); - - if (!doWeHaveAnyStatusNeededRefereshing(statusRefresh)) { - return; - } - - boolean resetTime = false; - - if (isPumpNotReachable()) { - aapsLogger.error("Pump unreachable."); - medtronicUtil.sendNotification(MedtronicNotificationType.PumpUnreachable, getResourceHelper(), rxBus); - - return; - } - - medtronicUtil.dismissNotification(MedtronicNotificationType.PumpUnreachable, rxBus); - - - if (hasTimeDateOrTimeZoneChanged) { - - checkTimeAndOptionallySetTime(); - - // read time if changed, set new time - hasTimeDateOrTimeZoneChanged = false; - } - - - // execute - Set refreshTypesNeededToReschedule = new HashSet<>(); - - for (Map.Entry refreshType : statusRefresh.entrySet()) { - - if (refreshType.getValue() > 0 && System.currentTimeMillis() > refreshType.getValue()) { - - switch (refreshType.getKey()) { - case PumpHistory: { - readPumpHistory(); - } - break; - - case PumpTime: { - checkTimeAndOptionallySetTime(); - refreshTypesNeededToReschedule.add(refreshType.getKey()); - resetTime = true; - } - break; - - case BatteryStatus: - case RemainingInsulin: { - rileyLinkMedtronicService.getMedtronicUIComm().executeCommand(refreshType.getKey().getCommandType(medtronicUtil.getMedtronicPumpModel())); - refreshTypesNeededToReschedule.add(refreshType.getKey()); - resetTime = true; - } - break; - - case Configuration: { - rileyLinkMedtronicService.getMedtronicUIComm().executeCommand(refreshType.getKey().getCommandType(medtronicUtil.getMedtronicPumpModel())); - resetTime = true; - } - break; - } - } - - // reschedule - for (MedtronicStatusRefreshType refreshType2 : refreshTypesNeededToReschedule) { - scheduleNextRefresh(refreshType2); - } - - } - - if (resetTime) - medtronicPumpStatus.setLastCommunicationToNow(); - - } - - - private boolean doWeHaveAnyStatusNeededRefereshing(Map statusRefresh) { - - for (Map.Entry refreshType : statusRefresh.entrySet()) { - - if (refreshType.getValue() > 0 && System.currentTimeMillis() > refreshType.getValue()) { - return true; - } - } - - return hasTimeDateOrTimeZoneChanged; - } - - - private void setRefreshButtonEnabled(boolean enabled) { - rxBus.send(new EventRefreshButtonState(enabled)); - } - - - private boolean initializePump(boolean realInit) { - - if (rileyLinkMedtronicService == null) - return false; - - aapsLogger.info(LTag.PUMP, getLogPrefix() + "initializePump - start"); - - rileyLinkMedtronicService.getDeviceCommunicationManager().setDoWakeUpBeforeCommand(false); - - setRefreshButtonEnabled(false); - - if (isRefresh) { - if (isPumpNotReachable()) { - aapsLogger.error(getLogPrefix() + "initializePump::Pump unreachable."); - medtronicUtil.sendNotification(MedtronicNotificationType.PumpUnreachable, getResourceHelper(), rxBus); - - setRefreshButtonEnabled(true); - - return true; - } - - medtronicUtil.dismissNotification(MedtronicNotificationType.PumpUnreachable, rxBus); - } - - // model (once) - if (medtronicUtil.getMedtronicPumpModel() == null) { - rileyLinkMedtronicService.getMedtronicUIComm().executeCommand(MedtronicCommandType.PumpModel); - } else { - if (medtronicPumpStatus.getMedtronicDeviceType() != medtronicUtil.getMedtronicPumpModel()) { - aapsLogger.warn(LTag.PUMP, getLogPrefix() + "Configured pump is not the same as one detected."); - medtronicUtil.sendNotification(MedtronicNotificationType.PumpTypeNotSame, getResourceHelper(), rxBus); - } - } - - this.pumpState = PumpDriverState.Connected; - - // time (1h) - checkTimeAndOptionallySetTime(); - - readPumpHistory(); - - // remaining insulin (>50 = 4h; 50-20 = 1h; 15m) - rileyLinkMedtronicService.getMedtronicUIComm().executeCommand(MedtronicCommandType.GetRemainingInsulin); - scheduleNextRefresh(MedtronicStatusRefreshType.RemainingInsulin, 10); - - // remaining power (1h) - rileyLinkMedtronicService.getMedtronicUIComm().executeCommand(MedtronicCommandType.GetBatteryStatus); - scheduleNextRefresh(MedtronicStatusRefreshType.BatteryStatus, 20); - - // configuration (once and then if history shows config changes) - rileyLinkMedtronicService.getMedtronicUIComm().executeCommand(MedtronicCommandType.getSettings(medtronicUtil.getMedtronicPumpModel())); - - // read profile (once, later its controlled by isThisProfileSet method) - getBasalProfiles(); - - int errorCount = rileyLinkMedtronicService.getMedtronicUIComm().getInvalidResponsesCount(); - - if (errorCount >= 5) { - aapsLogger.error("Number of error counts was 5 or more. Starting tunning."); - setRefreshButtonEnabled(true); - serviceTaskExecutor.startTask(new WakeAndTuneTask(getInjector())); - return true; - } - - medtronicPumpStatus.setLastCommunicationToNow(); - setRefreshButtonEnabled(true); - - if (!isRefresh) { - pumpState = PumpDriverState.Initialized; - } - - isInitialized = true; - // this.pumpState = PumpDriverState.Initialized; - - this.firstRun = false; - - return true; - } - - private void getBasalProfiles() { - - MedtronicUITask medtronicUITask = rileyLinkMedtronicService.getMedtronicUIComm().executeCommand(MedtronicCommandType.GetBasalProfileSTD); - - if (medtronicUITask.getResponseType() == MedtronicUIResponseType.Error) { - rileyLinkMedtronicService.getMedtronicUIComm().executeCommand(MedtronicCommandType.GetBasalProfileSTD); - } - } - - - @Override - public boolean isThisProfileSet(@NonNull Profile profile) { - aapsLogger.debug(LTag.PUMP, "isThisProfileSet: basalInitalized=" + medtronicPumpStatus.getBasalProfileStatus()); - - if (!isInitialized) - return true; - - if (medtronicPumpStatus.getBasalProfileStatus() == BasalProfileStatus.NotInitialized) { - // this shouldn't happen, but if there was problem we try again - getBasalProfiles(); - return isProfileSame(profile); - } else if (medtronicPumpStatus.getBasalProfileStatus() == BasalProfileStatus.ProfileChanged) { - return false; - } - - return (medtronicPumpStatus.getBasalProfileStatus() != BasalProfileStatus.ProfileOK) || isProfileSame(profile); - } - - - private boolean isProfileSame(Profile profile) { - - boolean invalid = false; - Double[] basalsByHour = medtronicPumpStatus.getBasalsByHour(); - - aapsLogger.debug(LTag.PUMP, "Current Basals (h): " - + (basalsByHour == null ? "null" : BasalProfile.getProfilesByHourToString(basalsByHour))); - - // int index = 0; - - if (basalsByHour == null) - return true; // we don't want to set profile again, unless we are sure - - StringBuilder stringBuilder = new StringBuilder("Requested Basals (h): "); - - for (Profile.ProfileValue basalValue : profile.getBasalValues()) { - - double basalValueValue = pumpDescription.getPumpType().determineCorrectBasalSize(basalValue.value); - - int hour = basalValue.timeAsSeconds / (60 * 60); - - if (!MedtronicUtil.isSame(basalsByHour[hour], basalValueValue)) { - invalid = true; - } - - stringBuilder.append(String.format(Locale.ENGLISH, "%.3f", basalValueValue)); - stringBuilder.append(" "); - } - - aapsLogger.debug(LTag.PUMP, stringBuilder.toString()); - - if (!invalid) { - aapsLogger.debug(LTag.PUMP, "Basal profile is same as AAPS one."); - } else { - aapsLogger.debug(LTag.PUMP, "Basal profile on Pump is different than the AAPS one."); - } - - return (!invalid); - } - - - @Override - public long lastDataTime() { - - if (medtronicPumpStatus.getLastConnection() != 0) { - return medtronicPumpStatus.getLastConnection(); - } - - return System.currentTimeMillis(); - } - - - @Override - public double getBaseBasalRate() { - return medtronicPumpStatus.getBasalProfileForHour(); - } - - - @Override - public double getReservoirLevel() { - return medtronicPumpStatus.getReservoirRemainingUnits(); - } - - - @Override - public int getBatteryLevel() { - return medtronicPumpStatus.getBatteryRemaining(); - } - - protected void triggerUIChange() { - rxBus.send(new EventMedtronicPumpValuesChanged()); - } - - @Override public long generateTempId(long timeMillis) { - return 0; - } - -// @Override public String getSerial() { -// return null; -// } - - private BolusDeliveryType bolusDeliveryType = BolusDeliveryType.Idle; - - private enum BolusDeliveryType { - Idle, // - DeliveryPrepared, // - Delivering, // - CancelDelivery - } - - - private void checkTimeAndOptionallySetTime() { - - aapsLogger.info(LTag.PUMP, "MedtronicPumpPlugin::checkTimeAndOptionallySetTime - Start"); - - setRefreshButtonEnabled(false); - - if (isPumpNotReachable()) { - aapsLogger.debug(LTag.PUMP, "MedtronicPumpPlugin::checkTimeAndOptionallySetTime - Pump Unreachable."); - setRefreshButtonEnabled(true); - return; - } - - medtronicUtil.dismissNotification(MedtronicNotificationType.PumpUnreachable, rxBus); - - rileyLinkMedtronicService.getMedtronicUIComm().executeCommand(MedtronicCommandType.GetRealTimeClock); - - ClockDTO clock = medtronicUtil.getPumpTime(); - - if (clock == null) { // retry - rileyLinkMedtronicService.getMedtronicUIComm().executeCommand(MedtronicCommandType.GetRealTimeClock); - - clock = medtronicUtil.getPumpTime(); - } - - if (clock == null) - return; - - int timeDiff = Math.abs(clock.getTimeDifference()); - - if (timeDiff > 20) { - - if ((clock.getLocalDeviceTime().getYear() <= 2015) || (timeDiff <= 24 * 60 * 60)) { - - aapsLogger.info(LTag.PUMP, String.format(Locale.ENGLISH, "MedtronicPumpPlugin::checkTimeAndOptionallySetTime - Time difference is %d s. Set time on pump.", timeDiff)); - - rileyLinkMedtronicService.getMedtronicUIComm().executeCommand(MedtronicCommandType.SetRealTimeClock); - - if (clock.getTimeDifference() == 0) { - Notification notification = new Notification(Notification.INSIGHT_DATE_TIME_UPDATED, getResourceHelper().gs(R.string.pump_time_updated), Notification.INFO, 60); - rxBus.send(new EventNewNotification(notification)); - } - } else { - if ((clock.getLocalDeviceTime().getYear() > 2015)) { - aapsLogger.error(String.format(Locale.ENGLISH, "MedtronicPumpPlugin::checkTimeAndOptionallySetTime - Time difference over 24h requested [diff=%d s]. Doing nothing.", timeDiff)); - medtronicUtil.sendNotification(MedtronicNotificationType.TimeChangeOver24h, getResourceHelper(), rxBus); - } - } - - } else { - aapsLogger.info(LTag.PUMP, String.format(Locale.ENGLISH, "MedtronicPumpPlugin::checkTimeAndOptionallySetTime - Time difference is %d s. Do nothing.", timeDiff)); - } - - scheduleNextRefresh(MedtronicStatusRefreshType.PumpTime, 0); - } - - - @NonNull - protected PumpEnactResult deliverBolus(final DetailedBolusInfo detailedBolusInfo) { - - aapsLogger.info(LTag.PUMP, "MedtronicPumpPlugin::deliverBolus - " + BolusDeliveryType.DeliveryPrepared); - - setRefreshButtonEnabled(false); - - if (detailedBolusInfo.insulin > medtronicPumpStatus.getReservoirRemainingUnits()) { - return new PumpEnactResult(getInjector()) // - .success(false) // - .enacted(false) // - .comment(getResourceHelper().gs(R.string.medtronic_cmd_bolus_could_not_be_delivered_no_insulin, - medtronicPumpStatus.getReservoirRemainingUnits(), - detailedBolusInfo.insulin)); - } - - bolusDeliveryType = BolusDeliveryType.DeliveryPrepared; - - if (isPumpNotReachable()) { - aapsLogger.debug(LTag.PUMP, "MedtronicPumpPlugin::deliverBolus - Pump Unreachable."); - return setNotReachable(true, false); - } - - medtronicUtil.dismissNotification(MedtronicNotificationType.PumpUnreachable, rxBus); - - if (bolusDeliveryType == BolusDeliveryType.CancelDelivery) { - // LOG.debug("MedtronicPumpPlugin::deliverBolus - Delivery Canceled."); - return setNotReachable(true, true); - } - - // LOG.debug("MedtronicPumpPlugin::deliverBolus - Starting wait period."); - - int sleepTime = sp.getInt(MedtronicConst.Prefs.BolusDelay, 10) * 1000; - - SystemClock.sleep(sleepTime); - - if (bolusDeliveryType == BolusDeliveryType.CancelDelivery) { - // LOG.debug("MedtronicPumpPlugin::deliverBolus - Delivery Canceled, before wait period."); - return setNotReachable(true, true); - } - - // LOG.debug("MedtronicPumpPlugin::deliverBolus - End wait period. Start delivery"); - - try { - - bolusDeliveryType = BolusDeliveryType.Delivering; - - // LOG.debug("MedtronicPumpPlugin::deliverBolus - Start delivery"); - - MedtronicUITask responseTask = rileyLinkMedtronicService.getMedtronicUIComm().executeCommand(MedtronicCommandType.SetBolus, - Arrays.asList(detailedBolusInfo.insulin)); - - Boolean response = (Boolean) responseTask.getResult(); - - setRefreshButtonEnabled(true); - - // LOG.debug("MedtronicPumpPlugin::deliverBolus - Response: {}", response); - - if (response) { - - if (bolusDeliveryType == BolusDeliveryType.CancelDelivery) { - // LOG.debug("MedtronicPumpPlugin::deliverBolus - Delivery Canceled after Bolus started."); - - new Thread(() -> { - // Looper.prepare(); - // LOG.debug("MedtronicPumpPlugin::deliverBolus - Show dialog - before"); - SystemClock.sleep(2000); - // LOG.debug("MedtronicPumpPlugin::deliverBolus - Show dialog. Context: " - // + MainApp.instance().getApplicationContext()); - - ErrorHelperActivity.Companion.runAlarm(context, getResourceHelper().gs(R.string.medtronic_cmd_cancel_bolus_not_supported), getResourceHelper().gs(R.string.medtronic_warning), R.raw.boluserror); - }).start(); - } - - long now = System.currentTimeMillis(); - - detailedBolusInfo.timestamp = now; - detailedBolusInfo.deliverAtTheLatest = now; // not sure about that one - - // TODO fix - if (usePumpSync) { - addBolusWithTempId(detailedBolusInfo, true); - } else { - activePlugin.getActiveTreatments().addToHistoryTreatment(detailedBolusInfo, true); - } - - // we subtract insulin, exact amount will be visible with next remainingInsulin update. - medtronicPumpStatus.setReservoirRemainingUnits(medtronicPumpStatus.getReservoirRemainingUnits() - detailedBolusInfo.insulin); - - incrementStatistics(detailedBolusInfo.getBolusType() == DetailedBolusInfo.BolusType.SMB ? MedtronicConst.Statistics.SMBBoluses - : MedtronicConst.Statistics.StandardBoluses); - - - // calculate time for bolus and set driver to busy for that time - int bolusTime = (int) (detailedBolusInfo.insulin * 42.0d); - long time = now + (bolusTime * 1000); - - this.busyTimestamps.add(time); - setEnableCustomAction(MedtronicCustomActionType.ClearBolusBlock, true); - - return new PumpEnactResult(getInjector()).success(true) // - .enacted(true) // - .bolusDelivered(detailedBolusInfo.insulin) // - .carbsDelivered(detailedBolusInfo.carbs); - - } else { - return new PumpEnactResult(getInjector()) // - .success(bolusDeliveryType == BolusDeliveryType.CancelDelivery) // - .enacted(false) // - .comment(R.string.medtronic_cmd_bolus_could_not_be_delivered); - } - - } finally { - finishAction("Bolus"); - this.bolusDeliveryType = BolusDeliveryType.Idle; - } - } - - - private PumpEnactResult setNotReachable(boolean isBolus, boolean success) { - setRefreshButtonEnabled(true); - - if (isBolus) { - bolusDeliveryType = BolusDeliveryType.Idle; - } - - if (success) { - return new PumpEnactResult(getInjector()) // - .success(true) // - .enacted(false); - } else { - return new PumpEnactResult(getInjector()) // - .success(false) // - .enacted(false) // - .comment(R.string.medtronic_pump_status_pump_unreachable); - } - } - - - public void stopBolusDelivering() { - - this.bolusDeliveryType = BolusDeliveryType.CancelDelivery; - - // if (isLoggingEnabled()) - // LOG.warn("MedtronicPumpPlugin::deliverBolus - Stop Bolus Delivery."); - } - - - private void incrementStatistics(String statsKey) { - long currentCount = sp.getLong(statsKey, 0L); - currentCount++; - sp.putLong(statsKey, currentCount); - } - - - // if enforceNew===true current temp basal is canceled and new TBR set (duration is prolonged), - // if false and the same rate is requested enacted=false and success=true is returned and TBR is not changed - @NonNull @Override - public PumpEnactResult setTempBasalAbsolute(double absoluteRate, int durationInMinutes, Profile profile, boolean enforceNew, @NonNull PumpSync.TemporaryBasalType tbrType) { - - setRefreshButtonEnabled(false); - - if (isPumpNotReachable()) { - - setRefreshButtonEnabled(true); - - return new PumpEnactResult(getInjector()) // - .success(false) // - .enacted(false) // - .comment(R.string.medtronic_pump_status_pump_unreachable); - } - - medtronicUtil.dismissNotification(MedtronicNotificationType.PumpUnreachable, rxBus); - - aapsLogger.info(LTag.PUMP, getLogPrefix() + "setTempBasalAbsolute: rate: " + absoluteRate + ", duration=" + durationInMinutes); - - // read current TBR - TempBasalPair tbrCurrent = readTBR(); - - if (tbrCurrent == null) { - aapsLogger.warn(LTag.PUMP, getLogPrefix() + "setTempBasalAbsolute - Could not read current TBR, canceling operation."); - finishAction("TBR"); - return new PumpEnactResult(getInjector()).success(false).enacted(false) - .comment(R.string.medtronic_cmd_cant_read_tbr); - } else { - aapsLogger.info(LTag.PUMP, getLogPrefix() + "setTempBasalAbsolute: Current Basal: duration: " + tbrCurrent.getDurationMinutes() + " min, rate=" + tbrCurrent.getInsulinRate()); - } - - if (!enforceNew) { - - if (MedtronicUtil.isSame(tbrCurrent.getInsulinRate(), absoluteRate)) { - - boolean sameRate = true; - if (MedtronicUtil.isSame(0.0d, absoluteRate) && durationInMinutes > 0) { - // if rate is 0.0 and duration>0 then the rate is not the same - sameRate = false; - } - - if (sameRate) { - aapsLogger.info(LTag.PUMP, getLogPrefix() + "setTempBasalAbsolute - No enforceNew and same rate. Exiting."); - finishAction("TBR"); - return new PumpEnactResult(getInjector()).success(true).enacted(false); - } - } - // if not the same rate, we cancel and start new - } - - // if TBR is running we will cancel it. - if (tbrCurrent.getInsulinRate() != 0.0f && tbrCurrent.getDurationMinutes() > 0) { - aapsLogger.info(LTag.PUMP, getLogPrefix() + "setTempBasalAbsolute - TBR running - so canceling it."); - - // CANCEL - - MedtronicUITask responseTask2 = rileyLinkMedtronicService.getMedtronicUIComm().executeCommand(MedtronicCommandType.CancelTBR); - - Boolean response = (Boolean) responseTask2.getResult(); - - if (response) { - aapsLogger.info(LTag.PUMP, getLogPrefix() + "setTempBasalAbsolute - Current TBR cancelled."); - } else { - aapsLogger.error(getLogPrefix() + "setTempBasalAbsolute - Cancel TBR failed."); - - finishAction("TBR"); - - return new PumpEnactResult(getInjector()).success(false).enacted(false) - .comment(R.string.medtronic_cmd_cant_cancel_tbr_stop_op); - } - } - - // now start new TBR - MedtronicUITask responseTask = rileyLinkMedtronicService.getMedtronicUIComm().executeCommand(MedtronicCommandType.SetTemporaryBasal, - Arrays.asList(absoluteRate, durationInMinutes)); - - Boolean response = (Boolean) responseTask.getResult(); - - aapsLogger.info(LTag.PUMP, getLogPrefix() + "setTempBasalAbsolute - setTBR. Response: " + response); - - if (response) { - // FIXME put this into UIPostProcessor - medtronicPumpStatus.setTempBasalStart(new Date()); - medtronicPumpStatus.setTempBasalAmount(absoluteRate); - medtronicPumpStatus.setTempBasalLength(durationInMinutes); - - TemporaryBasal tempStart = new TemporaryBasal(getInjector()) // - .date(System.currentTimeMillis()) // - .duration(durationInMinutes) // - .absolute(absoluteRate) // - .source(Source.USER); - - // TODO fix - if (usePumpSync) { - addTemporaryBasalRateWithTempId(tempStart, true); - } else { - activePlugin.getActiveTreatments().addToHistoryTempBasal(tempStart); - } - - - incrementStatistics(MedtronicConst.Statistics.TBRsSet); - - finishAction("TBR"); - - return new PumpEnactResult(getInjector()).success(true).enacted(true) // - .absolute(absoluteRate).duration(durationInMinutes); - - } else { - finishAction("TBR"); - - return new PumpEnactResult(getInjector()).success(false).enacted(false) // - .comment(R.string.medtronic_cmd_tbr_could_not_be_delivered); - } - - } - - - @NonNull @Override - public PumpEnactResult setTempBasalPercent(int percent, int durationInMinutes, @NonNull Profile profile, boolean enforceNew, @NonNull PumpSync.TemporaryBasalType tbrType) { - if (percent == 0) { - return setTempBasalAbsolute(0.0d, durationInMinutes, profile, enforceNew, tbrType); - } else { - double absoluteValue = profile.getBasal() * (percent / 100.0d); - absoluteValue = pumpDescription.getPumpType().determineCorrectBasalSize(absoluteValue); - aapsLogger.warn(LTag.PUMP, "setTempBasalPercent [MedtronicPumpPlugin] - You are trying to use setTempBasalPercent with percent other then 0% (" + percent + "). This will start setTempBasalAbsolute, with calculated value (" + absoluteValue + "). Result might not be 100% correct."); - return setTempBasalAbsolute(absoluteValue, durationInMinutes, profile, enforceNew, tbrType); - } - } - - - private void finishAction(String overviewKey) { - - if (overviewKey != null) - rxBus.send(new EventRefreshOverview(overviewKey, false)); - - triggerUIChange(); - - setRefreshButtonEnabled(true); - } - - - private void readPumpHistory() { - -// if (isLoggingEnabled()) -// LOG.error(getLogPrefix() + "readPumpHistory WIP."); - - readPumpHistoryLogic(); - - scheduleNextRefresh(MedtronicStatusRefreshType.PumpHistory); - - if (medtronicHistoryData.hasRelevantConfigurationChanged()) { - scheduleNextRefresh(MedtronicStatusRefreshType.Configuration, -1); - } - - if (medtronicHistoryData.hasPumpTimeChanged()) { - scheduleNextRefresh(MedtronicStatusRefreshType.PumpTime, -1); - } - - if (this.medtronicPumpStatus.getBasalProfileStatus() != BasalProfileStatus.NotInitialized - && medtronicHistoryData.hasBasalProfileChanged()) { - medtronicHistoryData.processLastBasalProfileChange(pumpDescription.getPumpType(), medtronicPumpStatus); - } - - PumpDriverState previousState = this.pumpState; - - if (medtronicHistoryData.isPumpSuspended()) { - this.pumpState = PumpDriverState.Suspended; - aapsLogger.debug(LTag.PUMP, getLogPrefix() + "isPumpSuspended: true"); - } else { - if (previousState == PumpDriverState.Suspended) { - this.pumpState = PumpDriverState.Ready; - } - aapsLogger.debug(LTag.PUMP, getLogPrefix() + "isPumpSuspended: false"); - } - - medtronicHistoryData.processNewHistoryData(); - - this.medtronicHistoryData.finalizeNewHistoryRecords(); - // this.medtronicHistoryData.setLastHistoryRecordTime(this.lastPumpHistoryEntry.atechDateTime); - - } - - - private void readPumpHistoryLogic() { - - boolean debugHistory = false; - - LocalDateTime targetDate = null; - - if (lastPumpHistoryEntry == null) { - - if (debugHistory) - aapsLogger.debug(LTag.PUMP, getLogPrefix() + "readPumpHistoryLogic(): lastPumpHistoryEntry: null"); - - Long lastPumpHistoryEntryTime = getLastPumpEntryTime(); - - LocalDateTime timeMinus36h = new LocalDateTime(); - timeMinus36h = timeMinus36h.minusHours(36); - medtronicHistoryData.setIsInInit(true); - - if (lastPumpHistoryEntryTime == 0L) { - if (debugHistory) - aapsLogger.debug(LTag.PUMP, getLogPrefix() + "readPumpHistoryLogic(): lastPumpHistoryEntryTime: 0L - targetDate: " - + targetDate); - targetDate = timeMinus36h; - } else { - // LocalDateTime lastHistoryRecordTime = DateTimeUtil.toLocalDateTime(lastPumpHistoryEntryTime); - - if (debugHistory) - aapsLogger.debug(LTag.PUMP, getLogPrefix() + "readPumpHistoryLogic(): lastPumpHistoryEntryTime: " + lastPumpHistoryEntryTime + " - targetDate: " + targetDate); - - medtronicHistoryData.setLastHistoryRecordTime(lastPumpHistoryEntryTime); - - LocalDateTime lastHistoryRecordTime = DateTimeUtil.toLocalDateTime(lastPumpHistoryEntryTime); - - lastHistoryRecordTime = lastHistoryRecordTime.minusHours(12); // we get last 12 hours of history to - // determine pump state - // (we don't process that data), we process only - - if (timeMinus36h.isAfter(lastHistoryRecordTime)) { - targetDate = timeMinus36h; - } - - targetDate = (timeMinus36h.isAfter(lastHistoryRecordTime) ? timeMinus36h : lastHistoryRecordTime); - - if (debugHistory) - aapsLogger.debug(LTag.PUMP, getLogPrefix() + "readPumpHistoryLogic(): targetDate: " + targetDate); - } - } else { - if (debugHistory) - aapsLogger.debug(LTag.PUMP, getLogPrefix() + "readPumpHistoryLogic(): lastPumpHistoryEntry: not null - " + medtronicUtil.getGsonInstance().toJson(lastPumpHistoryEntry)); - medtronicHistoryData.setIsInInit(false); - // medtronicHistoryData.setLastHistoryRecordTime(lastPumpHistoryEntry.atechDateTime); - - // targetDate = lastPumpHistoryEntry.atechDateTime; - } - - //aapsLogger.debug(LTag.PUMP, "HST: Target Date: " + targetDate); - - MedtronicUITask responseTask2 = rileyLinkMedtronicService.getMedtronicUIComm().executeCommand(MedtronicCommandType.GetHistoryData, - Arrays.asList(lastPumpHistoryEntry, targetDate)); - - if (debugHistory) - aapsLogger.debug(LTag.PUMP, "HST: After task"); - - PumpHistoryResult historyResult = (PumpHistoryResult) responseTask2.getResult(); - - if (debugHistory) - aapsLogger.debug(LTag.PUMP, "HST: History Result: " + historyResult.toString()); - - PumpHistoryEntry latestEntry = historyResult.getLatestEntry(); - - if (debugHistory) - aapsLogger.debug(LTag.PUMP, getLogPrefix() + "Last entry: " + latestEntry); - - if (latestEntry == null) // no new history to read - return; - - this.lastPumpHistoryEntry = latestEntry; - sp.putLong(MedtronicConst.Statistics.LastPumpHistoryEntry, latestEntry.getAtechDateTime()); - - if (debugHistory) - aapsLogger.debug(LTag.PUMP, "HST: History: valid=" + historyResult.getValidEntries().size() + ", unprocessed=" + historyResult.getUnprocessedEntries().size()); - - this.medtronicHistoryData.addNewHistory(historyResult); - this.medtronicHistoryData.filterNewEntries(); - - // determine if first run, if yes detrmine how much of update do we need - // first run: - // get last hiostory entry, if not there download 1.5 days of data - // - there: check if last entry is older than 1.5 days - // - yes: download 1.5 days - // - no: download with last entry - // - not there: download 1.5 days - // - // upload all new entries to NightScout (TBR, Bolus) - // determine pump status - // - // save last entry - // - // not first run: - // update to last entry - // - save - // - determine pump status - } - - - private Long getLastPumpEntryTime() { - Long lastPumpEntryTime = sp.getLong(MedtronicConst.Statistics.LastPumpHistoryEntry, 0L); - - try { - LocalDateTime localDateTime = DateTimeUtil.toLocalDateTime(lastPumpEntryTime); - - if (localDateTime.getYear() != (new GregorianCalendar().get(Calendar.YEAR))) { - aapsLogger.warn(LTag.PUMP, "Saved LastPumpHistoryEntry was invalid. Year was not the same."); - return 0L; - } - - return lastPumpEntryTime; - - } catch (Exception ex) { - aapsLogger.warn(LTag.PUMP, "Saved LastPumpHistoryEntry was invalid."); - return 0L; - } - - } - - - private void scheduleNextRefresh(MedtronicStatusRefreshType refreshType) { - scheduleNextRefresh(refreshType, 0); - } - - - private void scheduleNextRefresh(MedtronicStatusRefreshType refreshType, int additionalTimeInMinutes) { - switch (refreshType) { - - case RemainingInsulin: { - double remaining = medtronicPumpStatus.getReservoirRemainingUnits(); - int min; - if (remaining > 50) - min = 4 * 60; - else if (remaining > 20) - min = 60; - else - min = 15; - - workWithStatusRefresh(StatusRefreshAction.Add, refreshType, getTimeInFutureFromMinutes(min)); - } - break; - - case PumpTime: - case Configuration: - case BatteryStatus: - case PumpHistory: { - workWithStatusRefresh(StatusRefreshAction.Add, refreshType, - getTimeInFutureFromMinutes(refreshType.getRefreshTime() + additionalTimeInMinutes)); - } - break; - } - } - - private enum StatusRefreshAction { - Add, // - GetData - } - - - private synchronized Map workWithStatusRefresh(StatusRefreshAction action, // - MedtronicStatusRefreshType statusRefreshType, // - Long time) { - - switch (action) { - - case Add: { - statusRefreshMap.put(statusRefreshType, time); - return null; - } - - case GetData: { - return new HashMap<>(statusRefreshMap); - } - - default: - return null; - - } - - } - - - private long getTimeInFutureFromMinutes(int minutes) { - return System.currentTimeMillis() + getTimeInMs(minutes); - } - - - private long getTimeInMs(int minutes) { - return minutes * 60 * 1000L; - } - - - private TempBasalPair readTBR() { - MedtronicUITask responseTask = rileyLinkMedtronicService.getMedtronicUIComm().executeCommand(MedtronicCommandType.ReadTemporaryBasal); - - if (responseTask.hasData()) { - TempBasalPair tbr = (TempBasalPair) responseTask.getResult(); - - // we sometimes get rate returned even if TBR is no longer running - if (tbr.getDurationMinutes() == 0) { - tbr.setInsulinRate(0.0d); - } - - return tbr; - } else { - return null; - } - } - - - @NonNull @Override - public PumpEnactResult cancelTempBasal(boolean enforceNew) { - - aapsLogger.info(LTag.PUMP, getLogPrefix() + "cancelTempBasal - started"); - - if (isPumpNotReachable()) { - - setRefreshButtonEnabled(true); - - return new PumpEnactResult(getInjector()) // - .success(false) // - .enacted(false) // - .comment(R.string.medtronic_pump_status_pump_unreachable); - } - - medtronicUtil.dismissNotification(MedtronicNotificationType.PumpUnreachable, rxBus); - setRefreshButtonEnabled(false); - - TempBasalPair tbrCurrent = readTBR(); - - if (tbrCurrent != null) { - if (tbrCurrent.getInsulinRate() == 0.0f && tbrCurrent.getDurationMinutes() == 0) { - aapsLogger.info(LTag.PUMP, getLogPrefix() + "cancelTempBasal - TBR already canceled."); - finishAction("TBR"); - return new PumpEnactResult(getInjector()).success(true).enacted(false); - } - } else { - aapsLogger.warn(LTag.PUMP, getLogPrefix() + "cancelTempBasal - Could not read currect TBR, canceling operation."); - finishAction("TBR"); - return new PumpEnactResult(getInjector()).success(false).enacted(false) - .comment(R.string.medtronic_cmd_cant_read_tbr); - } - - MedtronicUITask responseTask2 = rileyLinkMedtronicService.getMedtronicUIComm().executeCommand(MedtronicCommandType.CancelTBR); - - Boolean response = (Boolean) responseTask2.getResult(); - - finishAction("TBR"); - - if (response) { - aapsLogger.info(LTag.PUMP, getLogPrefix() + "cancelTempBasal - Cancel TBR successful."); - - TemporaryBasal tempBasal = new TemporaryBasal(getInjector()) // - .date(System.currentTimeMillis()) // - .duration(0) // - .source(Source.USER); - - // TODO fix - activePlugin.getActiveTreatments().addToHistoryTempBasal(tempBasal); - - return new PumpEnactResult(getInjector()).success(true).enacted(true) // - .isTempCancel(true); - } else { - aapsLogger.info(LTag.PUMP, getLogPrefix() + "cancelTempBasal - Cancel TBR failed."); - - return new PumpEnactResult(getInjector()).success(response).enacted(response) // - .comment(R.string.medtronic_cmd_cant_cancel_tbr); - } - } - - @NonNull @Override - public ManufacturerType manufacturer() { - return pumpDescription.getPumpType().getManufacturer(); - } - - @NonNull @Override - public PumpType model() { - return pumpDescription.getPumpType(); - } - - @NonNull @Override - public String serialNumber() { - return medtronicPumpStatus.getSerialNumber(); - } - - @NonNull @Override - public PumpEnactResult setNewBasalProfile(@NonNull Profile profile) { - aapsLogger.info(LTag.PUMP, getLogPrefix() + "setNewBasalProfile"); - - // this shouldn't be needed, but let's do check if profile setting we are setting is same as current one - if (isProfileSame(profile)) { - return new PumpEnactResult(getInjector()) // - .success(true) // - .enacted(false) // - .comment(R.string.medtronic_cmd_basal_profile_not_set_is_same); - } - - setRefreshButtonEnabled(false); - - if (isPumpNotReachable()) { - - setRefreshButtonEnabled(true); - - return new PumpEnactResult(getInjector()) // - .success(false) // - .enacted(false) // - .comment(R.string.medtronic_pump_status_pump_unreachable); - } - - medtronicUtil.dismissNotification(MedtronicNotificationType.PumpUnreachable, rxBus); - - BasalProfile basalProfile = convertProfileToMedtronicProfile(profile); - - aapsLogger.debug("Basal Profile: " + basalProfile); - - String profileInvalid = isProfileValid(basalProfile); - - if (profileInvalid != null) { - return new PumpEnactResult(getInjector()) // - .success(false) // - .enacted(false) // - .comment(getResourceHelper().gs(R.string.medtronic_cmd_set_profile_pattern_overflow, profileInvalid)); - } - - MedtronicUITask responseTask = rileyLinkMedtronicService.getMedtronicUIComm().executeCommand(MedtronicCommandType.SetBasalProfileSTD, - Arrays.asList(basalProfile)); - - Boolean response = (Boolean) responseTask.getResult(); - - aapsLogger.info(LTag.PUMP, getLogPrefix() + "Basal Profile was set: " + response); - - if (response) { - return new PumpEnactResult(getInjector()).success(true).enacted(true); - } else { - return new PumpEnactResult(getInjector()).success(response).enacted(response) // - .comment(R.string.medtronic_cmd_basal_profile_could_not_be_set); - } - } - - - private String isProfileValid(BasalProfile basalProfile) { - - StringBuilder stringBuilder = new StringBuilder(); - - if (medtronicPumpStatus.getMaxBasal() == null) - return null; - - for (BasalProfileEntry profileEntry : basalProfile.getEntries()) { - - if (profileEntry.getRate() > medtronicPumpStatus.getMaxBasal()) { - stringBuilder.append(profileEntry.getStartTime().toString("HH:mm")); - stringBuilder.append("="); - stringBuilder.append(profileEntry.getRate()); - } - } - - return stringBuilder.length() == 0 ? null : stringBuilder.toString(); - } - - - @NonNull - private BasalProfile convertProfileToMedtronicProfile(Profile profile) { - - BasalProfile basalProfile = new BasalProfile(aapsLogger); - - for (int i = 0; i < 24; i++) { - double rate = profile.getBasalTimeFromMidnight(i * 60 * 60); - - double v = pumpDescription.getPumpType().determineCorrectBasalSize(rate); - - BasalProfileEntry basalEntry = new BasalProfileEntry(v, i, 0); - basalProfile.addEntry(basalEntry); - - } - - basalProfile.generateRawDataFromEntries(); - - return basalProfile; - } - - // OPERATIONS not supported by Pump or Plugin - - private List customActions = null; - - private final CustomAction customActionWakeUpAndTune = new CustomAction(R.string.medtronic_custom_action_wake_and_tune, - MedtronicCustomActionType.WakeUpAndTune); - - private final CustomAction customActionClearBolusBlock = new CustomAction( - R.string.medtronic_custom_action_clear_bolus_block, MedtronicCustomActionType.ClearBolusBlock, false); - - private final CustomAction customActionResetRLConfig = new CustomAction( - R.string.medtronic_custom_action_reset_rileylink, MedtronicCustomActionType.ResetRileyLinkConfiguration, true); - - - @Override - public List getCustomActions() { - - if (customActions == null) { - this.customActions = Arrays.asList(customActionWakeUpAndTune, // - customActionClearBolusBlock, // - customActionResetRLConfig); - } - - return this.customActions; - } - - - @Override - public void executeCustomAction(@NonNull CustomActionType customActionType) { - - MedtronicCustomActionType mcat = (MedtronicCustomActionType) customActionType; - - switch (mcat) { - - case WakeUpAndTune: { - if (rileyLinkMedtronicService.verifyConfiguration()) { - serviceTaskExecutor.startTask(new WakeAndTuneTask(getInjector())); - } else { - ErrorHelperActivity.Companion.runAlarm(context, getResourceHelper().gs(R.string.medtronic_error_operation_not_possible_no_configuration), getResourceHelper().gs(R.string.medtronic_warning), R.raw.boluserror); - } - } - break; - - case ClearBolusBlock: { - this.busyTimestamps.clear(); - this.customActionClearBolusBlock.setEnabled(false); - refreshCustomActionsList(); - } - break; - - case ResetRileyLinkConfiguration: { - serviceTaskExecutor.startTask(new ResetRileyLinkConfigurationTask(getInjector())); - } - break; - - default: - break; - } - - } - - @Override - public void timezoneOrDSTChanged(@NonNull TimeChangeType changeType) { - - aapsLogger.warn(LTag.PUMP, getLogPrefix() + "Time or TimeZone changed. "); - - this.hasTimeDateOrTimeZoneChanged = true; - } - - @Override - public boolean setNeutralTempAtFullHour() { - return sp.getBoolean(R.string.key_set_neutral_temps, true); - } - - - @SuppressWarnings("SameParameterValue") private void setEnableCustomAction(MedtronicCustomActionType customAction, boolean isEnabled) { - - if (customAction == MedtronicCustomActionType.ClearBolusBlock) { - this.customActionClearBolusBlock.setEnabled(isEnabled); - } else if (customAction == MedtronicCustomActionType.ResetRileyLinkConfiguration) { - this.customActionResetRLConfig.setEnabled(isEnabled); - } - - refreshCustomActionsList(); - } -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicPumpPlugin.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicPumpPlugin.kt new file mode 100644 index 0000000000..803f281b45 --- /dev/null +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicPumpPlugin.kt @@ -0,0 +1,1179 @@ +package info.nightscout.androidaps.plugins.pump.medtronic + +import android.content.ComponentName +import android.content.Context +import android.content.ServiceConnection +import android.os.IBinder +import android.os.SystemClock +import androidx.preference.Preference +import dagger.android.HasAndroidInjector +import info.nightscout.androidaps.activities.ErrorHelperActivity.Companion.runAlarm +import info.nightscout.androidaps.data.DetailedBolusInfo +import info.nightscout.androidaps.data.Profile +import info.nightscout.androidaps.data.PumpEnactResult +import info.nightscout.androidaps.db.Source +import info.nightscout.androidaps.db.TemporaryBasal +import info.nightscout.androidaps.events.EventRefreshOverview +import info.nightscout.androidaps.interfaces.* +import info.nightscout.androidaps.interfaces.PumpSync.TemporaryBasalType +import info.nightscout.androidaps.logging.AAPSLogger +import info.nightscout.androidaps.logging.LTag +import info.nightscout.androidaps.plugins.bus.RxBusWrapper +import info.nightscout.androidaps.plugins.common.ManufacturerType +import info.nightscout.androidaps.plugins.general.actions.defs.CustomAction +import info.nightscout.androidaps.plugins.general.actions.defs.CustomActionType +import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification +import info.nightscout.androidaps.plugins.general.overview.notifications.Notification +import info.nightscout.androidaps.plugins.pump.common.PumpPluginAbstract +import info.nightscout.androidaps.plugins.pump.common.data.PumpStatus +import info.nightscout.androidaps.plugins.pump.common.defs.PumpDriverState +import info.nightscout.androidaps.plugins.pump.common.defs.PumpType +import info.nightscout.androidaps.plugins.pump.common.events.EventRefreshButtonState +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkConst +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkPumpDevice +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkPumpInfo +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkServiceState +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.RileyLinkServiceData +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.tasks.ResetRileyLinkConfigurationTask +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.tasks.ServiceTaskExecutor +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.tasks.WakeAndTuneTask +import info.nightscout.androidaps.plugins.pump.common.utils.DateTimeUtil +import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.PumpHistoryEntry +import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.PumpHistoryResult +import info.nightscout.androidaps.plugins.pump.medtronic.data.MedtronicHistoryData +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.BasalProfile +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.BasalProfile.Companion.getProfilesByHourToString +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.BasalProfileEntry +import info.nightscout.androidaps.plugins.pump.medtronic.data.dto.TempBasalPair +import info.nightscout.androidaps.plugins.pump.medtronic.defs.* +import info.nightscout.androidaps.plugins.pump.medtronic.defs.MedtronicCommandType.Companion.getSettings +import info.nightscout.androidaps.plugins.pump.medtronic.driver.MedtronicPumpStatus +import info.nightscout.androidaps.plugins.pump.medtronic.events.EventMedtronicPumpConfigurationChanged +import info.nightscout.androidaps.plugins.pump.medtronic.events.EventMedtronicPumpValuesChanged +import info.nightscout.androidaps.plugins.pump.medtronic.service.RileyLinkMedtronicService +import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicConst +import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil +import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil.Companion.isSame +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.FabricPrivacy +import info.nightscout.androidaps.utils.TimeChangeType +import info.nightscout.androidaps.utils.resources.ResourceHelper +import info.nightscout.androidaps.utils.rx.AapsSchedulers +import info.nightscout.androidaps.utils.sharedPreferences.SP +import org.joda.time.LocalDateTime +import java.util.* +import javax.inject.Inject +import javax.inject.Singleton + +/** + * Created by andy on 23.04.18. + * + * @author Andy Rozman (andy.rozman@gmail.com) + */ +@Singleton +class MedtronicPumpPlugin @Inject constructor( + injector: HasAndroidInjector, + aapsLogger: AAPSLogger, + rxBus: RxBusWrapper, + context: Context, + resourceHelper: ResourceHelper, + activePlugin: ActivePlugin, + sp: SP, + commandQueue: CommandQueueProvider, + fabricPrivacy: FabricPrivacy, + private val medtronicUtil: MedtronicUtil, + private val medtronicPumpStatus: MedtronicPumpStatus, + private val medtronicHistoryData: MedtronicHistoryData, + private val rileyLinkServiceData: RileyLinkServiceData, + private val serviceTaskExecutor: ServiceTaskExecutor, + dateUtil: DateUtil, + aapsSchedulers: AapsSchedulers, + pumpSync: PumpSync +) : PumpPluginAbstract(PluginDescription() // + .mainType(PluginType.PUMP) // + .fragmentClass(MedtronicFragment::class.java.name) // + .pluginIcon(R.drawable.ic_veo_128) + .pluginName(R.string.medtronic_name) // + .shortName(R.string.medtronic_name_short) // + .preferencesId(R.xml.pref_medtronic) + .description(R.string.description_pump_medtronic), // + PumpType.MEDTRONIC_522_722, // we default to most basic model, correct model from config is loaded later + injector, resourceHelper, aapsLogger, commandQueue, rxBus, activePlugin, sp, context, fabricPrivacy, dateUtil, aapsSchedulers!!, pumpSync!! +), Pump, RileyLinkPumpDevice { + + private var rileyLinkMedtronicService: RileyLinkMedtronicService? = null + + // variables for handling statuses and history + private var firstRun = true + private var isRefresh = false + private val statusRefreshMap: MutableMap = HashMap() + private var isInitialized = false + private var lastPumpHistoryEntry: PumpHistoryEntry? = null + private val busyTimestamps: MutableList = ArrayList() + private var hasTimeDateOrTimeZoneChanged = false + private val usePumpSync = false + + override fun onStart() { + aapsLogger.debug(LTag.PUMP, deviceID() + " started.") + serviceConnection = object : ServiceConnection { + override fun onServiceDisconnected(name: ComponentName) { + aapsLogger.debug(LTag.PUMP, "RileyLinkMedtronicService is disconnected") + rileyLinkMedtronicService = null + } + + override fun onServiceConnected(name: ComponentName, service: IBinder) { + aapsLogger.debug(LTag.PUMP, "RileyLinkMedtronicService is connected") + val mLocalBinder = service as RileyLinkMedtronicService.LocalBinder + rileyLinkMedtronicService = mLocalBinder.serviceInstance + rileyLinkMedtronicService!!.verifyConfiguration() + Thread(Runnable { + for (i in 0..19) { + SystemClock.sleep(5000) + aapsLogger.debug(LTag.PUMP, "Starting Medtronic-RileyLink service") + if (rileyLinkMedtronicService!!.setNotInPreInit()) { + break + } + } + }).start() + } + } + super.onStart() + } + + override fun updatePreferenceSummary(pref: Preference) { + super.updatePreferenceSummary(pref) + if (pref.key == resourceHelper.gs(R.string.key_rileylink_mac_address)) { + val value = sp.getStringOrNull(R.string.key_rileylink_mac_address, null) + pref.summary = value ?: resourceHelper.gs(R.string.not_set_short) + } + } + + private val logPrefix: String + private get() = "MedtronicPumpPlugin::" + + override fun initPumpStatusData() { + medtronicPumpStatus.lastConnection = sp.getLong(RileyLinkConst.Prefs.LastGoodDeviceCommunicationTime, 0L) + medtronicPumpStatus.lastDataTime = medtronicPumpStatus.lastConnection + medtronicPumpStatus.previousConnection = medtronicPumpStatus.lastConnection + + //if (rileyLinkMedtronicService != null) rileyLinkMedtronicService.verifyConfiguration(); + aapsLogger.debug(LTag.PUMP, "initPumpStatusData: " + medtronicPumpStatus) + + // this is only thing that can change, by being configured + pumpDescription.maxTempAbsolute = if (medtronicPumpStatus.maxBasal != null) medtronicPumpStatus.maxBasal!! else 35.0 + + // set first Medtronic Pump Start + if (!sp.contains(MedtronicConst.Statistics.FirstPumpStart)) { + sp.putLong(MedtronicConst.Statistics.FirstPumpStart, System.currentTimeMillis()) + } + migrateSettings() + } + + override fun triggerPumpConfigurationChangedEvent() { + rxBus.send(EventMedtronicPumpConfigurationChanged()) + } + + private fun migrateSettings() { + if ("US (916 MHz)" == sp.getString(MedtronicConst.Prefs.PumpFrequency, "US (916 MHz)")) { + sp.putString(MedtronicConst.Prefs.PumpFrequency, resourceHelper.gs(R.string.key_medtronic_pump_frequency_us_ca)) + } + val encoding = sp.getString(MedtronicConst.Prefs.Encoding, "RileyLink 4b6b Encoding") + if ("RileyLink 4b6b Encoding" == encoding) { + sp.putString(MedtronicConst.Prefs.Encoding, resourceHelper.gs(R.string.key_medtronic_pump_encoding_4b6b_rileylink)) + } + if ("Local 4b6b Encoding" == encoding) { + sp.putString(MedtronicConst.Prefs.Encoding, resourceHelper.gs(R.string.key_medtronic_pump_encoding_4b6b_local)) + } + } + + override fun onStartCustomActions() { + + // check status every minute (if any status needs refresh we send readStatus command) + Thread(Runnable { + do { + SystemClock.sleep(60000) + if (this.isInitialized) { + val statusRefresh = workWithStatusRefresh( + StatusRefreshAction.GetData, null, null) + if (doWeHaveAnyStatusNeededRefereshing(statusRefresh)) { + if (!commandQueue.statusInQueue()) { + commandQueue.readStatus("Scheduled Status Refresh", null) + } + } + clearBusyQueue() + } + } while (serviceRunning) + }).start() + } + + override val serviceClass: Class<*> + get() = RileyLinkMedtronicService::class.java + + override val pumpStatusData: PumpStatus + get() = medtronicPumpStatus + + override fun deviceID(): String { + return "Medtronic" + } + + override val isFakingTempsByExtendedBoluses: Boolean + get() = false + + override fun canHandleDST(): Boolean { + return false + } + + // Pump Plugin + private val isServiceSet: Boolean + private get() = rileyLinkMedtronicService != null + + override fun getRileyLinkService(): RileyLinkMedtronicService? { + return rileyLinkMedtronicService + } + + override fun getPumpInfo(): RileyLinkPumpInfo { + val frequency = resourceHelper.gs(if (medtronicPumpStatus.pumpFrequency == "medtronic_pump_frequency_us_ca") R.string.medtronic_pump_frequency_us_ca else R.string.medtronic_pump_frequency_worldwide) + val model = if (medtronicPumpStatus.medtronicDeviceType == null) "???" else "Medtronic " + medtronicPumpStatus.medtronicDeviceType!!.pumpModel + val serialNumber = medtronicPumpStatus.serialNumber + return RileyLinkPumpInfo(frequency, model, serialNumber) + } + + override fun getLastConnectionTimeMillis(): Long { + return medtronicPumpStatus.lastConnection + } + + override fun setLastCommunicationToNow() { + medtronicPumpStatus.setLastCommunicationToNow() + } + + override fun isInitialized(): Boolean { + if (displayConnectionMessages) aapsLogger.debug(LTag.PUMP, "MedtronicPumpPlugin::isInitialized") + return isServiceSet && isInitialized + } + + override fun setBusy(busy: Boolean) { + isBusy = busy + } + + override fun isBusy(): Boolean { + if (displayConnectionMessages) aapsLogger.debug(LTag.PUMP, "MedtronicPumpPlugin::isBusy") + if (isServiceSet) { + if (isBusy) return true + if (busyTimestamps.size > 0) { + clearBusyQueue() + return busyTimestamps.size > 0 + } + } + return false + } + + @Synchronized + private fun clearBusyQueue() { + if (busyTimestamps.size == 0) { + return + } + val deleteFromQueue: MutableSet = HashSet() + for (busyTimestamp in busyTimestamps) { + if (System.currentTimeMillis() > busyTimestamp) { + deleteFromQueue.add(busyTimestamp) + } + } + if (deleteFromQueue.size == busyTimestamps.size) { + busyTimestamps.clear() + setEnableCustomAction(MedtronicCustomActionType.ClearBolusBlock, false) + } + if (deleteFromQueue.size > 0) { + busyTimestamps.removeAll(deleteFromQueue) + } + } + + override fun isConnected(): Boolean { + if (displayConnectionMessages) aapsLogger.debug(LTag.PUMP, "MedtronicPumpPlugin::isConnected") + return isServiceSet && rileyLinkMedtronicService!!.isInitialized + } + + override fun isConnecting(): Boolean { + if (displayConnectionMessages) aapsLogger.debug(LTag.PUMP, "MedtronicPumpPlugin::isConnecting") + return !isServiceSet || !rileyLinkMedtronicService!!.isInitialized + } + + override fun getPumpStatus(reason: String) { + var needRefresh = true + if (firstRun) { + needRefresh = initializePump(!isRefresh) + } else { + refreshAnyStatusThatNeedsToBeRefreshed() + } + if (needRefresh) rxBus.send(EventMedtronicPumpValuesChanged()) + } + + fun resetStatusState() { + firstRun = true + isRefresh = true + }// + + private val isPumpNotReachable: Boolean + private get() { + val rileyLinkServiceState = rileyLinkServiceData.rileyLinkServiceState + if (rileyLinkServiceState == null) { + aapsLogger.debug(LTag.PUMP, "RileyLink unreachable. RileyLinkServiceState is null.") + return false + } + if (rileyLinkServiceState != RileyLinkServiceState.PumpConnectorReady // + && rileyLinkServiceState != RileyLinkServiceState.RileyLinkReady // + && rileyLinkServiceState != RileyLinkServiceState.TuneUpDevice) { + aapsLogger.debug(LTag.PUMP, "RileyLink unreachable.") + return false + } + return !rileyLinkMedtronicService!!.deviceCommunicationManager.isDeviceReachable + } + + private fun refreshAnyStatusThatNeedsToBeRefreshed() { + val statusRefresh = workWithStatusRefresh(StatusRefreshAction.GetData, null, + null) + if (!doWeHaveAnyStatusNeededRefereshing(statusRefresh)) { + return + } + var resetTime = false + if (isPumpNotReachable) { + aapsLogger.error("Pump unreachable.") + medtronicUtil.sendNotification(MedtronicNotificationType.PumpUnreachable, resourceHelper, rxBus) + return + } + medtronicUtil.dismissNotification(MedtronicNotificationType.PumpUnreachable, rxBus) + if (hasTimeDateOrTimeZoneChanged) { + checkTimeAndOptionallySetTime() + + // read time if changed, set new time + hasTimeDateOrTimeZoneChanged = false + } + + // execute + val refreshTypesNeededToReschedule: MutableSet = HashSet() + for ((key, value) in statusRefresh!!) { + if (value!! > 0 && System.currentTimeMillis() > value) { + when (key) { + MedtronicStatusRefreshType.PumpHistory -> { + readPumpHistory() + } + + MedtronicStatusRefreshType.PumpTime -> { + checkTimeAndOptionallySetTime() + refreshTypesNeededToReschedule.add(key) + resetTime = true + } + + MedtronicStatusRefreshType.BatteryStatus, MedtronicStatusRefreshType.RemainingInsulin -> { + rileyLinkMedtronicService!!.medtronicUIComm!!.executeCommand(key.getCommandType(medtronicUtil.medtronicPumpModel)!!) + refreshTypesNeededToReschedule.add(key) + resetTime = true + } + + MedtronicStatusRefreshType.Configuration -> { + rileyLinkMedtronicService!!.medtronicUIComm!!.executeCommand(key.getCommandType(medtronicUtil.medtronicPumpModel)!!) + resetTime = true + } + } + } + + // reschedule + for (refreshType2 in refreshTypesNeededToReschedule) { + scheduleNextRefresh(refreshType2) + } + } + + if (resetTime) medtronicPumpStatus.setLastCommunicationToNow() + } + + private fun doWeHaveAnyStatusNeededRefereshing(statusRefresh: Map?): Boolean { + for ((_, value) in statusRefresh!!) { + if (value!! > 0 && System.currentTimeMillis() > value) { + return true + } + } + return hasTimeDateOrTimeZoneChanged + } + + private fun setRefreshButtonEnabled(enabled: Boolean) { + rxBus.send(EventRefreshButtonState(enabled)) + } + + private fun initializePump(realInit: Boolean): Boolean { + if (rileyLinkMedtronicService == null) return false + aapsLogger.info(LTag.PUMP, logPrefix + "initializePump - start") + rileyLinkMedtronicService!!.deviceCommunicationManager.setDoWakeUpBeforeCommand(false) + setRefreshButtonEnabled(false) + if (isRefresh) { + if (isPumpNotReachable) { + aapsLogger.error(logPrefix + "initializePump::Pump unreachable.") + medtronicUtil.sendNotification(MedtronicNotificationType.PumpUnreachable, resourceHelper, rxBus) + setRefreshButtonEnabled(true) + return true + } + medtronicUtil.dismissNotification(MedtronicNotificationType.PumpUnreachable, rxBus) + } + + // model (once) + if (medtronicUtil.medtronicPumpModel == null) { + rileyLinkMedtronicService!!.medtronicUIComm!!.executeCommand(MedtronicCommandType.PumpModel) + } else { + if (medtronicPumpStatus.medtronicDeviceType !== medtronicUtil.medtronicPumpModel) { + aapsLogger.warn(LTag.PUMP, logPrefix + "Configured pump is not the same as one detected.") + medtronicUtil.sendNotification(MedtronicNotificationType.PumpTypeNotSame, resourceHelper, rxBus) + } + } + pumpState = PumpDriverState.Connected + + // time (1h) + checkTimeAndOptionallySetTime() + readPumpHistory() + + // remaining insulin (>50 = 4h; 50-20 = 1h; 15m) + rileyLinkMedtronicService!!.medtronicUIComm!!.executeCommand(MedtronicCommandType.GetRemainingInsulin) + scheduleNextRefresh(MedtronicStatusRefreshType.RemainingInsulin, 10) + + // remaining power (1h) + rileyLinkMedtronicService!!.medtronicUIComm!!.executeCommand(MedtronicCommandType.GetBatteryStatus) + scheduleNextRefresh(MedtronicStatusRefreshType.BatteryStatus, 20) + + // configuration (once and then if history shows config changes) + rileyLinkMedtronicService!!.medtronicUIComm!!.executeCommand(getSettings(medtronicUtil.medtronicPumpModel)) + + // read profile (once, later its controlled by isThisProfileSet method) + basalProfiles + val errorCount = rileyLinkMedtronicService!!.medtronicUIComm!!.invalidResponsesCount + if (errorCount >= 5) { + aapsLogger.error("Number of error counts was 5 or more. Starting tunning.") + setRefreshButtonEnabled(true) + serviceTaskExecutor.startTask(WakeAndTuneTask(injector)) + return true + } + medtronicPumpStatus.setLastCommunicationToNow() + setRefreshButtonEnabled(true) + if (!isRefresh) { + pumpState = PumpDriverState.Initialized + } + isInitialized = true + // this.pumpState = PumpDriverState.Initialized; + firstRun = false + return true + } + + private val basalProfiles: Unit + private get() { + val medtronicUITask = rileyLinkMedtronicService!!.medtronicUIComm!!.executeCommand(MedtronicCommandType.GetBasalProfileSTD) + if (medtronicUITask.responseType === MedtronicUIResponseType.Error) { + rileyLinkMedtronicService!!.medtronicUIComm!!.executeCommand(MedtronicCommandType.GetBasalProfileSTD) + } + } + + override fun isThisProfileSet(profile: Profile): Boolean { + aapsLogger.debug(LTag.PUMP, "isThisProfileSet: basalInitalized=" + medtronicPumpStatus.basalProfileStatus) + if (!isInitialized) return true + if (medtronicPumpStatus.basalProfileStatus === BasalProfileStatus.NotInitialized) { + // this shouldn't happen, but if there was problem we try again + basalProfiles + return isProfileSame(profile) + } else if (medtronicPumpStatus.basalProfileStatus === BasalProfileStatus.ProfileChanged) { + return false + } + return medtronicPumpStatus.basalProfileStatus !== BasalProfileStatus.ProfileOK || isProfileSame(profile) + } + + private fun isProfileSame(profile: Profile): Boolean { + var invalid = false + val basalsByHour: Array? = medtronicPumpStatus.basalsByHour + aapsLogger.debug(LTag.PUMP, "Current Basals (h): " + + (basalsByHour?.let { getProfilesByHourToString(it) } ?: "null")) + + // int index = 0; + if (basalsByHour == null) return true // we don't want to set profile again, unless we are sure + val stringBuilder = StringBuilder("Requested Basals (h): ") + for (basalValue in profile.basalValues) { + val basalValueValue = pumpDescription.pumpType.determineCorrectBasalSize(basalValue.value) + val hour = basalValue.timeAsSeconds / (60 * 60) + if (!isSame(basalsByHour[hour]!!, basalValueValue)) { + invalid = true + } + stringBuilder.append(String.format(Locale.ENGLISH, "%.3f", basalValueValue)) + stringBuilder.append(" ") + } + aapsLogger.debug(LTag.PUMP, stringBuilder.toString()) + if (!invalid) { + aapsLogger.debug(LTag.PUMP, "Basal profile is same as AAPS one.") + } else { + aapsLogger.debug(LTag.PUMP, "Basal profile on Pump is different than the AAPS one.") + } + return !invalid + } + + override fun lastDataTime(): Long { + return if (medtronicPumpStatus.lastConnection > 0) { + medtronicPumpStatus.lastConnection + } else System.currentTimeMillis() + } + + override val baseBasalRate: Double + get() = medtronicPumpStatus.basalProfileForHour + + override val reservoirLevel: Double + get() = medtronicPumpStatus.reservoirRemainingUnits + + override val batteryLevel: Int + get() = medtronicPumpStatus.batteryRemaining + + override fun triggerUIChange() { + rxBus.send(EventMedtronicPumpValuesChanged()) + } + + override fun generateTempId(timeMillis: Long): Long { + return 0 + } + + // @Override public String getSerial() { + // return null; + // } + private var bolusDeliveryType = BolusDeliveryType.Idle + + private enum class BolusDeliveryType { + Idle, // + DeliveryPrepared, // + Delivering, // + CancelDelivery + } + + private fun checkTimeAndOptionallySetTime() { + aapsLogger.info(LTag.PUMP, "MedtronicPumpPlugin::checkTimeAndOptionallySetTime - Start") + setRefreshButtonEnabled(false) + if (isPumpNotReachable) { + aapsLogger.debug(LTag.PUMP, "MedtronicPumpPlugin::checkTimeAndOptionallySetTime - Pump Unreachable.") + setRefreshButtonEnabled(true) + return + } + medtronicUtil.dismissNotification(MedtronicNotificationType.PumpUnreachable, rxBus) + rileyLinkMedtronicService!!.medtronicUIComm!!.executeCommand(MedtronicCommandType.GetRealTimeClock) + var clock = medtronicUtil.pumpTime + if (clock == null) { // retry + rileyLinkMedtronicService!!.medtronicUIComm!!.executeCommand(MedtronicCommandType.GetRealTimeClock) + clock = medtronicUtil.pumpTime + } + if (clock == null) return + val timeDiff = Math.abs(clock.timeDifference) + if (timeDiff > 20) { + if (clock.localDeviceTime!!.year <= 2015 || timeDiff <= 24 * 60 * 60) { + aapsLogger.info(LTag.PUMP, String.format(Locale.ENGLISH, "MedtronicPumpPlugin::checkTimeAndOptionallySetTime - Time difference is %d s. Set time on pump.", timeDiff)) + rileyLinkMedtronicService!!.medtronicUIComm!!.executeCommand(MedtronicCommandType.SetRealTimeClock) + if (clock.timeDifference == 0) { + val notification = Notification(Notification.INSIGHT_DATE_TIME_UPDATED, resourceHelper.gs(R.string.pump_time_updated), Notification.INFO, 60) + rxBus.send(EventNewNotification(notification)) + } + } else { + if (clock.localDeviceTime!!.year > 2015) { + aapsLogger.error(String.format(Locale.ENGLISH, "MedtronicPumpPlugin::checkTimeAndOptionallySetTime - Time difference over 24h requested [diff=%d s]. Doing nothing.", timeDiff)) + medtronicUtil.sendNotification(MedtronicNotificationType.TimeChangeOver24h, resourceHelper, rxBus) + } + } + } else { + aapsLogger.info(LTag.PUMP, String.format(Locale.ENGLISH, "MedtronicPumpPlugin::checkTimeAndOptionallySetTime - Time difference is %d s. Do nothing.", timeDiff)) + } + scheduleNextRefresh(MedtronicStatusRefreshType.PumpTime, 0) + } + + override fun deliverBolus(detailedBolusInfo: DetailedBolusInfo?): PumpEnactResult { + aapsLogger.info(LTag.PUMP, "MedtronicPumpPlugin::deliverBolus - " + BolusDeliveryType.DeliveryPrepared) + setRefreshButtonEnabled(false) + if (detailedBolusInfo!!.insulin > medtronicPumpStatus.reservoirRemainingUnits) { + return PumpEnactResult(injector) // + .success(false) // + .enacted(false) // + .comment(resourceHelper.gs(R.string.medtronic_cmd_bolus_could_not_be_delivered_no_insulin, + medtronicPumpStatus.reservoirRemainingUnits, + detailedBolusInfo.insulin)) + } + bolusDeliveryType = BolusDeliveryType.DeliveryPrepared + if (isPumpNotReachable) { + aapsLogger.debug(LTag.PUMP, "MedtronicPumpPlugin::deliverBolus - Pump Unreachable.") + return setNotReachable(true, false) + } + medtronicUtil.dismissNotification(MedtronicNotificationType.PumpUnreachable, rxBus) + if (bolusDeliveryType == BolusDeliveryType.CancelDelivery) { + // LOG.debug("MedtronicPumpPlugin::deliverBolus - Delivery Canceled."); + return setNotReachable(true, true) + } + + // LOG.debug("MedtronicPumpPlugin::deliverBolus - Starting wait period."); + val sleepTime = sp.getInt(MedtronicConst.Prefs.BolusDelay, 10) * 1000 + SystemClock.sleep(sleepTime.toLong()) + return if (bolusDeliveryType == BolusDeliveryType.CancelDelivery) { + // LOG.debug("MedtronicPumpPlugin::deliverBolus - Delivery Canceled, before wait period."); + setNotReachable(true, true) + } else try { + bolusDeliveryType = BolusDeliveryType.Delivering + + // LOG.debug("MedtronicPumpPlugin::deliverBolus - Start delivery"); + val responseTask = rileyLinkMedtronicService!!.medtronicUIComm!!.executeCommand(MedtronicCommandType.SetBolus, + Arrays.asList(detailedBolusInfo.insulin)) + val response = responseTask.result as Boolean? + setRefreshButtonEnabled(true) + + // LOG.debug("MedtronicPumpPlugin::deliverBolus - Response: {}", response); + if (response!!) { + if (bolusDeliveryType == BolusDeliveryType.CancelDelivery) { + // LOG.debug("MedtronicPumpPlugin::deliverBolus - Delivery Canceled after Bolus started."); + Thread(Runnable { + + // Looper.prepare(); + // LOG.debug("MedtronicPumpPlugin::deliverBolus - Show dialog - before"); + SystemClock.sleep(2000) + // LOG.debug("MedtronicPumpPlugin::deliverBolus - Show dialog. Context: " + // + MainApp.instance().getApplicationContext()); + runAlarm(context, resourceHelper.gs(R.string.medtronic_cmd_cancel_bolus_not_supported), resourceHelper.gs(R.string.medtronic_warning), R.raw.boluserror) + }).start() + } + val now = System.currentTimeMillis() + detailedBolusInfo.timestamp = now + detailedBolusInfo.deliverAtTheLatest = now // not sure about that one + + // TODO fix + if (usePumpSync) { + addBolusWithTempId(detailedBolusInfo, true) + } else { + activePlugin.activeTreatments.addToHistoryTreatment(detailedBolusInfo, true) + } + + // we subtract insulin, exact amount will be visible with next remainingInsulin update. + medtronicPumpStatus.reservoirRemainingUnits = medtronicPumpStatus.reservoirRemainingUnits - detailedBolusInfo.insulin + incrementStatistics(if (detailedBolusInfo.bolusType === DetailedBolusInfo.BolusType.SMB) MedtronicConst.Statistics.SMBBoluses else MedtronicConst.Statistics.StandardBoluses) + + // calculate time for bolus and set driver to busy for that time + val bolusTime = (detailedBolusInfo.insulin * 42.0).toInt() + val time = now + bolusTime * 1000 + busyTimestamps.add(time) + setEnableCustomAction(MedtronicCustomActionType.ClearBolusBlock, true) + PumpEnactResult(injector).success(true) // + .enacted(true) // + .bolusDelivered(detailedBolusInfo.insulin) // + .carbsDelivered(detailedBolusInfo.carbs) + } else { + PumpEnactResult(injector) // + .success(bolusDeliveryType == BolusDeliveryType.CancelDelivery) // + .enacted(false) // + .comment(R.string.medtronic_cmd_bolus_could_not_be_delivered) + } + } finally { + finishAction("Bolus") + bolusDeliveryType = BolusDeliveryType.Idle + } + + // LOG.debug("MedtronicPumpPlugin::deliverBolus - End wait period. Start delivery"); + } + + private fun setNotReachable(isBolus: Boolean, success: Boolean): PumpEnactResult { + setRefreshButtonEnabled(true) + if (isBolus) { + bolusDeliveryType = BolusDeliveryType.Idle + } + return if (success) { + PumpEnactResult(injector) // + .success(true) // + .enacted(false) + } else { + PumpEnactResult(injector) // + .success(false) // + .enacted(false) // + .comment(R.string.medtronic_pump_status_pump_unreachable) + } + } + + override fun stopBolusDelivering() { + bolusDeliveryType = BolusDeliveryType.CancelDelivery + + // if (isLoggingEnabled()) + // LOG.warn("MedtronicPumpPlugin::deliverBolus - Stop Bolus Delivery."); + } + + private fun incrementStatistics(statsKey: String) { + var currentCount = sp.getLong(statsKey, 0L) + currentCount++ + sp.putLong(statsKey, currentCount) + } + + // if enforceNew===true current temp basal is canceled and new TBR set (duration is prolonged), + // if false and the same rate is requested enacted=false and success=true is returned and TBR is not changed + override fun setTempBasalAbsolute(absoluteRate: Double, durationInMinutes: Int, profile: Profile, enforceNew: Boolean, tbrType: TemporaryBasalType): PumpEnactResult { + setRefreshButtonEnabled(false) + if (isPumpNotReachable) { + setRefreshButtonEnabled(true) + return PumpEnactResult(injector) // + .success(false) // + .enacted(false) // + .comment(R.string.medtronic_pump_status_pump_unreachable) + } + medtronicUtil.dismissNotification(MedtronicNotificationType.PumpUnreachable, rxBus) + aapsLogger.info(LTag.PUMP, logPrefix + "setTempBasalAbsolute: rate: " + absoluteRate + ", duration=" + durationInMinutes) + + // read current TBR + val tbrCurrent = readTBR() + if (tbrCurrent == null) { + aapsLogger.warn(LTag.PUMP, logPrefix + "setTempBasalAbsolute - Could not read current TBR, canceling operation.") + finishAction("TBR") + return PumpEnactResult(injector).success(false).enacted(false) + .comment(R.string.medtronic_cmd_cant_read_tbr) + } else { + aapsLogger.info(LTag.PUMP, logPrefix + "setTempBasalAbsolute: Current Basal: duration: " + tbrCurrent.durationMinutes + " min, rate=" + tbrCurrent.insulinRate) + } + if (!enforceNew) { + if (isSame(tbrCurrent.insulinRate, absoluteRate)) { + var sameRate = true + if (isSame(0.0, absoluteRate) && durationInMinutes > 0) { + // if rate is 0.0 and duration>0 then the rate is not the same + sameRate = false + } + if (sameRate) { + aapsLogger.info(LTag.PUMP, logPrefix + "setTempBasalAbsolute - No enforceNew and same rate. Exiting.") + finishAction("TBR") + return PumpEnactResult(injector).success(true).enacted(false) + } + } + // if not the same rate, we cancel and start new + } + + // if TBR is running we will cancel it. + if (tbrCurrent.insulinRate > 0.0 && tbrCurrent.durationMinutes > 0) { + aapsLogger.info(LTag.PUMP, logPrefix + "setTempBasalAbsolute - TBR running - so canceling it.") + + // CANCEL + val responseTask2 = rileyLinkMedtronicService!!.medtronicUIComm!!.executeCommand(MedtronicCommandType.CancelTBR) + val response = responseTask2.result as Boolean? + if (response!!) { + aapsLogger.info(LTag.PUMP, logPrefix + "setTempBasalAbsolute - Current TBR cancelled.") + } else { + aapsLogger.error(logPrefix + "setTempBasalAbsolute - Cancel TBR failed.") + finishAction("TBR") + return PumpEnactResult(injector).success(false).enacted(false) + .comment(R.string.medtronic_cmd_cant_cancel_tbr_stop_op) + } + } + + // now start new TBR + val responseTask = rileyLinkMedtronicService!!.medtronicUIComm!!.executeCommand(MedtronicCommandType.SetTemporaryBasal, + Arrays.asList(absoluteRate, durationInMinutes)) + val response = responseTask.result as Boolean? + aapsLogger.info(LTag.PUMP, logPrefix + "setTempBasalAbsolute - setTBR. Response: " + response) + return if (response!!) { + // FIXME put this into UIPostProcessor + medtronicPumpStatus.tempBasalStart = Date() + medtronicPumpStatus.tempBasalAmount = absoluteRate + medtronicPumpStatus.tempBasalLength = durationInMinutes + val tempStart = TemporaryBasal(injector) // + .date(System.currentTimeMillis()) // + .duration(durationInMinutes) // + .absolute(absoluteRate) // + .source(Source.USER) + + // TODO fix + if (usePumpSync) { + addTemporaryBasalRateWithTempId(tempStart, true) + } else { + activePlugin.activeTreatments.addToHistoryTempBasal(tempStart) + } + incrementStatistics(MedtronicConst.Statistics.TBRsSet) + finishAction("TBR") + PumpEnactResult(injector).success(true).enacted(true) // + .absolute(absoluteRate).duration(durationInMinutes) + } else { + finishAction("TBR") + PumpEnactResult(injector).success(false).enacted(false) // + .comment(R.string.medtronic_cmd_tbr_could_not_be_delivered) + } + } + + override fun setTempBasalPercent(percent: Int, durationInMinutes: Int, profile: Profile, enforceNew: Boolean, tbrType: TemporaryBasalType): PumpEnactResult { + return if (percent == 0) { + setTempBasalAbsolute(0.0, durationInMinutes, profile, enforceNew, tbrType) + } else { + var absoluteValue = profile.basal * (percent / 100.0) + absoluteValue = pumpDescription.pumpType.determineCorrectBasalSize(absoluteValue) + aapsLogger.warn(LTag.PUMP, "setTempBasalPercent [MedtronicPumpPlugin] - You are trying to use setTempBasalPercent with percent other then 0% ($percent). This will start setTempBasalAbsolute, with calculated value ($absoluteValue). Result might not be 100% correct.") + setTempBasalAbsolute(absoluteValue, durationInMinutes, profile, enforceNew, tbrType) + } + } + + private fun finishAction(overviewKey: String?) { + if (overviewKey != null) rxBus.send(EventRefreshOverview(overviewKey, false)) + triggerUIChange() + setRefreshButtonEnabled(true) + } + + private fun readPumpHistory() { + +// if (isLoggingEnabled()) +// LOG.error(getLogPrefix() + "readPumpHistory WIP."); + readPumpHistoryLogic() + scheduleNextRefresh(MedtronicStatusRefreshType.PumpHistory) + if (medtronicHistoryData.hasRelevantConfigurationChanged()) { + scheduleNextRefresh(MedtronicStatusRefreshType.Configuration, -1) + } + if (medtronicHistoryData.hasPumpTimeChanged()) { + scheduleNextRefresh(MedtronicStatusRefreshType.PumpTime, -1) + } + if (medtronicPumpStatus.basalProfileStatus !== BasalProfileStatus.NotInitialized + && medtronicHistoryData.hasBasalProfileChanged()) { + medtronicHistoryData.processLastBasalProfileChange(pumpDescription.pumpType, medtronicPumpStatus) + } + val previousState = pumpState + if (medtronicHistoryData.isPumpSuspended) { + pumpState = PumpDriverState.Suspended + aapsLogger.debug(LTag.PUMP, logPrefix + "isPumpSuspended: true") + } else { + if (previousState === PumpDriverState.Suspended) { + pumpState = PumpDriverState.Ready + } + aapsLogger.debug(LTag.PUMP, logPrefix + "isPumpSuspended: false") + } + medtronicHistoryData.processNewHistoryData() + medtronicHistoryData.finalizeNewHistoryRecords() + // this.medtronicHistoryData.setLastHistoryRecordTime(this.lastPumpHistoryEntry.atechDateTime); + } + + private fun readPumpHistoryLogic() { + + val debugHistory = false + var targetDate: LocalDateTime? = null + if (lastPumpHistoryEntry == null) { + if (debugHistory) aapsLogger.debug(LTag.PUMP, logPrefix + "readPumpHistoryLogic(): lastPumpHistoryEntry: null") + val lastPumpHistoryEntryTime = lastPumpEntryTime + var timeMinus36h = LocalDateTime() + timeMinus36h = timeMinus36h.minusHours(36) + medtronicHistoryData.setIsInInit(true) + if (lastPumpHistoryEntryTime == 0L) { + if (debugHistory) aapsLogger.debug(LTag.PUMP, logPrefix + "readPumpHistoryLogic(): lastPumpHistoryEntryTime: 0L - targetDate: " + + targetDate) + targetDate = timeMinus36h + } else { + // LocalDateTime lastHistoryRecordTime = DateTimeUtil.toLocalDateTime(lastPumpHistoryEntryTime); + if (debugHistory) aapsLogger.debug(LTag.PUMP, logPrefix + "readPumpHistoryLogic(): lastPumpHistoryEntryTime: " + lastPumpHistoryEntryTime + " - targetDate: " + targetDate) + medtronicHistoryData.setLastHistoryRecordTime(lastPumpHistoryEntryTime) + var lastHistoryRecordTime = DateTimeUtil.toLocalDateTime(lastPumpHistoryEntryTime) + lastHistoryRecordTime = lastHistoryRecordTime.minusHours(12) // we get last 12 hours of history to + // determine pump state + // (we don't process that data), we process only + if (timeMinus36h.isAfter(lastHistoryRecordTime)) { + targetDate = timeMinus36h + } + targetDate = if (timeMinus36h.isAfter(lastHistoryRecordTime)) timeMinus36h else lastHistoryRecordTime + if (debugHistory) aapsLogger.debug(LTag.PUMP, logPrefix + "readPumpHistoryLogic(): targetDate: " + targetDate) + } + } else { + if (debugHistory) aapsLogger.debug(LTag.PUMP, logPrefix + "readPumpHistoryLogic(): lastPumpHistoryEntry: not null - " + medtronicUtil.gsonInstance.toJson(lastPumpHistoryEntry)) + medtronicHistoryData.setIsInInit(false) + // medtronicHistoryData.setLastHistoryRecordTime(lastPumpHistoryEntry.atechDateTime); + + // targetDate = lastPumpHistoryEntry.atechDateTime; + } + + //aapsLogger.debug(LTag.PUMP, "HST: Target Date: " + targetDate); + val responseTask2 = rileyLinkMedtronicService!!.medtronicUIComm!!.executeCommand(MedtronicCommandType.GetHistoryData, + Arrays.asList(lastPumpHistoryEntry, targetDate) as List?) + if (debugHistory) aapsLogger.debug(LTag.PUMP, "HST: After task") + val historyResult = responseTask2.result as PumpHistoryResult? + if (debugHistory) aapsLogger.debug(LTag.PUMP, "HST: History Result: " + historyResult.toString()) + val latestEntry = historyResult!!.latestEntry + if (debugHistory) aapsLogger.debug(LTag.PUMP, logPrefix + "Last entry: " + latestEntry) + if (latestEntry == null) // no new history to read + return + lastPumpHistoryEntry = latestEntry + sp.putLong(MedtronicConst.Statistics.LastPumpHistoryEntry, latestEntry.atechDateTime!!) + if (debugHistory) aapsLogger.debug(LTag.PUMP, "HST: History: valid=" + historyResult.validEntries.size + ", unprocessed=" + historyResult.unprocessedEntries.size) + medtronicHistoryData.addNewHistory(historyResult) + medtronicHistoryData.filterNewEntries() + + // determine if first run, if yes detrmine how much of update do we need + // first run: + // get last hiostory entry, if not there download 1.5 days of data + // - there: check if last entry is older than 1.5 days + // - yes: download 1.5 days + // - no: download with last entry + // - not there: download 1.5 days + // + // upload all new entries to NightScout (TBR, Bolus) + // determine pump status + // + // save last entry + // + // not first run: + // update to last entry + // - save + // - determine pump status + } + + private val lastPumpEntryTime: Long + private get() { + val lastPumpEntryTime = sp.getLong(MedtronicConst.Statistics.LastPumpHistoryEntry, 0L) + return try { + val localDateTime = DateTimeUtil.toLocalDateTime(lastPumpEntryTime) + if (localDateTime.year != GregorianCalendar()[Calendar.YEAR]) { + aapsLogger.warn(LTag.PUMP, "Saved LastPumpHistoryEntry was invalid. Year was not the same.") + return 0L + } + lastPumpEntryTime + } catch (ex: Exception) { + aapsLogger.warn(LTag.PUMP, "Saved LastPumpHistoryEntry was invalid.") + 0L + } + } + + private fun scheduleNextRefresh(refreshType: MedtronicStatusRefreshType?, additionalTimeInMinutes: Int = 0) { + when (refreshType) { + MedtronicStatusRefreshType.RemainingInsulin -> { + val remaining = medtronicPumpStatus.reservoirRemainingUnits + val min: Int + min = if (remaining > 50) 4 * 60 else if (remaining > 20) 60 else 15 + workWithStatusRefresh(StatusRefreshAction.Add, refreshType, getTimeInFutureFromMinutes(min)) + } + + MedtronicStatusRefreshType.PumpTime, MedtronicStatusRefreshType.Configuration, MedtronicStatusRefreshType.BatteryStatus, MedtronicStatusRefreshType.PumpHistory -> { + workWithStatusRefresh(StatusRefreshAction.Add, refreshType, + getTimeInFutureFromMinutes(refreshType.refreshTime + additionalTimeInMinutes)) + } + } + } + + private enum class StatusRefreshAction { + Add, // + GetData + } + + @Synchronized + private fun workWithStatusRefresh(action: StatusRefreshAction, // + statusRefreshType: MedtronicStatusRefreshType?, // + time: Long?): Map? { + return when (action) { + StatusRefreshAction.Add -> { + statusRefreshMap[statusRefreshType] = time + null + } + + StatusRefreshAction.GetData -> { + HashMap(statusRefreshMap) + } + + else -> null + } + } + + private fun getTimeInFutureFromMinutes(minutes: Int): Long { + return System.currentTimeMillis() + getTimeInMs(minutes) + } + + private fun getTimeInMs(minutes: Int): Long { + return minutes * 60 * 1000L + } + + private fun readTBR(): TempBasalPair? { + val responseTask = rileyLinkMedtronicService!!.medtronicUIComm!!.executeCommand(MedtronicCommandType.ReadTemporaryBasal) + return if (responseTask.hasData()) { + val tbr = responseTask.result as TempBasalPair? + + // we sometimes get rate returned even if TBR is no longer running + if (tbr!!.durationMinutes == 0) { + tbr.insulinRate = 0.0 + } + tbr + } else { + null + } + } + + override fun cancelTempBasal(enforceNew: Boolean): PumpEnactResult { + aapsLogger.info(LTag.PUMP, logPrefix + "cancelTempBasal - started") + if (isPumpNotReachable) { + setRefreshButtonEnabled(true) + return PumpEnactResult(injector) // + .success(false) // + .enacted(false) // + .comment(R.string.medtronic_pump_status_pump_unreachable) + } + medtronicUtil.dismissNotification(MedtronicNotificationType.PumpUnreachable, rxBus) + setRefreshButtonEnabled(false) + val tbrCurrent = readTBR() + if (tbrCurrent != null) { + if (tbrCurrent.insulinRate > 0.0f && tbrCurrent.durationMinutes == 0) { + aapsLogger.info(LTag.PUMP, logPrefix + "cancelTempBasal - TBR already canceled.") + finishAction("TBR") + return PumpEnactResult(injector).success(true).enacted(false) + } + } else { + aapsLogger.warn(LTag.PUMP, logPrefix + "cancelTempBasal - Could not read currect TBR, canceling operation.") + finishAction("TBR") + return PumpEnactResult(injector).success(false).enacted(false) + .comment(R.string.medtronic_cmd_cant_read_tbr) + } + val responseTask2 = rileyLinkMedtronicService!!.medtronicUIComm!!.executeCommand(MedtronicCommandType.CancelTBR) + val response = responseTask2.result as Boolean? + finishAction("TBR") + return if (response!!) { + aapsLogger.info(LTag.PUMP, logPrefix + "cancelTempBasal - Cancel TBR successful.") + val tempBasal = TemporaryBasal(injector) // + .date(System.currentTimeMillis()) // + .duration(0) // + .source(Source.USER) + + // TODO fix + activePlugin.activeTreatments.addToHistoryTempBasal(tempBasal) + PumpEnactResult(injector).success(true).enacted(true) // + .isTempCancel(true) + } else { + aapsLogger.info(LTag.PUMP, logPrefix + "cancelTempBasal - Cancel TBR failed.") + PumpEnactResult(injector).success(response).enacted(response) // + .comment(R.string.medtronic_cmd_cant_cancel_tbr) + } + } + + override fun manufacturer(): ManufacturerType { + return pumpDescription.pumpType.manufacturer!! + } + + override fun model(): PumpType { + return pumpDescription.pumpType + } + + override fun serialNumber(): String { + return medtronicPumpStatus.serialNumber!! + } + + override fun setNewBasalProfile(profile: Profile): PumpEnactResult { + aapsLogger.info(LTag.PUMP, logPrefix + "setNewBasalProfile") + + // this shouldn't be needed, but let's do check if profile setting we are setting is same as current one + if (isProfileSame(profile)) { + return PumpEnactResult(injector) // + .success(true) // + .enacted(false) // + .comment(R.string.medtronic_cmd_basal_profile_not_set_is_same) + } + setRefreshButtonEnabled(false) + if (isPumpNotReachable) { + setRefreshButtonEnabled(true) + return PumpEnactResult(injector) // + .success(false) // + .enacted(false) // + .comment(R.string.medtronic_pump_status_pump_unreachable) + } + medtronicUtil.dismissNotification(MedtronicNotificationType.PumpUnreachable, rxBus) + val basalProfile = convertProfileToMedtronicProfile(profile) + aapsLogger.debug("Basal Profile: $basalProfile") + val profileInvalid = isProfileValid(basalProfile) + if (profileInvalid != null) { + return PumpEnactResult(injector) // + .success(false) // + .enacted(false) // + .comment(resourceHelper.gs(R.string.medtronic_cmd_set_profile_pattern_overflow, profileInvalid)) + } + val responseTask = rileyLinkMedtronicService!!.medtronicUIComm!!.executeCommand(MedtronicCommandType.SetBasalProfileSTD, + Arrays.asList(basalProfile)) + val response = responseTask.result as Boolean? + aapsLogger.info(LTag.PUMP, logPrefix + "Basal Profile was set: " + response) + return if (response!!) { + PumpEnactResult(injector).success(true).enacted(true) + } else { + PumpEnactResult(injector).success(response).enacted(response) // + .comment(R.string.medtronic_cmd_basal_profile_could_not_be_set) + } + } + + private fun isProfileValid(basalProfile: BasalProfile): String? { + val stringBuilder = StringBuilder() + if (medtronicPumpStatus.maxBasal == null) return null + for (profileEntry in basalProfile.entries) { + if (profileEntry.rate > medtronicPumpStatus.maxBasal!!) { + stringBuilder.append(profileEntry.startTime!!.toString("HH:mm")) + stringBuilder.append("=") + stringBuilder.append(profileEntry.rate) + } + } + return if (stringBuilder.length == 0) null else stringBuilder.toString() + } + + private fun convertProfileToMedtronicProfile(profile: Profile): BasalProfile { + val basalProfile = BasalProfile(aapsLogger) + for (i in 0..23) { + val rate = profile.getBasalTimeFromMidnight(i * 60 * 60) + val v = pumpDescription.pumpType.determineCorrectBasalSize(rate) + val basalEntry = BasalProfileEntry(v, i, 0) + basalProfile.addEntry(basalEntry) + } + basalProfile.generateRawDataFromEntries() + return basalProfile + } + + // OPERATIONS not supported by Pump or Plugin + private var customActions: List? = null + private val customActionWakeUpAndTune = CustomAction(R.string.medtronic_custom_action_wake_and_tune, + MedtronicCustomActionType.WakeUpAndTune) + private val customActionClearBolusBlock = CustomAction( + R.string.medtronic_custom_action_clear_bolus_block, MedtronicCustomActionType.ClearBolusBlock, false) + private val customActionResetRLConfig = CustomAction( + R.string.medtronic_custom_action_reset_rileylink, MedtronicCustomActionType.ResetRileyLinkConfiguration, true) + + override fun getCustomActions(): List? { + if (customActions == null) { + customActions = Arrays.asList(customActionWakeUpAndTune, // + customActionClearBolusBlock, // + customActionResetRLConfig) + } + return customActions + } + + override fun executeCustomAction(customActionType: CustomActionType) { + val mcat = customActionType as MedtronicCustomActionType + when (mcat) { + MedtronicCustomActionType.WakeUpAndTune -> { + if (rileyLinkMedtronicService!!.verifyConfiguration()) { + serviceTaskExecutor.startTask(WakeAndTuneTask(injector)) + } else { + runAlarm(context, resourceHelper.gs(R.string.medtronic_error_operation_not_possible_no_configuration), resourceHelper.gs(R.string.medtronic_warning), R.raw.boluserror) + } + } + + MedtronicCustomActionType.ClearBolusBlock -> { + busyTimestamps.clear() + customActionClearBolusBlock.isEnabled = false + refreshCustomActionsList() + } + + MedtronicCustomActionType.ResetRileyLinkConfiguration -> { + serviceTaskExecutor.startTask(ResetRileyLinkConfigurationTask(injector)) + } + + else -> { + } + } + } + + override fun timezoneOrDSTChanged(changeType: TimeChangeType) { + aapsLogger.warn(LTag.PUMP, logPrefix + "Time or TimeZone changed. ") + hasTimeDateOrTimeZoneChanged = true + } + + override fun setNeutralTempAtFullHour(): Boolean { + return sp.getBoolean(R.string.key_set_neutral_temps, true) + } + + private fun setEnableCustomAction(customAction: MedtronicCustomActionType, isEnabled: Boolean) { + if (customAction === MedtronicCustomActionType.ClearBolusBlock) { + customActionClearBolusBlock.isEnabled = isEnabled + } else if (customAction === MedtronicCustomActionType.ResetRileyLinkConfiguration) { + customActionResetRLConfig.isEnabled = isEnabled + } + refreshCustomActionsList() + } + + companion object { + var isBusy = false + } + + init { + displayConnectionMessages = false + } +} \ No newline at end of file diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/MedtronicCommunicationManager.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/MedtronicCommunicationManager.java index 7773f28ba7..4e855dafa3 100644 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/MedtronicCommunicationManager.java +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/comm/MedtronicCommunicationManager.java @@ -599,6 +599,44 @@ public class MedtronicCommunicationManager extends RileyLinkCommunicationManager } + private T sendAndGetResponseWithCheck(MedtronicCommandType commandType, byte[] bodyData, Class clazz) { + + aapsLogger.debug(LTag.PUMPCOMM, "getDataFromPump: " + commandType); + + for (int retries = 0; retries < MAX_COMMAND_TRIES; retries++) { + + try { + PumpMessage response = sendAndGetResponse(commandType, bodyData, DEFAULT_TIMEOUT + (DEFAULT_TIMEOUT * retries)); + + String check = checkResponseContent(response, commandType.getCommandDescription(), commandType.getExpectedLength()); + + if (check == null) { + + T dataResponse = (T)medtronicConverter.convertResponse(medtronicPumpPlugin.getPumpDescription().getPumpType(), commandType, response.getRawContent()); + + if (dataResponse != null) { + this.errorMessage = null; + aapsLogger.debug(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "Converted response for %s is %s.", commandType.name(), dataResponse)); + + return dataResponse; + } else { + this.errorMessage = "Error decoding response."; + } + } else { + this.errorMessage = check; + // return null; + } + + } catch (RileyLinkCommunicationException e) { + aapsLogger.warn(LTag.PUMPCOMM, String.format(Locale.ENGLISH, "Error getting response from RileyLink (error=%s, retry=%d)", e.getMessage(), retries + 1)); + } + + } + + return null; + } + + private String checkResponseContent(PumpMessage response, String method, int expectedLength) { if (!response.isValid()) { diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/BasalProfile.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/BasalProfile.kt index 489e2b2c36..92720b72f2 100644 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/BasalProfile.kt +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/data/dto/BasalProfile.kt @@ -32,7 +32,8 @@ class BasalProfile { private val aapsLogger: AAPSLogger - @Expose var rawData : ByteArray? = null // store as byte array to make transport (via parcel) easier + @Expose + lateinit var rawData : ByteArray // store as byte array to make transport (via parcel) easier private set private var listEntries: MutableList? = null @@ -48,14 +49,11 @@ class BasalProfile { } fun init() { - rawData = ByteArray(MAX_RAW_DATA_SIZE) - rawData!![0] = 0 - rawData!![1] = 0 - rawData!![2] = 0x3f + rawData = byteArrayOf(0,0,0x3f) } private fun setRawData(data: ByteArray): Boolean { - var dataInternal: ByteArray? = data + var dataInternal: ByteArray = data if (dataInternal == null) { aapsLogger.error(LTag.PUMPCOMM, "setRawData: buffer is null!") return false @@ -81,17 +79,16 @@ class BasalProfile { return false } rawData = ByteArray(MAX_RAW_DATA_SIZE) - val item = 0 var i = 0 while (i < data.size - 2) { if (data[i] == 0.toByte() && data[i + 1] == 0.toByte() && data[i + 2] == 0.toByte()) { - rawData!![i] = 0 - rawData!![i + 1] = 0 - rawData!![i + 2] = 0 + rawData[i] = 0 + rawData[i + 1] = 0 + rawData[i + 2] = 0 } - rawData!![i] = data[i + 1] - rawData!![i + 1] = data[i + 2] - rawData!![i + 2] = data[i] + rawData[i] = data[i + 1] + rawData[i + 1] = data[i + 2] + rawData[i + 2] = data[i] i += 3 } return true @@ -306,7 +303,7 @@ class BasalProfile { } @JvmStatic - fun getProfilesByHourToString(data: Array): String { + fun getProfilesByHourToString(data: Array): String { val stringBuilder = StringBuilder() for (value in data) { stringBuilder.append(String.format("%.3f", value)) diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/dialog/MedtronicHistoryActivity.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/dialog/MedtronicHistoryActivity.java deleted file mode 100644 index fc986f4f83..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/dialog/MedtronicHistoryActivity.java +++ /dev/null @@ -1,250 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.medtronic.dialog; - -import android.os.Bundle; -import android.os.SystemClock; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.AdapterView; -import android.widget.ArrayAdapter; -import android.widget.Spinner; -import android.widget.TextView; - -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; - -import java.util.ArrayList; -import java.util.List; - -import javax.inject.Inject; - -import info.nightscout.androidaps.activities.NoSplashAppCompatActivity; -import info.nightscout.androidaps.plugins.pump.common.defs.PumpHistoryEntryGroup; -import info.nightscout.androidaps.plugins.pump.medtronic.R; -import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.PumpHistoryEntry; -import info.nightscout.androidaps.plugins.pump.medtronic.data.MedtronicHistoryData; -import info.nightscout.androidaps.utils.resources.ResourceHelper; - - -public class MedtronicHistoryActivity extends NoSplashAppCompatActivity { - - @Inject MedtronicHistoryData medtronicHistoryData; - @Inject ResourceHelper resourceHelper; - - Spinner historyTypeSpinner; - TextView statusView; - RecyclerView recyclerView; - LinearLayoutManager llm; - - static TypeList showingType = null; - static PumpHistoryEntryGroup selectedGroup = PumpHistoryEntryGroup.All; - List filteredHistoryList = new ArrayList<>(); - - RecyclerViewAdapter recyclerViewAdapter; - boolean manualChange = false; - - List typeListFull; - - - private void filterHistory(PumpHistoryEntryGroup group) { - - this.filteredHistoryList.clear(); - - List list = new ArrayList<>(); - list.addAll(medtronicHistoryData.getAllHistory()); - - //LOG.debug("Items on full list: {}", list.size()); - - if (group == PumpHistoryEntryGroup.All) { - this.filteredHistoryList.addAll(list); - } else { - for (PumpHistoryEntry pumpHistoryEntry : list) { - if (pumpHistoryEntry.getEntryType().getGroup() == group) { - this.filteredHistoryList.add(pumpHistoryEntry); - } - } - } - - if (this.recyclerViewAdapter != null) { - this.recyclerViewAdapter.setHistoryList(this.filteredHistoryList); - this.recyclerViewAdapter.notifyDataSetChanged(); - } - - //LOG.debug("Items on filtered list: {}", filteredHistoryList.size()); - } - - - @Override - protected void onResume() { - super.onResume(); - filterHistory(selectedGroup); - setHistoryTypeSpinner(); - } - - - private void setHistoryTypeSpinner() { - this.manualChange = true; - - for (int i = 0; i < typeListFull.size(); i++) { - if (typeListFull.get(i).entryGroup == selectedGroup) { - historyTypeSpinner.setSelection(i); - break; - } - } - - SystemClock.sleep(200); - this.manualChange = false; - } - - - @Override - protected void onPause() { - super.onPause(); - } - - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.medtronic_history_activity); - - historyTypeSpinner = findViewById(R.id.medtronic_historytype); - statusView = findViewById(R.id.medtronic_historystatus); - recyclerView = findViewById(R.id.medtronic_history_recyclerview); - - recyclerView.setHasFixedSize(true); - llm = new LinearLayoutManager(this); - recyclerView.setLayoutManager(llm); - - recyclerViewAdapter = new RecyclerViewAdapter(filteredHistoryList); - recyclerView.setAdapter(recyclerViewAdapter); - - statusView.setVisibility(View.GONE); - - typeListFull = getTypeList(PumpHistoryEntryGroup.getTranslatedList(resourceHelper)); - - ArrayAdapter spinnerAdapter = new ArrayAdapter<>(this, R.layout.spinner_centered, typeListFull); - historyTypeSpinner.setAdapter(spinnerAdapter); - - historyTypeSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { - - @Override - public void onItemSelected(AdapterView parent, View view, int position, long id) { - if (manualChange) - return; - TypeList selected = (TypeList) historyTypeSpinner.getSelectedItem(); - showingType = selected; - selectedGroup = selected.entryGroup; - filterHistory(selectedGroup); - } - - - @Override - public void onNothingSelected(AdapterView parent) { - if (manualChange) - return; - filterHistory(PumpHistoryEntryGroup.All); - } - }); - - } - - - private List getTypeList(List list) { - - ArrayList typeList = new ArrayList<>(); - - for (PumpHistoryEntryGroup pumpHistoryEntryGroup : list) { - typeList.add(new TypeList(pumpHistoryEntryGroup)); - } - - return typeList; - } - - public static class TypeList { - - PumpHistoryEntryGroup entryGroup; - String name; - - - TypeList(PumpHistoryEntryGroup entryGroup) { - this.entryGroup = entryGroup; - this.name = entryGroup.getTranslated(); - } - - - @Override - public String toString() { - return name; - } - } - - public static class RecyclerViewAdapter extends RecyclerView.Adapter { - - List historyList; - - - RecyclerViewAdapter(List historyList) { - this.historyList = historyList; - } - - - public void setHistoryList(List historyList) { - // this.historyList.clear(); - // this.historyList.addAll(historyList); - - this.historyList = historyList; - - // this.notifyDataSetChanged(); - } - - - @Override - public HistoryViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) { - View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.medtronic_history_item, // - viewGroup, false); - return new HistoryViewHolder(v); - } - - - @Override - public void onBindViewHolder(HistoryViewHolder holder, int position) { - PumpHistoryEntry record = historyList.get(position); - - if (record != null) { - holder.timeView.setText(record.getDateTimeString()); - holder.typeView.setText(record.getEntryType().getDescription()); - holder.valueView.setText(record.getDisplayableValue()); - } - } - - - @Override - public int getItemCount() { - return historyList.size(); - } - - - @Override - public void onAttachedToRecyclerView(RecyclerView recyclerView) { - super.onAttachedToRecyclerView(recyclerView); - } - - static class HistoryViewHolder extends RecyclerView.ViewHolder { - - TextView timeView; - TextView typeView; - TextView valueView; - - - HistoryViewHolder(View itemView) { - super(itemView); - // cv = (CardView)itemView.findViewById(R.id.rileylink_history_item); - timeView = itemView.findViewById(R.id.medtronic_history_time); - typeView = itemView.findViewById(R.id.medtronic_history_source); - valueView = itemView.findViewById(R.id.medtronic_history_description); - } - } - } - -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/dialog/MedtronicHistoryActivity.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/dialog/MedtronicHistoryActivity.kt new file mode 100644 index 0000000000..e5a047ab97 --- /dev/null +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/dialog/MedtronicHistoryActivity.kt @@ -0,0 +1,200 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.dialog + +import android.os.Bundle +import android.os.SystemClock +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.AdapterView +import android.widget.ArrayAdapter +import android.widget.Spinner +import android.widget.TextView +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import dagger.android.DaggerActivity +import dagger.android.DispatchingAndroidInjector +import info.nightscout.androidaps.plugins.pump.common.defs.PumpHistoryEntryGroup +import info.nightscout.androidaps.plugins.pump.common.defs.PumpHistoryEntryGroup.Companion.getTranslatedList +import info.nightscout.androidaps.plugins.pump.medtronic.R +import info.nightscout.androidaps.plugins.pump.medtronic.comm.history.pump.PumpHistoryEntry +import info.nightscout.androidaps.plugins.pump.medtronic.data.MedtronicHistoryData +import info.nightscout.androidaps.plugins.pump.medtronic.databinding.MedtronicHistoryActivityBinding +import info.nightscout.androidaps.utils.resources.ResourceHelper +import java.util.* +import javax.inject.Inject + +class MedtronicHistoryActivity : DaggerActivity() { + + @Inject lateinit var medtronicHistoryData: MedtronicHistoryData + @Inject lateinit var resourceHelper: ResourceHelper + + lateinit var historyTypeSpinner: Spinner + lateinit var statusView: TextView + lateinit var recyclerView: RecyclerView + lateinit var llm: LinearLayoutManager + lateinit var recyclerViewAdapter: RecyclerViewAdapter + + var filteredHistoryList: MutableList = ArrayList() + var manualChange = false + var typeListFull: List? = null + + private var _binding: MedtronicHistoryActivityBinding? = null + + //@Inject + //var fragmentInjector: DispatchingAndroidInjector? = null + + // This property is only valid between onCreateView and + // onDestroyView. + private val binding get() = _binding!! + + private fun filterHistory(group: PumpHistoryEntryGroup) { + filteredHistoryList.clear() + val list: MutableList = ArrayList() + list.addAll(medtronicHistoryData.allHistory) + + //LOG.debug("Items on full list: {}", list.size()); + if (group === PumpHistoryEntryGroup.All) { + filteredHistoryList.addAll(list) + } else { + for (pumpHistoryEntry in list) { + if (pumpHistoryEntry.entryType!!.group === group) { + filteredHistoryList.add(pumpHistoryEntry) + } + } + } + + recyclerViewAdapter.setHistoryListInternal(filteredHistoryList) + recyclerViewAdapter.notifyDataSetChanged() + + //LOG.debug("Items on filtered list: {}", filteredHistoryList.size()); + } + + override fun onResume() { + super.onResume() + filterHistory(selectedGroup) + setHistoryTypeSpinner() + } + + private fun setHistoryTypeSpinner() { + manualChange = true + for (i in typeListFull!!.indices) { + if (typeListFull!![i].entryGroup === selectedGroup) { + historyTypeSpinner.setSelection(i) + break + } + } + SystemClock.sleep(200) + manualChange = false + } + + override fun onPause() { + super.onPause() + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + //setContentView(R.layout.medtronic_history_activity) + + _binding = MedtronicHistoryActivityBinding.inflate(getLayoutInflater()) //(inflater, container, false) + + historyTypeSpinner = binding.medtronicHistorytype //findViewById(R.id.medtronic_historytype) + statusView = binding.medtronicHistorystatus //findViewById(R.id.medtronic_historystatus) + recyclerView = binding.medtronicHistoryRecyclerview //findViewById(R.id.medtronic_history_recyclerview) + recyclerView.setHasFixedSize(true) + llm = LinearLayoutManager(this) + recyclerView.setLayoutManager(llm) + recyclerViewAdapter = RecyclerViewAdapter(filteredHistoryList) + recyclerView.setAdapter(recyclerViewAdapter) + statusView.setVisibility(View.GONE) + typeListFull = getTypeList(getTranslatedList(resourceHelper!!)) + val spinnerAdapter = ArrayAdapter(this, R.layout.spinner_centered, typeListFull) + historyTypeSpinner.setAdapter(spinnerAdapter) + historyTypeSpinner.setOnItemSelectedListener(object : AdapterView.OnItemSelectedListener { + override fun onItemSelected(parent: AdapterView<*>?, view: View, position: Int, id: Long) { + if (manualChange) return + val selected = historyTypeSpinner.getSelectedItem() as TypeList + showingType = selected + selectedGroup = selected.entryGroup + filterHistory(selectedGroup) + } + + override fun onNothingSelected(parent: AdapterView<*>?) { + if (manualChange) return + filterHistory(PumpHistoryEntryGroup.All) + } + }) + } + + private fun getTypeList(list: List?): List { + val typeList = ArrayList() + for (pumpHistoryEntryGroup in list!!) { + typeList.add(TypeList(pumpHistoryEntryGroup)) + } + return typeList + } + + class TypeList internal constructor(var entryGroup: PumpHistoryEntryGroup) { + var name: String + override fun toString(): String { + return name + } + + init { + name = entryGroup.translated!! + } + } + + class RecyclerViewAdapter internal constructor(var historyList: List) : RecyclerView.Adapter() { + + + fun setHistoryListInternal(historyList: List) { + // this.historyList.clear(); + // this.historyList.addAll(historyList); + this.historyList = historyList + + // this.notifyDataSetChanged(); + } + + override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): HistoryViewHolder { + val v = LayoutInflater.from(viewGroup.context).inflate(R.layout.medtronic_history_item, // + viewGroup, false) + return HistoryViewHolder(v) + } + + override fun onBindViewHolder(holder: HistoryViewHolder, position: Int) { + val record = historyList[position] + if (record != null) { + holder.timeView.text = record.dateTimeString + holder.typeView.text = record.entryType!!.description + holder.valueView.text = record.displayableValue + } + } + + override fun getItemCount(): Int { + return historyList.size + } + + override fun onAttachedToRecyclerView(recyclerView: RecyclerView) { + super.onAttachedToRecyclerView(recyclerView) + } + + class HistoryViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + var timeView: TextView + var typeView: TextView + var valueView: TextView + + init { + // cv = (CardView)itemView.findViewById(R.id.rileylink_history_item); + timeView = itemView.findViewById(R.id.medtronic_history_time) + typeView = itemView.findViewById(R.id.medtronic_history_source) + valueView = itemView.findViewById(R.id.medtronic_history_description) + } + } + + } + + companion object { + var showingType: TypeList? = null + var selectedGroup = PumpHistoryEntryGroup.All + } +} \ No newline at end of file diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/dialog/RileyLinkStatusDeviceMedtronic.java b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/dialog/RileyLinkStatusDeviceMedtronic.java deleted file mode 100644 index e4b6c0e13a..0000000000 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/dialog/RileyLinkStatusDeviceMedtronic.java +++ /dev/null @@ -1,166 +0,0 @@ -package info.nightscout.androidaps.plugins.pump.medtronic.dialog; - -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.BaseAdapter; -import android.widget.ListView; -import android.widget.TextView; - -import java.util.ArrayList; -import java.util.List; - -import javax.inject.Inject; - -import dagger.android.support.DaggerFragment; -import info.nightscout.androidaps.plugins.pump.common.dialog.RefreshableInterface; -import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.data.RLHistoryItem; -import info.nightscout.androidaps.plugins.pump.common.utils.StringUtil; -import info.nightscout.androidaps.plugins.pump.medtronic.R; -import info.nightscout.androidaps.utils.DateUtil; -import info.nightscout.androidaps.utils.resources.ResourceHelper; - -/** - * Created by andy on 5/19/18. - *

- * This is for 3rd tab, called Medtronic (in RileyLink stats), that should work similarly as the one in Loop. - *

- * Showing currently selected RL, speed of RL, ability to issue simple commands (getModel, tuneUp, gerProfile) - */ - -// TODO needs to be implemented -public class RileyLinkStatusDeviceMedtronic extends DaggerFragment implements RefreshableInterface { - - @Inject ResourceHelper resourceHelper; - @Inject DateUtil dateUtil; - - // @BindView(R.id.rileylink_history_list) - ListView listView; - - RileyLinkCommandListAdapter adapter; - - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View rootView = inflater.inflate(R.layout.rileylink_status_device, container, false); - - adapter = new RileyLinkCommandListAdapter(); - - return rootView; - } - - - @Override - public void onStart() { - super.onStart(); - - this.listView = getActivity().findViewById(R.id.rileylink_history_list); - - listView.setAdapter(adapter); - - refreshData(); - } - - - @Override - public void refreshData() { - // adapter.addItemsAndClean(RileyLinkUtil.getRileyLinkHistory()); - } - - static class ViewHolder { - - TextView itemTime; - TextView itemSource; - TextView itemDescription; - } - - private class RileyLinkCommandListAdapter extends BaseAdapter { - - private final List historyItemList; - private final LayoutInflater mInflator; - - - public RileyLinkCommandListAdapter() { - super(); - historyItemList = new ArrayList<>(); - mInflator = RileyLinkStatusDeviceMedtronic.this.getLayoutInflater(); - } - - - public void addItem(RLHistoryItem item) { - if (!historyItemList.contains(item)) { - historyItemList.add(item); - notifyDataSetChanged(); - } - } - - - public RLHistoryItem getHistoryItem(int position) { - return historyItemList.get(position); - } - - - public void addItemsAndClean(List items) { - this.historyItemList.clear(); - - for (RLHistoryItem item : items) { - - if (!historyItemList.contains(item)) { - historyItemList.add(item); - } - } - - notifyDataSetChanged(); - } - - - public void clear() { - historyItemList.clear(); - notifyDataSetChanged(); - } - - - @Override - public int getCount() { - return historyItemList.size(); - } - - - @Override - public Object getItem(int i) { - return historyItemList.get(i); - } - - - @Override - public long getItemId(int i) { - return i; - } - - - @Override - public View getView(int i, View view, ViewGroup viewGroup) { - RileyLinkStatusDeviceMedtronic.ViewHolder viewHolder; - // General ListView optimization code. - if (view == null) { - view = mInflator.inflate(R.layout.rileylink_status_device_item, null); - viewHolder = new RileyLinkStatusDeviceMedtronic.ViewHolder(); - viewHolder.itemTime = view.findViewById(R.id.rileylink_history_time); - viewHolder.itemSource = view.findViewById(R.id.rileylink_history_source); - viewHolder.itemDescription = view.findViewById(R.id.rileylink_history_description); - view.setTag(viewHolder); - } else { - viewHolder = (RileyLinkStatusDeviceMedtronic.ViewHolder) view.getTag(); - } - - RLHistoryItem item = historyItemList.get(i); - viewHolder.itemTime.setText(StringUtil.toDateTimeString(dateUtil, item.getDateTime())); - viewHolder.itemSource.setText("Riley Link"); // for now - viewHolder.itemDescription.setText(item.getDescription(resourceHelper)); - - return view; - } - } - -} diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/dialog/RileyLinkStatusDeviceMedtronic.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/dialog/RileyLinkStatusDeviceMedtronic.kt new file mode 100644 index 0000000000..b753ac5795 --- /dev/null +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/dialog/RileyLinkStatusDeviceMedtronic.kt @@ -0,0 +1,137 @@ +package info.nightscout.androidaps.plugins.pump.medtronic.dialog + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.BaseAdapter +import android.widget.ListView +import android.widget.TextView +import dagger.android.support.DaggerFragment +import info.nightscout.androidaps.plugins.pump.common.dialog.RefreshableInterface +import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.data.RLHistoryItem +import info.nightscout.androidaps.plugins.pump.common.utils.StringUtil +import info.nightscout.androidaps.plugins.pump.medtronic.R +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.resources.ResourceHelper +import io.reactivex.disposables.CompositeDisposable +import java.util.* +import javax.inject.Inject + +/** + * Created by andy on 5/19/18. + * NOTE: This class is not used yet, so it has no Bindings + * + * This is for 3rd tab, called Medtronic (in RileyLink stats), that should work similarly as the one in Loop. + * + * + * Showing currently selected RL, speed of RL, ability to issue simple commands (getModel, tuneUp, gerProfile) + */ +// TODO needs to be implemented +class RileyLinkStatusDeviceMedtronic : DaggerFragment(), RefreshableInterface { + + @Inject lateinit var resourceHelper: ResourceHelper + @Inject lateinit var dateUtil: DateUtil + + var listView: ListView? = null + var adapter: RileyLinkCommandListAdapter? = null + + private var disposable: CompositeDisposable = CompositeDisposable() + //private var _binding: RileyLinkStatusDeviceBinding? = null + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + + // _binding = LoopFragmentBinding.inflate(inflater, container, false) + // return binding.root + + + val rootView = inflater.inflate(R.layout.rileylink_status_device, container, false) + adapter = RileyLinkCommandListAdapter() + return rootView + } + + override fun onStart() { + super.onStart() + //listView = activity!!.findViewById(R.id.rileylink_history_list) + //listView.setAdapter(adapter) + refreshData() + } + + override fun refreshData() { + // adapter.addItemsAndClean(RileyLinkUtil.getRileyLinkHistory()); + } + + internal class ViewHolder { + var itemTime: TextView? = null + var itemSource: TextView? = null + var itemDescription: TextView? = null + } + + inner class RileyLinkCommandListAdapter : BaseAdapter() { + private val historyItemList: MutableList + private val mInflator: LayoutInflater + fun addItem(item: RLHistoryItem) { + if (!historyItemList.contains(item)) { + historyItemList.add(item) + notifyDataSetChanged() + } + } + + fun getHistoryItem(position: Int): RLHistoryItem { + return historyItemList[position] + } + + fun addItemsAndClean(items: List) { + historyItemList.clear() + for (item in items) { + if (!historyItemList.contains(item)) { + historyItemList.add(item) + } + } + notifyDataSetChanged() + } + + fun clear() { + historyItemList.clear() + notifyDataSetChanged() + } + + override fun getCount(): Int { + return historyItemList.size + } + + override fun getItem(i: Int): Any { + return historyItemList[i] + } + + override fun getItemId(i: Int): Long { + return i.toLong() + } + + override fun getView(i: Int, view: View, viewGroup: ViewGroup): View { + var view = view + val viewHolder: ViewHolder + // General ListView optimization code. + if (view == null) { + view = mInflator.inflate(R.layout.rileylink_status_device_item, null) + viewHolder = ViewHolder() + viewHolder.itemTime = view.findViewById(R.id.rileylink_history_time) + viewHolder.itemSource = view.findViewById(R.id.rileylink_history_source) + viewHolder.itemDescription = view.findViewById(R.id.rileylink_history_description) + view.tag = viewHolder + } else { + viewHolder = view.tag as ViewHolder + } + val item = historyItemList[i] + viewHolder.itemTime!!.text = StringUtil.toDateTimeString(dateUtil, item.dateTime) + viewHolder.itemSource!!.text = "Riley Link" // for now + viewHolder.itemDescription!!.text = item.getDescription(resourceHelper) + return view + } + + init { + historyItemList = ArrayList() + mInflator = this@RileyLinkStatusDeviceMedtronic.layoutInflater + } + } +} \ No newline at end of file diff --git a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/service/RileyLinkMedtronicService.kt b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/service/RileyLinkMedtronicService.kt index 89a31552d2..205742a336 100644 --- a/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/service/RileyLinkMedtronicService.kt +++ b/medtronic/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/service/RileyLinkMedtronicService.kt @@ -49,6 +49,7 @@ class RileyLinkMedtronicService // This empty constructor must be kept, otherwi private var encodingType: RileyLinkEncodingType? = null private var encodingChanged = false private var inPreInit = true + override fun onCreate() { super.onCreate() aapsLogger.debug(LTag.PUMPCOMM, "RileyLinkMedtronicService newly created") @@ -267,19 +268,19 @@ class RileyLinkMedtronicService // This empty constructor must be kept, otherwi } private fun checkParameterValue(key: Int, defaultValue: String, defaultValueDouble: Double): Double { - var `val`: Double + var valueDouble: Double val value = sp.getString(key, defaultValue) - `val` = try { + valueDouble = try { value.toDouble() } catch (ex: Exception) { aapsLogger.error("Error parsing setting: %s, value found %s", key, value) defaultValueDouble } - if (`val` > defaultValueDouble) { + if (valueDouble > defaultValueDouble) { sp.putString(key, defaultValue) - `val` = defaultValueDouble + valueDouble = defaultValueDouble } - return `val` + return valueDouble } fun setNotInPreInit(): Boolean { diff --git a/rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/defs/PumpHistoryEntryGroup.kt b/rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/defs/PumpHistoryEntryGroup.kt index 8b856d7036..8c7cbafada 100644 --- a/rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/defs/PumpHistoryEntryGroup.kt +++ b/rileylink/src/main/java/info/nightscout/androidaps/plugins/pump/common/defs/PumpHistoryEntryGroup.kt @@ -33,6 +33,7 @@ enum class PumpHistoryEntryGroup(val resourceId: Int) { companion object { private var translatedList: MutableList? = null + private fun doTranslation(resourceHelper: ResourceHelper) { translatedList = ArrayList() for (pumpHistoryEntryGroup in values()) { @@ -41,7 +42,8 @@ enum class PumpHistoryEntryGroup(val resourceId: Int) { } } - @JvmStatic fun getTranslatedList(resourceHelper: ResourceHelper): List? { + @JvmStatic + fun getTranslatedList(resourceHelper: ResourceHelper): List? { if (translatedList == null) doTranslation(resourceHelper) return translatedList } diff --git a/rileylink/src/main/res/layout/rileylink_status_device.xml b/rileylink/src/main/res/layout/rileylink_status_device.xml index edf3cbdd7e..f56c4b0d6d 100644 --- a/rileylink/src/main/res/layout/rileylink_status_device.xml +++ b/rileylink/src/main/res/layout/rileylink_status_device.xml @@ -2,7 +2,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - tools:context=".plugins.pump.common.hw.rileylink.dialog.RileyLinkStatusDevice"> + tools:context=".plugins.pump.medtronic.dialog.RileyLinkStatusDevice">