This commit is contained in:
Johannes Mockenhaupt 2017-10-27 00:03:01 +02:00
parent e895ba65eb
commit b47559647f
No known key found for this signature in database
GPG key ID: 9E1EA6AF7BBBB0D1
18 changed files with 364 additions and 185 deletions

View file

@ -52,3 +52,18 @@ Testing:
- Try to reproduce and open a ticket, add tag if any, otherwise add the hash of the commit used (right-click on the branch name select - Try to reproduce and open a ticket, add tag if any, otherwise add the hash of the commit used (right-click on the branch name select
_Copy revision number_ or use _git show_ on the command-line) the branch name. Attach the log to the issue and label it as a bug. _Copy revision number_ or use _git show_ on the command-line) the branch name. Attach the log to the issue and label it as a bug.
The logs can be found in _/storage/emulated/0/Android/data/info.nightscout.androidaps/_ The logs can be found in _/storage/emulated/0/Android/data/info.nightscout.androidaps/_
v2 usage
- When a BOLUS/TBR CANCELLED alert starts on the pump during bolusing or setting a TBR, this is caused by disconnect
between pump and phone. The app will try to reconnect and confirm the alert and then retry the last action. Therefore,
such an alarm shall be ignored (cancelling it is not a big issue, but will lead to the currently active action to
have to wait till the pump's display turns off before it can reconnect to the pump).
If the pump's alarm continues, the last action might have failed, in which case the user needs to confirm the alarm
v3 TODOs
- Reading and displaying TDDs
Maybes:
- reading/writing basal profiles to pump
- splitted bolus/slower bolus delivery
- extended bolus support

View file

@ -1,7 +1,5 @@
package info.nightscout.androidaps; package info.nightscout.androidaps;
import com.j256.ormlite.stmt.query.In;
/** /**
* Created by mike on 07.06.2016. * Created by mike on 07.06.2016.
*/ */
@ -24,7 +22,7 @@ public class Constants {
public static final int hoursToKeepInDatabase = 72; public static final int hoursToKeepInDatabase = 72;
public static final int daysToKeepHistoryInDatabase = 30; public static final int daysToKeepHistoryInDatabase = 30;
public static final long keepAliveMsecs = 5 * 60 * 1000L; public static final long keepAliveMsecs = 60 * 1000L;
// SMS COMMUNICATOR // SMS COMMUNICATOR
public static final long remoteBolusMinDistance = 15 * 60 * 1000L; public static final long remoteBolusMinDistance = 15 * 60 * 1000L;

View file

@ -30,7 +30,8 @@ import info.nightscout.utils.DecimalFormatter;
public class ComboFragment extends SubscriberFragment implements View.OnClickListener { public class ComboFragment extends SubscriberFragment implements View.OnClickListener {
private static Logger log = LoggerFactory.getLogger(ComboFragment.class); private static Logger log = LoggerFactory.getLogger(ComboFragment.class);
private TextView statusView; private TextView stateView;
private TextView activityView;
private TextView batteryView; private TextView batteryView;
private TextView reservoirView; private TextView reservoirView;
private TextView lastConnectionView; private TextView lastConnectionView;
@ -44,7 +45,8 @@ public class ComboFragment extends SubscriberFragment implements View.OnClickLis
Bundle savedInstanceState) { Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.combopump_fragment, container, false); View view = inflater.inflate(R.layout.combopump_fragment, container, false);
statusView = (TextView) view.findViewById(R.id.combo_status); stateView = (TextView) view.findViewById(R.id.combo_state);
activityView = (TextView) view.findViewById(R.id.combo_activity);
batteryView = (TextView) view.findViewById(R.id.combo_pumpstate_battery); batteryView = (TextView) view.findViewById(R.id.combo_pumpstate_battery);
reservoirView = (TextView) view.findViewById(R.id.combo_insulinstate); reservoirView = (TextView) view.findViewById(R.id.combo_insulinstate);
lastConnectionView = (TextView) view.findViewById(R.id.combo_lastconnection); lastConnectionView = (TextView) view.findViewById(R.id.combo_lastconnection);
@ -70,52 +72,48 @@ public class ComboFragment extends SubscriberFragment implements View.OnClickLis
}); });
thread.start(); thread.start();
break; break;
case R.id.combo_history: case R.id.combo_error_history:
// TODO show popup with warnings/errors from the pump // TODO show popup with pump errors and comm problems
break; break;
case R.id.combo_stats: case R.id.combo_stats:
// TODO show TDD stats from the pump // TODO show TDD stats from the pump (later)
break; break;
} }
} }
@Subscribe @Subscribe
public void onStatusEvent(final EventComboPumpUpdateGUI ev) { public void onStatusEvent(final EventComboPumpUpdateGUI ev) {
if (ev.status != null) { updateGUI();
Activity activity = getActivity();
if (activity != null)
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
statusView.setText(ev.status);
statusView.setTextColor(Color.WHITE);
}
});
} else {
updateGUI();
}
} }
public void updateGUI() { public void updateGUI() {
Activity activity = getActivity(); final Activity activity = getActivity();
log.debug("aCtI: activity available? " + (activity != null));
if (activity != null) if (activity != null)
activity.runOnUiThread(new Runnable() { activity.runOnUiThread(new Runnable() {
@Override @Override
public void run() { public void run() {
ComboPlugin plugin = ComboPlugin.getPlugin(); ComboPlugin plugin = ComboPlugin.getPlugin();
// activity
String activity = plugin.getPump().activity;
activityView.setText(activity != null ? activity : "Idle");
if (plugin.isInitialized()) { if (plugin.isInitialized()) {
// status // status
statusView.setText(plugin.getPump().state.getStateSummary()); PumpState ps = plugin.getPump().state;
if (plugin.getPump().state.errorMsg != null) { stateView.setText(plugin.getPump().state.getStateSummary());
statusView.setTextColor(Color.RED); if (plugin.getPump().state.errorMsg != null
|| ps.insulinState == PumpState.EMPTY
|| ps.batteryState == PumpState.EMPTY) {
stateView.setTextColor(Color.RED);
} else if (plugin.getPump().state.suspended) { } else if (plugin.getPump().state.suspended) {
statusView.setTextColor(Color.YELLOW); stateView.setTextColor(Color.YELLOW);
} else { } else {
statusView.setTextColor(Color.WHITE); stateView.setTextColor(Color.WHITE);
} }
// battery // battery
PumpState ps = plugin.getPump().state;
if (ps.batteryState == PumpState.EMPTY) { if (ps.batteryState == PumpState.EMPTY) {
batteryView.setText("{fa-battery-empty}"); batteryView.setText("{fa-battery-empty}");
batteryView.setTextColor(Color.RED); batteryView.setTextColor(Color.RED);
@ -143,15 +141,14 @@ public class ComboFragment extends SubscriberFragment implements View.OnClickLis
if (lastCmdResult != null) { if (lastCmdResult != null) {
String minAgo = DateUtil.minAgo(lastCmdResult.completionTime); String minAgo = DateUtil.minAgo(lastCmdResult.completionTime);
String time = DateUtil.timeString(lastCmdResult.completionTime); String time = DateUtil.timeString(lastCmdResult.completionTime);
// TODO must not be within if (lastCmdResult) so we can complain if NO command ever worked; also move from completionTime to new times
// TODO check all access to completionTime. useful anymore?
if (plugin.getPump().lastSuccessfulConnection < System.currentTimeMillis() + 30 * 60 * 1000) { if (plugin.getPump().lastSuccessfulConnection < System.currentTimeMillis() + 30 * 60 * 1000) {
lastConnectionView.setText( lastConnectionView.setText("No connection for " + minAgo + " min");
"No successful connection" +
"\nwithin the last " + minAgo + " min");
lastConnectionView.setTextColor(Color.RED); lastConnectionView.setTextColor(Color.RED);
} }
if (plugin.getPump().lastConnectionAttempt > plugin.getPump().lastSuccessfulConnection) { if (plugin.getPump().lastConnectionAttempt > plugin.getPump().lastSuccessfulConnection) {
lastConnectionView.setText("" + minAgo + " (" + time + ")" + lastConnectionView.setText("Last connect attempt failed");
"\nLast connect attempt failed");
lastConnectionView.setTextColor(Color.YELLOW); lastConnectionView.setTextColor(Color.YELLOW);
} else { } else {
lastConnectionView.setText("" + minAgo + " (" + time + ")"); lastConnectionView.setText("" + minAgo + " (" + time + ")");

View file

@ -1,13 +1,7 @@
package info.nightscout.androidaps.plugins.PumpCombo; package info.nightscout.androidaps.plugins.PumpCombo;
import android.app.NotificationManager;
import android.content.Context;
import android.graphics.Color;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.SystemClock; import android.os.SystemClock;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.v4.app.NotificationCompat;
import org.json.JSONObject; import org.json.JSONObject;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -22,7 +16,7 @@ import de.jotomo.ruffy.spi.CommandResult;
import de.jotomo.ruffy.spi.PumpState; import de.jotomo.ruffy.spi.PumpState;
import de.jotomo.ruffy.spi.RuffyCommands; import de.jotomo.ruffy.spi.RuffyCommands;
import de.jotomo.ruffy.spi.history.Bolus; import de.jotomo.ruffy.spi.history.Bolus;
import de.jotomo.ruffy.spi.history.Error; import de.jotomo.ruffy.spi.history.PumpError;
import de.jotomo.ruffy.spi.history.PumpHistoryRequest; import de.jotomo.ruffy.spi.history.PumpHistoryRequest;
import de.jotomo.ruffy.spi.history.Tbr; import de.jotomo.ruffy.spi.history.Tbr;
import de.jotomo.ruffyscripter.RuffyCommandsV1Impl; import de.jotomo.ruffyscripter.RuffyCommandsV1Impl;
@ -44,7 +38,6 @@ import info.nightscout.androidaps.plugins.Overview.events.EventNewNotification;
import info.nightscout.androidaps.plugins.Overview.events.EventOverviewBolusProgress; import info.nightscout.androidaps.plugins.Overview.events.EventOverviewBolusProgress;
import info.nightscout.androidaps.plugins.PumpCombo.events.EventComboPumpUpdateGUI; import info.nightscout.androidaps.plugins.PumpCombo.events.EventComboPumpUpdateGUI;
import info.nightscout.utils.DateUtil; import info.nightscout.utils.DateUtil;
import info.nightscout.utils.SP;
/** /**
* Created by mike on 05.08.2016. * Created by mike on 05.08.2016.
@ -205,23 +198,24 @@ public class ComboPlugin implements PluginBase, PumpInterface {
@NonNull @NonNull
@Override @Override
public Date lastDataTime() { public Date lastDataTime() {
CommandResult lastCmdResult = pump.lastCmdResult; return new Date(pump.lastSuccessfulConnection);
return lastCmdResult != null ? new Date(lastCmdResult.completionTime) : new Date(0);
} }
@Override @Override
public synchronized void refreshDataFromPump(String reason) { public synchronized void refreshDataFromPump(String reason) {
log.debug("RefreshDataFromPump called"); log.debug("RefreshDataFromPump called");
if (pump.lastCmdResult == null) { boolean firstRun = pump.lastCmdResult == null;
runCommand("Refreshing", new CommandExecution() {
@Override
public CommandResult execute() {
return ruffyScripter.readHistory(new PumpHistoryRequest().reservoirLevel(true).bolusHistory(PumpHistoryRequest.LAST));
}
});
if (firstRun) {
initializePump(); initializePump();
} else {
runCommand("Refreshing", new CommandExecution() {
@Override
public CommandResult execute() {
return ruffyScripter.readHistory(new PumpHistoryRequest().reservoirLevel(true).bolusHistory(PumpHistoryRequest.LAST));
}
});
} }
} }
@ -585,8 +579,10 @@ public class ComboPlugin implements PluginBase, PumpInterface {
} }
private CommandResult runCommand(String status, boolean checkTbrMisMatch, CommandExecution commandExecution) { private CommandResult runCommand(String activity, boolean checkTbrMisMatch, CommandExecution commandExecution) {
MainApp.bus().post(new EventComboPumpUpdateGUI(status)); pump.activity = activity;
MainApp.bus().post(new EventComboPumpUpdateGUI());
CommandResult commandResult = commandExecution.execute(); CommandResult commandResult = commandExecution.execute();
if (commandResult.success) { if (commandResult.success) {
@ -606,17 +602,19 @@ public class ComboPlugin implements PluginBase, PumpInterface {
// get the current state, then decide what makes sense to do further, if anything, // get the current state, then decide what makes sense to do further, if anything,
// send next request. // send next request.
// then request state again ... ? // then request state again ... ?
if (commandResult.state.errorMsg != null) { // TODO rethink this errorMsg field;
/* if (commandResult.state.errorMsg != null) {
CommandResult takeOverAlarmResult = ruffyScripter.takeOverAlarms(); CommandResult takeOverAlarmResult = ruffyScripter.takeOverAlarms();
for (Error error : takeOverAlarmResult.history.errorHistory) { for (PumpError pumpError : takeOverAlarmResult.history.pumpErrorHistory) {
MainApp.bus().post(new EventNewNotification( MainApp.bus().post(new EventNewNotification(
new Notification(Notification.COMBO_PUMP_ERROR, new Notification(Notification.COMBO_PUMP_ALARM,
"Pump alarm: " + error.message, Notification.URGENT))); "Pump alarm: " + pumpError.message, Notification.URGENT)));
} }
commandResult.state = takeOverAlarmResult.state; commandResult.state = takeOverAlarmResult.state;
} }*/
pump.lastCmdResult = commandResult; pump.lastCmdResult = commandResult;
pump.state = commandResult.state; pump.state = commandResult.state;
@ -632,20 +630,21 @@ public class ComboPlugin implements PluginBase, PumpInterface {
log.error("JOE: no!"); log.error("JOE: no!");
} else { } else {
// still crashable ... // still crashable ...
// TODO only update if command was successful? -> KeepAliveReceiver, triggering alarm on unavaiblae pump
pump.lastCmdResult.completionTime = System.currentTimeMillis(); pump.lastCmdResult.completionTime = System.currentTimeMillis();
} }
// TOOD // TODO merge all new history here?
if (commandResult.history != null) { if (commandResult.history != null) {
if (commandResult.history.reservoirLevel != -1) { if (commandResult.history.reservoirLevel != -1) {
pump.reservoirLevel = commandResult.history.reservoirLevel; pump.reservoirLevel = commandResult.history.reservoirLevel;
} }
pump.history = commandResult.history; pump.history = commandResult.history;
if (pump.history.bolusHistory.size() > 0) {
pump.lastBolus = pump.history.bolusHistory.get(0);
}
} }
// TODO in the event of an error schedule a resync
pump.activity = null;
MainApp.bus().post(new EventComboPumpUpdateGUI()); MainApp.bus().post(new EventComboPumpUpdateGUI());
return commandResult; return commandResult;
} }

View file

@ -10,16 +10,17 @@ import de.jotomo.ruffy.spi.history.Bolus;
import de.jotomo.ruffy.spi.history.PumpHistory; import de.jotomo.ruffy.spi.history.PumpHistory;
class ComboPump { class ComboPump {
public long lastSuccessfulConnection; // TODO actually ... this isn't about successful command execution, but whether we could connect to the pump at all
public long lastConnectionAttempt; volatile long lastSuccessfulConnection;
volatile long lastConnectionAttempt;
@Nullable @Nullable
volatile CommandResult lastCmdResult; volatile CommandResult lastCmdResult;
public volatile String activity;
@NonNull @NonNull
volatile PumpState state = new PumpState(); volatile PumpState state = new PumpState();
@NonNull
volatile PumpHistory history = new PumpHistory();
@Nullable
volatile Bolus lastBolus;
volatile int reservoirLevel = -1; volatile int reservoirLevel = -1;
@NonNull
volatile PumpHistory history = new PumpHistory();
} }

View file

@ -5,11 +5,4 @@ package info.nightscout.androidaps.plugins.PumpCombo.events;
*/ */
public class EventComboPumpUpdateGUI { public class EventComboPumpUpdateGUI {
public EventComboPumpUpdateGUI() {}
public EventComboPumpUpdateGUI(String status) {
this.status = status;
}
public String status;
} }

View file

@ -68,6 +68,7 @@ public class KeepAliveReceiver extends BroadcastReceiver {
// fixing the problem takes longer (or is not immediately possible because the pump was forgotten)? // fixing the problem takes longer (or is not immediately possible because the pump was forgotten)?
// suppress this for another 25m if the message was dismissed? // suppress this for another 25m if the message was dismissed?
// The alarm sound is played back as regular media, that means it might be muted if sound level is at 0 // 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, "Pump unreachable", Notification.URGENT); Notification n = new Notification(Notification.PUMP_UNREACHABLE, "Pump unreachable", Notification.URGENT);
n.soundId = R.raw.alarm; n.soundId = R.raw.alarm;
MainApp.bus().post(new EventNewNotification(n)); MainApp.bus().post(new EventNewNotification(n));

View file

@ -29,7 +29,7 @@
android:layout_weight="1.5" android:layout_weight="1.5"
android:gravity="end" android:gravity="end"
android:paddingRight="5dp" android:paddingRight="5dp"
android:text="Status" android:text="State"
android:textSize="14sp" /> android:textSize="14sp" />
<TextView <TextView
@ -43,14 +43,13 @@
android:textSize="14sp" /> android:textSize="14sp" />
<TextView <TextView
android:id="@+id/combo_status" android:id="@+id/combo_state"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:gravity="start" android:gravity="start"
android:paddingLeft="5dp" android:paddingLeft="5dp"
android:textColor="@android:color/white" android:textColor="@android:color/white"
android:text="Initializating"
android:textSize="14sp" /> android:textSize="14sp" />
</LinearLayout> </LinearLayout>
@ -145,6 +144,51 @@
</LinearLayout> </LinearLayout>
<View
android:layout_width="fill_parent"
android:layout_height="2dip"
android:layout_marginBottom="5dp"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:layout_marginTop="5dp"
android:background="@color/listdelimiter" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1.5"
android:gravity="end"
android:paddingRight="5dp"
android:text="Activity"
android:textSize="14sp" />
<TextView
android:layout_width="5dp"
android:layout_height="wrap_content"
android:layout_weight="0"
android:gravity="center_horizontal"
android:paddingEnd="2dp"
android:paddingStart="2dp"
android:text=":"
android:textSize="14sp" />
<TextView
android:id="@+id/combo_activity"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="start"
android:paddingLeft="5dp"
android:textColor="@android:color/white"
android:textSize="14sp" />
</LinearLayout>
<View <View
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="2dip" android:layout_height="2dip"
@ -316,17 +360,6 @@
android:paddingRight="0dp" android:paddingRight="0dp"
android:text="@string/combo_refresh" /> android:text="@string/combo_refresh" />
<Button
android:id="@+id/combo_history"
style="@style/ButtonSmallFontStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:drawableTop="@drawable/icon_danarhistory"
android:paddingLeft="0dp"
android:paddingRight="0dp"
android:text="@string/pump_history" />
<Button <Button
android:id="@+id/combo_stats" android:id="@+id/combo_stats"
style="@style/ButtonSmallFontStyle" style="@style/ButtonSmallFontStyle"
@ -336,8 +369,20 @@
android:drawableTop="@drawable/icon_danarstats" android:drawableTop="@drawable/icon_danarstats"
android:paddingLeft="0dp" android:paddingLeft="0dp"
android:paddingRight="0dp" android:paddingRight="0dp"
android:visibility="gone"
android:text="@string/combo_stats" /> android:text="@string/combo_stats" />
<Button
android:id="@+id/combo_error_history"
style="@style/ButtonSmallFontStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:drawableTop="@drawable/icon_cp_aaps_offline"
android:paddingLeft="0dp"
android:paddingRight="0dp"
android:text="@string/pump_errors_history" />
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>

View file

@ -269,7 +269,7 @@
<string name="danarprofile_dia">DIA [h]</string> <string name="danarprofile_dia">DIA [h]</string>
<string name="danarprofile_dia_summary">Duration of Insulin Activity</string> <string name="danarprofile_dia_summary">Duration of Insulin Activity</string>
<string name="failedupdatebasalprofile">Failed to update basal profile</string> <string name="failedupdatebasalprofile">Failed to update basal profile</string>
<string name="pump_history">History</string> <string name="pump_errors_history">Errors</string>
<string name="danar_historyreload">Reload</string> <string name="danar_historyreload">Reload</string>
<string name="uploading">Uploading</string> <string name="uploading">Uploading</string>
<string name="danar_ebolus">E bolus</string> <string name="danar_ebolus">E bolus</string>

View file

@ -1,10 +1,8 @@
package de.jotomo.ruffy.spi; package de.jotomo.ruffy.spi;
import java.util.Date;
/** State displayed on the main screen of the pump. */ /** State displayed on the main screen of the pump. */
public class PumpState { public class PumpState {
public Date timestamp = new Date(); public String menu = null;
public boolean tbrActive = false; public boolean tbrActive = false;
/** TBR percentage. 100% means no TBR active, just the normal basal rate running. */ /** TBR percentage. 100% means no TBR active, just the normal basal rate running. */
public int tbrPercent = -1; public int tbrPercent = -1;
@ -30,6 +28,11 @@ public class PumpState {
public int activeBasalProfileNumber; public int activeBasalProfileNumber;
public PumpState menu(String menu) {
this.menu = menu;
return this;
}
public PumpState tbrActive(boolean tbrActive) { public PumpState tbrActive(boolean tbrActive) {
this.tbrActive = tbrActive; this.tbrActive = tbrActive;
return this; return this;
@ -76,17 +79,19 @@ public class PumpState {
} }
public String getStateSummary() { public String getStateSummary() {
if (errorMsg != null) if (menu == null)
return errorMsg; return "Unreachable";
else if (suspended && (batteryState == EMPTY || insulinState == EMPTY))
return "Suspended due to error";
else if (suspended) else if (suspended)
return "Suspended"; return "Suspended by user";
return "Running"; return "Running";
} }
@Override @Override
public String toString() { public String toString() {
return "PumpState{" + return "PumpState{" +
"timestamp=" + timestamp + "menu=" + menu +
", tbrActive=" + tbrActive + ", tbrActive=" + tbrActive +
", tbrPercent=" + tbrPercent + ", tbrPercent=" + tbrPercent +
", tbrRate=" + tbrRate + ", tbrRate=" + tbrRate +

View file

@ -13,14 +13,73 @@ public interface RuffyCommands {
CommandResult cancelTbr(); 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 /** 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.errorHistory. */ * 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(); 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?
*/
boolean isPumpAvailable(); boolean isPumpAvailable();
boolean isPumpBusy(); boolean isPumpBusy();
// start everything with this: read pump state.
// see if there's an error active.
CommandResult readPumpState(); CommandResult readPumpState();
CommandResult readHistory(PumpHistoryRequest request); CommandResult readHistory(PumpHistoryRequest request);

View file

@ -1,12 +1,12 @@
package de.jotomo.ruffy.spi.history; package de.jotomo.ruffy.spi.history;
public class Error extends HistoryRecord { 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). */ /** 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;
/** Error message, in the language configured on the pump. */ /** Error message, in the language configured on the pump. */
public final String message; public final String message;
public Error(long timestamp, String code, String message) { public PumpError(long timestamp, String code, String message) {
super(timestamp); super(timestamp);
this.code = code; this.code = code;
this.message = message; this.message = message;

View file

@ -3,7 +3,6 @@ package de.jotomo.ruffy.spi.history;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
public class PumpHistory { public class PumpHistory {
@ -13,7 +12,7 @@ public class PumpHistory {
@NonNull @NonNull
public List<Tbr> tbrHistory = new ArrayList<>(); public List<Tbr> tbrHistory = new ArrayList<>();
@NonNull @NonNull
public List<Error> errorHistory = new ArrayList<>(); public List<PumpError> pumpErrorHistory = new ArrayList<>();
@NonNull @NonNull
public List<Tdd> tddHistory = new ArrayList<>(); public List<Tdd> tddHistory = new ArrayList<>();
@ -33,8 +32,8 @@ public class PumpHistory {
return this; return this;
} }
public PumpHistory errorHistory(List<Error> errorHistory) { public PumpHistory errorHistory(List<PumpError> pumpErrorHistory) {
this.errorHistory = errorHistory; this.pumpErrorHistory = pumpErrorHistory;
return this; return this;
} }
@ -49,7 +48,7 @@ public class PumpHistory {
"reservoirLevel=" + reservoirLevel + "reservoirLevel=" + reservoirLevel +
", bolusHistory=" + bolusHistory.size() + ", bolusHistory=" + bolusHistory.size() +
", tbrHistory=" + tbrHistory.size() + ", tbrHistory=" + tbrHistory.size() +
", errorHistory=" + errorHistory.size() + ", pumpErrorHistory=" + pumpErrorHistory.size() +
", tddHistory=" + tddHistory.size() + ", tddHistory=" + tddHistory.size() +
'}'; '}';
} }

View file

@ -14,7 +14,7 @@ public class PumpHistoryRequest {
public long bolusHistory = SKIP; public long bolusHistory = SKIP;
public long tbrHistory = SKIP; public long tbrHistory = SKIP;
public long errorHistory = SKIP; public long pumpErrorHistory = SKIP;
public long tddHistory = SKIP; public long tddHistory = SKIP;
public PumpHistoryRequest reservoirLevel(boolean reservoirLevel) { public PumpHistoryRequest reservoirLevel(boolean reservoirLevel) {
@ -33,7 +33,7 @@ public class PumpHistoryRequest {
} }
public PumpHistoryRequest errorHistory(long errorHistory) { public PumpHistoryRequest errorHistory(long errorHistory) {
this.errorHistory = errorHistory; this.pumpErrorHistory = errorHistory;
return this; return this;
} }
@ -48,7 +48,7 @@ public class PumpHistoryRequest {
"reservoirLevel=" + reservoirLevel + "reservoirLevel=" + reservoirLevel +
", bolusHistory=" + bolusHistory + ", bolusHistory=" + bolusHistory +
", tbrHistory=" + tbrHistory + ", tbrHistory=" + tbrHistory +
", errorHistory=" + errorHistory + ", pumpErrorHistory=" + pumpErrorHistory +
", tddHistory=" + tddHistory + ", tddHistory=" + tddHistory +
'}'; '}';
} }

View file

@ -29,7 +29,6 @@ import de.jotomo.ruffy.spi.BolusProgressReporter;
import de.jotomo.ruffy.spi.CommandResult; import de.jotomo.ruffy.spi.CommandResult;
import de.jotomo.ruffy.spi.PumpState; import de.jotomo.ruffy.spi.PumpState;
import de.jotomo.ruffy.spi.RuffyCommands; import de.jotomo.ruffy.spi.RuffyCommands;
import de.jotomo.ruffy.spi.history.Error;
import de.jotomo.ruffy.spi.history.PumpHistoryRequest; import de.jotomo.ruffy.spi.history.PumpHistoryRequest;
import de.jotomo.ruffyscripter.commands.BolusCommand; import de.jotomo.ruffyscripter.commands.BolusCommand;
import de.jotomo.ruffyscripter.commands.CancelTbrCommand; import de.jotomo.ruffyscripter.commands.CancelTbrCommand;
@ -40,6 +39,7 @@ import de.jotomo.ruffyscripter.commands.ReadHistoryCommand;
import de.jotomo.ruffyscripter.commands.ReadPumpStateCommand; import de.jotomo.ruffyscripter.commands.ReadPumpStateCommand;
import de.jotomo.ruffyscripter.commands.SetBasalProfileCommand; import de.jotomo.ruffyscripter.commands.SetBasalProfileCommand;
import de.jotomo.ruffyscripter.commands.SetTbrCommand; 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. // 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 // Automatically compare against AAPS db, or log all requests in the PumpInterface (maybe Milos
@ -256,12 +256,12 @@ public class RuffyScripter implements RuffyCommands {
return new CommandResult().message(Joiner.on("\n").join(violations)).state(readPumpStateInternal()); return new CommandResult().message(Joiner.on("\n").join(violations)).state(readPumpStateInternal());
} }
// TODO simplify, hard to reason about exists
synchronized (RuffyScripter.class) { synchronized (RuffyScripter.class) {
try { try {
activeCmd = cmd; activeCmd = cmd;
long connectStart = System.currentTimeMillis(); long connectStart = System.currentTimeMillis();
ensureConnected(); ensureConnected();
final RuffyScripter scripter = this;
final Returnable returnable = new Returnable(); final Returnable returnable = new Returnable();
class CommandRunner { class CommandRunner {
public void run() { public void run() {
@ -294,7 +294,7 @@ public class RuffyScripter implements RuffyCommands {
PumpState pumpState = readPumpStateInternal(); PumpState pumpState = readPumpStateInternal();
log.debug("Pump state before running command: " + pumpState); log.debug("Pump state before running command: " + pumpState);
long cmdStartTime = System.currentTimeMillis(); long cmdStartTime = System.currentTimeMillis();
cmd.setScripter(scripter); cmd.setScripter(RuffyScripter.this);
returnable.cmdResult = cmd.execute(); returnable.cmdResult = cmd.execute();
long cmdEndTime = System.currentTimeMillis(); long cmdEndTime = System.currentTimeMillis();
returnable.cmdResult.completionTime = cmdEndTime; returnable.cmdResult.completionTime = cmdEndTime;
@ -333,6 +333,11 @@ public class RuffyScripter implements RuffyCommands {
reconnect(); reconnect();
// TODO at least for bigger boluses we should check history after reconnect to make sure // 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 ... // 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() { cmdThread = new Thread(new Runnable() {
@Override @Override
public void run() { public void run() {
@ -421,6 +426,11 @@ public class RuffyScripter implements RuffyCommands {
ensureConnected(); ensureConnected();
if (getCurrentMenu().getType() == MenuType.WARNING_OR_ERROR) { if (getCurrentMenu().getType() == MenuType.WARNING_OR_ERROR) {
String errorMessage = (String) getCurrentMenu().getAttribute(MenuAttribute.MESSAGE); String errorMessage = (String) getCurrentMenu().getAttribute(MenuAttribute.MESSAGE);
// TODO bolus cancelled is raised BEFORE a bolus is started. if disconnect occurs after
// bolus has started (or the user interacts with the pump, the bolus continues.
// in that case, reconnecting and restarting the command lets to a DUPLICATE bolus.
// add a method restartAllowed(), which accesses a flag bolusDelivering started, which
// is set false then?
if (activeCmd.getReconnectAlarm() != null && activeCmd.getReconnectAlarm().equals(errorMessage)) { if (activeCmd.getReconnectAlarm() != null && activeCmd.getReconnectAlarm().equals(errorMessage)) {
log.debug("Confirming alert caused by disconnect: " + errorMessage); log.debug("Confirming alert caused by disconnect: " + errorMessage);
// confirm alert // confirm alert
@ -454,6 +464,7 @@ public class RuffyScripter implements RuffyCommands {
return false; return false;
} }
// aren't at main_menu after alert anyways? unless bolus i still going (also main_menu); low cartridge on bolus needs to be handled specially, by bolus command
returnToRootMenu(); returnToRootMenu();
return getCurrentMenu().getType() == MenuType.MAIN_MENU; return getCurrentMenu().getType() == MenuType.MAIN_MENU;
@ -530,9 +541,12 @@ public class RuffyScripter implements RuffyCommands {
PumpState state = new PumpState(); PumpState state = new PumpState();
Menu menu = currentMenu; Menu menu = currentMenu;
if (menu == null) { if (menu == null) {
return new PumpState().errorMsg("Menu is not available"); return state;
} }
MenuType menuType = menu.getType(); MenuType menuType = menu.getType();
state.menu = menuType.name();
if (menuType == MenuType.MAIN_MENU) { if (menuType == MenuType.MAIN_MENU) {
Double tbrPercentage = (Double) menu.getAttribute(MenuAttribute.TBR); Double tbrPercentage = (Double) menu.getAttribute(MenuAttribute.TBR);
if (tbrPercentage != 100) { if (tbrPercentage != 100) {
@ -545,10 +559,6 @@ public class RuffyScripter implements RuffyCommands {
} }
state.batteryState = ((int) menu.getAttribute(MenuAttribute.BATTERY_STATE)); state.batteryState = ((int) menu.getAttribute(MenuAttribute.BATTERY_STATE));
state.insulinState = ((int) menu.getAttribute(MenuAttribute.INSULIN_STATE)); state.insulinState = ((int) menu.getAttribute(MenuAttribute.INSULIN_STATE));
// TODO v2, read current base basal rate, which is shown center when no TBR is active.
// Check if that holds true when an extended bolus is running.
// Add a field to PumpStatus, rather than renaming/overloading tbrRate to mean
// either TBR rate or basal rate depending on whether a TBR is active.
} else if (menuType == MenuType.WARNING_OR_ERROR) { } else if (menuType == MenuType.WARNING_OR_ERROR) {
state.errorMsg = (String) menu.getAttribute(MenuAttribute.MESSAGE); state.errorMsg = (String) menu.getAttribute(MenuAttribute.MESSAGE);
} else if (menuType == MenuType.STOP) { } else if (menuType == MenuType.STOP) {
@ -556,14 +566,7 @@ public class RuffyScripter implements RuffyCommands {
state.batteryState = ((int) menu.getAttribute(MenuAttribute.BATTERY_STATE)); state.batteryState = ((int) menu.getAttribute(MenuAttribute.BATTERY_STATE));
state.insulinState = ((int) menu.getAttribute(MenuAttribute.INSULIN_STATE)); state.insulinState = ((int) menu.getAttribute(MenuAttribute.INSULIN_STATE));
} else { } else {
StringBuilder sb = new StringBuilder(); // just return the PumpState with the menu set
for (MenuAttribute menuAttribute : menu.attributes()) {
sb.append(menuAttribute);
sb.append(": ");
sb.append(menu.getAttribute(menuAttribute));
sb.append("\n");
}
state.errorMsg = "Pump is on menu " + menuType + ", listing attributes: \n" + sb.toString();
} }
return state; return state;
} }
@ -619,6 +622,7 @@ public class RuffyScripter implements RuffyCommands {
// TODO this is probably due to a disconnect and rtDisconnect having nulled currentMenu. // TODO this is probably due to a disconnect and rtDisconnect having nulled currentMenu.
// This here might just work, but needs a more controlled approach when implementing // This here might just work, but needs a more controlled approach when implementing
// something to deal with connection loses // something to deal with connection loses
// TODO force reconnect? and retry?
while (currentMenu == null) { while (currentMenu == null) {
if (System.currentTimeMillis() > timeout) { if (System.currentTimeMillis() > timeout) {
throw new CommandException().message("Unable to read current menu"); throw new CommandException().message("Unable to read current menu");
@ -630,14 +634,9 @@ public class RuffyScripter implements RuffyCommands {
} }
public void pressUpKey() { public void pressUpKey() {
// wrapNoNotTheSubwayKind(new Step() {
// @Override
// public void doStep() {
log.debug("Pressing up key"); log.debug("Pressing up key");
pressKey(Key.UP); pressKey(Key.UP);
log.debug("Releasing up key"); log.debug("Releasing up key");
// }
// });
} }
public void pressDownKey() { public void pressDownKey() {
@ -742,7 +741,7 @@ public class RuffyScripter implements RuffyCommands {
waitForMenuUpdate(60, "Timeout waiting for menu update"); waitForMenuUpdate(60, "Timeout waiting for menu update");
} }
public void waitForMenuUpdate(long timeoutInSeconds, String errorMessage) { private void waitForMenuUpdate(long timeoutInSeconds, String errorMessage) {
long timeoutExpired = System.currentTimeMillis() + timeoutInSeconds * 1000; long timeoutExpired = System.currentTimeMillis() + timeoutInSeconds * 1000;
long initialUpdateTime = menuLastUpdated; long initialUpdateTime = menuLastUpdated;
while (initialUpdateTime == menuLastUpdated) { while (initialUpdateTime == menuLastUpdated) {
@ -869,32 +868,7 @@ public class RuffyScripter implements RuffyCommands {
@Override @Override
public CommandResult takeOverAlarms() { public CommandResult takeOverAlarms() {
if (getCurrentMenu().getType() != MenuType.WARNING_OR_ERROR) { return runCommand(new TakeOverAlarmsCommand());
return new CommandResult().success(false).enacted(false).message("No alarm active on the pump");
}
while (currentMenu.getType() == MenuType.WARNING_OR_ERROR) {
new Error(System.currentTimeMillis(),
"",
// TODO
// codes unqiue across W/E?
// (int) currentMenu.getAttribute(MenuAttribute.WARNING),
// (int) currentMenu.getAttribute(MenuAttribute.ERROR),
(String) currentMenu.getAttribute(MenuAttribute.MESSAGE));
}
// confirm alert
verifyMenuIsDisplayed(MenuType.WARNING_OR_ERROR);
pressCheckKey();
// dismiss alert
verifyMenuIsDisplayed(MenuType.WARNING_OR_ERROR);
pressCheckKey();
waitForMenuToBeLeft(MenuType.WARNING_OR_ERROR);
PumpState pumpState = readPumpStateInternal();
return new CommandResult()
.success(true)
.enacted(false /* well, no treatments were enacted ... */)
.message(pumpState.errorMsg) // todo yikes?
.state(pumpState);
} }
@Override @Override

View file

@ -26,6 +26,11 @@ public class BolusCommand extends BaseCommand {
private final BolusProgressReporter bolusProgressReporter; private final BolusProgressReporter bolusProgressReporter;
private volatile boolean cancelRequested; 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) { public BolusCommand(double bolus, BolusProgressReporter bolusProgressReporter) {
this.bolus = bolus; this.bolus = bolus;
this.bolusProgressReporter = bolusProgressReporter; this.bolusProgressReporter = bolusProgressReporter;

View file

@ -1,5 +1,7 @@
package de.jotomo.ruffyscripter.commands; 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.MenuAttribute;
import org.monkey.d.ruffy.ruffy.driver.display.MenuType; 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.BolusType;
@ -30,50 +32,38 @@ public class ReadHistoryCommand extends BaseCommand {
} }
if (request.bolusHistory != PumpHistoryRequest.SKIP if (request.bolusHistory != PumpHistoryRequest.SKIP
|| request.tbrHistory != PumpHistoryRequest.SKIP || request.tbrHistory != PumpHistoryRequest.SKIP
|| request.errorHistory != PumpHistoryRequest.SKIP || request.pumpErrorHistory != PumpHistoryRequest.SKIP
|| request.tddHistory != PumpHistoryRequest.SKIP) { || request.tddHistory != PumpHistoryRequest.SKIP) {
scripter.navigateToMenu(MenuType.MY_DATA_MENU); scripter.navigateToMenu(MenuType.MY_DATA_MENU);
scripter.verifyMenuIsDisplayed(MenuType.MY_DATA_MENU); scripter.verifyMenuIsDisplayed(MenuType.MY_DATA_MENU);
scripter.pressCheckKey(); scripter.pressCheckKey();
// bolus history
scripter.verifyMenuIsDisplayed(MenuType.BOLUS_DATA); scripter.verifyMenuIsDisplayed(MenuType.BOLUS_DATA);
if (request.bolusHistory != PumpHistoryRequest.SKIP) { if (request.bolusHistory != PumpHistoryRequest.SKIP) {
if (request.bolusHistory == PumpHistoryRequest.LAST) { int totalRecords = (int) scripter.getCurrentMenu().getAttribute(MenuAttribute.TOTAL_RECORD);
// Could also be extended, multiwave: if (totalRecords > 0) {
BolusType bolusType = (BolusType) scripter.getCurrentMenu().getAttribute(MenuAttribute.BOLUS_TYPE); if (request.bolusHistory == PumpHistoryRequest.LAST) {
if (!bolusType.equals(BolusType.NORMAL)) { Bolus bolus = readBolusRecord();
throw new CommandException().success(false).enacted(false).message("Unsupported bolus type encountered: " + bolusType); history.bolusHistory.add(bolus);
} else {
readBolusRecords(request.bolusHistory);
} }
Double bolus = (Double) scripter.getCurrentMenu().getAttribute(MenuAttribute.BOLUS);
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();
history.bolusHistory.add(new Bolus(recordDate, bolus));
// int record = (int) scripter.getCurrentMenu().getAttribute(MenuAttribute.CURRENT_RECORD);
// int totalRecords = (int) scripter.getCurrentMenu().getAttribute(MenuAttribute.TOTAL_RECORD);
/*
read displayed date, bolus, add to history
while data > last known, press up to go through history
*/
} }
} }
// error history
scripter.pressMenuKey(); scripter.pressMenuKey();
scripter.verifyMenuIsDisplayed(MenuType.ERROR_DATA); scripter.verifyMenuIsDisplayed(MenuType.ERROR_DATA);
if (request.errorHistory != PumpHistoryRequest.SKIP) { if (request.pumpErrorHistory != PumpHistoryRequest.SKIP) {
int code = (int) scripter.getCurrentMenu().getAttribute(MenuAttribute.WARNING); int code = (int) scripter.getCurrentMenu().getAttribute(MenuAttribute.WARNING);
String message = (String) scripter.getCurrentMenu().getAttribute(MenuAttribute.MESSAGE); String message = (String) scripter.getCurrentMenu().getAttribute(MenuAttribute.MESSAGE);
MenuDate date = (MenuDate) scripter.getCurrentMenu().getAttribute(MenuAttribute.DATE); MenuDate date = (MenuDate) scripter.getCurrentMenu().getAttribute(MenuAttribute.DATE);
MenuTime time = (MenuTime) scripter.getCurrentMenu().getAttribute(MenuAttribute.TIME); MenuTime time = (MenuTime) scripter.getCurrentMenu().getAttribute(MenuAttribute.TIME);
} }
// tdd history
scripter.pressMenuKey(); scripter.pressMenuKey();
scripter.verifyMenuIsDisplayed(MenuType.DAILY_DATA); scripter.verifyMenuIsDisplayed(MenuType.DAILY_DATA);
if (request.tddHistory != PumpHistoryRequest.SKIP) { if (request.tddHistory != PumpHistoryRequest.SKIP) {
@ -81,6 +71,8 @@ public class ReadHistoryCommand extends BaseCommand {
MenuDate date = (MenuDate) scripter.getCurrentMenu().getAttribute(MenuAttribute.DATE); MenuDate date = (MenuDate) scripter.getCurrentMenu().getAttribute(MenuAttribute.DATE);
MenuTime time = (MenuTime) scripter.getCurrentMenu().getAttribute(MenuAttribute.TIME); MenuTime time = (MenuTime) scripter.getCurrentMenu().getAttribute(MenuAttribute.TIME);
} }
// tbr history
scripter.pressMenuKey(); scripter.pressMenuKey();
scripter.verifyMenuIsDisplayed(MenuType.TBR_DATA); scripter.verifyMenuIsDisplayed(MenuType.TBR_DATA);
if (request.tbrHistory != PumpHistoryRequest.SKIP) { if (request.tbrHistory != PumpHistoryRequest.SKIP) {
@ -90,13 +82,50 @@ public class ReadHistoryCommand extends BaseCommand {
// TODO start or end time? // TODO start or end time?
MenuTime time = (MenuTime) scripter.getCurrentMenu().getAttribute(MenuAttribute.TIME); MenuTime time = (MenuTime) scripter.getCurrentMenu().getAttribute(MenuAttribute.TIME);
} }
scripter.pressBackKey(); scripter.pressBackKey();
scripter.returnToRootMenu(); scripter.returnToRootMenu();
scripter.verifyRootMenuIsDisplayed();
} }
scripter.verifyRootMenuIsDisplayed();
return new CommandResult().success(true).enacted(false).history(history); return new CommandResult().success(true).enacted(false).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) {
Bolus bolus = readBolusRecord();
if (requestedTime != PumpHistoryRequest.FULL && bolus.timestamp < requestedTime) {
break;
}
history.bolusHistory.add(bolus);
record = (int) scripter.getCurrentMenu().getAttribute(MenuAttribute.CURRENT_RECORD);
}
}
@NonNull
private Bolus readBolusRecord() {
// 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);
}
// TODO no bolus etc yet? How would that ever look?
Double bolus = (Double) scripter.getCurrentMenu().getAttribute(MenuAttribute.BOLUS);
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() { private void readReservoirLevel() {
scripter.verifyRootMenuIsDisplayed(); scripter.verifyRootMenuIsDisplayed();
scripter.pressCheckKey(); scripter.pressCheckKey();
@ -112,4 +141,13 @@ public class ReadHistoryCommand extends BaseCommand {
public List<String> validateArguments() { public List<String> validateArguments() {
return Collections.emptyList(); return Collections.emptyList();
} }
@Override
public String toString() {
return "ReadHistoryCommand{" +
"request=" + request +
", history=" + history +
'}';
}
} }

View file

@ -0,0 +1,50 @@
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();
}
}