package info.nightscout.androidaps.plugins.DanaR; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.content.SharedPreferences; import android.os.IBinder; import android.preference.PreferenceManager; import android.support.annotation.Nullable; import com.squareup.otto.Subscribe; import org.json.JSONException; import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Date; import java.util.Objects; import info.nightscout.androidaps.BuildConfig; import info.nightscout.androidaps.Config; import info.nightscout.androidaps.Constants; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.data.PumpEnactResult; import info.nightscout.androidaps.db.TempBasal; import info.nightscout.androidaps.db.Treatment; import info.nightscout.androidaps.events.EventAppExit; import info.nightscout.androidaps.events.EventPreferenceChange; import info.nightscout.androidaps.interfaces.ConstraintsInterface; import info.nightscout.androidaps.interfaces.PluginBase; import info.nightscout.androidaps.interfaces.ProfileInterface; import info.nightscout.androidaps.interfaces.PumpDescription; import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.DanaR.Services.ExecutionService; import info.nightscout.androidaps.plugins.NSProfile.NSProfilePlugin; import info.nightscout.androidaps.plugins.Overview.Notification; import info.nightscout.androidaps.plugins.Overview.events.EventDismissNotification; import info.nightscout.androidaps.plugins.Overview.events.EventNewNotification; import info.nightscout.androidaps.plugins.NSClientInternal.data.NSProfile; import info.nightscout.utils.DateUtil; import info.nightscout.utils.DecimalFormatter; import info.nightscout.utils.Round; /** * Created by mike on 05.08.2016. */ public class DanaRPlugin implements PluginBase, PumpInterface, ConstraintsInterface, ProfileInterface { private static Logger log = LoggerFactory.getLogger(DanaRPlugin.class); @Override public String getFragmentClass() { return DanaRFragment.class.getName(); } static boolean fragmentPumpEnabled = true; static boolean fragmentProfileEnabled = true; static boolean fragmentPumpVisible = true; public static ExecutionService sExecutionService; private static DanaRPump sDanaRPump = new DanaRPump(); private static boolean useExtendedBoluses = false; public static PumpDescription pumpDescription = new PumpDescription(); public static DanaRPump getDanaRPump() { return sDanaRPump; } public DanaRPlugin() { SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(MainApp.instance().getApplicationContext()); useExtendedBoluses = sharedPreferences.getBoolean("danar_useextended", false); Context context = MainApp.instance().getApplicationContext(); Intent intent = new Intent(context, ExecutionService.class); context.bindService(intent, mConnection, Context.BIND_AUTO_CREATE); MainApp.bus().register(this); pumpDescription.isBolusCapable = true; // TODO: use description in setTempBasalAbsolute pumpDescription.bolusStep = 0.05d; pumpDescription.isExtendedBolusCapable = true; pumpDescription.extendedBolusStep = 0.05d; pumpDescription.isTempBasalCapable = true; pumpDescription.lowTempBasalStyle = PumpDescription.PERCENT; pumpDescription.highTempBasalStyle = useExtendedBoluses ? PumpDescription.EXTENDED : PumpDescription.PERCENT; pumpDescription.maxHighTempPercent = 200; pumpDescription.maxHighTempAbsolute = 0; pumpDescription.lowTempPercentStep = 10; pumpDescription.lowTempAbsoluteStep = 0; pumpDescription.lowTempPercentDuration = 60; pumpDescription.lowTempAbsoluteDuration = 60; pumpDescription.highTempPercentStep = 10; pumpDescription.highTempAbsoluteStep = 0.05d; pumpDescription.highTempPercentDuration = 60; pumpDescription.highTempAbsoluteDuration = 30; pumpDescription.isSetBasalProfileCapable = true; pumpDescription.basalStep = 0.01d; pumpDescription.basalMinimumRate = 0.04d; pumpDescription.isRefillingCapable = true; } ServiceConnection mConnection = new ServiceConnection() { public void onServiceDisconnected(ComponentName name) { log.debug("Service is disconnected"); sExecutionService = null; } public void onServiceConnected(ComponentName name, IBinder service) { log.debug("Service is connected"); ExecutionService.LocalBinder mLocalBinder = (ExecutionService.LocalBinder) service; sExecutionService = mLocalBinder.getServiceInstance(); } }; @SuppressWarnings("UnusedParameters") @Subscribe public void onStatusEvent(final EventAppExit e) { MainApp.instance().getApplicationContext().unbindService(mConnection); } @Subscribe public void onStatusEvent(final EventPreferenceChange s) { if (isEnabled(PUMP)) { boolean previousValue = useExtendedBoluses; SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(MainApp.instance().getApplicationContext()); useExtendedBoluses = sharedPreferences.getBoolean("danar_useextended", false); pumpDescription.highTempBasalStyle = useExtendedBoluses ? PumpDescription.EXTENDED : PumpDescription.PERCENT; if (useExtendedBoluses != previousValue && isExtendedBoluslInProgress()) { sExecutionService.extendedBolusStop(); } } } // Plugin base interface @Override public int getType() { return PluginBase.PUMP; } @Override public String getName() { return MainApp.instance().getString(R.string.danarpump); } @Override public String getNameShort() { String name = MainApp.sResources.getString(R.string.danarpump_shortname); if (!name.trim().isEmpty()){ //only if translation exists return name; } // use long name as fallback return getName(); } @Override public boolean isEnabled(int type) { if (type == PluginBase.PROFILE) return fragmentProfileEnabled && fragmentPumpEnabled; else if (type == PluginBase.PUMP) return fragmentPumpEnabled; else if (type == PluginBase.CONSTRAINTS) return fragmentPumpEnabled; return false; } @Override public boolean isVisibleInTabs(int type) { if (type == PluginBase.PROFILE || type == PluginBase.CONSTRAINTS) return false; else if (type == PluginBase.PUMP) return fragmentPumpVisible; return false; } @Override public boolean canBeHidden(int type) { return true; } @Override public void setFragmentEnabled(int type, boolean fragmentEnabled) { if (type == PluginBase.PROFILE) this.fragmentProfileEnabled = fragmentEnabled; else if (type == PluginBase.PUMP) this.fragmentPumpEnabled = fragmentEnabled; // if pump profile was enabled need to switch to another too if (type == PluginBase.PUMP && !fragmentEnabled && this.fragmentProfileEnabled) { setFragmentEnabled(PluginBase.PROFILE, false); setFragmentVisible(PluginBase.PROFILE, false); MainApp.getSpecificPlugin(NSProfilePlugin.class).setFragmentEnabled(PluginBase.PROFILE, true); MainApp.getSpecificPlugin(NSProfilePlugin.class).setFragmentVisible(PluginBase.PROFILE, true); } } @Override public void setFragmentVisible(int type, boolean fragmentVisible) { if (type == PluginBase.PUMP) this.fragmentPumpVisible = fragmentVisible; } @Override public boolean isInitialized() { return getDanaRPump().lastConnection.getTime() > 0 && getDanaRPump().isExtendedBolusEnabled; } @Override public boolean isSuspended() { return getDanaRPump().pumpSuspended; } @Override public boolean isBusy() { if (sExecutionService == null) return false; return sExecutionService.isConnected() || sExecutionService.isConnecting(); } // Pump interface @Override public boolean isTempBasalInProgress() { if (getRealTempBasal() != null) return true; if (getExtendedBolus() != null && useExtendedBoluses) return true; return false; } public boolean isRealTempBasalInProgress() { return getRealTempBasal() != null; //TODO: crosscheck here } @Override public boolean isExtendedBoluslInProgress() { return getExtendedBolus() != null; //TODO: crosscheck here } @Override public int setNewBasalProfile(NSProfile profile) { if (sExecutionService == null) { log.error("setNewBasalProfile sExecutionService is null"); return FAILED; } if (!isInitialized()) { log.error("setNewBasalProfile not initialized"); Notification notification = new Notification(Notification.PROFILE_NOT_SET_NOT_INITIALIZED, MainApp.sResources.getString(R.string.pumpNotInitializedProfileNotSet), Notification.URGENT); MainApp.bus().post(new EventNewNotification(notification)); return FAILED; } else { MainApp.bus().post(new EventDismissNotification(Notification.PROFILE_NOT_SET_NOT_INITIALIZED)); } if (!sExecutionService.updateBasalsInPump(profile)) { Notification notification = new Notification(Notification.FAILED_UDPATE_PROFILE, MainApp.sResources.getString(R.string.failedupdatebasalprofile), Notification.URGENT); MainApp.bus().post(new EventNewNotification(notification)); return FAILED; } else { MainApp.bus().post(new EventDismissNotification(Notification.PROFILE_NOT_SET_NOT_INITIALIZED)); MainApp.bus().post(new EventDismissNotification(Notification.FAILED_UDPATE_PROFILE)); return SUCCESS; } } @Override public boolean isThisProfileSet(NSProfile profile) { if (!isInitialized()) return true; // TODO: not sure what's better. so far TRUE to prevent too many SMS DanaRPump pump = getDanaRPump(); if (pump.pumpProfiles == null) return true; // TODO: not sure what's better. so far TRUE to prevent too many SMS int basalValues = pump.basal48Enable ? 48 : 24; int basalIncrement = pump.basal48Enable ? 30 * 60 : 60 * 60; for (int h = 0; h < basalValues; h++) { Double pumpValue = pump.pumpProfiles[pump.activeProfile][h]; Double profileValue = profile.getBasal(h * basalIncrement); if (profileValue == null) return true; if (Math.abs(pumpValue - profileValue) > getPumpDescription().basalStep) { log.debug("Diff found. Hour: " + h + " Pump: " + pumpValue + " Profile: " + profileValue); return false; } } return true; } @Override public Date lastDataTime() { return getDanaRPump().lastConnection; } @Override public void refreshDataFromPump(String reason) { if (!isConnected() && !isConnecting()) { doConnect(reason); } } @Override public double getBaseBasalRate() { return getDanaRPump().currentBasal; } @Override public double getTempBasalAbsoluteRate() { TempBasal tb = getRealTempBasal(); if (tb != null) { if (tb.isAbsolute) { return tb.absolute; } else { Double baseRate = getBaseBasalRate(); Double tempRate = baseRate * (tb.percent / 100d); return tempRate; } } TempBasal eb = getExtendedBolus(); if (eb != null && useExtendedBoluses) { return getBaseBasalRate() + eb.absolute; } return 0; } @Override public double getTempBasalRemainingMinutes() { if (isRealTempBasalInProgress()) return getRealTempBasal().getPlannedRemainingMinutes(); if (isExtendedBoluslInProgress() && useExtendedBoluses) return getExtendedBolus().getPlannedRemainingMinutes(); return 0; } @Override public TempBasal getTempBasal() { if (isRealTempBasalInProgress()) return getRealTempBasal(); if (isExtendedBoluslInProgress() && useExtendedBoluses) return getExtendedBolus(); return null; } public TempBasal getTempBasal(Date time) { TempBasal temp = MainApp.getConfigBuilder().getActiveTempBasals().getTempBasal(time); if (temp != null) return temp; if (useExtendedBoluses) return MainApp.getConfigBuilder().getActiveTempBasals().getExtendedBolus(time); return null; } public TempBasal getRealTempBasal() { return MainApp.getConfigBuilder().getActiveTempBasals().getTempBasal(new Date()); } @Override public TempBasal getExtendedBolus() { return MainApp.getConfigBuilder().getActiveTempBasals().getExtendedBolus(new Date()); } @Override public PumpEnactResult deliverTreatment(Double insulin, Integer carbs, Context context) { ConfigBuilderPlugin configBuilderPlugin = MainApp.getConfigBuilder(); insulin = configBuilderPlugin.applyBolusConstraints(insulin); if (insulin > 0 || carbs > 0) { Treatment t = new Treatment(); boolean connectionOK = false; if (insulin > 0 || carbs > 0) connectionOK = sExecutionService.bolus(insulin, carbs, t); PumpEnactResult result = new PumpEnactResult(); result.success = connectionOK; result.bolusDelivered = t.insulin; result.carbsDelivered = carbs; result.comment = MainApp.instance().getString(R.string.virtualpump_resultok); if (Config.logPumpActions) log.debug("deliverTreatment: OK. Asked: " + insulin + " Delivered: " + result.bolusDelivered); return result; } else { PumpEnactResult result = new PumpEnactResult(); result.success = false; result.bolusDelivered = 0d; result.carbsDelivered = 0; result.comment = MainApp.instance().getString(R.string.danar_invalidinput); log.error("deliverTreatment: Invalid input"); return result; } } @Override public void stopBolusDelivering() { if (sExecutionService == null) { log.error("stopBolusDelivering sExecutionService is null"); return; } sExecutionService.bolusStop(); } // This is called from APS @Override public PumpEnactResult setTempBasalAbsolute(Double absoluteRate, Integer durationInMinutes) { // Recheck pump status if older than 30 min if (getDanaRPump().lastConnection.getTime() + 30 * 60 * 1000L < new Date().getTime()) { doConnect("setTempBasalAbsolute old data"); } PumpEnactResult result = new PumpEnactResult(); ConfigBuilderPlugin configBuilderPlugin = MainApp.getConfigBuilder(); absoluteRate = configBuilderPlugin.applyBasalConstraints(absoluteRate); final boolean doTempOff = getBaseBasalRate() - absoluteRate == 0d; final boolean doLowTemp = absoluteRate < getBaseBasalRate(); final boolean doHighTemp = absoluteRate > getBaseBasalRate() && !useExtendedBoluses; final boolean doExtendedTemp = absoluteRate > getBaseBasalRate() && useExtendedBoluses; if (doTempOff) { // If extended in progress if (isExtendedBoluslInProgress() && useExtendedBoluses) { if (Config.logPumpActions) log.debug("setTempBasalAbsolute: Stopping extended bolus (doTempOff)"); return cancelExtendedBolus(); } // If temp in progress if (isRealTempBasalInProgress()) { if (Config.logPumpActions) log.debug("setTempBasalAbsolute: Stopping temp basal (doTempOff)"); return cancelRealTempBasal(); } result.success = true; result.enacted = false; result.percent = 100; result.isPercent = true; result.isTempCancel = true; if (Config.logPumpActions) log.debug("setTempBasalAbsolute: doTempOff OK"); return result; } if (doLowTemp || doHighTemp) { Integer percentRate = Double.valueOf(absoluteRate / getBaseBasalRate() * 100).intValue(); if (percentRate < 100) percentRate = Round.ceilTo((double) percentRate, 10d).intValue(); else percentRate = Round.floorTo((double) percentRate, 10d).intValue(); if (percentRate > 200) { percentRate = 200; } // If extended in progress if (isExtendedBoluslInProgress() && useExtendedBoluses) { if (Config.logPumpActions) log.debug("setTempBasalAbsolute: Stopping extended bolus (doLowTemp || doHighTemp)"); result = cancelExtendedBolus(); if (!result.success) { log.error("setTempBasalAbsolute: Failed to stop previous extended bolus (doLowTemp || doHighTemp)"); return result; } } // Check if some temp is already in progress if (isRealTempBasalInProgress()) { // Correct basal already set ? if (getRealTempBasal().percent == percentRate) { result.success = true; result.percent = percentRate; result.absolute = getTempBasalAbsoluteRate(); result.enacted = false; result.duration = ((Double) getTempBasalRemainingMinutes()).intValue(); result.isPercent = true; result.isTempCancel = false; if (Config.logPumpActions) log.debug("setTempBasalAbsolute: Correct temp basal already set (doLowTemp || doHighTemp)"); return result; } else { if (Config.logPumpActions) log.debug("setTempBasalAbsolute: Stopping temp basal (doLowTemp || doHighTemp)"); result = cancelRealTempBasal(); // Check for proper result if (!result.success) { log.error("setTempBasalAbsolute: Failed to stop previous temp basal (doLowTemp || doHighTemp)"); return result; } } } // Convert duration from minutes to hours if (Config.logPumpActions) log.debug("setTempBasalAbsolute: Setting temp basal " + percentRate + "% for " + durationInMinutes + " mins (doLowTemp || doHighTemp)"); return setTempBasalPercent(percentRate, durationInMinutes); } if (doExtendedTemp) { // Check if some temp is already in progress if (isRealTempBasalInProgress()) { if (Config.logPumpActions) log.debug("setTempBasalAbsolute: Stopping temp basal (doExtendedTemp)"); result = cancelRealTempBasal(); // Check for proper result if (!result.success) { log.error("setTempBasalAbsolute: Failed to stop previous temp basal (doExtendedTemp)"); return result; } } // Calculate # of halfHours from minutes Integer durationInHalfHours = Math.max(durationInMinutes / 30, 1); // We keep current basal running so need to sub current basal Double extendedRateToSet = absoluteRate - getBaseBasalRate(); extendedRateToSet = configBuilderPlugin.applyBasalConstraints(extendedRateToSet); // needs to be rounded to 0.1 extendedRateToSet = Round.roundTo(extendedRateToSet, 0.1d); // What is current rate of extended bolusing in u/h? if (Config.logPumpActions) { log.debug("setTempBasalAbsolute: Extended bolus in progress: " + isExtendedBoluslInProgress() + " rate: " + getDanaRPump().extendedBolusAbsoluteRate + "U/h duration remaining: " + getDanaRPump().extendedBolusRemainingMinutes + "min"); log.debug("setTempBasalAbsolute: Rate to set: " + extendedRateToSet + "U/h"); } // Compare with extended rate in progress if (isExtendedBoluslInProgress() && Math.abs(getDanaRPump().extendedBolusAbsoluteRate - extendedRateToSet) < getPumpDescription().extendedBolusStep) { // correct extended already set result.success = true; result.absolute = getDanaRPump().extendedBolusAbsoluteRate; result.enacted = false; result.duration = getDanaRPump().extendedBolusRemainingMinutes; result.isPercent = false; result.isTempCancel = false; if (Config.logPumpActions) log.debug("setTempBasalAbsolute: Correct extended already set"); return result; } // Now set new extended, no need to to stop previous (if running) because it's replaced Double extendedAmount = extendedRateToSet / 2 * durationInHalfHours; if (Config.logPumpActions) log.debug("setTempBasalAbsolute: Setting extended: " + extendedAmount + "U halfhours: " + durationInHalfHours); result = setExtendedBolus(extendedAmount, durationInMinutes); if (!result.success) { log.error("setTempBasalAbsolute: Failed to set extended bolus"); return result; } if (Config.logPumpActions) log.debug("setTempBasalAbsolute: Extended bolus set ok"); result.absolute = result.absolute + getBaseBasalRate(); return result; } // We should never end here log.error("setTempBasalAbsolute: Internal error"); result.success = false; result.comment = "Internal error"; return result; } @Override public PumpEnactResult setTempBasalPercent(Integer percent, Integer durationInMinutes) { PumpEnactResult result = new PumpEnactResult(); ConfigBuilderPlugin configBuilderPlugin = MainApp.getConfigBuilder(); percent = configBuilderPlugin.applyBasalConstraints(percent); if (percent < 0) { result.isTempCancel = false; result.enacted = false; result.success = false; result.comment = MainApp.instance().getString(R.string.danar_invalidinput); log.error("setTempBasalPercent: Invalid input"); return result; } if (percent > 200) percent = 200; if (getDanaRPump().isTempBasalInProgress && getDanaRPump().tempBasalPercent == percent) { result.enacted = false; result.success = true; result.isTempCancel = false; result.comment = MainApp.instance().getString(R.string.virtualpump_resultok); result.duration = getDanaRPump().tempBasalRemainingMin; result.percent = getDanaRPump().tempBasalPercent; result.isPercent = true; if (Config.logPumpActions) log.debug("setTempBasalPercent: Correct value already set"); return result; } int durationInHours = Math.max(durationInMinutes / 60, 1); boolean connectionOK = sExecutionService.tempBasal(percent, durationInHours); if (connectionOK && getDanaRPump().isTempBasalInProgress && getDanaRPump().tempBasalPercent == percent) { result.enacted = true; result.success = true; result.comment = MainApp.instance().getString(R.string.virtualpump_resultok); result.isTempCancel = false; result.duration = getDanaRPump().tempBasalRemainingMin; result.percent = getDanaRPump().tempBasalPercent; result.isPercent = true; if (Config.logPumpActions) log.debug("setTempBasalPercent: OK"); return result; } result.enacted = false; result.success = false; result.comment = MainApp.instance().getString(R.string.danar_valuenotsetproperly); log.error("setTempBasalPercent: Failed to set temp basal"); return result; } @Override public PumpEnactResult setExtendedBolus(Double insulin, Integer durationInMinutes) { ConfigBuilderPlugin configBuilderPlugin = MainApp.getConfigBuilder(); insulin = configBuilderPlugin.applyBolusConstraints(insulin); // needs to be rounded insulin = Round.roundTo(insulin, getPumpDescription().extendedBolusStep); PumpEnactResult result = new PumpEnactResult(); if (getDanaRPump().isExtendedInProgress && Math.abs(getDanaRPump().extendedBolusAmount - insulin) < getPumpDescription().extendedBolusStep) { result.enacted = false; result.success = true; result.comment = MainApp.instance().getString(R.string.virtualpump_resultok); result.duration = getDanaRPump().extendedBolusRemainingMinutes; result.absolute = getDanaRPump().extendedBolusAbsoluteRate; result.isPercent = false; result.isTempCancel = false; if (Config.logPumpActions) log.debug("setExtendedBolus: Correct extended bolus already set. Current: " + getDanaRPump().extendedBolusAmount + " Asked: " + insulin); return result; } int durationInHalfHours = Math.max(durationInMinutes / 30, 1); boolean connectionOK = sExecutionService.extendedBolus(insulin, durationInHalfHours); if (connectionOK && getDanaRPump().isExtendedInProgress && Math.abs(getDanaRPump().extendedBolusAmount - insulin) < getPumpDescription().extendedBolusStep) { result.enacted = true; result.success = true; result.comment = MainApp.instance().getString(R.string.virtualpump_resultok); result.isTempCancel = false; result.duration = getDanaRPump().extendedBolusRemainingMinutes; result.absolute = getDanaRPump().extendedBolusAbsoluteRate; result.bolusDelivered = getDanaRPump().extendedBolusAmount; result.isPercent = false; if (Config.logPumpActions) log.debug("setExtendedBolus: OK"); return result; } result.enacted = false; result.success = false; result.comment = MainApp.instance().getString(R.string.danar_valuenotsetproperly); log.error("setExtendedBolus: Failed to extended bolus"); return result; } @Override public PumpEnactResult cancelTempBasal() { if (isRealTempBasalInProgress()) return cancelRealTempBasal(); if (isExtendedBoluslInProgress()) return cancelExtendedBolus(); PumpEnactResult result = new PumpEnactResult(); result.success = true; result.enacted = false; result.comment = MainApp.instance().getString(R.string.virtualpump_resultok); result.isTempCancel = true; return result; } public PumpEnactResult cancelRealTempBasal() { PumpEnactResult result = new PumpEnactResult(); if (getDanaRPump().isTempBasalInProgress) { sExecutionService.tempBasalStop(); result.enacted = true; result.isTempCancel = true; } if (!getDanaRPump().isTempBasalInProgress) { result.success = true; result.isTempCancel = true; result.comment = MainApp.instance().getString(R.string.virtualpump_resultok); if (Config.logPumpActions) log.debug("cancelRealTempBasal: OK"); return result; } else { result.success = false; result.comment = MainApp.instance().getString(R.string.danar_valuenotsetproperly); result.isTempCancel = true; log.error("cancelRealTempBasal: Failed to cancel temp basal"); return result; } } @Override public PumpEnactResult cancelExtendedBolus() { PumpEnactResult result = new PumpEnactResult(); if (getDanaRPump().isExtendedInProgress) { sExecutionService.extendedBolusStop(); result.enacted = true; result.isTempCancel = true; } if (!getDanaRPump().isExtendedInProgress) { result.success = true; result.comment = MainApp.instance().getString(R.string.virtualpump_resultok); if (Config.logPumpActions) log.debug("cancelExtendedBolus: OK"); return result; } else { result.success = false; result.comment = MainApp.instance().getString(R.string.danar_valuenotsetproperly); log.error("cancelExtendedBolus: Failed to cancel extended bolus"); return result; } } public static void doConnect(String from) { if (sExecutionService != null) sExecutionService.connect(from); } public static boolean isConnected() { return sExecutionService != null && sExecutionService.isConnected(); } public static boolean isConnecting() { return sExecutionService != null && sExecutionService.isConnecting(); } public static void doDisconnect(String from) { if (sExecutionService != null) sExecutionService.disconnect(from); } @Override public JSONObject getJSONStatus() { if (getDanaRPump().lastConnection.getTime() + 5 * 60 * 1000L < new Date().getTime()) { return null; } JSONObject pump = new JSONObject(); JSONObject battery = new JSONObject(); JSONObject status = new JSONObject(); JSONObject extended = new JSONObject(); try { battery.put("percent", getDanaRPump().batteryRemaining); status.put("status", getDanaRPump().pumpSuspended ? "suspended" : "normal"); status.put("timestamp", DateUtil.toISOString(getDanaRPump().lastConnection)); extended.put("Version", BuildConfig.VERSION_NAME + "-" + BuildConfig.BUILDVERSION); extended.put("PumpIOB", getDanaRPump().iob); extended.put("LastBolus", getDanaRPump().lastBolusTime.toLocaleString()); extended.put("LastBolusAmount", getDanaRPump().lastBolusAmount); TempBasal tb = getTempBasal(); if (tb != null) { extended.put("TempBasalAbsoluteRate", getTempBasalAbsoluteRate()); extended.put("TempBasalStart", tb.timeStart.toLocaleString()); extended.put("TempBasalRemaining", tb.getPlannedRemainingMinutes()); extended.put("IsExtended", tb.isExtended); } extended.put("BaseBasalRate", getBaseBasalRate()); try { extended.put("ActiveProfile", MainApp.getConfigBuilder().getActiveProfile().getProfile().getActiveProfile()); } catch (Exception e) { } pump.put("battery", battery); pump.put("status", status); pump.put("extended", extended); pump.put("reservoir", (int) getDanaRPump().reservoirRemainingUnits); pump.put("clock", DateUtil.toISOString(new Date())); } catch (JSONException e) { e.printStackTrace(); } return pump; } @Override public String deviceID() { return getDanaRPump().serialNumber; } @Override public PumpDescription getPumpDescription() { return pumpDescription; } /** * Constraint interface */ @Override public boolean isLoopEnabled() { return true; } @Override public boolean isClosedModeEnabled() { return true; } @Override public boolean isAutosensModeEnabled() { return true; } @Override public boolean isAMAModeEnabled() { return true; } @SuppressWarnings("PointlessBooleanExpression") @Override public Double applyBasalConstraints(Double absoluteRate) { double origAbsoluteRate = absoluteRate; if (getDanaRPump() != null) { if (absoluteRate > getDanaRPump().maxBasal) { absoluteRate = getDanaRPump().maxBasal; if (Config.logConstraintsChanges && origAbsoluteRate != Constants.basalAbsoluteOnlyForCheckLimit) log.debug("Limiting rate " + origAbsoluteRate + "U/h by pump constraint to " + absoluteRate + "U/h"); } } return absoluteRate; } @SuppressWarnings("PointlessBooleanExpression") @Override public Integer applyBasalConstraints(Integer percentRate) { Integer origPercentRate = percentRate; if (percentRate < 0) percentRate = 0; if (percentRate > 200) percentRate = 200; if (!Objects.equals(percentRate, origPercentRate) && Config.logConstraintsChanges && !Objects.equals(origPercentRate, Constants.basalPercentOnlyForCheckLimit)) log.debug("Limiting percent rate " + origPercentRate + "% to " + percentRate + "%"); return percentRate; } @SuppressWarnings("PointlessBooleanExpression") @Override public Double applyBolusConstraints(Double insulin) { double origInsulin = insulin; if (getDanaRPump() != null) { if (insulin > getDanaRPump().maxBolus) { insulin = getDanaRPump().maxBolus; if (Config.logConstraintsChanges && origInsulin != Constants.bolusOnlyForCheckLimit) log.debug("Limiting bolus " + origInsulin + "U by pump constraint to " + insulin + "U"); } } return insulin; } @Override public Integer applyCarbsConstraints(Integer carbs) { return carbs; } @Override public Double applyMaxIOBConstraints(Double maxIob) { return maxIob; } @Nullable @Override public NSProfile getProfile() { DanaRPump pump = getDanaRPump(); if (pump.lastSettingsRead.getTime() == 0) return null; // no info now return pump.createConvertedProfile(); } // Reply for sms communicator public String shortStatus(boolean veryShort) { String ret = ""; if (getDanaRPump().lastConnection.getTime() != 0) { Long agoMsec = new Date().getTime() - getDanaRPump().lastConnection.getTime(); int agoMin = (int) (agoMsec / 60d / 1000d); ret += "LastConn: " + agoMin + " minago\n"; } if (getDanaRPump().lastBolusTime.getTime() != 0) { ret += "LastBolus: " + DecimalFormatter.to2Decimal(getDanaRPump().lastBolusAmount) + "U @" + android.text.format.DateFormat.format("HH:mm", getDanaRPump().lastBolusTime) + "\n"; } if (isRealTempBasalInProgress()) { ret += "Temp: " + getRealTempBasal().toString() + "\n"; } if (isExtendedBoluslInProgress()) { ret += "Extended: " + getExtendedBolus().toString() + "\n"; } if (!veryShort){ ret += "TDD: " + DecimalFormatter.to0Decimal(getDanaRPump().dailyTotalUnits) + " / " + getDanaRPump().maxDailyTotalUnits + " U\n"; } ret += "IOB: " + getDanaRPump().iob + "U\n"; ret += "Reserv: " + DecimalFormatter.to0Decimal(getDanaRPump().reservoirRemainingUnits) + "U\n"; ret += "Batt: " + getDanaRPump().batteryRemaining + "\n"; return ret; } // TODO: daily total constraint }