Day 1
* Some UI rework * Start removing (retry) logic from ruffyscripter to ComboPlugin * Cleanups all over * Remove TDD stuff, this can be done independently of a pump * New SPI/API methods confirmAlert, readReservoirLevelAndLastBolus * Add warning and error codes from Combo manual * Rework commands to just execute an action (verification will be in ComboPlugin eventually, together with retry logic) * Rework commands to update state in field as command progresses rather than returnin/throwing. * Initial version reading bolus and error history (no DB sync yet).
This commit is contained in:
parent
14307cb77b
commit
6b6d252173
|
@ -15,8 +15,6 @@ import com.squareup.otto.Subscribe;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import de.jotomo.ruffy.spi.CommandResult;
|
||||
import de.jotomo.ruffy.spi.PumpState;
|
||||
import de.jotomo.ruffy.spi.history.Bolus;
|
||||
|
@ -62,20 +60,17 @@ public class ComboFragment extends SubscriberFragment implements View.OnClickLis
|
|||
public void onClick(View view) {
|
||||
switch (view.getId()) {
|
||||
case R.id.combo_refresh:
|
||||
Thread thread = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
ComboPlugin.getPlugin().refreshDataFromPump("User request");
|
||||
}
|
||||
});
|
||||
Thread thread = new Thread(() -> ComboPlugin.getPlugin().refreshDataFromPump("User request"));
|
||||
thread.start();
|
||||
break;
|
||||
case R.id.combo_error_history:
|
||||
// TODO show popup with pump errors and comm problems
|
||||
break;
|
||||
case R.id.combo_stats:
|
||||
// case R.id.combo_stats:
|
||||
// TODO show TDD stats from the pump (later)
|
||||
break;
|
||||
// how about rather making this a pump agnostic thing, it's all in the DB,
|
||||
// add a TDD tab to Treatments?
|
||||
// break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -85,99 +80,97 @@ public class ComboFragment extends SubscriberFragment implements View.OnClickLis
|
|||
}
|
||||
|
||||
public void updateGUI() {
|
||||
Activity activity = getActivity();
|
||||
if (activity != null)
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
ComboPlugin plugin = ComboPlugin.getPlugin();
|
||||
Activity fragmentActivity = getActivity();
|
||||
if (fragmentActivity != null)
|
||||
fragmentActivity.runOnUiThread(() -> {
|
||||
ComboPlugin plugin = ComboPlugin.getPlugin();
|
||||
|
||||
// activity
|
||||
String activity = plugin.getPump().activity;
|
||||
activityView.setText(activity != null ? activity : getString(R.string.combo_action_idle));
|
||||
// state
|
||||
stateView.setText(plugin.getStateSummary());
|
||||
PumpState ps = plugin.getPump().state;
|
||||
if (plugin.getPump().state.errorMsg != null
|
||||
|| ps.insulinState == PumpState.EMPTY
|
||||
|| ps.batteryState == PumpState.EMPTY) {
|
||||
stateView.setTextColor(Color.RED);
|
||||
} else if (plugin.getPump().state.suspended) {
|
||||
stateView.setTextColor(Color.YELLOW);
|
||||
} else {
|
||||
stateView.setTextColor(Color.WHITE);
|
||||
}
|
||||
|
||||
if (plugin.isInitialized()) {
|
||||
// state
|
||||
stateView.setText(plugin.getStateSummary());
|
||||
// activity
|
||||
String activity = plugin.getPump().activity;
|
||||
activityView.setText(activity != null ? activity : getString(R.string.combo_action_idle));
|
||||
|
||||
PumpState ps = plugin.getPump().state;
|
||||
if (plugin.getPump().state.errorMsg != null
|
||||
|| ps.insulinState == PumpState.EMPTY
|
||||
|| ps.batteryState == PumpState.EMPTY) {
|
||||
stateView.setTextColor(Color.RED);
|
||||
} else if (plugin.getPump().state.suspended) {
|
||||
stateView.setTextColor(Color.YELLOW);
|
||||
if (plugin.isInitialized()) {
|
||||
// battery
|
||||
if (ps.batteryState == PumpState.EMPTY) {
|
||||
batteryView.setText("{fa-battery-empty}");
|
||||
batteryView.setTextColor(Color.RED);
|
||||
} else if (ps.batteryState == PumpState.LOW) {
|
||||
batteryView.setText("{fa-battery-quarter}");
|
||||
batteryView.setTextColor(Color.YELLOW);
|
||||
} else if (ps.batteryState == PumpState.UNKNOWN) {
|
||||
batteryView.setText("");
|
||||
batteryView.setTextColor(Color.YELLOW);
|
||||
} else {
|
||||
batteryView.setText("{fa-battery-full}");
|
||||
batteryView.setTextColor(Color.WHITE);
|
||||
}
|
||||
|
||||
// reservoir
|
||||
int reservoirLevel = plugin.getPump().reservoirLevel;
|
||||
reservoirView.setText(reservoirLevel == -1 ? "" : "" + reservoirLevel + " U");
|
||||
if (ps.insulinState == PumpState.LOW) {
|
||||
reservoirView.setTextColor(Color.YELLOW);
|
||||
} else if (ps.insulinState == PumpState.EMPTY) {
|
||||
reservoirView.setTextColor(Color.RED);
|
||||
} else {
|
||||
reservoirView.setTextColor(Color.WHITE);
|
||||
}
|
||||
|
||||
// last connection
|
||||
CommandResult lastCmdResult = plugin.getPump().lastCmdResult;
|
||||
if (lastCmdResult != null) {
|
||||
String minAgo = DateUtil.minAgo(plugin.getPump().lastSuccessfulConnection);
|
||||
String time = DateUtil.timeString(plugin.getPump().lastSuccessfulConnection);
|
||||
if (plugin.getPump().lastSuccessfulConnection < System.currentTimeMillis() + 30 * 60 * 1000) {
|
||||
lastConnectionView.setText(getString(R.string.combo_no_pump_connection, minAgo));
|
||||
lastConnectionView.setTextColor(Color.RED);
|
||||
}
|
||||
if (plugin.getPump().lastConnectionAttempt > plugin.getPump().lastSuccessfulConnection) {
|
||||
lastConnectionView.setText(R.string.combo_connect_attempt_failed);
|
||||
lastConnectionView.setTextColor(Color.YELLOW);
|
||||
} else {
|
||||
stateView.setTextColor(Color.WHITE);
|
||||
lastConnectionView.setText(getString(R.string.combo_last_connection_time, minAgo, time));
|
||||
lastConnectionView.setTextColor(Color.WHITE);
|
||||
}
|
||||
|
||||
// battery
|
||||
if (ps.batteryState == PumpState.EMPTY) {
|
||||
batteryView.setText("{fa-battery-empty}");
|
||||
batteryView.setTextColor(Color.RED);
|
||||
} else if (ps.batteryState == PumpState.LOW) {
|
||||
batteryView.setText("{fa-battery-quarter}");
|
||||
batteryView.setTextColor(Color.YELLOW);
|
||||
// last bolus
|
||||
Bolus bolus = plugin.getPump().lastBolus;
|
||||
if (bolus != null && bolus.timestamp + 6 * 60 * 60 * 1000 >= System.currentTimeMillis()) {
|
||||
long agoMsc = System.currentTimeMillis() - bolus.timestamp;
|
||||
double agoHours = agoMsc / 60d / 60d / 1000d;
|
||||
lastBolusView.setText(getString(R.string.combo_last_bolus,
|
||||
bolus.amount,
|
||||
agoHours,
|
||||
getString(R.string.hoursago),
|
||||
DateUtil.timeString(bolus.timestamp)));
|
||||
} else {
|
||||
batteryView.setText("{fa-battery-full}");
|
||||
batteryView.setTextColor(Color.WHITE);
|
||||
lastBolusView.setText("");
|
||||
}
|
||||
|
||||
// reservoir
|
||||
int reservoirLevel = plugin.getPump().reservoirLevel;
|
||||
reservoirView.setText(reservoirLevel == -1 ? "" : "" + reservoirLevel + " U");
|
||||
if (ps.insulinState == PumpState.LOW) {
|
||||
reservoirView.setTextColor(Color.YELLOW);
|
||||
} else if (ps.insulinState == PumpState.EMPTY) {
|
||||
reservoirView.setTextColor(Color.RED);
|
||||
} else {
|
||||
reservoirView.setTextColor(Color.WHITE);
|
||||
}
|
||||
|
||||
// last connection
|
||||
CommandResult lastCmdResult = plugin.getPump().lastCmdResult;
|
||||
if (lastCmdResult != null) {
|
||||
String minAgo = DateUtil.minAgo(lastCmdResult.completionTime);
|
||||
String time = DateUtil.timeString(lastCmdResult.completionTime);
|
||||
if (plugin.getPump().lastSuccessfulConnection < System.currentTimeMillis() + 30 * 60 * 1000) {
|
||||
lastConnectionView.setText(getString(R.string.combo_no_pump_connection, minAgo));
|
||||
lastConnectionView.setTextColor(Color.RED);
|
||||
}
|
||||
if (plugin.getPump().lastConnectionAttempt > plugin.getPump().lastSuccessfulConnection) {
|
||||
lastConnectionView.setText(R.string.combo_connect_attempt_failed);
|
||||
lastConnectionView.setTextColor(Color.YELLOW);
|
||||
} else {
|
||||
lastConnectionView.setText(getString(R.string.combo_last_connection_time, minAgo, time));
|
||||
lastConnectionView.setTextColor(Color.WHITE);
|
||||
}
|
||||
|
||||
// last bolus
|
||||
List<Bolus> history = plugin.getPump().history.bolusHistory;
|
||||
if (!history.isEmpty() && history.get(0).timestamp + 6 * 60 * 60 * 1000 >= System.currentTimeMillis()) {
|
||||
Bolus bolus = history.get(0);
|
||||
long agoMsc = System.currentTimeMillis() - bolus.timestamp;
|
||||
double agoHours = agoMsc / 60d / 60d / 1000d;
|
||||
lastBolusView.setText(getString(R.string.combo_last_bolus,
|
||||
bolus.amount,
|
||||
agoHours,
|
||||
getString(R.string.hoursago),
|
||||
DateUtil.timeString(bolus.timestamp)));
|
||||
} else {
|
||||
lastBolusView.setText("");
|
||||
}
|
||||
|
||||
// TBR
|
||||
boolean tbrActive = ps.tbrPercent != -1 && ps.tbrPercent != 100;
|
||||
String tbrStr = "";
|
||||
if (tbrActive) {
|
||||
long minSinceRead = (System.currentTimeMillis() - lastCmdResult.completionTime) / 1000 / 60;
|
||||
long remaining = ps.tbrRemainingDuration - minSinceRead;
|
||||
if (remaining >= 0) {
|
||||
tbrStr = getString(R.string.combo_tbr_remaining, ps.tbrPercent, remaining);
|
||||
}
|
||||
}
|
||||
tempBasalText.setText(tbrStr);
|
||||
// TBR
|
||||
boolean tbrActive = ps.tbrPercent != -1 && ps.tbrPercent != 100;
|
||||
String tbrStr = "";
|
||||
if (tbrActive) {
|
||||
long minSinceRead = (System.currentTimeMillis() - plugin.getPump().state.timestamp) / 1000 / 60;
|
||||
long remaining = ps.tbrRemainingDuration - minSinceRead;
|
||||
if (remaining >= 0) {
|
||||
tbrStr = getString(R.string.combo_tbr_remaining, ps.tbrPercent, remaining);
|
||||
}
|
||||
}
|
||||
tempBasalText.setText(tbrStr);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -35,6 +35,7 @@ import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin;
|
|||
import info.nightscout.androidaps.plugins.Overview.events.EventOverviewBolusProgress;
|
||||
import info.nightscout.androidaps.plugins.PumpCombo.events.EventComboPumpUpdateGUI;
|
||||
import info.nightscout.utils.DateUtil;
|
||||
import info.nightscout.utils.DecimalFormatter;
|
||||
|
||||
/**
|
||||
* Created by mike on 05.08.2016.
|
||||
|
@ -51,7 +52,7 @@ public class ComboPlugin implements PluginBase, PumpInterface {
|
|||
private final RuffyCommands ruffyScripter;
|
||||
|
||||
// TODO access to pump (and its members) is chaotic and needs an update
|
||||
private ComboPump pump = new ComboPump();
|
||||
private static ComboPump pump = new ComboPump();
|
||||
|
||||
private static ComboPlugin plugin = null;
|
||||
|
||||
|
@ -128,12 +129,12 @@ public class ComboPlugin implements PluginBase, PumpInterface {
|
|||
String getStateSummary() {
|
||||
PumpState ps = pump.state;
|
||||
if (ps.menu == null)
|
||||
return MainApp.sResources.getString(R.string.combo_pump_state_unreachable);
|
||||
return MainApp.sResources.getString(R.string.combo_pump_state_disconnected);
|
||||
else if (ps.suspended && (ps.batteryState == PumpState.EMPTY || ps.insulinState == PumpState.EMPTY))
|
||||
return MainApp.sResources.getString(R.string.combo_pump_state_suspended_due_to_error);
|
||||
else if (ps.suspended)
|
||||
return MainApp.sResources.getString(R.string.combo_pump_state_suspended_by_user);
|
||||
return MainApp.sResources.getString(R.string.combo_pump_state_running);
|
||||
return MainApp.sResources.getString(R.string.combo_pump_state_normal);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -207,41 +208,46 @@ public class ComboPlugin implements PluginBase, PumpInterface {
|
|||
return new Date(pump.lastSuccessfulConnection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs pump initializing if needed, checks for boluses given on the pump, updates the
|
||||
* reservoir level and checks the running TBR on the pump.
|
||||
*/
|
||||
@Override
|
||||
public synchronized void refreshDataFromPump(String reason) {
|
||||
log.debug("RefreshDataFromPump called");
|
||||
|
||||
if (!pump.initialized) {
|
||||
runCommand(MainApp.sResources.getString(R.string.connecting), new CommandExecution() {
|
||||
@Override
|
||||
public CommandResult execute() {
|
||||
return ruffyScripter.readPumpState();
|
||||
// TODO reading profile
|
||||
long maxWait = System.currentTimeMillis() + 15 * 1000;
|
||||
while (!ruffyScripter.isPumpAvailable()) {
|
||||
log.debug("Waiting for ruffy service to be connected ...");
|
||||
SystemClock.sleep(100);
|
||||
if (System.currentTimeMillis() > maxWait) {
|
||||
log.debug("ruffy service unavailable, wtf");
|
||||
return;
|
||||
}
|
||||
});
|
||||
checkPumpHistory();
|
||||
}
|
||||
runCommand("Initializing", () -> ruffyScripter.readHistory(new PumpHistoryRequest()));
|
||||
pump.initialized = true;
|
||||
} else {
|
||||
runCommand(MainApp.sResources.getString(R.string.combo_action_refreshing), new CommandExecution() {
|
||||
@Override
|
||||
public CommandResult execute() {
|
||||
return ruffyScripter.readHistory(new PumpHistoryRequest().reservoirLevel(true).bolusHistory(PumpHistoryRequest.LAST));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
runCommand("Refreshing", ruffyScripter::readReservoirLevelAndLastBolus);
|
||||
|
||||
// TODO fuse the below into 'sync'? or make checkForTbrMismatch jut a trigger to issue a sync if needed; don't run sync twice as is nice
|
||||
// checkForTbrMismatch();
|
||||
checkPumpHistory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if there are any changes on the pump AAPS isn't aware of yet and if so, read the
|
||||
* full pump history and update AAPS' DB.
|
||||
*/
|
||||
private void checkPumpHistory() {
|
||||
CommandResult commandResult = runCommand("Checking pump history", false, new CommandExecution() {
|
||||
@Override
|
||||
public CommandResult execute() {
|
||||
return ruffyScripter.readHistory(
|
||||
CommandResult commandResult = runCommand(MainApp.sResources.getString(R.string.combo_pump_action_checking_history), () ->
|
||||
ruffyScripter.readHistory(
|
||||
new PumpHistoryRequest()
|
||||
.reservoirLevel(true)
|
||||
.bolusHistory(PumpHistoryRequest.LAST)
|
||||
.tbrHistory(PumpHistoryRequest.LAST)
|
||||
.errorHistory(PumpHistoryRequest.LAST));
|
||||
}
|
||||
});
|
||||
.errorHistory(PumpHistoryRequest.LAST)));
|
||||
|
||||
if (!commandResult.success || commandResult.history == null) {
|
||||
// TODO error case, command
|
||||
|
@ -250,6 +256,7 @@ public class ComboPlugin implements PluginBase, PumpInterface {
|
|||
|
||||
// TODO opt, construct PumpHistoryRequest to requset only what needs updating
|
||||
boolean syncNeeded = false;
|
||||
PumpHistoryRequest request = new PumpHistoryRequest();
|
||||
|
||||
// last bolus
|
||||
List<Treatment> treatments = MainApp.getConfigBuilder().getTreatmentsFromHistory();
|
||||
|
@ -267,11 +274,10 @@ public class ComboPlugin implements PluginBase, PumpInterface {
|
|||
pumpBolus = bolusHistory.get(0);
|
||||
}
|
||||
|
||||
if (aapsBolus == null || pumpBolus == null) {
|
||||
syncNeeded = true;
|
||||
} else if (Math.abs(aapsBolus.insulin - pumpBolus.amount) > 0.05
|
||||
|| aapsBolus.date != pumpBolus.timestamp) {
|
||||
if ((aapsBolus == null || pumpBolus == null)
|
||||
|| (Math.abs(aapsBolus.insulin - pumpBolus.amount) > 0.05 || aapsBolus.date != pumpBolus.timestamp)) {
|
||||
syncNeeded = true;
|
||||
request.bolusHistory = PumpHistoryRequest.FULL;
|
||||
}
|
||||
|
||||
// last tbr
|
||||
|
@ -282,106 +288,89 @@ public class ComboPlugin implements PluginBase, PumpInterface {
|
|||
}
|
||||
Tbr pumpTbr = null;
|
||||
List<Tbr> tbrHistory = commandResult.history.tbrHistory;
|
||||
if(!tbrHistory.isEmpty()) {
|
||||
if (!tbrHistory.isEmpty()) {
|
||||
pumpTbr = tbrHistory.get(0);
|
||||
}
|
||||
if (aapsTbr == null || pumpTbr == null) {
|
||||
syncNeeded = true;
|
||||
} else if (aapsTbr.percentRate != pumpTbr.percent || aapsTbr.durationInMinutes != pumpTbr.duration) {
|
||||
if ((aapsTbr == null || pumpTbr == null)
|
||||
|| (aapsTbr.percentRate != pumpTbr.percent || aapsTbr.durationInMinutes != pumpTbr.duration)) {
|
||||
syncNeeded = true;
|
||||
request.tbrHistory = PumpHistoryRequest.FULL;
|
||||
}
|
||||
|
||||
// last error
|
||||
// TODO add DB table
|
||||
// TODO add DB table ... or just keep in memory? does android allow that (fragment kill frenzy) without workarounds?
|
||||
// is comboplugin a service or a class with statics?
|
||||
request.pumpErrorHistory = PumpHistoryRequest.FULL;
|
||||
|
||||
// tdd
|
||||
// TODO; ... just fetch them on-deamand when the user opens the fragment?
|
||||
|
||||
|
||||
if (syncNeeded) {
|
||||
runFullSync();
|
||||
runFullSync(request);
|
||||
}
|
||||
|
||||
// TODO
|
||||
// detectStateMismatch(): expensive sync, checking everything (detectTbrMisMatch us called for every command)
|
||||
// check 'lasts' of pump against treatment db, request full sync if needed
|
||||
// and also remove treatments the pump doesn't have.
|
||||
// warn about this with a notification? show what was removed on combo tab?
|
||||
|
||||
}
|
||||
|
||||
// TODO uses profile values for the time being
|
||||
// this get's called multiple times a minute, must absolutely be cached
|
||||
@Override
|
||||
public double getBaseBasalRate() {
|
||||
/* if (pump.basalProfile == null) {
|
||||
// TODO when to force refresh this?
|
||||
CommandResult result = runCommand("Reading basal profile", new CommandExecution() {
|
||||
@Override
|
||||
public CommandResult execute() {
|
||||
return ruffyScripter.readBasalProfile(1);
|
||||
}
|
||||
});
|
||||
pump.basalProfile = result.basalProfile;
|
||||
// TODO error handling ...
|
||||
}
|
||||
return pump.basalProfile.hourlyRates[Calendar.getInstance().get(Calendar.HOUR_OF_DAY)];
|
||||
*/
|
||||
|
||||
Profile profile = MainApp.getConfigBuilder().getProfile();
|
||||
Double basal = profile.getBasal();
|
||||
log.trace("getBaseBasalrate returning " + basal);
|
||||
return basal;
|
||||
}
|
||||
|
||||
private static BolusProgressReporter nullBolusProgressReporter = new BolusProgressReporter() {
|
||||
@Override
|
||||
public void report(State state, int percent, double delivered) {}
|
||||
private static BolusProgressReporter nullBolusProgressReporter = (state, percent, delivered) -> {
|
||||
};
|
||||
|
||||
private static BolusProgressReporter bolusProgressReporter =
|
||||
new BolusProgressReporter() {
|
||||
@Override
|
||||
public void report(BolusProgressReporter.State state, int percent, double delivered) {
|
||||
EventOverviewBolusProgress event = EventOverviewBolusProgress.getInstance();
|
||||
switch (state) {
|
||||
case PROGRAMMING:
|
||||
event.status = MainApp.sResources.getString(R.string.combo_programming_bolus);
|
||||
break;
|
||||
case DELIVERING:
|
||||
event.status = String.format(MainApp.sResources.getString(R.string.bolusdelivering), delivered);
|
||||
break;
|
||||
case DELIVERED:
|
||||
event.status = String.format(MainApp.sResources.getString(R.string.bolusdelivered), delivered);
|
||||
break;
|
||||
case STOPPING:
|
||||
event.status = MainApp.sResources.getString(R.string.bolusstopping);
|
||||
break;
|
||||
case STOPPED:
|
||||
event.status = MainApp.sResources.getString(R.string.bolusstopped);
|
||||
break;
|
||||
case FINISHED:
|
||||
// no state, just percent below to close bolus progress dialog
|
||||
break;
|
||||
}
|
||||
event.percent = percent;
|
||||
MainApp.bus().post(event);
|
||||
}
|
||||
};
|
||||
private static BolusProgressReporter bolusProgressReporter = (state, percent, delivered) -> {
|
||||
EventOverviewBolusProgress event = EventOverviewBolusProgress.getInstance();
|
||||
switch (state) {
|
||||
case PROGRAMMING:
|
||||
event.status = MainApp.sResources.getString(R.string.combo_programming_bolus);
|
||||
break;
|
||||
case DELIVERING:
|
||||
event.status = String.format(MainApp.sResources.getString(R.string.bolusdelivering), delivered);
|
||||
break;
|
||||
case DELIVERED:
|
||||
event.status = String.format(MainApp.sResources.getString(R.string.bolusdelivered), delivered);
|
||||
break;
|
||||
case STOPPING:
|
||||
event.status = MainApp.sResources.getString(R.string.bolusstopping);
|
||||
break;
|
||||
case STOPPED:
|
||||
event.status = MainApp.sResources.getString(R.string.bolusstopped);
|
||||
break;
|
||||
case FINISHED:
|
||||
// no state, just percent below to close bolus progress dialog
|
||||
break;
|
||||
}
|
||||
event.percent = percent;
|
||||
MainApp.bus().post(event);
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates Treatment records with carbs and boluses and delivers a bolus if needed
|
||||
*/
|
||||
@Override
|
||||
public PumpEnactResult deliverTreatment(DetailedBolusInfo detailedBolusInfo) {
|
||||
// TODO for non-SMB: read resorvoir level first to make sure there's enough insulin left
|
||||
try {
|
||||
if (detailedBolusInfo.insulin > 0 || detailedBolusInfo.carbs > 0) {
|
||||
if (detailedBolusInfo.insulin > 0) {
|
||||
// bolus needed, ask pump to deliver it
|
||||
return deliverBolus(detailedBolusInfo);
|
||||
} else {
|
||||
// no bolus required, carb only treatment
|
||||
|
||||
// TODO the ui freezes when the calculator issues a carb-only treatment
|
||||
// so just wait, yeah, this is dumb. for now; proper fix via GL#10
|
||||
// info.nightscout.androidaps.plugins.Overview.Dialogs.BolusProgressDialog.scheduleDismiss()
|
||||
SystemClock.sleep(6000);
|
||||
PumpEnactResult pumpEnactResult = new PumpEnactResult();
|
||||
pumpEnactResult.success = true;
|
||||
pumpEnactResult.enacted = true;
|
||||
pumpEnactResult.bolusDelivered = 0d;
|
||||
pumpEnactResult.carbsDelivered = detailedBolusInfo.carbs;
|
||||
pumpEnactResult.comment = MainApp.instance().getString(R.string.virtualpump_resultok);
|
||||
MainApp.getConfigBuilder().addToHistoryTreatment(detailedBolusInfo);
|
||||
|
||||
EventOverviewBolusProgress bolusingEvent = EventOverviewBolusProgress.getInstance();
|
||||
bolusingEvent.percent = 100;
|
||||
MainApp.bus().post(bolusingEvent);
|
||||
return pumpEnactResult;
|
||||
}
|
||||
} else {
|
||||
if (detailedBolusInfo.insulin == 0 && detailedBolusInfo.carbs == 0) {
|
||||
// neither carbs nor bolus requested
|
||||
PumpEnactResult pumpEnactResult = new PumpEnactResult();
|
||||
pumpEnactResult.success = false;
|
||||
|
@ -391,6 +380,24 @@ public class ComboPlugin implements PluginBase, PumpInterface {
|
|||
pumpEnactResult.comment = MainApp.instance().getString(R.string.danar_invalidinput);
|
||||
log.error("deliverTreatment: Invalid input");
|
||||
return pumpEnactResult;
|
||||
} else if (detailedBolusInfo.insulin > 0) {
|
||||
// bolus needed, ask pump to deliver it
|
||||
return deliverBolus(detailedBolusInfo);
|
||||
} else {
|
||||
// no bolus required, carb only treatment
|
||||
SystemClock.sleep(6000);
|
||||
PumpEnactResult pumpEnactResult = new PumpEnactResult();
|
||||
pumpEnactResult.success = true;
|
||||
pumpEnactResult.enacted = true;
|
||||
pumpEnactResult.bolusDelivered = 0d;
|
||||
pumpEnactResult.carbsDelivered = detailedBolusInfo.carbs;
|
||||
pumpEnactResult.comment = MainApp.instance().getString(R.string.virtualpump_resultok);
|
||||
MainApp.getConfigBuilder().addToHistoryTreatment(detailedBolusInfo);
|
||||
|
||||
EventOverviewBolusProgress bolusingEvent = EventOverviewBolusProgress.getInstance();
|
||||
bolusingEvent.percent = 100;
|
||||
MainApp.bus().post(bolusingEvent);
|
||||
return pumpEnactResult;
|
||||
}
|
||||
} finally {
|
||||
MainApp.bus().post(new EventComboPumpUpdateGUI());
|
||||
|
@ -399,18 +406,18 @@ public class ComboPlugin implements PluginBase, PumpInterface {
|
|||
|
||||
@NonNull
|
||||
private PumpEnactResult deliverBolus(final DetailedBolusInfo detailedBolusInfo) {
|
||||
CommandResult bolusCmdResult = runCommand(MainApp.sResources.getString(R.string.combo_action_bolusing), new CommandExecution() {
|
||||
@Override
|
||||
public CommandResult execute() {
|
||||
return ruffyScripter.deliverBolus(detailedBolusInfo.insulin,
|
||||
detailedBolusInfo.isSMB ? nullBolusProgressReporter : bolusProgressReporter);
|
||||
}
|
||||
});
|
||||
// TODO
|
||||
// before non-SMB: check enough insulin is available, check we're up to date on boluses
|
||||
// after bolus: update reservoir level and check the bolus we just did is actually there
|
||||
|
||||
// retry flag: reconnect, kill warning, check if command can be restarted, restart
|
||||
CommandResult bolusCmdResult = runCommand(MainApp.sResources.getString(R.string.combo_action_bolusing), () -> ruffyScripter.deliverBolus(detailedBolusInfo.insulin,
|
||||
detailedBolusInfo.isSMB ? nullBolusProgressReporter : bolusProgressReporter));
|
||||
|
||||
PumpEnactResult pumpEnactResult = new PumpEnactResult();
|
||||
pumpEnactResult.success = bolusCmdResult.success;
|
||||
pumpEnactResult.enacted = bolusCmdResult.enacted;
|
||||
pumpEnactResult.comment = bolusCmdResult.message;
|
||||
// pumpEnactResult.comment = bolusCmdResult.message;
|
||||
|
||||
// if enacted, add bolus and carbs to treatment history
|
||||
if (pumpEnactResult.enacted) {
|
||||
|
@ -421,7 +428,7 @@ public class ComboPlugin implements PluginBase, PumpInterface {
|
|||
pumpEnactResult.bolusDelivered = detailedBolusInfo.insulin;
|
||||
pumpEnactResult.carbsDelivered = detailedBolusInfo.carbs;
|
||||
|
||||
detailedBolusInfo.date = bolusCmdResult.completionTime;
|
||||
detailedBolusInfo.date = System.currentTimeMillis();
|
||||
MainApp.getConfigBuilder().addToHistoryTreatment(detailedBolusInfo);
|
||||
} else {
|
||||
pumpEnactResult.bolusDelivered = 0d;
|
||||
|
@ -432,6 +439,8 @@ public class ComboPlugin implements PluginBase, PumpInterface {
|
|||
|
||||
@Override
|
||||
public void stopBolusDelivering() {
|
||||
// TODO note that we requested this, so we can thandle this proper in runCommand;
|
||||
// or is it fine if the command returns success with noting enacted and history checks as well/**/
|
||||
ruffyScripter.cancelBolus();
|
||||
}
|
||||
|
||||
|
@ -471,16 +480,11 @@ public class ComboPlugin implements PluginBase, PumpInterface {
|
|||
}
|
||||
|
||||
final int finalAdjustedPercent = adjustedPercent;
|
||||
CommandResult commandResult = runCommand(MainApp.sResources.getString(R.string.combo_action_setting_tbr), new CommandExecution() {
|
||||
@Override
|
||||
public CommandResult execute() {
|
||||
return ruffyScripter.setTbr(finalAdjustedPercent, durationInMinutes);
|
||||
}
|
||||
}
|
||||
);
|
||||
CommandResult commandResult = runCommand(MainApp.sResources.getString(R.string.combo_action_setting_tbr), () -> ruffyScripter.setTbr(finalAdjustedPercent, durationInMinutes));
|
||||
|
||||
if (commandResult.enacted) {
|
||||
TemporaryBasal tempStart = new TemporaryBasal(commandResult.completionTime);
|
||||
pump.tbrSetTime = System.currentTimeMillis();
|
||||
TemporaryBasal tempStart = new TemporaryBasal(pump.tbrSetTime);
|
||||
// TODO commandResult.state.tbrRemainingDuration might already display 29 if 30 was set, since 29:59 is shown as 29 ...
|
||||
// we should check this, but really ... something must be really screwed up if that number was anything different
|
||||
// TODO actually ... might setting 29 help with gaps between TBRs? w/o the hack in TemporaryBasal?
|
||||
|
@ -496,7 +500,7 @@ public class ComboPlugin implements PluginBase, PumpInterface {
|
|||
PumpEnactResult pumpEnactResult = new PumpEnactResult();
|
||||
pumpEnactResult.success = commandResult.success;
|
||||
pumpEnactResult.enacted = commandResult.enacted;
|
||||
pumpEnactResult.comment = commandResult.message;
|
||||
// pumpEnactResult.comment = commandResult.message;
|
||||
pumpEnactResult.isPercent = true;
|
||||
// Combo would have bailed if this wasn't set properly. Maybe we should
|
||||
// have the command return this anyways ...
|
||||
|
@ -524,21 +528,16 @@ public class ComboPlugin implements PluginBase, PumpInterface {
|
|||
if (activeTemp == null || userRequested) {
|
||||
/* v1 compatibility to sync DB to pump if they diverged (activeTemp == null) */
|
||||
log.debug("cancelTempBasal: hard-cancelling TBR since user requested");
|
||||
commandResult = runCommand(MainApp.sResources.getString(R.string.combo_action_cancelling_tbr), new CommandExecution() {
|
||||
@Override
|
||||
public CommandResult execute() {
|
||||
return ruffyScripter.cancelTbr();
|
||||
}
|
||||
});
|
||||
commandResult = runCommand(MainApp.sResources.getString(R.string.combo_action_cancelling_tbr), ruffyScripter::cancelTbr);
|
||||
|
||||
if (commandResult.enacted) {
|
||||
tempBasal = new TemporaryBasal(commandResult.completionTime);
|
||||
tempBasal = new TemporaryBasal(System.currentTimeMillis());
|
||||
tempBasal.durationInMinutes = 0;
|
||||
tempBasal.source = Source.USER;
|
||||
pumpEnactResult.isTempCancel = true;
|
||||
}
|
||||
} else if ((activeTemp.percentRate >= 90 && activeTemp.percentRate <= 110) && activeTemp.getPlannedRemainingMinutes() <= 15) {
|
||||
// Let fake neutral temp keep running (see below)
|
||||
// Let fake neutral temp keep run (see below)
|
||||
log.debug("cancelTempBasal: skipping changing tbr since it already is at " + activeTemp.percentRate + "% and running for another " + activeTemp.getPlannedRemainingMinutes() + " mins.");
|
||||
pumpEnactResult.comment = "cancelTempBasal skipping changing tbr since it already is at " + activeTemp.percentRate + "% and running for another " + activeTemp.getPlannedRemainingMinutes() + " mins.";
|
||||
// TODO check what AAPS does with this; no DB update is required;
|
||||
|
@ -552,15 +551,10 @@ public class ComboPlugin implements PluginBase, PumpInterface {
|
|||
// on whether the TBR we're cancelling is above or below 100%.
|
||||
final int percentage = (activeTemp.percentRate > 100) ? 110 : 90;
|
||||
log.debug("cancelTempBasal: changing TBR to " + percentage + "% for 15 mins.");
|
||||
commandResult = runCommand(MainApp.sResources.getString(R.string.combo_action_setting_tbr), new CommandExecution() {
|
||||
@Override
|
||||
public CommandResult execute() {
|
||||
return ruffyScripter.setTbr(percentage, 15);
|
||||
}
|
||||
});
|
||||
commandResult = runCommand(MainApp.sResources.getString(R.string.combo_action_setting_tbr), () -> ruffyScripter.setTbr(percentage, 15));
|
||||
|
||||
if (commandResult.enacted) {
|
||||
tempBasal = new TemporaryBasal(commandResult.completionTime);
|
||||
tempBasal = new TemporaryBasal(System.currentTimeMillis());
|
||||
tempBasal.durationInMinutes = 15;
|
||||
tempBasal.source = Source.USER;
|
||||
tempBasal.percentRate = percentage;
|
||||
|
@ -576,7 +570,7 @@ public class ComboPlugin implements PluginBase, PumpInterface {
|
|||
if (commandResult != null) {
|
||||
pumpEnactResult.success = commandResult.success;
|
||||
pumpEnactResult.enacted = commandResult.enacted;
|
||||
pumpEnactResult.comment = commandResult.message;
|
||||
// pumpEnactResult.comment = commandResult.message;
|
||||
}
|
||||
return pumpEnactResult;
|
||||
}
|
||||
|
@ -585,23 +579,47 @@ public class ComboPlugin implements PluginBase, PumpInterface {
|
|||
CommandResult execute();
|
||||
}
|
||||
|
||||
private CommandResult runCommand(String status, CommandExecution commandExecution) {
|
||||
return runCommand(status, true, commandExecution);
|
||||
// TODO if there was an error (or the pump was suspended) force a resync before a bolus;
|
||||
// transport a message, e.g. 'new bolus found on pump, synced, check and issue bolus again'
|
||||
// back to the user?b
|
||||
|
||||
}
|
||||
private synchronized CommandResult runCommand(String activity, CommandExecution commandExecution) {
|
||||
if (activity != null) {
|
||||
pump.activity = activity;
|
||||
MainApp.bus().post(new EventComboPumpUpdateGUI());
|
||||
}
|
||||
|
||||
private CommandResult runCommand(String activity, boolean checkTbrMisMatch, CommandExecution commandExecution) {
|
||||
pump.activity = activity;
|
||||
MainApp.bus().post(new EventComboPumpUpdateGUI());
|
||||
// CommandResult precheck = ruffyScripter.readPumpState();
|
||||
// tbrcheck?
|
||||
// check for active alert; if warning confirm; on warning confirm, read history (bolus, errors), which shall raise alerts if appropriate
|
||||
//
|
||||
// precheck.
|
||||
|
||||
CommandResult commandResult = commandExecution.execute();
|
||||
|
||||
pump.lastCmdResult = commandResult;
|
||||
pump.lastConnectionAttempt = System.currentTimeMillis();
|
||||
if (commandResult.success) {
|
||||
pump.lastSuccessfulConnection = System.currentTimeMillis();
|
||||
} else {
|
||||
pump.lastConnectionAttempt = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
// copy over state (as supplied) so it will still be available when another command runs that doesn't return that data
|
||||
pump.state = commandResult.state;
|
||||
if (commandResult.reservoirLevel != -1) {
|
||||
pump.reservoirLevel = commandResult.reservoirLevel;
|
||||
}
|
||||
|
||||
if (commandResult.lastBolus != null) {
|
||||
pump.lastBolus = commandResult.lastBolus;
|
||||
}
|
||||
|
||||
if (commandResult.history != null) {
|
||||
if (!commandResult.history.bolusHistory.isEmpty()) {
|
||||
pump.lastBolus = commandResult.history.bolusHistory.get(0);
|
||||
}
|
||||
pump.history = commandResult.history;
|
||||
}
|
||||
|
||||
|
||||
// TODO hm... automatically confirm messages and return them and handle them here proper?
|
||||
// with an option to corfirm all messages, non-critical (letting occlusion alert ring on phone and pump)
|
||||
// or let all alarms ring and don't try to control the pump in any way
|
||||
|
@ -627,105 +645,76 @@ public class ComboPlugin implements PluginBase, PumpInterface {
|
|||
commandResult.state = takeOverAlarmResult.state;
|
||||
}*/
|
||||
|
||||
pump.lastCmdResult = commandResult;
|
||||
pump.state = commandResult.state;
|
||||
|
||||
// TODO call this explicitely when needed after/before calling this?
|
||||
if (checkTbrMisMatch) {
|
||||
checkForTbrMismatch();
|
||||
}
|
||||
// TODO call this explicitely when needed after/before calling this?
|
||||
// if (checkTbrMisMatch) {
|
||||
// checkForTbrMismatch();
|
||||
// }
|
||||
|
||||
|
||||
// TODO not propely set all the time ...
|
||||
if (pump.lastCmdResult == null) {
|
||||
log.error("JOE: no!");
|
||||
} else {
|
||||
// still crashable ...
|
||||
// TODO only update if command was successful? -> KeepAliveReceiver, triggering alarm on unavaiblae pump
|
||||
pump.lastCmdResult.completionTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
// TODO merge all new history here?
|
||||
if (commandResult.history != null) {
|
||||
if (commandResult.history.reservoirLevel != -1) {
|
||||
pump.reservoirLevel = commandResult.history.reservoirLevel;
|
||||
}
|
||||
pump.history = commandResult.history;
|
||||
}
|
||||
|
||||
// TODO in the event of an error schedule a resync
|
||||
|
||||
pump.activity = null;
|
||||
MainApp.bus().post(new EventComboPumpUpdateGUI());
|
||||
if (activity != null) {
|
||||
pump.activity = null;
|
||||
MainApp.bus().post(new EventComboPumpUpdateGUI());
|
||||
}
|
||||
return commandResult;
|
||||
}
|
||||
|
||||
// TODO rename to checkState or so and also check time (& date) of pump
|
||||
private void checkForTbrMismatch() {
|
||||
// detectTbrMismatch(): 'quick' check with not overhead on the pump side
|
||||
// detectTbrMismatch(): 'quick' check with no overhead on the pump side
|
||||
// TODO check if this works with pump suspend, esp. around pump suspend there'll be syncing to do;
|
||||
|
||||
// TODO we need to tolerate differences of 1-2 minutes due to the time it takes to programm a tbr
|
||||
// mismatching a 5m interval etc
|
||||
|
||||
TemporaryBasal aapsTbr = MainApp.getConfigBuilder().getTempBasalFromHistory(System.currentTimeMillis());
|
||||
// if (true) {
|
||||
//
|
||||
// // not yet
|
||||
// } else
|
||||
boolean sync = false;
|
||||
if (aapsTbr == null && pump.state.tbrActive) {
|
||||
// pump runs TBR AAPS is unaware off
|
||||
// => fetch full history so the full TBR is added to treatments
|
||||
log.debug("JOE: sync required 1");
|
||||
runFullSync();
|
||||
log.debug("Pump runs TBR AAPS is unaware of, reading last 3h of pump TBR history");
|
||||
sync = true;
|
||||
} else if (aapsTbr != null && !pump.state.tbrActive) {
|
||||
// AAPS has a TBR but the pump isn't running a TBR
|
||||
// => remove the TBR from treatments
|
||||
// => fetch full history, so that if the TBR was cancelled but ran some time we get the IOB from that partial TBR
|
||||
log.debug("JOE: sync required 2");
|
||||
log.debug("AAPS shows TBR but pump isn't running a TBR; deleting TBR in AAPS and reading last 3h of pump TBR history");
|
||||
MainApp.getDbHelper().delete(aapsTbr);
|
||||
runFullSync();
|
||||
sync = true;
|
||||
} else if (aapsTbr != null && pump.state.tbrActive) {
|
||||
// both AAPS and pump have a TBR ...
|
||||
if (aapsTbr.percentRate != pump.state.tbrPercent) {
|
||||
// ... but they have different percentages
|
||||
// => remove TBR from treatments
|
||||
// => full history sync so we get up to date on actual IOB
|
||||
log.debug("JOE: sync required 3");
|
||||
log.debug("TBR percentage differs between AAPS and pump; deleting TBR in AAPS and reading last 3h of pump TBR history");
|
||||
MainApp.getDbHelper().delete(aapsTbr);
|
||||
runFullSync();
|
||||
sync = true;
|
||||
}
|
||||
int durationDiff = Math.abs(aapsTbr.getPlannedRemainingMinutes() - pump.state.tbrRemainingDuration);
|
||||
if (durationDiff > 2) {
|
||||
// ... but they have different runtimes
|
||||
// ^ same as above, merge branches
|
||||
log.debug("JOE: sync required 4");
|
||||
log.debug("TBR duration differs between AAPS and pump; deleting TBR in AAPS and reading last 3h of pump TBR history");
|
||||
MainApp.getDbHelper().delete(aapsTbr);
|
||||
runFullSync();
|
||||
sync = true;
|
||||
}
|
||||
}
|
||||
if (sync) {
|
||||
runFullSync(new PumpHistoryRequest().tbrHistory(System.currentTimeMillis() - 3 * 60 * 60 * 1000));
|
||||
}
|
||||
|
||||
// TODO request a loop run to (re)apply a TBR/SMB given this new information? or just wait till next iteration?
|
||||
// could take 15m or so if there are missed SGVs ...
|
||||
// new sensitivity calc required, no?
|
||||
|
||||
}
|
||||
|
||||
private void runFullSync() {
|
||||
// TODO separate fetching and comparing
|
||||
if (1 == 1 ) {
|
||||
log.error("Skipping full sync - not implemented yet");
|
||||
return;
|
||||
}
|
||||
CommandResult commandResult = runCommand("Syncing full pump history", false, new CommandExecution() {
|
||||
@Override
|
||||
public CommandResult execute() {
|
||||
return ruffyScripter.readHistory(
|
||||
new PumpHistoryRequest()
|
||||
.reservoirLevel(true)
|
||||
.bolusHistory(PumpHistoryRequest.FULL)
|
||||
.tbrHistory(PumpHistoryRequest.FULL)
|
||||
.errorHistory(PumpHistoryRequest.FULL)
|
||||
.tddHistory(PumpHistoryRequest.FULL)
|
||||
);
|
||||
}
|
||||
});
|
||||
private void runFullSync(final PumpHistoryRequest request) {
|
||||
CommandResult result = runCommand("Syncing full pump history", () -> ruffyScripter.readHistory(request));
|
||||
|
||||
// boluses
|
||||
|
||||
// TBRs
|
||||
|
||||
// errors
|
||||
// TODO
|
||||
|
||||
}
|
||||
|
||||
|
@ -734,44 +723,55 @@ public class ComboPlugin implements PluginBase, PumpInterface {
|
|||
return OPERATION_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
// Returns the state of the pump as it was received during last pump comms.
|
||||
// TODO v2 add battery, reservoir info when we start reading that and clean up the code
|
||||
@Override
|
||||
public JSONObject getJSONStatus() {
|
||||
CommandResult lastCmdResult = pump.lastCmdResult;
|
||||
if (lastCmdResult == null || lastCmdResult.completionTime + 5 * 60 * 1000L < System.currentTimeMillis()) {
|
||||
if (!pump.initialized) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
JSONObject pumpJson = new JSONObject();
|
||||
pumpJson.put("clock", DateUtil.toISOString(pump.lastSuccessfulConnection));
|
||||
pumpJson.put("reservoir", pump.reservoirLevel);
|
||||
|
||||
JSONObject statusJson = new JSONObject();
|
||||
JSONObject extendedJson = new JSONObject();
|
||||
statusJson.put("status", getStateSummary());
|
||||
extendedJson.put("Version", BuildConfig.VERSION_NAME + "-" + BuildConfig.BUILDVERSION);
|
||||
try {
|
||||
extendedJson.put("ActiveProfile", MainApp.getConfigBuilder().getProfileName());
|
||||
} catch (Exception e) {
|
||||
}
|
||||
statusJson.put("timestamp", lastCmdResult.completionTime);
|
||||
|
||||
PumpState ps = pump.state;
|
||||
if (ps != null) {
|
||||
if (ps.tbrActive) {
|
||||
extendedJson.put("TempBasalAbsoluteRate", ps.tbrRate);
|
||||
extendedJson.put("TempBasalPercent", ps.tbrPercent);
|
||||
extendedJson.put("TempBasalRemaining", ps.tbrRemainingDuration);
|
||||
}
|
||||
if (ps.errorMsg != null) {
|
||||
extendedJson.put("ErrorMessage", ps.errorMsg);
|
||||
}
|
||||
}
|
||||
|
||||
// more info here .... look at dana plugin
|
||||
|
||||
statusJson.put("timestamp", pump.lastSuccessfulConnection);
|
||||
pumpJson.put("status", statusJson);
|
||||
|
||||
JSONObject extendedJson = new JSONObject();
|
||||
extendedJson.put("Version", BuildConfig.VERSION_NAME + "-" + BuildConfig.BUILDVERSION);
|
||||
extendedJson.put("ActiveProfile", MainApp.getConfigBuilder().getProfileName());
|
||||
if (pump.lastBolus != null) {
|
||||
extendedJson.put("LastBolus", new Date(pump.lastBolus.timestamp).toLocaleString());
|
||||
extendedJson.put("LastBolusAmount", DecimalFormatter.to1Decimal(pump.lastBolus.amount));
|
||||
}
|
||||
PumpState ps = pump.state;
|
||||
if (ps.tbrActive) {
|
||||
extendedJson.put("TempBasalAbsoluteRate", ps.tbrRate);
|
||||
extendedJson.put("TempBasalPercent", ps.tbrPercent);
|
||||
extendedJson.put("TempBasalRemaining", ps.tbrRemainingDuration);
|
||||
}
|
||||
if (ps.errorMsg != null) {
|
||||
extendedJson.put("ErrorMessage", ps.errorMsg);
|
||||
}
|
||||
pumpJson.put("extended", extendedJson);
|
||||
pumpJson.put("clock", DateUtil.toISOString(lastCmdResult.completionTime));
|
||||
|
||||
JSONObject batteryJson = new JSONObject();
|
||||
int battery;
|
||||
switch (ps.batteryState) {
|
||||
case PumpState.EMPTY:
|
||||
battery = 0;
|
||||
break;
|
||||
case PumpState.LOW:
|
||||
battery = 25;
|
||||
break;
|
||||
default:
|
||||
battery = 75;
|
||||
break;
|
||||
}
|
||||
batteryJson.put("percent", battery);
|
||||
pumpJson.put("battery", batteryJson);
|
||||
|
||||
return pumpJson;
|
||||
} catch (Exception e) {
|
||||
|
@ -781,10 +781,8 @@ public class ComboPlugin implements PluginBase, PumpInterface {
|
|||
return null;
|
||||
}
|
||||
|
||||
// TODO
|
||||
@Override
|
||||
public String deviceID() {
|
||||
// Serial number here
|
||||
return "Combo";
|
||||
}
|
||||
|
||||
|
@ -795,7 +793,6 @@ public class ComboPlugin implements PluginBase, PumpInterface {
|
|||
|
||||
@Override
|
||||
public String shortStatus(boolean veryShort) {
|
||||
// TODO trim for wear if veryShort==true
|
||||
return getStateSummary();
|
||||
}
|
||||
|
||||
|
|
|
@ -3,24 +3,31 @@ package info.nightscout.androidaps.plugins.PumpCombo;
|
|||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import de.jotomo.ruffy.spi.BasalProfile;
|
||||
import de.jotomo.ruffy.spi.PumpState;
|
||||
import de.jotomo.ruffy.spi.CommandResult;
|
||||
import de.jotomo.ruffy.spi.history.Bolus;
|
||||
import de.jotomo.ruffy.spi.history.PumpHistory;
|
||||
|
||||
class ComboPump {
|
||||
// TODO actually ... this isn't about successful command execution, but whether we could connect to the pump at all
|
||||
// TODO all non-state (==main screen) data is overriden by commands, no? put them seperately
|
||||
// at least skim over how dana does it!
|
||||
boolean initialized = false;
|
||||
|
||||
// TODO actually ... this isn't about successful command execution, but whether we could connect to the pump at all
|
||||
volatile long lastSuccessfulConnection;
|
||||
volatile long lastConnectionAttempt;
|
||||
|
||||
@Nullable
|
||||
volatile CommandResult lastCmdResult;
|
||||
|
||||
public volatile String activity;
|
||||
@NonNull
|
||||
volatile PumpState state = new PumpState();
|
||||
volatile int reservoirLevel = -1;
|
||||
volatile Bolus lastBolus = null;
|
||||
@Nullable
|
||||
volatile BasalProfile basalProfile;
|
||||
@NonNull
|
||||
|
||||
volatile PumpHistory history = new PumpHistory();
|
||||
/** Time the active TBR was set (if any) */
|
||||
long tbrSetTime;
|
||||
}
|
||||
|
|
|
@ -56,7 +56,9 @@ public class KeepAliveReceiver extends BroadcastReceiver {
|
|||
private void checkPump() {
|
||||
final PumpInterface pump = MainApp.getConfigBuilder();
|
||||
final Profile profile = MainApp.getConfigBuilder().getProfile();
|
||||
if (pump != null && pump.isInitialized() && profile != null && profile.getBasal() != null) {
|
||||
if (pump != null && /* TODO does this prevent the error in case the pump is never initialized because it was never reachable? */
|
||||
// pump.isInitialized() &&
|
||||
profile != null && profile.getBasal() != null) {
|
||||
Date lastConnection = pump.lastDataTime();
|
||||
|
||||
boolean isStatusOutdated = lastConnection.getTime() + 15 * 60 * 1000L < System.currentTimeMillis();
|
||||
|
@ -71,7 +73,7 @@ public class KeepAliveReceiver extends BroadcastReceiver {
|
|||
// The alarm sound is played back as regular media, that means it might be muted if sound level is at 0
|
||||
// a simple 'Enable/disable alarms' button on the actions tab?
|
||||
Notification n = new Notification(Notification.PUMP_UNREACHABLE,
|
||||
MainApp.sResources.getString(R.string.combo_pump_state_unreachable), Notification.URGENT);
|
||||
MainApp.sResources.getString(R.string.combo_pump_state_disconnected), Notification.URGENT);
|
||||
n.soundId = R.raw.alarm;
|
||||
MainApp.bus().post(new EventNewNotification(n));
|
||||
} else if (SP.getBoolean("syncprofiletopump", false) && !pump.isThisProfileSet(profile)) {
|
||||
|
|
|
@ -95,7 +95,7 @@
|
|||
android:gravity="start"
|
||||
android:paddingLeft="5dp"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="20dp" />
|
||||
android:textSize="20sp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
@ -360,19 +360,6 @@
|
|||
android:paddingRight="0dp"
|
||||
android:text="@string/combo_refresh" />
|
||||
|
||||
<!-- TODO v3-->
|
||||
<Button
|
||||
android:id="@+id/combo_stats"
|
||||
style="@style/ButtonSmallFontStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:drawableTop="@drawable/icon_danarstats"
|
||||
android:paddingLeft="0dp"
|
||||
android:paddingRight="0dp"
|
||||
android:visibility="gone"
|
||||
android:text="@string/combo_stats" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/combo_error_history"
|
||||
style="@style/ButtonSmallFontStyle"
|
||||
|
|
|
@ -684,13 +684,12 @@
|
|||
<string name="pump_errors_history">Fehlerprotokol</string>
|
||||
<string name="treatments_wizard_tt_label">TZ</string>
|
||||
<string name="combo_pump_state_label">Status</string>
|
||||
<string name="combo_pump_state_unreachable">Keine Verbindung zur Pumpe</string>
|
||||
<string name="combo_pump_state_disconnected">Keine Verbindung zur Pumpe</string>
|
||||
<string name="combo_pump_state_suspended_by_user">Durch Benuzter gestoppt</string>
|
||||
<string name="combo_pump_state_suspended_due_to_error">Wegen Fehler gestoppt</string>
|
||||
<string name="combo_pump_state_running">Normaler Betrieb</string>
|
||||
<string name="combo_pump_state_normal">Normaler Betrieb</string>
|
||||
<string name="combo_stats">Statistiken</string>
|
||||
<string name="combo_programming_bolus">Bolusabgabe wird vorbereitet</string>
|
||||
<string name="combo_action_refreshing">Aktualisierung</string>
|
||||
<string name="combo_action_cancelling_tbr">TBR wird abgebrochen</string>
|
||||
<string name="combo_action_setting_tbr">TBR wird gesetzt</string>
|
||||
<string name="combo_action_bolusing">Bolus wird abgegeben</string>
|
||||
|
|
|
@ -748,7 +748,6 @@
|
|||
<string name="reuse">reuse</string>
|
||||
<string name="wearcontrol_title">Controls from Watch</string>
|
||||
<string name="wearcontrol_summary">Set Temp-Targets and enter Treatments from the watch.</string>
|
||||
<!-- TODO combo: still needed? -->
|
||||
<string name="connectiontimedout">Connection timed out</string>
|
||||
<string name="active"><![CDATA[<Active>]]></string>
|
||||
<string name="waitingforestimatedbolusend" formatted="false">Waiting for estimated bolus end. Remaining %d sec.</string>
|
||||
|
@ -766,12 +765,12 @@
|
|||
<string name="combo_connect_attempt_failed">Last connect attempt failed</string>
|
||||
<string name="combo_last_connection_time">%s (%s)</string>
|
||||
<string name="combo_tbr_remaining">%d%% (%d remaining)</string>
|
||||
<string name="combo_last_bolus">%.2f U (%.1f %s, %s)</string>
|
||||
<string name="combo_pump_state_unreachable">Pump unreachable</string>
|
||||
<string name="combo_last_bolus">%.1f U (%.1f %s, %s)</string>
|
||||
<string name="combo_pump_state_disconnected">Pump disconnected</string>
|
||||
<string name="combo_pump_state_suspended_due_to_error">Suspended due to error</string>
|
||||
<string name="combo_pump_state_suspended_by_user">Suspended by user</string>
|
||||
<string name="combo_pump_state_running">Running</string>
|
||||
<string name="combo_action_refreshing">Refreshing</string>
|
||||
<string name="combo_pump_state_normal">Normal</string>
|
||||
<string name="combo_pump_action_checking_history">Checking pump history</string>
|
||||
<string name="combo_action_cancelling_tbr">Cancelling TBR</string>
|
||||
<string name="combo_action_setting_tbr">Setting TBR</string>
|
||||
<string name="combo_action_bolusing">Bolusing</string>
|
||||
|
|
|
@ -1,38 +1,43 @@
|
|||
package de.jotomo.ruffy.spi;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Date;
|
||||
|
||||
import de.jotomo.ruffy.spi.history.Bolus;
|
||||
import de.jotomo.ruffy.spi.history.PumpHistory;
|
||||
|
||||
public class CommandResult {
|
||||
/** The request made made to the pump, like setting a TBR. */
|
||||
public String request;
|
||||
/** Whether the command was executed successfully. */
|
||||
public boolean success;
|
||||
/** Whether any changes were made, e.g. if a the request was to cancel a running TBR,
|
||||
* but not TBR was active, this will be false. */
|
||||
public boolean enacted;
|
||||
/** Time the command completed. */
|
||||
public long completionTime;
|
||||
/** Null unless an unhandled exception was raised. */
|
||||
public Exception exception;
|
||||
/** (Error)message describing the result of the command. */
|
||||
public String message;
|
||||
// TODO work outh this message
|
||||
// public String message;
|
||||
/** State of the pump *after* command execution. */
|
||||
public PumpState state;
|
||||
/** History if requested by the command. */
|
||||
public PumpHistory history;
|
||||
/** Basal rate profile if requested. */
|
||||
public List<BasalProfile> basalProfiles;
|
||||
public BasalProfile basalProfile;
|
||||
/** Total duration the command took. */
|
||||
public String duration;
|
||||
|
||||
public CommandResult() {
|
||||
}
|
||||
/** Whether an alert (warning only) was confirmed. This can happen during boluses.
|
||||
* Request error history to see which errors occured. */
|
||||
public boolean alertConfirmed;
|
||||
/** BolusCommand: if a cancel request was successful */
|
||||
public boolean wasSuccessfullyCancelled;
|
||||
|
||||
public CommandResult request(String request) {
|
||||
this.request = request;
|
||||
return this;
|
||||
public int reservoirLevel = -1;
|
||||
|
||||
public Bolus lastBolus;
|
||||
|
||||
public long pumpTime;
|
||||
|
||||
public CommandResult() {
|
||||
}
|
||||
|
||||
public CommandResult success(boolean success) {
|
||||
|
@ -45,11 +50,6 @@ public class CommandResult {
|
|||
return this;
|
||||
}
|
||||
|
||||
public CommandResult completionTime(long completionTime) {
|
||||
this.completionTime = completionTime;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandResult duration(String duration) {
|
||||
this.duration = duration;
|
||||
return this;
|
||||
|
@ -60,10 +60,10 @@ public class CommandResult {
|
|||
return this;
|
||||
}
|
||||
|
||||
public CommandResult message(String message) {
|
||||
this.message = message;
|
||||
return this;
|
||||
}
|
||||
// public CommandResult message(String message) {
|
||||
// this.message = message;
|
||||
// return this;
|
||||
// }
|
||||
|
||||
public CommandResult state(PumpState state) {
|
||||
this.state = state;
|
||||
|
@ -75,24 +75,24 @@ public class CommandResult {
|
|||
return this;
|
||||
}
|
||||
|
||||
public CommandResult basalProfile(List<BasalProfile> basalProfiles) {
|
||||
this.basalProfiles = basalProfiles;
|
||||
public CommandResult basalProfile(BasalProfile basalProfile) {
|
||||
this.basalProfile = basalProfile;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CommandResult{" +
|
||||
"request='" + request + '\'' +
|
||||
", success=" + success +
|
||||
", enacted=" + enacted +
|
||||
", completionTime=" + completionTime +
|
||||
", exception=" + exception +
|
||||
", message='" + message + '\'' +
|
||||
// ", message='" + message + '\'' +
|
||||
", state=" + state +
|
||||
", history=" + history +
|
||||
", basalProfiles=" + basalProfiles +
|
||||
", basalProfile=" + basalProfile +
|
||||
", duration='" + duration + '\'' +
|
||||
", alertConfirmed='" + alertConfirmed + '\'' +
|
||||
", wasSuccessfullyCancelled='" + wasSuccessfullyCancelled + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
package de.jotomo.ruffy.spi;
|
||||
|
||||
public class PumpErrorCodes {
|
||||
public static final int CARTRIDGE_EMPTY = 1;
|
||||
public static final int BATTERY_EMPTY = 2;
|
||||
public static final int AUTOMATIC_OFF = 3;
|
||||
public static final int OCCLUSION = 4;
|
||||
public static final int END_OF_OPERATION_BACKUP_PUMP = 5;
|
||||
public static final int MECHANICAL_ERROR = 6;
|
||||
public static final int ELECTRONIC_ERROR = 7;
|
||||
public static final int POWER_INTERRUPT = 8;
|
||||
public static final int END_OF_OPERATION_LOAN_PUMP = 9;
|
||||
public static final int CARTRIDGE_ERROR = 10;
|
||||
public static final int SET_NOT_PRIMED = 11;
|
||||
public static final int DATA_INTERRUPTED = 12;
|
||||
public static final int LANGUAGE_ERROR = 13;
|
||||
}
|
|
@ -2,6 +2,7 @@ package de.jotomo.ruffy.spi;
|
|||
|
||||
/** State displayed on the main screen of the pump. */
|
||||
public class PumpState {
|
||||
public long timestamp;
|
||||
public String menu = null;
|
||||
public boolean tbrActive = false;
|
||||
/** TBR percentage. 100% means no TBR active, just the normal basal rate running. */
|
||||
|
@ -22,10 +23,11 @@ public class PumpState {
|
|||
public String errorMsg;
|
||||
public boolean suspended;
|
||||
|
||||
public static final int UNKNOWN = -1;
|
||||
public static final int LOW = 1;
|
||||
public static final int EMPTY = 2;
|
||||
public int batteryState = - 1;
|
||||
public int insulinState = -1;
|
||||
public int batteryState = UNKNOWN;
|
||||
public int insulinState = UNKNOWN;
|
||||
|
||||
public int activeBasalProfileNumber;
|
||||
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
package de.jotomo.ruffy.spi;
|
||||
|
||||
public class PumpWarningCodes {
|
||||
public static final int CARTRIDGE_LOW = 1;
|
||||
public static final int BATTERY_LOW = 2;
|
||||
public static final int REVIEW_TIME = 3;
|
||||
public static final int CALL_FOR_UPDATE = 4;
|
||||
public static final int PUMP_TIMER = 5;
|
||||
public static final int TBR_CANCELLED = 6;
|
||||
public static final int TBR_OVER = 7;
|
||||
public static final int BOLUS_CANCELLED = 8;
|
||||
public static final int LOANTIME_WARNING = 9;
|
||||
public static final int BLUETOOTH_FAULT = 10;
|
||||
}
|
|
@ -13,81 +13,24 @@ public interface RuffyCommands {
|
|||
|
||||
CommandResult cancelTbr();
|
||||
|
||||
// TODO read Dana code wrt to syncing and such
|
||||
|
||||
/** Confirms an active alarm on the pump. The state returned is the state after the alarm
|
||||
* has been confirmed. Confirmed alerts are returned in history.pumpErrorHistory. */
|
||||
@Deprecated
|
||||
// TODO rename to confirmActiveAlarm (single)
|
||||
// add a field activeAlarm to PumpState and put logic in ComboPlugin.
|
||||
CommandResult takeOverAlarms();
|
||||
// let this be an actual command in RS? or extend readPumpState wit ha flag to return confirmed errors?
|
||||
|
||||
/* plan:
|
||||
|
||||
let errors ring on the pump (all require interacting with the pump anyways and they
|
||||
should not be subject to errors in AAPS/ruffy.
|
||||
|
||||
When connecting to the pump, read the current state. If a warning(s) is ongoing
|
||||
confirm it and forward it and let AAPS display it.
|
||||
Check if a previous command failed (tbr/bolus) and if so, DON'T report a warning
|
||||
caused by an interrupted command.
|
||||
Put the logic in AAPS: don't have readPumpState automatically confirm anything,
|
||||
but read state, call takeOverAlarm(alarm)
|
||||
|
||||
concrete warnings???
|
||||
tbr cancelled => we can almost always just confirm this, we sync aaps if neded
|
||||
bolus => basically the same with SMBs, for user-initiated boluses and error should be reported
|
||||
properly, but that should work since it's interactive and PumpEneact result is
|
||||
or should be checked (verify it is)
|
||||
deliwerTreatment knows if it's SMB or not, whether it's okay to dismiss bolus cancelled alarm.
|
||||
|
||||
battery low => always forward (not configurable, whole point is to make AAPS master)
|
||||
cartridge low => always forward
|
||||
|
||||
when sync detects a significant mismatch it should alert?
|
||||
big bolus on pump aaps didn't knew about???
|
||||
removing a big bolus in aaps db since it's not in pump history? warn? aaps bug.
|
||||
|
||||
==> think this whole comm errors thing through (incl. auto/reconnect, auto-confirm
|
||||
), on LoD - 1 and go back to coding after that
|
||||
|
||||
open: dealing with low cartridge/battery during bolus.
|
||||
also: dealing with empty cartridge/battery alarms we let ring (force resync
|
||||
when 'next possible'. force resync when coming from a suspended state, or any
|
||||
error state in general.
|
||||
if cartridge is low, check reservoir before each bolus and don't attempt
|
||||
to bolus if not enough in reservoir for bolus?
|
||||
(testers: bolus with cartridge very low to run in such scenarios - forget the
|
||||
cartridge is low).
|
||||
|
||||
so: * confirm pump warnings; for tbr/bolus cancelled caused by connection loss don't
|
||||
report them to the user (on disconnect set a flag 'error caused by us' or use.
|
||||
also note connection lose and force history resync if appropriate(?) (LAST).
|
||||
* stuff low warnings: confirm, show in combo tab and give overview notification
|
||||
* keepaliver receiver raises an urgent error if there have been no pump comms in
|
||||
> 25m
|
||||
* errors always ring on the pump, are critical and require the pump to be interacted
|
||||
with, so they're not confirmed. if encountered, they're read and also reported as
|
||||
urgent on the phone. cancel again if we see the user has confirmed them on the pump?
|
||||
(parent monitoring, needing to see an error).
|
||||
make Combo->Status red and "Occlusion" for errors?
|
||||
*/
|
||||
CommandResult confirmAlert(int warningCode);
|
||||
|
||||
boolean isPumpAvailable();
|
||||
|
||||
boolean isPumpBusy();
|
||||
|
||||
// start everything with this: read pump state.
|
||||
// see if there's an error active.
|
||||
CommandResult readPumpState();
|
||||
|
||||
CommandResult readReservoirLevelAndLastBolus();
|
||||
|
||||
CommandResult readHistory(PumpHistoryRequest request);
|
||||
|
||||
CommandResult readBasalProfile(int number);
|
||||
|
||||
CommandResult setBasalProfile(BasalProfile basalProfile);
|
||||
|
||||
CommandResult getDateAndTime();
|
||||
|
||||
CommandResult setDateAndTime(Date date);
|
||||
|
||||
void requestPairing();
|
||||
|
|
|
@ -7,4 +7,12 @@ public class Bolus extends HistoryRecord {
|
|||
super(timestamp);
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Bolus{" +
|
||||
"timestamp=" + timestamp +
|
||||
", amount=" + amount +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,13 +2,28 @@ package de.jotomo.ruffy.spi.history;
|
|||
|
||||
public class PumpError extends HistoryRecord {
|
||||
/** Code is an E for error or W for warning, followed by a single digit, e.g. W7 (TBR cancelled). */
|
||||
public final String code;
|
||||
// public final String code;
|
||||
public final Integer warningCode;
|
||||
|
||||
|
||||
public final Integer errorCode;
|
||||
/** Error message, in the language configured on the pump. */
|
||||
public final String message;
|
||||
|
||||
public PumpError(long timestamp, String code, String message) {
|
||||
public PumpError(long timestamp, Integer warningCode, Integer errorCode, String message) {
|
||||
super(timestamp);
|
||||
this.code = code;
|
||||
this.warningCode = warningCode;
|
||||
this.errorCode = errorCode;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PumpError{" +
|
||||
"timestamp=" + timestamp +
|
||||
", warningCode=" + warningCode +
|
||||
", errorCode=" + errorCode +
|
||||
", message='" + message + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,10 +3,10 @@ package de.jotomo.ruffy.spi.history;
|
|||
import android.support.annotation.NonNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class PumpHistory {
|
||||
public int reservoirLevel = -1;
|
||||
@NonNull
|
||||
public List<Bolus> bolusHistory = new ArrayList<>();
|
||||
@NonNull
|
||||
|
@ -16,12 +16,6 @@ public class PumpHistory {
|
|||
@NonNull
|
||||
public List<Tdd> tddHistory = new ArrayList<>();
|
||||
|
||||
public PumpHistory reservoirLevel(int reservoirLevel) {
|
||||
this.reservoirLevel = reservoirLevel
|
||||
;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PumpHistory bolusHistory(List<Bolus> bolusHistory) {
|
||||
this.bolusHistory = bolusHistory;
|
||||
return this;
|
||||
|
@ -32,7 +26,7 @@ public class PumpHistory {
|
|||
return this;
|
||||
}
|
||||
|
||||
public PumpHistory errorHistory(List<PumpError> pumpErrorHistory) {
|
||||
public PumpHistory pumpErrorHistory(List<PumpError> pumpErrorHistory) {
|
||||
this.pumpErrorHistory = pumpErrorHistory;
|
||||
return this;
|
||||
}
|
||||
|
@ -45,7 +39,6 @@ public class PumpHistory {
|
|||
@Override
|
||||
public String toString() {
|
||||
return "PumpHistory{" +
|
||||
"reservoirLevel=" + reservoirLevel +
|
||||
", bolusHistory=" + bolusHistory.size() +
|
||||
", tbrHistory=" + tbrHistory.size() +
|
||||
", pumpErrorHistory=" + pumpErrorHistory.size() +
|
||||
|
|
|
@ -2,8 +2,6 @@ package de.jotomo.ruffy.spi.history;
|
|||
|
||||
/** What data a 'read history' request should return. */
|
||||
public class PumpHistoryRequest {
|
||||
public boolean reservoirLevel;
|
||||
|
||||
/* History to read:
|
||||
Either the timestamp of the last known record to fetch all newer records,
|
||||
or one of the constants to read no history or all of it.
|
||||
|
@ -15,12 +13,6 @@ public class PumpHistoryRequest {
|
|||
public long bolusHistory = SKIP;
|
||||
public long tbrHistory = SKIP;
|
||||
public long pumpErrorHistory = SKIP;
|
||||
public long tddHistory = SKIP;
|
||||
|
||||
public PumpHistoryRequest reservoirLevel(boolean reservoirLevel) {
|
||||
this.reservoirLevel = reservoirLevel;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PumpHistoryRequest bolusHistory(long bolusHistory) {
|
||||
this.bolusHistory = bolusHistory;
|
||||
|
@ -37,19 +29,12 @@ public class PumpHistoryRequest {
|
|||
return this;
|
||||
}
|
||||
|
||||
public PumpHistoryRequest tddHistory(long tddHistory) {
|
||||
this.tddHistory = tddHistory;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PumpHistoryRequest{" +
|
||||
"reservoirLevel=" + reservoirLevel +
|
||||
", bolusHistory=" + bolusHistory +
|
||||
", tbrHistory=" + tbrHistory +
|
||||
", pumpErrorHistory=" + pumpErrorHistory +
|
||||
", tddHistory=" + tddHistory +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,4 +9,13 @@ public class Tbr extends HistoryRecord {
|
|||
this.duration = duration;
|
||||
this.percent = percent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Tbr{" +
|
||||
"timestamp=" + timestamp +
|
||||
", duration=" + duration +
|
||||
", percent=" + percent +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,4 +8,12 @@ public class Tdd extends HistoryRecord {
|
|||
super(timestamp);
|
||||
this.total = total;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Tdd{" +
|
||||
"timestamp=" + timestamp +
|
||||
", total=" + total +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,12 +21,21 @@ public class RuffyCommandsV1Impl implements RuffyCommands {
|
|||
return delegate;
|
||||
}
|
||||
|
||||
private RuffyCommandsV1Impl() {
|
||||
private RuffyCommandsV1Impl() {}
|
||||
|
||||
@Override
|
||||
public CommandResult getDateAndTime() {
|
||||
return delegate.getDateAndTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandResult takeOverAlarms() {
|
||||
return delegate.takeOverAlarms();
|
||||
public CommandResult readReservoirLevelAndLastBolus() {
|
||||
return delegate.readReservoirLevelAndLastBolus();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandResult confirmAlert(int warningCode) {
|
||||
return delegate.confirmAlert(warningCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -34,12 +34,13 @@ import de.jotomo.ruffyscripter.commands.BolusCommand;
|
|||
import de.jotomo.ruffyscripter.commands.CancelTbrCommand;
|
||||
import de.jotomo.ruffyscripter.commands.Command;
|
||||
import de.jotomo.ruffyscripter.commands.CommandException;
|
||||
import de.jotomo.ruffyscripter.commands.ConfirmAlertCommand;
|
||||
import de.jotomo.ruffyscripter.commands.ReadBasalProfileCommand;
|
||||
import de.jotomo.ruffyscripter.commands.ReadHistoryCommand;
|
||||
import de.jotomo.ruffyscripter.commands.ReadPumpStateCommand;
|
||||
import de.jotomo.ruffyscripter.commands.ReadReservoirLevelAndLastBolus;
|
||||
import de.jotomo.ruffyscripter.commands.SetBasalProfileCommand;
|
||||
import de.jotomo.ruffyscripter.commands.SetTbrCommand;
|
||||
import de.jotomo.ruffyscripter.commands.TakeOverAlarmsCommand;
|
||||
|
||||
// TODO regularly read "My data" history (boluses, TBR) to double check all commands ran successfully.
|
||||
// Automatically compare against AAPS db, or log all requests in the PumpInterface (maybe Milos
|
||||
|
@ -54,6 +55,7 @@ public class RuffyScripter implements RuffyCommands {
|
|||
private static final Logger log = LoggerFactory.getLogger(RuffyScripter.class);
|
||||
|
||||
private IRuffyService ruffyService;
|
||||
// TODO never written
|
||||
private String unrecoverableError = null;
|
||||
|
||||
@Nullable
|
||||
|
@ -75,6 +77,8 @@ public class RuffyScripter implements RuffyCommands {
|
|||
|
||||
@Override
|
||||
public void fail(String message) throws RemoteException {
|
||||
// 10-28 19:50:54.059 1426 1826 W RuffyScripter: [Thread-268] WARN [de.jotomo.ruffyscripter.RuffyScripter$1:78]: Ruffy warns: no connection possible
|
||||
|
||||
log.warn("Ruffy warns: " + message);
|
||||
}
|
||||
|
||||
|
@ -87,13 +91,11 @@ public class RuffyScripter implements RuffyCommands {
|
|||
public void rtStopped() throws RemoteException {
|
||||
log.debug("rtStopped callback invoked");
|
||||
currentMenu = null;
|
||||
connected = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rtStarted() throws RemoteException {
|
||||
log.debug("rtStarted callback invoked");
|
||||
connected = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -115,16 +117,6 @@ public class RuffyScripter implements RuffyCommands {
|
|||
synchronized (screenlock) {
|
||||
screenlock.notifyAll();
|
||||
}
|
||||
|
||||
// TODO v2 switch to using IRuffyService.isConnected, rather than guessing connectivity state
|
||||
// passed on screen updates
|
||||
connected = true;
|
||||
|
||||
// note that a WARNING_OR_ERROR menu can be a valid temporary state (cancelling TBR)
|
||||
// of a running command
|
||||
if (activeCmd == null && currentMenu.getType() == MenuType.WARNING_OR_ERROR) {
|
||||
log.warn("Warning/error menu encountered without a command running");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -174,9 +166,6 @@ public class RuffyScripter implements RuffyCommands {
|
|||
return started;
|
||||
}
|
||||
|
||||
private volatile boolean connected = false;
|
||||
private volatile long lastDisconnected = 0;
|
||||
|
||||
private Thread idleDisconnectMonitorThread = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
@ -184,25 +173,16 @@ public class RuffyScripter implements RuffyCommands {
|
|||
try {
|
||||
long now = System.currentTimeMillis();
|
||||
long connectionTimeOutMs = 5000;
|
||||
if (connected && activeCmd == null
|
||||
&& now > lastCmdExecutionTime + connectionTimeOutMs
|
||||
// don't disconnect too frequently, confuses ruffy?
|
||||
&& now > lastDisconnected + 15 * 1000) {
|
||||
if (ruffyService.isConnected() && activeCmd == null
|
||||
&& now > lastCmdExecutionTime + connectionTimeOutMs) {
|
||||
log.debug("Disconnecting after " + (connectionTimeOutMs / 1000) + "s inactivity timeout");
|
||||
lastDisconnected = now;
|
||||
ruffyService.doRTDisconnect();
|
||||
connected = false;
|
||||
// don't attempt anything fancy in the next 10s, let the pump settle
|
||||
SystemClock.sleep(10 * 1000);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// TODO do we need to catch this exception somewhere else too? right now it's
|
||||
// converted into a command failure, but it's not classified as unrecoverable;
|
||||
// eventually we might try to recover ... check docs, there's also another
|
||||
// execption we should watch interacting with a remote service.
|
||||
// SecurityException was the other, when there's an AIDL mismatch;
|
||||
//unrecoverableError = "Ruffy service went away";
|
||||
log.debug("Exception in idle disconnect monitor thread, carrying on", e);
|
||||
log.debug("Exception in idle disconnect monitor thread, taking a break and then carrying on", e);
|
||||
SystemClock.sleep(10 * 1000);
|
||||
}
|
||||
SystemClock.sleep(1000);
|
||||
}
|
||||
|
@ -219,18 +199,15 @@ public class RuffyScripter implements RuffyCommands {
|
|||
return runCommand(new ReadPumpStateCommand());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandResult readReservoirLevelAndLastBolus() {
|
||||
return runCommand(new ReadReservoirLevelAndLastBolus());
|
||||
}
|
||||
|
||||
public void returnToRootMenu() {
|
||||
// returning to main menu using the 'back' key does not cause a vibration
|
||||
MenuType menuType = getCurrentMenu().getType();
|
||||
while (menuType != MenuType.MAIN_MENU && menuType != MenuType.STOP && menuType != MenuType.WARNING_OR_ERROR) {
|
||||
// if (getCurrentMenu().getType() == MenuType.WARNING_OR_ERROR) {
|
||||
// String errorMsg = (String) getCurrentMenu().getAttribute(MenuAttribute.MESSAGE);
|
||||
// confirmAlert(errorMsg, 1000);
|
||||
// // TODO this isn't gonna work out ... this method can't know if something was enacted ...
|
||||
// // gotta keep that state in the command instance
|
||||
// throw new CommandException().success(false).enacted(false)
|
||||
// .message("Warning/error " + errorMsg + " raised while returning to main menu");
|
||||
// }
|
||||
log.debug("Going back to main menu, currently at " + menuType);
|
||||
pressBackKey();
|
||||
waitForMenuUpdate();
|
||||
|
@ -238,17 +215,11 @@ public class RuffyScripter implements RuffyCommands {
|
|||
}
|
||||
}
|
||||
|
||||
private static class Returnable {
|
||||
CommandResult cmdResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Always returns a CommandResult, never throws
|
||||
*/
|
||||
public CommandResult runCommand(final Command cmd) {
|
||||
/** Always returns a CommandResult, never throws */
|
||||
private CommandResult runCommand(final Command cmd) {
|
||||
log.debug("Attempting to run cmd: " + cmd);
|
||||
if (unrecoverableError != null) {
|
||||
return new CommandResult().success(false).enacted(false).message(unrecoverableError);
|
||||
return new CommandResult().success(false).enacted(false).message(unrecoverableError).state(readPumpStateInternal());
|
||||
}
|
||||
|
||||
List<String> violations = cmd.validateArguments();
|
||||
|
@ -262,59 +233,36 @@ public class RuffyScripter implements RuffyCommands {
|
|||
activeCmd = cmd;
|
||||
long connectStart = System.currentTimeMillis();
|
||||
ensureConnected();
|
||||
final Returnable returnable = new Returnable();
|
||||
class CommandRunner {
|
||||
public void run() {
|
||||
try {
|
||||
|
||||
// Except for GetPumpStateCommand: fail on all requests if the pump is suspended.
|
||||
// All trickery of not executing but returning success, so that AAPS can non-sensically TBR away when suspended
|
||||
// are dangerous in the current model where commands are dispatched without checking state beforehand, so
|
||||
// the above tactic would result in boluses not being applied and no warning being raised.
|
||||
// (Doing this check on the ComboPlugin level would require the plugin to fetch state from the pump,
|
||||
// deal with states changes (running/stopped), propagating that to AAPS and so on, adding more state,
|
||||
// which adds complexity I don't want in v1 and which requires more up-front design to do well,
|
||||
// esp. with AAPS).
|
||||
|
||||
// So, for v1, just check the pump is not suspended before executing commands and raising an error for all
|
||||
// but the GetPumpStateCommand. For v2, we'll have to come up with a better idea how to deal with the pump's
|
||||
// state. Maybe having read-only commands and write/treatment commands treated differently, or maybe
|
||||
// build an abstraction on top of the commands, so that e.g. a method on RuffyScripter encapsulates checking
|
||||
// pre-condititions, running one or several commands, checking-post conditions and what not.
|
||||
// Or maybe stick with commands, have them specify if they can run in stop mode. Think properly at which
|
||||
// level to handle state and logic.
|
||||
// For now, when changing cartridges and such: tell AAPS to stop the loop, change cartridge and resume the loop.
|
||||
if (currentMenu == null || currentMenu.getType() == MenuType.STOP) {
|
||||
if (cmd.needsRunMode()) {
|
||||
returnable.cmdResult = new CommandResult().success(false).enacted(false).message("Pump is suspended");
|
||||
return;
|
||||
}
|
||||
log.debug("Connection ready to execute cmd " + cmd);
|
||||
Thread cmdThread = new Thread(() -> {
|
||||
try {
|
||||
// TODO fail if currentMenu is not available?
|
||||
Menu localCurrentMenu = currentMenu;
|
||||
if (localCurrentMenu == null || localCurrentMenu.getType() == MenuType.STOP) {
|
||||
if (cmd.needsRunMode()) {
|
||||
activeCmd.getResult().success(false).message("Pump is suspended but operations requires to the pump to be running");
|
||||
return;
|
||||
}
|
||||
log.debug("Connection ready to execute cmd " + cmd);
|
||||
PumpState pumpState = readPumpStateInternal();
|
||||
log.debug("Pump state before running command: " + pumpState);
|
||||
long cmdStartTime = System.currentTimeMillis();
|
||||
cmd.setScripter(RuffyScripter.this);
|
||||
returnable.cmdResult = cmd.execute();
|
||||
long cmdEndTime = System.currentTimeMillis();
|
||||
returnable.cmdResult.completionTime = cmdEndTime;
|
||||
log.debug("Executing " + cmd + " took " + (cmdEndTime - cmdStartTime) + "ms");
|
||||
} catch (CommandException e) {
|
||||
returnable.cmdResult = e.toCommandResult();
|
||||
} catch (Exception e) {
|
||||
log.error("Unexpected exception running cmd", e);
|
||||
returnable.cmdResult = new CommandResult().exception(e).message("Unexpected exception running cmd");
|
||||
} finally {
|
||||
lastCmdExecutionTime = System.currentTimeMillis();
|
||||
}
|
||||
if (localCurrentMenu != null && localCurrentMenu.getType() == MenuType.WARNING_OR_ERROR) {
|
||||
// TODO unless confirmALert command fail here and return message
|
||||
}
|
||||
PumpState pumpState = readPumpStateInternal();
|
||||
log.debug("Pump state before running command: " + pumpState);
|
||||
cmd.setScripter(RuffyScripter.this);
|
||||
long cmdStartTime = System.currentTimeMillis();
|
||||
cmd.execute();
|
||||
long cmdEndTime = System.currentTimeMillis();
|
||||
log.debug("Executing " + cmd + " took " + (cmdEndTime - cmdStartTime) + "ms");
|
||||
} catch (CommandException e) {
|
||||
activeCmd.getResult().message(e.getMessage());
|
||||
} catch (Exception e) {
|
||||
log.error("Unexpected exception running cmd", e);
|
||||
activeCmd.getResult().message("Unexpected exception running cmd");
|
||||
} finally {
|
||||
lastCmdExecutionTime = System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
Thread cmdThread = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
new CommandRunner().run();
|
||||
}
|
||||
}, cmd.toString());
|
||||
}, cmd.getClass().getSimpleName());
|
||||
long executionStart = System.currentTimeMillis();
|
||||
cmdThread.start();
|
||||
|
||||
|
@ -331,23 +279,6 @@ public class RuffyScripter implements RuffyCommands {
|
|||
maxReconnectAttempts--;
|
||||
cmdThread.interrupt();
|
||||
reconnect();
|
||||
// TODO at least for bigger boluses we should check history after reconnect to make sure
|
||||
// we haven't issued that bolus within the last 1-2m? in case there's a bug in the code ...
|
||||
|
||||
// TODO: only do the reconnect to confirm the alert, then return and let the ComboPlugin decide what to do next;
|
||||
// for bolus: how ... run 'step 2': checking/reading history?! step1 being bolus delivery, so have different resume points? ggrrrmpf
|
||||
// less logic in scripter; just reconnect to confirm alert if needed, then return with error;
|
||||
// let CP read history.LAST to see what actually happened and then resume appropriately.
|
||||
cmdThread = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
new CommandRunner().run();
|
||||
}
|
||||
}, cmd.toString());
|
||||
cmdThread.start();
|
||||
// reset timeouts after reconnect
|
||||
dynamicTimeout = calculateCmdInactivityTimeout();
|
||||
overallTimeout = calculateOverallCmdTimeout();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -363,42 +294,33 @@ public class RuffyScripter implements RuffyCommands {
|
|||
cmdThread.interrupt();
|
||||
SystemClock.sleep(5000);
|
||||
log.error("Timed out thread dead yet? " + cmdThread.isAlive());
|
||||
return new CommandResult().success(false).enacted(false).message("Command stalled, check pump!");
|
||||
activeCmd.getResult().success(false).message("Command stalled, check pump!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (now > overallTimeout) {
|
||||
String msg = "Command " + cmd + " timed out after 4 min, check pump!";
|
||||
log.error(msg);
|
||||
return new CommandResult().success(false).enacted(false).message(msg);
|
||||
activeCmd.getResult().success(false).message(msg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (returnable.cmdResult.state == null) {
|
||||
returnable.cmdResult.state = readPumpStateInternal();
|
||||
activeCmd.getResult().state = readPumpStateInternal();
|
||||
CommandResult result = activeCmd.getResult();
|
||||
if (log.isDebugEnabled()) {
|
||||
long connectDurationSec = (executionStart - connectStart) / 1000;
|
||||
long executionDurationSec = (System.currentTimeMillis() - executionStart) / 1000;
|
||||
log.debug("Command result: " + result);
|
||||
log.debug("Connect: " + connectDurationSec + "s, execution: " + executionDurationSec + "s");
|
||||
}
|
||||
long connectDurationSec = (executionStart - connectStart) / 1000;
|
||||
long executionDurationSec = (System.currentTimeMillis() - executionStart) / 1000;
|
||||
returnable.cmdResult.duration = "Connect: " + connectDurationSec + "s, execution: " + executionDurationSec + "s";
|
||||
returnable.cmdResult.request = cmd.toString();
|
||||
log.debug("Command result: " + returnable.cmdResult);
|
||||
return returnable.cmdResult;
|
||||
return result;
|
||||
} catch (CommandException e) {
|
||||
CommandResult commandResult = e.toCommandResult();
|
||||
if (commandResult.state == null) commandResult.state = readPumpStateInternal();
|
||||
return commandResult;
|
||||
return activeCmd.getResult().success(false).message(e.getMessage()).state(readPumpStateInternal());
|
||||
} catch (Exception e) {
|
||||
// TODO catching E here AND in CommandRunner?
|
||||
// TODO detect and report pump warnings/errors differently?
|
||||
log.error("Error in ruffyscripter/ruffy", e);
|
||||
try {
|
||||
return new CommandResult()
|
||||
.exception(e)
|
||||
.message("Unexpected exception communication with ruffy: " + e.getMessage())
|
||||
.state(readPumpStateInternal());
|
||||
} catch (Exception e1) {
|
||||
// nothing more we can try
|
||||
}
|
||||
return new CommandResult().exception(e).message("Unexpected exception communication with ruffy: " + e.getMessage());
|
||||
log.error("Unexpected exception communication with ruffy", e);
|
||||
return activeCmd.getResult().success(false).exception(e)
|
||||
.message("Unexpected exception communication with ruffy: " + e.getMessage()).state(readPumpStateInternal());
|
||||
} finally {
|
||||
activeCmd = null;
|
||||
}
|
||||
|
@ -420,6 +342,17 @@ public class RuffyScripter implements RuffyCommands {
|
|||
*
|
||||
* @return whether the reconnect and return to main menu was successful
|
||||
*/
|
||||
// TODO only reconnect, confirm the warning the disconnect caused ond then return to ComboPlugin, which shall decide whether/how to restart
|
||||
// requires turning CommandResult from a return type into a command field
|
||||
|
||||
// TODO at least for bigger boluses we should check history after reconnect to make sure
|
||||
// we haven't issued that bolus within the last 1-2m? in case there's a bug in the code ...
|
||||
|
||||
// TODO: only do the reconnect to confirm the alert, then return and let the ComboPlugin decide what to do next;
|
||||
// for bolus: how ... run 'step 2': checking/reading history?! step1 being bolus delivery, so have different resume points? ggrrrmpf
|
||||
// less logic in scripter; just reconnect to confirm alert if needed, then return with error;
|
||||
// let CP read history.LAST to see what actually happened and then resume appropriately.
|
||||
|
||||
private boolean reconnect() {
|
||||
try {
|
||||
log.debug("Connection was lost, trying to reconnect");
|
||||
|
@ -475,58 +408,37 @@ public class RuffyScripter implements RuffyCommands {
|
|||
*/
|
||||
private void ensureConnected() {
|
||||
try {
|
||||
boolean menuUpdateRecentlyReceived = currentMenu != null && menuLastUpdated + 1000 > System.currentTimeMillis();
|
||||
log.debug("ensureConnect, connected: " + connected + ", receiving menu updates: " + menuUpdateRecentlyReceived);
|
||||
if (menuUpdateRecentlyReceived) {
|
||||
log.debug("Pump is sending us menu updates, so we're connected");
|
||||
if (ruffyService.isConnected()) {
|
||||
log.debug("Already connected");
|
||||
return;
|
||||
}
|
||||
|
||||
// Occasionally the rtConnect is called a few seconds after the rtDisconnected
|
||||
// callback was called, in response to your disconnect request via doRtDisconnect.
|
||||
// When connecting again shortly after disconnecting, the pump sometimes fails
|
||||
// to come up. So for v1, just wait. This happens rarely, so no overly fancy logic needed.
|
||||
// TODO v2 see if we can do this cleaner, use isDisconnected as well maybe. GL#34.
|
||||
// TODO remove this, will be in the way of quickly reconnecting after an exception and dealing
|
||||
// with an alarm; we'll then see if the pump can deal with this
|
||||
/* if (System.currentTimeMillis() < lastDisconnected + 10 * 1000) {
|
||||
log.debug("Waiting 10s to let pump settle after recent disconnect");
|
||||
SystemClock.sleep(10 * 1000);
|
||||
}*/
|
||||
|
||||
boolean connectInitSuccessful = ruffyService.doRTConnect() == 0;
|
||||
log.debug("Connect init successful: " + connectInitSuccessful);
|
||||
log.debug("Waiting for first menu update to be sent");
|
||||
// Note: there was an 'if(currentMenu == null)' around the next call, since
|
||||
// the rtDisconnected callback sets currentMenu = null. However, there were
|
||||
// race conditions, so it was removed. And really, waiting for an update
|
||||
// to come in is a much safer bet.
|
||||
|
||||
// waitForMenuUpdate times out after 60s and throws a CommandException.
|
||||
// if the user just pressed a button on the combo, the screen needs to time first
|
||||
// before a connection is possible. In that case, it takes 45s before the
|
||||
// connection comes up.
|
||||
// waitForMenuUpdate(90, "Timeout connecting to pump");
|
||||
long timeoutExpired = System.currentTimeMillis() + 90 * 1000;
|
||||
long initialUpdateTime = menuLastUpdated;
|
||||
long again = System.currentTimeMillis() + 30 * 1000;
|
||||
while (initialUpdateTime == menuLastUpdated) {
|
||||
if (System.currentTimeMillis() > timeoutExpired) {
|
||||
throw new CommandException().message("Timeout connecting to pump");
|
||||
throw new CommandException("Timeout connecting to pump");
|
||||
}
|
||||
SystemClock.sleep(50);
|
||||
if (again < System.currentTimeMillis()) {
|
||||
// TODO test
|
||||
log.debug("Connecting taking long, forcing disconnect first");
|
||||
ruffyService.doRTDisconnect();
|
||||
SystemClock.sleep(2000);
|
||||
log.debug("Connecting again");
|
||||
ruffyService.doRTConnect();
|
||||
SystemClock.sleep(1000);
|
||||
again = System.currentTimeMillis() + 30 * 1000;
|
||||
}
|
||||
}
|
||||
} catch (CommandException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
throw new CommandException().exception(e).message("Unexpected exception while initiating/restoring pump connection");
|
||||
throw new CommandException("Unexpected exception while initiating/restoring pump connection", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -539,6 +451,7 @@ public class RuffyScripter implements RuffyCommands {
|
|||
*/
|
||||
public PumpState readPumpStateInternal() {
|
||||
PumpState state = new PumpState();
|
||||
state.timestamp = System.currentTimeMillis();
|
||||
Menu menu = currentMenu;
|
||||
if (menu == null) {
|
||||
return state;
|
||||
|
@ -571,6 +484,16 @@ public class RuffyScripter implements RuffyCommands {
|
|||
return state;
|
||||
}
|
||||
|
||||
public int readWarningCode() {
|
||||
verifyMenuIsDisplayed(MenuType.WARNING_OR_ERROR);
|
||||
Integer warningCode = (Integer) getCurrentMenu().getAttribute(MenuAttribute.WARNING);
|
||||
while (warningCode == null) {
|
||||
waitForScreenUpdate();
|
||||
warningCode = (Integer) getCurrentMenu().getAttribute(MenuAttribute.WARNING);
|
||||
}
|
||||
return warningCode;
|
||||
}
|
||||
|
||||
// below: methods to be used by commands
|
||||
// TODO move into a new Operations(scripter) class commands can delegate to,
|
||||
// so this class can focus on providing a connection to run commands
|
||||
|
@ -625,7 +548,7 @@ public class RuffyScripter implements RuffyCommands {
|
|||
// TODO force reconnect? and retry?
|
||||
while (currentMenu == null) {
|
||||
if (System.currentTimeMillis() > timeout) {
|
||||
throw new CommandException().message("Unable to read current menu");
|
||||
throw new CommandException("Unable to read current menu");
|
||||
}
|
||||
log.debug("currentMenu == null, waiting");
|
||||
waitForMenuUpdate();
|
||||
|
@ -678,21 +601,20 @@ public class RuffyScripter implements RuffyCommands {
|
|||
ruffyService.rtSendKey(Key.NO_KEY, true);
|
||||
log.debug("Releasing key");
|
||||
} catch (Exception e) {
|
||||
throw new CommandException().exception(e).message("Error while pressing buttons");
|
||||
throw new CommandException("Error while pressing buttons");
|
||||
}
|
||||
}
|
||||
|
||||
// TODO sort out usages of this method and waitForMenu update, which have the same intent,
|
||||
// but approach things differently;
|
||||
public boolean waitForScreenUpdate(long timeout) {
|
||||
private void waitForScreenUpdate() {
|
||||
synchronized (screenlock) {
|
||||
try {
|
||||
screenlock.wait(timeout);
|
||||
screenlock.wait((long) 2000); // usually ~500, occassionally up to 1100ms
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
log.debug("Ignoring exception in wait for screenlock", e);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO v2, rework these two methods: waitForMenuUpdate shoud only be used by commands
|
||||
|
@ -702,54 +624,19 @@ public class RuffyScripter implements RuffyCommands {
|
|||
|
||||
// TODO confirmAlarms? and report back which were cancelled?
|
||||
|
||||
/**
|
||||
* Confirms and dismisses the given alert if it's raised before the timeout
|
||||
*/
|
||||
public boolean confirmAlert(String alertMessage, int maxWaitMs) {
|
||||
long inFiveSeconds = System.currentTimeMillis() + maxWaitMs;
|
||||
boolean alertProcessed = false;
|
||||
while (System.currentTimeMillis() < inFiveSeconds && !alertProcessed) {
|
||||
if (getCurrentMenu().getType() == MenuType.WARNING_OR_ERROR) {
|
||||
// Note that the message is permanently displayed, while the error code is blinking.
|
||||
// A wait till the error code can be read results in the code hanging, despite
|
||||
// menu updates coming in, so just check the message.
|
||||
// TODO quick try if the can't make reading the error code work ..
|
||||
String errorMsg = (String) getCurrentMenu().getAttribute(MenuAttribute.MESSAGE);
|
||||
if (!errorMsg.equals(alertMessage)) {
|
||||
throw new CommandException().success(false).enacted(false)
|
||||
.message("An alert other than the expected " + alertMessage + " was raised by the pump: "
|
||||
+ errorMsg + ". Please check the pump.");
|
||||
}
|
||||
// confirm alert
|
||||
verifyMenuIsDisplayed(MenuType.WARNING_OR_ERROR);
|
||||
pressCheckKey();
|
||||
// dismiss alert
|
||||
verifyMenuIsDisplayed(MenuType.WARNING_OR_ERROR);
|
||||
pressCheckKey();
|
||||
waitForMenuToBeLeft(MenuType.WARNING_OR_ERROR);
|
||||
alertProcessed = true;
|
||||
}
|
||||
SystemClock.sleep(10);
|
||||
}
|
||||
return alertProcessed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait until the menu is updated
|
||||
*/
|
||||
public void waitForMenuUpdate() {
|
||||
waitForMenuUpdate(60, "Timeout waiting for menu update");
|
||||
}
|
||||
|
||||
private void waitForMenuUpdate(long timeoutInSeconds, String errorMessage) {
|
||||
long timeoutExpired = System.currentTimeMillis() + timeoutInSeconds * 1000;
|
||||
long initialUpdateTime = menuLastUpdated;
|
||||
while (initialUpdateTime == menuLastUpdated) {
|
||||
if (System.currentTimeMillis() > timeoutExpired) {
|
||||
throw new CommandException().message(errorMessage);
|
||||
}
|
||||
SystemClock.sleep(50);
|
||||
}
|
||||
waitForScreenUpdate();
|
||||
// long timeoutExpired = System.currentTimeMillis() + 60 * 1000;
|
||||
// long initialUpdateTime = menuLastUpdated;
|
||||
// while (initialUpdateTime == menuLastUpdated) {
|
||||
// if (System.currentTimeMillis() > timeoutExpired) {
|
||||
// throw new CommandException("Timeout waiting for menu update");
|
||||
// }
|
||||
// SystemClock.sleep(10);
|
||||
// }
|
||||
}
|
||||
|
||||
private void pressKey(final byte key) {
|
||||
|
@ -758,7 +645,7 @@ public class RuffyScripter implements RuffyCommands {
|
|||
SystemClock.sleep(150);
|
||||
ruffyService.rtSendKey(Key.NO_KEY, true);
|
||||
} catch (Exception e) {
|
||||
throw new CommandException().exception(e).message("Error while pressing buttons");
|
||||
throw new CommandException("Error while pressing buttons");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -775,7 +662,7 @@ public class RuffyScripter implements RuffyCommands {
|
|||
// + ". Check menu settings on your pump to ensure it's not hidden.");
|
||||
// }
|
||||
if (retries == 0) {
|
||||
throw new CommandException().message("Menu not found searching for " + desiredMenu
|
||||
throw new CommandException("Menu not found searching for " + desiredMenu
|
||||
+ ". Check menu settings on your pump to ensure it's not hidden.");
|
||||
}
|
||||
pressMenuKey();
|
||||
|
@ -792,7 +679,7 @@ public class RuffyScripter implements RuffyCommands {
|
|||
long timeout = System.currentTimeMillis() + 60 * 1000;
|
||||
while (getCurrentMenu().getType() == menuType) {
|
||||
if (System.currentTimeMillis() > timeout) {
|
||||
throw new CommandException().message("Timeout waiting for menu " + menuType + " to be left");
|
||||
throw new CommandException("Timeout waiting for menu " + menuType + " to be left");
|
||||
}
|
||||
SystemClock.sleep(10);
|
||||
}
|
||||
|
@ -812,7 +699,7 @@ public class RuffyScripter implements RuffyCommands {
|
|||
if (failureMessage == null) {
|
||||
failureMessage = "Invalid pump state, expected to be in menu " + expectedMenu + ", but current menu is " + currentMenu.getType();
|
||||
}
|
||||
throw new CommandException().message(failureMessage);
|
||||
throw new CommandException(failureMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -824,7 +711,7 @@ public class RuffyScripter implements RuffyCommands {
|
|||
SystemClock.sleep(100);
|
||||
retries = retries - 1;
|
||||
} else {
|
||||
throw new CommandException().message("Invalid pump state, expected to be in menu MAIN or STOP but current menu is " + currentMenu.getType());
|
||||
throw new CommandException("Invalid pump state, expected to be in menu MAIN or STOP but current menu is " + currentMenu.getType());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -835,10 +722,10 @@ public class RuffyScripter implements RuffyCommands {
|
|||
Object value = getCurrentMenu().getAttribute(attribute);
|
||||
while (!expectedType.isInstance(value)) {
|
||||
value = getCurrentMenu().getAttribute(attribute);
|
||||
waitForScreenUpdate(1000);
|
||||
waitForScreenUpdate();
|
||||
retries--;
|
||||
if (retries == 0) {
|
||||
throw new CommandException().message("Failed to read blinkng value: " + attribute + "=" + value + " type=" + value);
|
||||
throw new CommandException("Failed to read blinkng value: " + attribute + "=" + value + " type=" + value);
|
||||
}
|
||||
}
|
||||
return (T) value;
|
||||
|
@ -867,8 +754,8 @@ public class RuffyScripter implements RuffyCommands {
|
|||
}
|
||||
|
||||
@Override
|
||||
public CommandResult takeOverAlarms() {
|
||||
return runCommand(new TakeOverAlarmsCommand());
|
||||
public CommandResult confirmAlert(int warningCode) {
|
||||
return runCommand(new ConfirmAlertCommand(warningCode));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -886,10 +773,15 @@ public class RuffyScripter implements RuffyCommands {
|
|||
return runCommand(new SetBasalProfileCommand(basalProfile));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandResult getDateAndTime() {
|
||||
return new CommandResult().success(false).enacted(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandResult setDateAndTime(Date date) {
|
||||
// TODO I'm a faker!
|
||||
return new CommandResult().success(true).enacted(false);
|
||||
// TODO
|
||||
return new CommandResult().success(false).enacted(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -906,4 +798,36 @@ public class RuffyScripter implements RuffyCommands {
|
|||
public void unpair() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirms and dismisses the given alert if it's raised before the timeout
|
||||
*/
|
||||
public boolean confirmAlert(int warningCode, int maxWaitMs) {
|
||||
long timeout = System.currentTimeMillis() + maxWaitMs;
|
||||
while (System.currentTimeMillis() < timeout) {
|
||||
if (getCurrentMenu().getType() == MenuType.WARNING_OR_ERROR) {
|
||||
// Note that the message is permanently displayed, while the error code is blinking.
|
||||
// A wait till the error code can be read results in the code hanging, despite
|
||||
// menu updates coming in, so just check the message.
|
||||
|
||||
int displayedWarningCode = readWarningCode();
|
||||
String errorMsg = (String) getCurrentMenu().getAttribute(MenuAttribute.MESSAGE);
|
||||
if (displayedWarningCode != warningCode) {
|
||||
throw new CommandException("An alert other than the expected warning " + warningCode+ " was raised by the pump: "
|
||||
+ displayedWarningCode + "(" + errorMsg + "). Please check the pump.");
|
||||
}
|
||||
// confirm alert
|
||||
verifyMenuIsDisplayed(MenuType.WARNING_OR_ERROR);
|
||||
pressCheckKey();
|
||||
// dismiss alert
|
||||
verifyMenuIsDisplayed(MenuType.WARNING_OR_ERROR);
|
||||
pressCheckKey();
|
||||
/* // TODO multiple alerts in a row ... can this occur in non-freak circumstances?
|
||||
waitForMenuToBeLeft(MenuType.WARNING_OR_ERROR);*/
|
||||
return true;
|
||||
}
|
||||
SystemClock.sleep(10);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,17 @@
|
|||
package de.jotomo.ruffyscripter.commands;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import de.jotomo.ruffy.spi.CommandResult;
|
||||
import de.jotomo.ruffyscripter.RuffyScripter;
|
||||
|
||||
public abstract class BaseCommand implements Command {
|
||||
// RS will inject itself here
|
||||
protected RuffyScripter scripter;
|
||||
|
||||
protected CommandResult result = new CommandResult();
|
||||
|
||||
@Override
|
||||
public void setScripter(RuffyScripter scripter) {
|
||||
this.scripter = scripter;
|
||||
|
@ -18,8 +24,23 @@ public abstract class BaseCommand implements Command {
|
|||
|
||||
// TODO i18n; can we work with error codes instead of messages? Like W07? that way we're language agnostic
|
||||
// error message ist still needed to cancel TBR though, let next-gen ruffy take care of that?
|
||||
/** An alarm (or null) caused by a disconnect we can safely confirm on reconnect,
|
||||
* knowing it's not severe as it was caused by this command. */
|
||||
|
||||
/**
|
||||
* An alarm (or null) caused by a disconnect we can safely confirm on reconnect,
|
||||
* knowing it's not severe as it was caused by this command.
|
||||
*/
|
||||
@Override
|
||||
public String getReconnectAlarm() { return null; }
|
||||
public String getReconnectAlarm() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> validateArguments() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandResult getResult() {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,15 +9,18 @@ import org.slf4j.LoggerFactory;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import de.jotomo.ruffy.spi.history.Bolus;
|
||||
import de.jotomo.ruffy.spi.history.PumpHistory;
|
||||
import de.jotomo.ruffyscripter.RuffyScripter;
|
||||
import de.jotomo.ruffy.spi.BolusProgressReporter;
|
||||
import de.jotomo.ruffy.spi.CommandResult;
|
||||
import de.jotomo.ruffy.spi.PumpWarningCodes;
|
||||
import de.jotomo.ruffyscripter.RuffyScripter;
|
||||
|
||||
import static de.jotomo.ruffy.spi.BolusProgressReporter.State.*;
|
||||
import static de.jotomo.ruffy.spi.BolusProgressReporter.State.DELIVERED;
|
||||
import static de.jotomo.ruffy.spi.BolusProgressReporter.State.DELIVERING;
|
||||
import static de.jotomo.ruffy.spi.BolusProgressReporter.State.FINISHED;
|
||||
import static de.jotomo.ruffy.spi.BolusProgressReporter.State.PROGRAMMING;
|
||||
import static de.jotomo.ruffy.spi.BolusProgressReporter.State.STOPPED;
|
||||
import static de.jotomo.ruffy.spi.BolusProgressReporter.State.STOPPING;
|
||||
|
||||
public class BolusCommand extends BaseCommand {
|
||||
private static final Logger log = LoggerFactory.getLogger(BolusCommand.class);
|
||||
|
@ -26,14 +29,10 @@ public class BolusCommand extends BaseCommand {
|
|||
private final BolusProgressReporter bolusProgressReporter;
|
||||
private volatile boolean cancelRequested;
|
||||
|
||||
// TODO make CommandResult a field that is updated as the command progress so that we can
|
||||
// (also) see via cmdResult.enacted if something has been enacted and how safe it is to
|
||||
// restart the command (also checking history of course, but lets double check this cruical
|
||||
// part)
|
||||
|
||||
public BolusCommand(double bolus, BolusProgressReporter bolusProgressReporter) {
|
||||
this.bolus = bolus;
|
||||
this.bolusProgressReporter = bolusProgressReporter;
|
||||
this.result = new CommandResult();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -53,28 +52,26 @@ public class BolusCommand extends BaseCommand {
|
|||
}
|
||||
|
||||
@Override
|
||||
public CommandResult execute() {
|
||||
public void execute() {
|
||||
try {
|
||||
// TODO read reservoir level and reject request if reservoir < bolus
|
||||
// TODO also check if there's a bolus in history we're not aware of
|
||||
// press check twice to get reservoir level and last bolus quickly
|
||||
|
||||
bolusProgressReporter.report(PROGRAMMING, 0, 0);
|
||||
enterBolusMenu();
|
||||
inputBolusAmount();
|
||||
verifyDisplayedBolusAmount();
|
||||
|
||||
// last chance to abort before confirm the bolus
|
||||
if (cancelRequested) {
|
||||
bolusProgressReporter.report(STOPPING, 0, 0);
|
||||
scripter.returnToRootMenu();
|
||||
bolusProgressReporter.report(STOPPED, 0, 0);
|
||||
return new CommandResult().success(true).enacted(false)
|
||||
.message("Bolus cancelled as per user request with no insulin delivered");
|
||||
result.success = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// confirm bolus
|
||||
scripter.verifyMenuIsDisplayed(MenuType.BOLUS_ENTER);
|
||||
scripter.pressCheckKey();
|
||||
result.enacted = true;
|
||||
|
||||
// the pump displays the entered bolus and waits a few seconds to let user check and cancel
|
||||
while (scripter.getCurrentMenu().getType() == MenuType.BOLUS_ENTER) {
|
||||
|
@ -83,47 +80,47 @@ public class BolusCommand extends BaseCommand {
|
|||
scripter.pressUpKey();
|
||||
// wait up to 1s for a BOLUS_CANCELLED alert, if it doesn't happen we missed
|
||||
// the window, simply continue and let the next cancel attempt try its luck
|
||||
boolean alertWasCancelled = scripter.confirmAlert("BOLUS CANCELLED", 1000);
|
||||
boolean alertWasCancelled = scripter.confirmAlert(PumpWarningCodes.BOLUS_CANCELLED, 1000);
|
||||
if (alertWasCancelled) {
|
||||
bolusProgressReporter.report(STOPPED, 0, 0);
|
||||
return new CommandResult().success(true).enacted(false)
|
||||
.message("Bolus cancelled as per user request with no insulin delivered");
|
||||
result.success = true;
|
||||
return;
|
||||
}
|
||||
SystemClock.sleep(10);
|
||||
}
|
||||
SystemClock.sleep(10);
|
||||
}
|
||||
|
||||
// the bolus progress is displayed on the main menu
|
||||
scripter.verifyMenuIsDisplayed(MenuType.MAIN_MENU,
|
||||
"Pump did not return to MAIN_MEU from BOLUS_ENTER to deliver bolus. "
|
||||
+ "Check pump manually, the bolus might not have been delivered.");
|
||||
|
||||
bolusProgressReporter.report(DELIVERING, 0, 0);
|
||||
Double bolusRemaining = (Double) scripter.getCurrentMenu().getAttribute(MenuAttribute.BOLUS_REMAINING);
|
||||
double lastBolusReported = 0;
|
||||
boolean lowCartdrigeAlarmTriggered = false;
|
||||
// wait for bolus delivery to complete; the remaining units to deliver are counted
|
||||
// down and are displayed on the main menu.
|
||||
// TODO extract into method
|
||||
|
||||
// TODO 'low cartrdige' alarm must be handled inside, since the bolus continues regardless;
|
||||
// it must be cleared so we can see the remaining bolus again;
|
||||
// wait for bolus delivery to complete; the remaining units to deliver are counted down
|
||||
boolean cancelInProgress = false;
|
||||
double lastBolusReported = 0;
|
||||
Double bolusRemaining = (Double) scripter.getCurrentMenu().getAttribute(MenuAttribute.BOLUS_REMAINING);
|
||||
while (bolusRemaining != null) {
|
||||
if (cancelRequested) {
|
||||
// cancel running bolus by pressing up for 3s, while raise a BOLUS CANCELLED
|
||||
// alert, unless the bolus finished within those 3s.
|
||||
if (cancelRequested && !cancelInProgress) {
|
||||
bolusProgressReporter.report(STOPPING, 0, 0);
|
||||
scripter.pressKeyMs(RuffyScripter.Key.UP, 3000);
|
||||
bolusProgressReporter.report(STOPPED, 0, 0);
|
||||
// if the bolus finished while we attempted to cancel it, there'll be no alarm
|
||||
long timeout = System.currentTimeMillis() + 2000;
|
||||
while (scripter.getCurrentMenu().getType() != MenuType.WARNING_OR_ERROR && System.currentTimeMillis() < timeout) {
|
||||
SystemClock.sleep(10);
|
||||
cancelInProgress = true;
|
||||
new Thread(() -> scripter.pressKeyMs(RuffyScripter.Key.UP, 3000), "bolus-canceller").start();
|
||||
}
|
||||
if (scripter.getCurrentMenu().getType() == MenuType.WARNING_OR_ERROR) {
|
||||
// confirm warning alerts and update the result to indicate alerts occurred
|
||||
int warningCode = scripter.readWarningCode();
|
||||
if (warningCode == PumpWarningCodes.BOLUS_CANCELLED) {
|
||||
scripter.confirmAlert(PumpWarningCodes.BOLUS_CANCELLED, 2000);
|
||||
bolusProgressReporter.report(STOPPED, 0, 0);
|
||||
result.wasSuccessfullyCancelled = true;
|
||||
result.alertConfirmed = true;
|
||||
} else if (warningCode == PumpWarningCodes.CARTRIDGE_LOW) {
|
||||
scripter.confirmAlert(PumpWarningCodes.CARTRIDGE_LOW, 2000);
|
||||
result.alertConfirmed = true;
|
||||
} else if (warningCode == PumpWarningCodes.BATTERY_LOW) {
|
||||
scripter.confirmAlert(PumpWarningCodes.BATTERY_LOW, 2000);
|
||||
result.alertConfirmed = true;
|
||||
}
|
||||
while (scripter.getCurrentMenu().getType() == MenuType.WARNING_OR_ERROR) {
|
||||
// TODO make this cleaner, extract method, needed below too
|
||||
scripter.pressCheckKey();
|
||||
SystemClock.sleep(200);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (lastBolusReported != bolusRemaining) {
|
||||
log.debug("Delivering bolus, remaining: " + bolusRemaining);
|
||||
|
@ -132,94 +129,13 @@ public class BolusCommand extends BaseCommand {
|
|||
lastBolusReported = bolusRemaining;
|
||||
}
|
||||
|
||||
/*
|
||||
// TODO think through situatiotns where an alarm can be raised, not just when pressing a button,
|
||||
// but a 'low battery' alarm can trigger at any time ...
|
||||
if (scripter.getCurrentMenu().getType() == MenuType.WARNING_OR_ERROR) {
|
||||
String message = (String) scripter.getCurrentMenu().getAttribute(MenuAttribute.MESSAGE);
|
||||
if (message.equals("LOW CARTRIDGE")) {
|
||||
lowCartdrigeAlarmTriggered = true;
|
||||
scripter.confirmAlert("LOW CARTRIDGE", 2000);
|
||||
} else {
|
||||
// any other alert
|
||||
break;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
SystemClock.sleep(50);
|
||||
bolusRemaining = (Double) scripter.getCurrentMenu().getAttribute(MenuAttribute.BOLUS_REMAINING);
|
||||
}
|
||||
bolusProgressReporter.report(DELIVERED, 100, bolus);
|
||||
|
||||
/*
|
||||
// wait up to 2s for any possible warning to be raised, if not raised already
|
||||
// TODO what could be raised here, other than those alarms than can ring at any time anyways?
|
||||
long timeout = System.currentTimeMillis() + 2 * 1000;
|
||||
while (scripter.getCurrentMenu().getType() != MenuType.WARNING_OR_ERROR && System.currentTimeMillis() < timeout) {
|
||||
SystemClock.sleep(50);
|
||||
}
|
||||
|
||||
// process warnings (confirm them, report back to AAPS about them)
|
||||
// while (scripter.getCurrentMenu().getType() == MenuType.WARNING_OR_ERROR || System.currentTimeMillis() < timeout) {
|
||||
if (scripter.getCurrentMenu().getType() == MenuType.WARNING_OR_ERROR) {
|
||||
scripter.confirmAlert(((String) scripter.getCurrentMenu().getAttribute(MenuAttribute.MESSAGE)), 1000);
|
||||
}
|
||||
// SystemClock.sleep(50);
|
||||
// }
|
||||
*/
|
||||
|
||||
// TODO what if we hit 'cartridge low' alert here? is it immediately displayed or after the bolus?
|
||||
// TODO how are error states reported back to the caller that occur outside of calls in genal? Low battery, low cartridge?
|
||||
|
||||
// make sure no alert (occlusion, cartridge empty) has occurred.
|
||||
scripter.verifyMenuIsDisplayed(MenuType.MAIN_MENU,
|
||||
"Bolus delivery did not complete as expected. "
|
||||
+ "Check pump manually, the bolus might not have been delivered.");
|
||||
|
||||
// TODO report back what was read from history
|
||||
|
||||
// read last bolus record; those menus display static data and therefore
|
||||
// only a single menu update is sent
|
||||
scripter.navigateToMenu(MenuType.MY_DATA_MENU);
|
||||
scripter.verifyMenuIsDisplayed(MenuType.MY_DATA_MENU);
|
||||
scripter.pressCheckKey();
|
||||
if (scripter.getCurrentMenu().getType() != MenuType.BOLUS_DATA) {
|
||||
scripter.waitForMenuUpdate();
|
||||
}
|
||||
|
||||
if (!scripter.getCurrentMenu().attributes().contains(MenuAttribute.BOLUS)) {
|
||||
throw new CommandException().success(false).enacted(true)
|
||||
.message("Bolus was delivered, but unable to confirm it with history record");
|
||||
}
|
||||
|
||||
// TODO check date so we don't pick a false record if the previous bolus had the same amount;
|
||||
// also, report back partial bolus. Just call ReadHistory(timestamp, boluses=true) cmd ...
|
||||
double lastBolusInHistory = (double) scripter.getCurrentMenu().getAttribute(MenuAttribute.BOLUS);
|
||||
if (Math.abs(bolus - lastBolusInHistory) > 0.05) {
|
||||
throw new CommandException().success(false).enacted(true)
|
||||
.message("Last bolus shows " + lastBolusInHistory
|
||||
+ " U delievered, but " + bolus + " U were requested");
|
||||
}
|
||||
log.debug("Bolus record in history confirms delivered bolus");
|
||||
|
||||
// TODO how would this call fail? more generally ......
|
||||
scripter.returnToRootMenu();
|
||||
if (scripter.getCurrentMenu().getType() != MenuType.MAIN_MENU) {
|
||||
throw new CommandException().success(false).enacted(true)
|
||||
.message("Bolus was correctly delivered and checked against history, but we "
|
||||
+ "did not return the main menu successfully.");
|
||||
}
|
||||
|
||||
ArrayList<Bolus> boluses = new ArrayList<>();
|
||||
boluses.add(new Bolus(System.currentTimeMillis(), bolus));
|
||||
return new CommandResult().success(true).enacted(true)
|
||||
.message(String.format(Locale.US, "Delivered %02.1f U", bolus))
|
||||
.history(new PumpHistory().bolusHistory(boluses));
|
||||
} catch (CommandException e) {
|
||||
return e.toCommandResult();
|
||||
result.success = true;
|
||||
} finally {
|
||||
bolusProgressReporter.report(null, 100, 0);
|
||||
bolusProgressReporter.report(FINISHED, 100, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -257,7 +173,7 @@ public class BolusCommand extends BaseCommand {
|
|||
|
||||
log.debug("Final bolus: " + displayedBolus);
|
||||
if (Math.abs(displayedBolus - bolus) > 0.05) {
|
||||
throw new CommandException().message("Failed to set correct bolus. Expected: " + bolus + ", actual: " + displayedBolus);
|
||||
throw new CommandException("Failed to set correct bolus. Expected: " + bolus + ", actual: " + displayedBolus);
|
||||
}
|
||||
|
||||
// check again to ensure the displayed value hasn't change due to due scrolling taking extremely long
|
||||
|
@ -265,7 +181,7 @@ public class BolusCommand extends BaseCommand {
|
|||
scripter.verifyMenuIsDisplayed(MenuType.BOLUS_ENTER);
|
||||
double refreshedDisplayedBolus = scripter.readBlinkingValue(Double.class, MenuAttribute.BOLUS);
|
||||
if (Math.abs(displayedBolus - refreshedDisplayedBolus) > 0.05) {
|
||||
throw new CommandException().message("Failed to set bolus: bolus changed after input stopped from "
|
||||
throw new CommandException("Failed to set bolus: bolus changed after input stopped from "
|
||||
+ displayedBolus + " -> " + refreshedDisplayedBolus);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,11 +4,7 @@ import org.monkey.d.ruffy.ruffy.driver.display.MenuType;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import de.jotomo.ruffy.spi.PumpState;
|
||||
import de.jotomo.ruffy.spi.CommandResult;
|
||||
|
||||
// TODO robustness: can a TBR run out, whilst we're trying to cancel it?
|
||||
// Hm, we could just ignore TBRs that run out within the next 60s (0:01 or even 0:02
|
||||
|
@ -16,41 +12,21 @@ import de.jotomo.ruffy.spi.CommandResult;
|
|||
public class CancelTbrCommand extends BaseCommand {
|
||||
private static final Logger log = LoggerFactory.getLogger(CancelTbrCommand.class);
|
||||
|
||||
@Override
|
||||
public List<String> validateArguments() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getReconnectAlarm() {
|
||||
return "TBR CANCELLED";
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandResult execute() {
|
||||
try {
|
||||
scripter.verifyMenuIsDisplayed(MenuType.MAIN_MENU);
|
||||
PumpState pumpState = scripter.readPumpStateInternal();
|
||||
if (!pumpState.tbrActive) {
|
||||
log.debug("No TBR active to cancel");
|
||||
return new CommandResult()
|
||||
.success(true)
|
||||
// Technically, nothing was enacted, but AAPS needs this to recover
|
||||
// when there was an issue and AAPS thinks a TBR is still active,
|
||||
// so the ComboPlugin can create a TempporaryBasel to mark the TBR
|
||||
// as finished to get in sync with the pump state.
|
||||
.enacted(true)
|
||||
.message("No TBR active");
|
||||
}
|
||||
|
||||
log.debug("Cancelling active TBR of " + pumpState.tbrPercent
|
||||
+ "% with " + pumpState.tbrRemainingDuration + " min remaining");
|
||||
SetTbrCommand setTbrCommand = new SetTbrCommand(100, 0);
|
||||
setTbrCommand.setScripter(scripter);
|
||||
return setTbrCommand.execute();
|
||||
} catch (CommandException e) {
|
||||
return e.toCommandResult();
|
||||
}
|
||||
public void execute() {
|
||||
scripter.verifyMenuIsDisplayed(MenuType.MAIN_MENU);
|
||||
PumpState pumpState = scripter.readPumpStateInternal();
|
||||
log.debug("Cancelling active TBR of " + pumpState.tbrPercent
|
||||
+ "% with " + pumpState.tbrRemainingDuration + " min remaining");
|
||||
SetTbrCommand setTbrCommand = new SetTbrCommand(100, 0);
|
||||
setTbrCommand.setScripter(scripter);
|
||||
setTbrCommand.execute();
|
||||
result = setTbrCommand.result;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -13,9 +13,10 @@ import de.jotomo.ruffy.spi.CommandResult;
|
|||
* sequence, letting the methods take care of waits.
|
||||
*/
|
||||
public interface Command {
|
||||
CommandResult execute();
|
||||
List<String> validateArguments();
|
||||
void setScripter(RuffyScripter scripter);
|
||||
List<String> validateArguments();
|
||||
boolean needsRunMode();
|
||||
void execute();
|
||||
CommandResult getResult();
|
||||
String getReconnectAlarm();
|
||||
}
|
||||
|
|
|
@ -1,52 +1,11 @@
|
|||
package de.jotomo.ruffyscripter.commands;
|
||||
|
||||
import de.jotomo.ruffy.spi.CommandResult;
|
||||
|
||||
public class CommandException extends RuntimeException {
|
||||
public boolean success = false;
|
||||
public boolean enacted = false;
|
||||
public Exception exception = null;
|
||||
public String message = null;
|
||||
|
||||
public CommandException() {
|
||||
public CommandException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public CommandException success(boolean success) {
|
||||
this.success = success;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandException enacted(boolean enacted) {
|
||||
this.enacted = enacted;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandException exception(Exception exception) {
|
||||
this.exception = exception;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public CommandException message(String message) {
|
||||
this.message = message;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandResult toCommandResult() {
|
||||
return new CommandResult().success(success).enacted(enacted).exception(exception).message(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CommandException{" +
|
||||
"success=" + success +
|
||||
", enacted=" + enacted +
|
||||
", exception=" + exception +
|
||||
", message='" + message + '\'' +
|
||||
'}';
|
||||
public CommandException(String message, Exception exception) {
|
||||
super(message, exception);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
package de.jotomo.ruffyscripter.commands;
|
||||
|
||||
public class ConfirmAlertCommand extends BaseCommand {
|
||||
private final int warningCode;
|
||||
|
||||
public ConfirmAlertCommand(int warningCode) {
|
||||
this.warningCode = warningCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
result.success(scripter.confirmAlert(warningCode, 5000));
|
||||
}
|
||||
}
|
|
@ -1,11 +1,5 @@
|
|||
package de.jotomo.ruffyscripter.commands;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import de.jotomo.ruffyscripter.RuffyScripter;
|
||||
import de.jotomo.ruffy.spi.CommandResult;
|
||||
|
||||
public class ReadBasalProfileCommand extends BaseCommand {
|
||||
private final int number;
|
||||
|
||||
|
@ -14,17 +8,7 @@ public class ReadBasalProfileCommand extends BaseCommand {
|
|||
}
|
||||
|
||||
@Override
|
||||
public CommandResult execute() {
|
||||
return new CommandResult().success(false).enacted(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> validateArguments() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setScripter(RuffyScripter scripter) {
|
||||
|
||||
public void execute() {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,17 +7,19 @@ import org.monkey.d.ruffy.ruffy.driver.display.MenuType;
|
|||
import org.monkey.d.ruffy.ruffy.driver.display.menu.BolusType;
|
||||
import org.monkey.d.ruffy.ruffy.driver.display.menu.MenuDate;
|
||||
import org.monkey.d.ruffy.ruffy.driver.display.menu.MenuTime;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import de.jotomo.ruffy.spi.CommandResult;
|
||||
import de.jotomo.ruffy.spi.history.Bolus;
|
||||
import de.jotomo.ruffy.spi.history.PumpError;
|
||||
import de.jotomo.ruffy.spi.history.PumpHistory;
|
||||
import de.jotomo.ruffy.spi.history.PumpHistoryRequest;
|
||||
|
||||
public class ReadHistoryCommand extends BaseCommand {
|
||||
private static Logger log = LoggerFactory.getLogger(ReadHistoryCommand.class);
|
||||
|
||||
private final PumpHistoryRequest request;
|
||||
private final PumpHistory history = new PumpHistory();
|
||||
|
||||
|
@ -26,24 +28,22 @@ public class ReadHistoryCommand extends BaseCommand {
|
|||
}
|
||||
|
||||
@Override
|
||||
public CommandResult execute() {
|
||||
if (request.reservoirLevel) {
|
||||
readReservoirLevel();
|
||||
}
|
||||
public void execute() {
|
||||
if (request.bolusHistory != PumpHistoryRequest.SKIP
|
||||
|| request.tbrHistory != PumpHistoryRequest.SKIP
|
||||
|| request.pumpErrorHistory != PumpHistoryRequest.SKIP
|
||||
|| request.tddHistory != PumpHistoryRequest.SKIP) {
|
||||
|| request.pumpErrorHistory != PumpHistoryRequest.SKIP) {
|
||||
scripter.navigateToMenu(MenuType.MY_DATA_MENU);
|
||||
scripter.verifyMenuIsDisplayed(MenuType.MY_DATA_MENU);
|
||||
scripter.pressCheckKey();
|
||||
|
||||
// TODO see how dana does time mangling for timezones
|
||||
|
||||
// bolus history
|
||||
scripter.verifyMenuIsDisplayed(MenuType.BOLUS_DATA);
|
||||
if (request.bolusHistory != PumpHistoryRequest.SKIP) {
|
||||
int totalRecords = (int) scripter.getCurrentMenu().getAttribute(MenuAttribute.TOTAL_RECORD);
|
||||
if (totalRecords > 0) {
|
||||
if (request.bolusHistory == PumpHistoryRequest.LAST) {
|
||||
if (true || request.bolusHistory == PumpHistoryRequest.LAST) {
|
||||
Bolus bolus = readBolusRecord();
|
||||
history.bolusHistory.add(bolus);
|
||||
} else {
|
||||
|
@ -56,22 +56,21 @@ public class ReadHistoryCommand extends BaseCommand {
|
|||
scripter.pressMenuKey();
|
||||
scripter.verifyMenuIsDisplayed(MenuType.ERROR_DATA);
|
||||
if (request.pumpErrorHistory != PumpHistoryRequest.SKIP) {
|
||||
int code = (int) scripter.getCurrentMenu().getAttribute(MenuAttribute.WARNING);
|
||||
String message = (String) scripter.getCurrentMenu().getAttribute(MenuAttribute.MESSAGE);
|
||||
MenuDate date = (MenuDate) scripter.getCurrentMenu().getAttribute(MenuAttribute.DATE);
|
||||
MenuTime time = (MenuTime) scripter.getCurrentMenu().getAttribute(MenuAttribute.TIME);
|
||||
|
||||
int totalRecords = (int) scripter.getCurrentMenu().getAttribute(MenuAttribute.TOTAL_RECORD);
|
||||
if (totalRecords > 0) {
|
||||
if (true || request.pumpErrorHistory == PumpHistoryRequest.LAST) {
|
||||
PumpError error = readErrorRecord();
|
||||
history.pumpErrorHistory.add(error);
|
||||
} else {
|
||||
readErrorRecords(request.pumpErrorHistory);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// tdd history
|
||||
scripter.pressMenuKey();
|
||||
scripter.verifyMenuIsDisplayed(MenuType.DAILY_DATA);
|
||||
if (request.tddHistory != PumpHistoryRequest.SKIP) {
|
||||
Double total = (Double) scripter.getCurrentMenu().getAttribute(MenuAttribute.DAILY_TOTAL);
|
||||
MenuDate date = (MenuDate) scripter.getCurrentMenu().getAttribute(MenuAttribute.DATE);
|
||||
MenuTime time = (MenuTime) scripter.getCurrentMenu().getAttribute(MenuAttribute.TIME);
|
||||
}
|
||||
|
||||
/*
|
||||
// tbr history
|
||||
scripter.pressMenuKey();
|
||||
scripter.verifyMenuIsDisplayed(MenuType.TBR_DATA);
|
||||
|
@ -82,64 +81,102 @@ public class ReadHistoryCommand extends BaseCommand {
|
|||
// TODO start or end time?
|
||||
MenuTime time = (MenuTime) scripter.getCurrentMenu().getAttribute(MenuAttribute.TIME);
|
||||
}
|
||||
*/
|
||||
|
||||
scripter.pressBackKey();
|
||||
scripter.returnToRootMenu();
|
||||
scripter.verifyRootMenuIsDisplayed();
|
||||
}
|
||||
|
||||
return new CommandResult().success(true).enacted(false).history(history);
|
||||
if (log.isDebugEnabled()) {
|
||||
if (!history.bolusHistory.isEmpty()) {
|
||||
log.debug("Read bolus history:");
|
||||
for (Bolus bolus : history.bolusHistory) {
|
||||
log.debug(new Date(bolus.timestamp) + ": " + bolus.toString());
|
||||
}
|
||||
}
|
||||
if (!history.pumpErrorHistory.isEmpty()) {
|
||||
log.debug("Read error history:");
|
||||
for (PumpError pumpError : history.pumpErrorHistory) {
|
||||
log.debug(new Date(pumpError.timestamp) + ": " + pumpError.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
result.success(true).history(history);
|
||||
}
|
||||
|
||||
private void readBolusRecords(long requestedTime) {
|
||||
int record = (int) scripter.getCurrentMenu().getAttribute(MenuAttribute.CURRENT_RECORD);
|
||||
int totalRecords = (int) scripter.getCurrentMenu().getAttribute(MenuAttribute.TOTAL_RECORD);
|
||||
while (record <= totalRecords) {
|
||||
while (true) {
|
||||
log.debug("Reading bolus record #" + record + "/" + totalRecords);
|
||||
Bolus bolus = readBolusRecord();
|
||||
if (requestedTime != PumpHistoryRequest.FULL && bolus.timestamp < requestedTime) {
|
||||
if (requestedTime != PumpHistoryRequest.FULL && bolus.timestamp <= requestedTime) {
|
||||
break;
|
||||
}
|
||||
history.bolusHistory.add(bolus);
|
||||
scripter.pressDownKey();
|
||||
scripter.waitForMenuUpdate();
|
||||
record = (int) scripter.getCurrentMenu().getAttribute(MenuAttribute.CURRENT_RECORD);
|
||||
if (record == totalRecords) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private Bolus readBolusRecord() {
|
||||
scripter.verifyMenuIsDisplayed(MenuType.BOLUS_DATA);
|
||||
// Could also be extended, multiwave
|
||||
BolusType bolusType = (BolusType) scripter.getCurrentMenu().getAttribute(MenuAttribute.BOLUS_TYPE);
|
||||
if (!bolusType.equals(BolusType.NORMAL)) {
|
||||
throw new CommandException().success(false).enacted(false).message("Unsupported bolus type encountered: " + bolusType);
|
||||
throw new CommandException("Unsupported bolus type encountered: " + bolusType);
|
||||
}
|
||||
// TODO no bolus etc yet? How would that ever look?
|
||||
Double bolus = (Double) scripter.getCurrentMenu().getAttribute(MenuAttribute.BOLUS);
|
||||
long recordDate = readRecordDate();
|
||||
return new Bolus(recordDate, bolus);
|
||||
}
|
||||
|
||||
private void readErrorRecords(long requestedTime) {
|
||||
int record = (int) scripter.getCurrentMenu().getAttribute(MenuAttribute.CURRENT_RECORD);
|
||||
int totalRecords = (int) scripter.getCurrentMenu().getAttribute(MenuAttribute.TOTAL_RECORD);
|
||||
while (true) {
|
||||
log.debug("Reading error record #" + record + "/" + totalRecords);
|
||||
PumpError error = readErrorRecord();
|
||||
if (requestedTime != PumpHistoryRequest.FULL && error.timestamp <= requestedTime) {
|
||||
break;
|
||||
}
|
||||
history.pumpErrorHistory.add(error);
|
||||
scripter.pressDownKey();
|
||||
scripter.waitForMenuUpdate();
|
||||
record = (int) scripter.getCurrentMenu().getAttribute(MenuAttribute.CURRENT_RECORD);
|
||||
if (record == totalRecords) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private PumpError readErrorRecord() {
|
||||
scripter.verifyMenuIsDisplayed(MenuType.ERROR_DATA);
|
||||
|
||||
Integer warningCode = (Integer) scripter.getCurrentMenu().getAttribute(MenuAttribute.WARNING);
|
||||
Integer errorCode = (Integer) scripter.getCurrentMenu().getAttribute(MenuAttribute.ERROR);
|
||||
String message = (String) scripter.getCurrentMenu().getAttribute(MenuAttribute.MESSAGE);
|
||||
long recordDate = readRecordDate();
|
||||
return new PumpError(recordDate, warningCode, errorCode, message);
|
||||
}
|
||||
|
||||
private long readRecordDate() {
|
||||
MenuDate date = (MenuDate) scripter.getCurrentMenu().getAttribute(MenuAttribute.DATE);
|
||||
MenuTime time = (MenuTime) scripter.getCurrentMenu().getAttribute(MenuAttribute.TIME);
|
||||
|
||||
// TODO handle year changes; if current month == 1 and record date == 12, use $YEAR-1
|
||||
int currentMonth = new Date().getMonth() + 1;
|
||||
int currentYear = new Date().getYear() + 1900;
|
||||
if (currentMonth == 1 && date.getMonth() == 12) {
|
||||
currentYear -= 1;
|
||||
}
|
||||
long recordDate = new Date(currentYear - 1900, date.getMonth() - 1, date.getDay(), time.getHour(), time.getMinute()).getTime();
|
||||
return new Bolus(recordDate, bolus);
|
||||
}
|
||||
|
||||
private void readReservoirLevel() {
|
||||
scripter.verifyRootMenuIsDisplayed();
|
||||
scripter.pressCheckKey();
|
||||
scripter.waitForMenuToBeLeft(MenuType.MAIN_MENU);
|
||||
scripter.waitForMenuToBeLeft(MenuType.STOP);
|
||||
scripter.verifyMenuIsDisplayed(MenuType.QUICK_INFO);
|
||||
int remainingInsulin = ((Double) scripter.getCurrentMenu().getAttribute(MenuAttribute.REMAINING_INSULIN)).intValue();
|
||||
scripter.returnToRootMenu();
|
||||
history.reservoirLevel = remainingInsulin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> validateArguments() {
|
||||
return Collections.emptyList();
|
||||
return new Date(currentYear - 1900, date.getMonth() - 1, date.getDay(), time.getHour(), time.getMinute()).getTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -149,5 +186,4 @@ public class ReadHistoryCommand extends BaseCommand {
|
|||
", history=" + history +
|
||||
'}';
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,25 +1,12 @@
|
|||
package de.jotomo.ruffyscripter.commands;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import de.jotomo.ruffy.spi.CommandResult;
|
||||
import de.jotomo.ruffyscripter.RuffyScripter;
|
||||
|
||||
public class ReadPumpStateCommand extends BaseCommand {
|
||||
@Override
|
||||
public CommandResult execute() {
|
||||
return new CommandResult().success(true).enacted(false).state(scripter.readPumpStateInternal());
|
||||
public void execute() {
|
||||
// nothing to do, scripter adds state to all command results
|
||||
result.success = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> validateArguments() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setScripter(RuffyScripter scripter) {}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ReadPumpStateCommand{}";
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
package de.jotomo.ruffyscripter.commands;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import org.monkey.d.ruffy.ruffy.driver.display.MenuAttribute;
|
||||
import org.monkey.d.ruffy.ruffy.driver.display.MenuType;
|
||||
import org.monkey.d.ruffy.ruffy.driver.display.menu.BolusType;
|
||||
import org.monkey.d.ruffy.ruffy.driver.display.menu.MenuDate;
|
||||
import org.monkey.d.ruffy.ruffy.driver.display.menu.MenuTime;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import de.jotomo.ruffy.spi.history.Bolus;
|
||||
|
||||
public class ReadReservoirLevelAndLastBolus extends BaseCommand {
|
||||
@Override
|
||||
public void execute() {
|
||||
scripter.verifyRootMenuIsDisplayed();
|
||||
scripter.pressCheckKey();
|
||||
scripter.waitForMenuToBeLeft(MenuType.MAIN_MENU);
|
||||
scripter.waitForMenuToBeLeft(MenuType.STOP);
|
||||
scripter.verifyMenuIsDisplayed(MenuType.QUICK_INFO);
|
||||
result.reservoirLevel = ((Double) scripter.getCurrentMenu().getAttribute(MenuAttribute.REMAINING_INSULIN)).intValue();
|
||||
scripter.pressCheckKey();
|
||||
result.lastBolus = readBolusRecord();
|
||||
scripter.returnToRootMenu();
|
||||
result.success = true;
|
||||
}
|
||||
|
||||
// TODO deduplicate -> ReadHistoryCommand
|
||||
@NonNull
|
||||
private Bolus readBolusRecord() {
|
||||
scripter.verifyMenuIsDisplayed(MenuType.BOLUS_DATA);
|
||||
// Could also be extended, multiwave
|
||||
BolusType bolusType = (BolusType) scripter.getCurrentMenu().getAttribute(MenuAttribute.BOLUS_TYPE);
|
||||
if (!bolusType.equals(BolusType.NORMAL)) {
|
||||
throw new CommandException("Unsupported bolus type encountered: " + bolusType);
|
||||
}
|
||||
Double bolus = (Double) scripter.getCurrentMenu().getAttribute(MenuAttribute.BOLUS);
|
||||
long recordDate = readRecordDate();
|
||||
return new Bolus(recordDate, bolus);
|
||||
}
|
||||
|
||||
private long readRecordDate() {
|
||||
MenuDate date = (MenuDate) scripter.getCurrentMenu().getAttribute(MenuAttribute.DATE);
|
||||
MenuTime time = (MenuTime) scripter.getCurrentMenu().getAttribute(MenuAttribute.TIME);
|
||||
|
||||
int currentMonth = new Date().getMonth() + 1;
|
||||
int currentYear = new Date().getYear() + 1900;
|
||||
if (currentMonth == 1 && date.getMonth() == 12) {
|
||||
currentYear -= 1;
|
||||
}
|
||||
return new Date(currentYear - 1900, date.getMonth() - 1, date.getDay(), time.getHour(), time.getMinute()).getTime();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,28 +1,29 @@
|
|||
package de.jotomo.ruffyscripter.commands;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import de.jotomo.ruffy.spi.BasalProfile;
|
||||
import de.jotomo.ruffyscripter.RuffyScripter;
|
||||
import de.jotomo.ruffy.spi.CommandResult;
|
||||
|
||||
public class SetBasalProfileCommand extends BaseCommand {
|
||||
public SetBasalProfileCommand(BasalProfile basalProfile) {
|
||||
private final BasalProfile basalProfile;
|
||||
|
||||
public SetBasalProfileCommand(BasalProfile basalProfile) {
|
||||
this.basalProfile = basalProfile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandResult execute() {
|
||||
return null;
|
||||
public void execute() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> validateArguments() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setScripter(RuffyScripter scripter) {
|
||||
ArrayList<String> violations = new ArrayList<>();
|
||||
if (basalProfile == null) {
|
||||
violations.add("No basal profile supplied");
|
||||
}
|
||||
|
||||
return violations;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
package de.jotomo.ruffyscripter.commands;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import de.jotomo.ruffy.spi.CommandResult;
|
||||
|
||||
public class SetBasalRateProfileCommand extends BaseCommand {
|
||||
@Override
|
||||
public CommandResult execute() {
|
||||
// TODO stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> validateArguments() {
|
||||
// TODO stub
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -12,7 +12,7 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import de.jotomo.ruffy.spi.CommandResult;
|
||||
import de.jotomo.ruffy.spi.PumpWarningCodes;
|
||||
|
||||
public class SetTbrCommand extends BaseCommand {
|
||||
private static final Logger log = LoggerFactory.getLogger(SetTbrCommand.class);
|
||||
|
@ -58,47 +58,42 @@ public class SetTbrCommand extends BaseCommand {
|
|||
}
|
||||
|
||||
@Override
|
||||
public CommandResult execute() {
|
||||
try {
|
||||
boolean cancellingTbr = percentage == 100;
|
||||
public void execute() {
|
||||
boolean cancellingTbr = percentage == 100;
|
||||
|
||||
enterTbrMenu();
|
||||
boolean increasingPercentage = inputTbrPercentage();
|
||||
verifyDisplayedTbrPercentage(increasingPercentage);
|
||||
enterTbrMenu();
|
||||
boolean increasingPercentage = inputTbrPercentage();
|
||||
verifyDisplayedTbrPercentage(increasingPercentage);
|
||||
|
||||
if (cancellingTbr) {
|
||||
cancelTbrAndConfirmCancellationWarning();
|
||||
} else {
|
||||
// switch to TBR_DURATION menu by pressing menu key
|
||||
scripter.verifyMenuIsDisplayed(MenuType.TBR_SET);
|
||||
scripter.pressMenuKey();
|
||||
scripter.waitForMenuUpdate();
|
||||
scripter.verifyMenuIsDisplayed(MenuType.TBR_DURATION);
|
||||
if (cancellingTbr) {
|
||||
cancelTbrAndConfirmCancellationWarning();
|
||||
} else {
|
||||
// switch to TBR_DURATION menu by pressing menu key
|
||||
scripter.verifyMenuIsDisplayed(MenuType.TBR_SET);
|
||||
scripter.pressMenuKey();
|
||||
scripter.waitForMenuUpdate();
|
||||
scripter.verifyMenuIsDisplayed(MenuType.TBR_DURATION);
|
||||
|
||||
boolean increasingDuration = inputTbrDuration();
|
||||
verifyDisplayedTbrDuration(increasingDuration);
|
||||
boolean increasingDuration = inputTbrDuration();
|
||||
verifyDisplayedTbrDuration(increasingDuration);
|
||||
|
||||
// confirm TBR
|
||||
scripter.pressCheckKey();
|
||||
scripter.waitForMenuToBeLeft(MenuType.TBR_DURATION);
|
||||
}
|
||||
// confirm TBR
|
||||
scripter.pressCheckKey();
|
||||
scripter.waitForMenuToBeLeft(MenuType.TBR_DURATION);
|
||||
}
|
||||
|
||||
scripter.verifyMenuIsDisplayed(MenuType.MAIN_MENU,
|
||||
"Pump did not return to MAIN_MEU after setting TBR. " +
|
||||
"Check pump manually, the TBR might not have been set/cancelled.");
|
||||
scripter.verifyMenuIsDisplayed(MenuType.MAIN_MENU,
|
||||
"Pump did not return to MAIN_MEU after setting TBR. " +
|
||||
"Check pump manually, the TBR might not have been set/cancelled.");
|
||||
|
||||
// check main menu shows the same values we just set
|
||||
if (cancellingTbr) {
|
||||
verifyMainMenuShowsNoActiveTbr();
|
||||
return new CommandResult().success(true).enacted(true).message("TBR was cancelled");
|
||||
} else {
|
||||
verifyMainMenuShowsExpectedTbrActive();
|
||||
return new CommandResult().success(true).enacted(true).message(
|
||||
String.format(Locale.US, "TBR set to %d%% for %d min", percentage, duration));
|
||||
}
|
||||
|
||||
} catch (CommandException e) {
|
||||
return e.toCommandResult();
|
||||
// check main menu shows the same values we just set
|
||||
if (cancellingTbr) {
|
||||
verifyMainMenuShowsNoActiveTbr();
|
||||
result.success(true).enacted(true).message("TBR was cancelled");
|
||||
} else {
|
||||
verifyMainMenuShowsExpectedTbrActive();
|
||||
result.success(true).enacted(true)
|
||||
.message(String.format(Locale.US, "TBR set to %d%% for %d min", percentage, duration));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -146,7 +141,7 @@ public class SetTbrCommand extends BaseCommand {
|
|||
}
|
||||
log.debug("Final displayed TBR percentage: " + displayedPercentage);
|
||||
if (displayedPercentage != percentage) {
|
||||
throw new CommandException().message("Failed to set TBR percentage, requested: "
|
||||
throw new CommandException("Failed to set TBR percentage, requested: "
|
||||
+ percentage + ", actual: " + displayedPercentage);
|
||||
}
|
||||
|
||||
|
@ -156,7 +151,7 @@ public class SetTbrCommand extends BaseCommand {
|
|||
scripter.verifyMenuIsDisplayed(MenuType.TBR_SET);
|
||||
long refreshedDisplayedTbrPecentage = readDisplayedPercentage();
|
||||
if (displayedPercentage != refreshedDisplayedTbrPecentage) {
|
||||
throw new CommandException().message("Failed to set TBR percentage: " +
|
||||
throw new CommandException("Failed to set TBR percentage: " +
|
||||
"percentage changed after input stopped from "
|
||||
+ displayedPercentage + " -> " + refreshedDisplayedTbrPecentage);
|
||||
}
|
||||
|
@ -208,7 +203,7 @@ public class SetTbrCommand extends BaseCommand {
|
|||
|
||||
log.debug("Final displayed TBR duration: " + displayedDuration);
|
||||
if (displayedDuration != duration) {
|
||||
throw new CommandException().message("Failed to set TBR duration, requested: "
|
||||
throw new CommandException("Failed to set TBR duration, requested: "
|
||||
+ duration + ", actual: " + displayedDuration);
|
||||
}
|
||||
|
||||
|
@ -218,7 +213,7 @@ public class SetTbrCommand extends BaseCommand {
|
|||
scripter.verifyMenuIsDisplayed(MenuType.TBR_DURATION);
|
||||
long refreshedDisplayedTbrDuration = readDisplayedDuration();
|
||||
if (displayedDuration != refreshedDisplayedTbrDuration) {
|
||||
throw new CommandException().message("Failed to set TBR duration: " +
|
||||
throw new CommandException("Failed to set TBR duration: " +
|
||||
"duration changed after input stopped from "
|
||||
+ displayedDuration + " -> " + refreshedDisplayedTbrDuration);
|
||||
}
|
||||
|
@ -234,7 +229,7 @@ public class SetTbrCommand extends BaseCommand {
|
|||
// We could read the remaining duration from MAIN_MENU, but by the time we're here,
|
||||
// the pump could have moved from 0:02 to 0:01, so instead, check if a "TBR CANCELLED" alert
|
||||
// is raised and if so dismiss it
|
||||
scripter.confirmAlert("TBR CANCELLED", 5000);
|
||||
scripter.confirmAlert(PumpWarningCodes.TBR_CANCELLED, 2000);
|
||||
}
|
||||
|
||||
private void verifyMainMenuShowsNoActiveTbr() {
|
||||
|
@ -242,7 +237,7 @@ public class SetTbrCommand extends BaseCommand {
|
|||
Double tbrPercentage = (Double) scripter.getCurrentMenu().getAttribute(MenuAttribute.TBR);
|
||||
boolean runtimeDisplayed = scripter.getCurrentMenu().attributes().contains(MenuAttribute.RUNTIME);
|
||||
if (tbrPercentage != 100 || runtimeDisplayed) {
|
||||
throw new CommandException().message("Cancelling TBR failed, TBR is still set according to MAIN_MENU");
|
||||
throw new CommandException("Cancelling TBR failed, TBR is still set according to MAIN_MENU");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -251,7 +246,7 @@ public class SetTbrCommand extends BaseCommand {
|
|||
// new TBR set; percentage and duration must be displayed ...
|
||||
if (!scripter.getCurrentMenu().attributes().contains(MenuAttribute.TBR) ||
|
||||
!scripter.getCurrentMenu().attributes().contains(MenuAttribute.RUNTIME)) {
|
||||
throw new CommandException().message("Setting TBR failed, according to MAIN_MENU no TBR is active");
|
||||
throw new CommandException("Setting TBR failed, according to MAIN_MENU no TBR is active");
|
||||
}
|
||||
Double mmTbrPercentage = (Double) scripter.getCurrentMenu().getAttribute(MenuAttribute.TBR);
|
||||
MenuTime mmTbrDuration = (MenuTime) scripter.getCurrentMenu().getAttribute(MenuAttribute.RUNTIME);
|
||||
|
@ -260,7 +255,7 @@ public class SetTbrCommand extends BaseCommand {
|
|||
// 29 minutes and 59 seconds, so that 29 minutes are displayed
|
||||
int mmTbrDurationInMinutes = mmTbrDuration.getHour() * 60 + mmTbrDuration.getMinute();
|
||||
if (mmTbrPercentage != percentage || (mmTbrDurationInMinutes != duration && mmTbrDurationInMinutes + 1 != duration)) {
|
||||
throw new CommandException().message("Setting TBR failed, TBR in MAIN_MENU differs from expected");
|
||||
throw new CommandException("Setting TBR failed, TBR in MAIN_MENU differs from expected");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
package de.jotomo.ruffyscripter.commands;
|
||||
|
||||
import org.monkey.d.ruffy.ruffy.driver.display.MenuAttribute;
|
||||
import org.monkey.d.ruffy.ruffy.driver.display.MenuType;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import de.jotomo.ruffy.spi.CommandResult;
|
||||
import de.jotomo.ruffy.spi.PumpState;
|
||||
import de.jotomo.ruffy.spi.history.PumpError;
|
||||
|
||||
// TODO rename to ConfirmALarm(alarm) => logic in CP, just report back alarm, then explicitely confirm that one alarm.
|
||||
// multiple alarms oncy occur for errors (battery/cartdige low/occlusion) => let ring.
|
||||
public class TakeOverAlarmsCommand extends BaseCommand {
|
||||
@Override
|
||||
public CommandResult execute() {
|
||||
if (scripter.getCurrentMenu().getType() != MenuType.WARNING_OR_ERROR) {
|
||||
return new CommandResult().success(false).enacted(false).message("No alarm active on the pump");
|
||||
}
|
||||
while (scripter.getCurrentMenu().getType() == MenuType.WARNING_OR_ERROR) {
|
||||
new PumpError(System.currentTimeMillis(),
|
||||
"",
|
||||
// TODO
|
||||
// codes unqiue across W/E?
|
||||
// (int) currentMenu.getAttribute(MenuAttribute.WARNING),
|
||||
// (int) currentMenu.getAttribute(MenuAttribute.ERROR),
|
||||
(String) scripter.getCurrentMenu().getAttribute(MenuAttribute.MESSAGE));
|
||||
}
|
||||
// confirm alert
|
||||
scripter.verifyMenuIsDisplayed(MenuType.WARNING_OR_ERROR);
|
||||
scripter.pressCheckKey();
|
||||
// dismiss alert
|
||||
scripter.verifyMenuIsDisplayed(MenuType.WARNING_OR_ERROR);
|
||||
scripter.pressCheckKey();
|
||||
scripter.waitForMenuToBeLeft(MenuType.WARNING_OR_ERROR);
|
||||
|
||||
PumpState pumpState = scripter.readPumpStateInternal();
|
||||
return new CommandResult()
|
||||
.success(true)
|
||||
.enacted(false /* well, no treatments were enacted ... */)
|
||||
// .message(pumpState.errorMsg) // todo yikes?
|
||||
.state(pumpState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> validateArguments() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue