[0.4.0]
- refactored Broadcast receiving - implementation of SetProfile finished - added additional null checking in RadioResponse class
This commit is contained in:
parent
43f2f69133
commit
03e38158f4
21 changed files with 949 additions and 610 deletions
|
@ -68,6 +68,7 @@ import info.nightscout.androidaps.plugins.PumpDanaRS.DanaRSPlugin;
|
||||||
import info.nightscout.androidaps.plugins.PumpDanaRv2.DanaRv2Plugin;
|
import info.nightscout.androidaps.plugins.PumpDanaRv2.DanaRv2Plugin;
|
||||||
import info.nightscout.androidaps.plugins.PumpInsight.InsightPlugin;
|
import info.nightscout.androidaps.plugins.PumpInsight.InsightPlugin;
|
||||||
import info.nightscout.androidaps.plugins.PumpMDI.MDIPlugin;
|
import info.nightscout.androidaps.plugins.PumpMDI.MDIPlugin;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.MedtronicPumpPlugin;
|
||||||
import info.nightscout.androidaps.plugins.PumpVirtual.VirtualPumpPlugin;
|
import info.nightscout.androidaps.plugins.PumpVirtual.VirtualPumpPlugin;
|
||||||
import info.nightscout.androidaps.plugins.Sensitivity.SensitivityAAPSPlugin;
|
import info.nightscout.androidaps.plugins.Sensitivity.SensitivityAAPSPlugin;
|
||||||
import info.nightscout.androidaps.plugins.Sensitivity.SensitivityOref0Plugin;
|
import info.nightscout.androidaps.plugins.Sensitivity.SensitivityOref0Plugin;
|
||||||
|
@ -174,8 +175,10 @@ public class MainApp extends Application {
|
||||||
if (Config.PUMPDRIVERS)
|
if (Config.PUMPDRIVERS)
|
||||||
pluginsList.add(DanaRSPlugin.getPlugin());
|
pluginsList.add(DanaRSPlugin.getPlugin());
|
||||||
pluginsList.add(CareportalPlugin.getPlugin());
|
pluginsList.add(CareportalPlugin.getPlugin());
|
||||||
if (Config.PUMPDRIVERS && engineeringMode)
|
if (Config.PUMPDRIVERS && engineeringMode) {
|
||||||
pluginsList.add(InsightPlugin.getPlugin()); // <-- Enable Insight plugin here
|
pluginsList.add(InsightPlugin.getPlugin()); // <-- Enable Insight plugin here
|
||||||
|
pluginsList.add(MedtronicPumpPlugin.getPlugin());
|
||||||
|
}
|
||||||
if (Config.PUMPDRIVERS)
|
if (Config.PUMPDRIVERS)
|
||||||
pluginsList.add(ComboPlugin.getPlugin());
|
pluginsList.add(ComboPlugin.getPlugin());
|
||||||
if (Config.MDI)
|
if (Config.MDI)
|
||||||
|
|
|
@ -211,7 +211,7 @@ public abstract class RileyLinkCommunicationManager {
|
||||||
|
|
||||||
byte[] pumpMsgContent = createPumpMessageContent(RLMessageType.ReadSimpleData);
|
byte[] pumpMsgContent = createPumpMessageContent(RLMessageType.ReadSimpleData);
|
||||||
RFSpyResponse resp = rfspy.transmitThenReceive(new RadioPacket(pumpMsgContent), (byte)0, (byte)0,
|
RFSpyResponse resp = rfspy.transmitThenReceive(new RadioPacket(pumpMsgContent), (byte)0, (byte)0,
|
||||||
(byte)0, (byte)0, 500, (byte)0);
|
(byte)0, (byte)0, 1000, (byte)0);
|
||||||
if (resp.wasTimeout()) {
|
if (resp.wasTimeout()) {
|
||||||
LOG.error("scanForPump: Failed to find pump at frequency {}", frequencies[i]);
|
LOG.error("scanForPump: Failed to find pump at frequency {}", frequencies[i]);
|
||||||
} else if (resp.looksLikeRadioPacket()) {
|
} else if (resp.looksLikeRadioPacket()) {
|
||||||
|
|
|
@ -55,7 +55,7 @@ public class RileyLinkUtil {
|
||||||
private static RileyLinkTargetFrequency rileyLinkTargetFrequency;
|
private static RileyLinkTargetFrequency rileyLinkTargetFrequency;
|
||||||
|
|
||||||
// Broadcasts: RileyLinkBLE, RileyLinkService,
|
// Broadcasts: RileyLinkBLE, RileyLinkService,
|
||||||
//private static RileyLinkIPCConnection rileyLinkIPCConnection;
|
// private static RileyLinkIPCConnection rileyLinkIPCConnection;
|
||||||
private static RileyLinkTargetDevice targetDevice;
|
private static RileyLinkTargetDevice targetDevice;
|
||||||
private static RileyLinkEncodingType encoding;
|
private static RileyLinkEncodingType encoding;
|
||||||
private static RileyLinkSelectPreference rileyLinkSelectPreference;
|
private static RileyLinkSelectPreference rileyLinkSelectPreference;
|
||||||
|
@ -157,7 +157,8 @@ public class RileyLinkUtil {
|
||||||
|
|
||||||
|
|
||||||
public static boolean sendNotification(ServiceNotification notification, Integer clientHashcode) {
|
public static boolean sendNotification(ServiceNotification notification, Integer clientHashcode) {
|
||||||
return RileyLinkUtil.rileyLinkService.sendNotification(notification, clientHashcode);
|
// return RileyLinkUtil.rileyLinkIPCConnection.sendNotification(notification, clientHashcode);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -200,6 +201,10 @@ public class RileyLinkUtil {
|
||||||
// RileyLinkUtil.rileyLinkIPCConnection = rileyLinkIPCConnection;
|
// RileyLinkUtil.rileyLinkIPCConnection = rileyLinkIPCConnection;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
// public static RileyLinkIPCConnection getRileyLinkIPCConnection() {
|
||||||
|
// return RileyLinkUtil.rileyLinkIPCConnection;
|
||||||
|
// }
|
||||||
|
|
||||||
public static RileyLinkTargetFrequency getRileyLinkTargetFrequency() {
|
public static RileyLinkTargetFrequency getRileyLinkTargetFrequency() {
|
||||||
return RileyLinkUtil.rileyLinkTargetFrequency;
|
return RileyLinkUtil.rileyLinkTargetFrequency;
|
||||||
}
|
}
|
||||||
|
@ -217,6 +222,7 @@ public class RileyLinkUtil {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public static BleAdvertisedData parseAdertisedData(byte[] advertisedData) {
|
public static BleAdvertisedData parseAdertisedData(byte[] advertisedData) {
|
||||||
List<UUID> uuids = new ArrayList<UUID>();
|
List<UUID> uuids = new ArrayList<UUID>();
|
||||||
String name = null;
|
String name = null;
|
||||||
|
|
|
@ -11,6 +11,7 @@ import info.nightscout.androidaps.plugins.PumpCommon.utils.CRC;
|
||||||
/**
|
/**
|
||||||
* Created by geoff on 7/31/15.
|
* Created by geoff on 7/31/15.
|
||||||
*/
|
*/
|
||||||
|
// TODO refactor this DRY
|
||||||
public class RFTools {
|
public class RFTools {
|
||||||
|
|
||||||
public static final byte[] codes = new byte[] { 21, 49, 50, 35, 52, 37, 38, 22, 26, 25, 42, 11, 44, 13, 14, 28 };
|
public static final byte[] codes = new byte[] { 21, 49, 50, 35, 52, 37, 38, 22, 26, 25, 42, 11, 44, 13, 14, 28 };
|
||||||
|
|
|
@ -100,8 +100,17 @@ public class RadioResponse {
|
||||||
decodedPayload = encodedPayload;
|
decodedPayload = encodedPayload;
|
||||||
break;
|
break;
|
||||||
case FourByteSixByte:
|
case FourByteSixByte:
|
||||||
|
LOG.debug("encodedPayload: {}", ByteUtil.getHex(encodedPayload));
|
||||||
byte[] decodeThis = RFTools.decode4b6b(encodedPayload);
|
byte[] decodeThis = RFTools.decode4b6b(encodedPayload);
|
||||||
|
LOG.debug("decodedPayload: {}", ByteUtil.getHex(decodeThis));
|
||||||
decodedOK = true;
|
decodedOK = true;
|
||||||
|
|
||||||
|
if (decodeThis == null || decodeThis.length == 0) {
|
||||||
|
LOG.error("Decoded payload length is zero.");
|
||||||
|
decodedOK = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
decodedPayload = ByteUtil.substring(decodeThis, 0, decodeThis.length - 1);
|
decodedPayload = ByteUtil.substring(decodeThis, 0, decodeThis.length - 1);
|
||||||
receivedCRC = decodeThis[decodeThis.length - 1];
|
receivedCRC = decodeThis[decodeThis.length - 1];
|
||||||
byte calculatedCRC = CRC.crc8(decodedPayload);
|
byte calculatedCRC = CRC.crc8(decodedPayload);
|
||||||
|
@ -114,7 +123,7 @@ public class RadioResponse {
|
||||||
throw new NotImplementedException("this {" + RileyLinkUtil.getEncoding().toString()
|
throw new NotImplementedException("this {" + RileyLinkUtil.getEncoding().toString()
|
||||||
+ "} encoding is not supported");
|
+ "} encoding is not supported");
|
||||||
}
|
}
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
decodedOK = false;
|
decodedOK = false;
|
||||||
LOG.error("Failed to decode radio data: " + ByteUtil.shortHexString(encodedPayload));
|
LOG.error("Failed to decode radio data: " + ByteUtil.shortHexString(encodedPayload));
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,296 @@
|
||||||
|
package info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by andy on 10/23/18.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import android.bluetooth.BluetoothAdapter;
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.IntentFilter;
|
||||||
|
import android.support.v4.content.LocalBroadcastManager;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.RileyLinkConst;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.RileyLinkUtil;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.ble.defs.RileyLinkFirmwareVersion;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.defs.RileyLinkError;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.defs.RileyLinkServiceState;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.service.tasks.DiscoverGattServicesTask;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.service.tasks.InitializePumpManagerTask;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.service.tasks.ServiceTask;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.service.tasks.ServiceTaskExecutor;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.service.tasks.WakeAndTuneTask;
|
||||||
|
import info.nightscout.utils.SP;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* I added this class outside of RileyLinkService, because for now it's very important part of RL framework and
|
||||||
|
* where we get a lot of problems. Especially merging between AAPS and RileyLinkAAPS. I might put it back at
|
||||||
|
* later time
|
||||||
|
*/
|
||||||
|
public class RileyLinkBroadcastReceiver extends BroadcastReceiver {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(RileyLinkBroadcastReceiver.class);
|
||||||
|
|
||||||
|
RileyLinkService serviceInstance;
|
||||||
|
// protected RileyLinkIPCConnection rileyLinkIPCConnection;
|
||||||
|
protected Map<String, List<String>> broadcastIdentifiers = null;
|
||||||
|
String deviceSpecificPrefix;
|
||||||
|
Context context;
|
||||||
|
|
||||||
|
|
||||||
|
public RileyLinkBroadcastReceiver(RileyLinkService serviceInstance, Context context) {
|
||||||
|
this.serviceInstance = serviceInstance;
|
||||||
|
this.context = context;
|
||||||
|
|
||||||
|
// TODO remove in AAPS -- Andy
|
||||||
|
// rileyLinkIPCConnection = new RileyLinkIPCConnection(context);
|
||||||
|
// RileyLinkUtil.setRileyLinkIPCConnection(rileyLinkIPCConnection);
|
||||||
|
|
||||||
|
createBroadcastIdentifiers();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void createBroadcastIdentifiers() {
|
||||||
|
|
||||||
|
this.broadcastIdentifiers = new HashMap<>();
|
||||||
|
|
||||||
|
// Bluetooth
|
||||||
|
this.broadcastIdentifiers.put("Bluetooth", Arrays.asList( //
|
||||||
|
RileyLinkConst.Intents.BluetoothConnected, //
|
||||||
|
RileyLinkConst.Intents.BluetoothReconnected, //
|
||||||
|
RileyLinkConst.Intents.BluetoothReconnected));
|
||||||
|
|
||||||
|
// TuneUp
|
||||||
|
this.broadcastIdentifiers.put("TuneUp", Arrays.asList( //
|
||||||
|
// RT2Const.IPC.MSG_PUMP_tunePump, //
|
||||||
|
// RT2Const.IPC.MSG_PUMP_quickTune, //
|
||||||
|
RileyLinkConst.IPC.MSG_PUMP_tunePump, //
|
||||||
|
RileyLinkConst.IPC.MSG_PUMP_quickTune));
|
||||||
|
|
||||||
|
// RileyLink
|
||||||
|
this.broadcastIdentifiers.put("RileyLink", Arrays.asList( //
|
||||||
|
RileyLinkConst.Intents.RileyLinkDisconnected, //
|
||||||
|
RileyLinkConst.Intents.RileyLinkReady, //
|
||||||
|
RileyLinkConst.Intents.RileyLinkDisconnected, //
|
||||||
|
RileyLinkConst.Intents.RileyLinkNewAddressSet, //
|
||||||
|
RileyLinkConst.Intents.RileyLinkDisconnect));
|
||||||
|
|
||||||
|
// Device Specific
|
||||||
|
deviceSpecificPrefix = serviceInstance.getDeviceSpecificBroadcastsIdentifierPrefix();
|
||||||
|
|
||||||
|
// Application specific
|
||||||
|
// this.broadcastIdentifiers.put("AppSpecific", Arrays.asList( //
|
||||||
|
// RT2Const.serviceLocal.ipcBound, //
|
||||||
|
// RT2Const.IPC.MSG_ServiceCommand, //
|
||||||
|
// RT2Const.serviceLocal.INTENT_sessionCompleted));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
|
||||||
|
if (intent == null) {
|
||||||
|
LOG.error("onReceive: received null intent");
|
||||||
|
} else {
|
||||||
|
String action = intent.getAction();
|
||||||
|
if (action == null) {
|
||||||
|
LOG.error("onReceive: null action");
|
||||||
|
} else {
|
||||||
|
LOG.debug("Received Broadcast: " + action);
|
||||||
|
|
||||||
|
if (!processBluetoothBroadcasts(action) && //
|
||||||
|
!processRileyLinkBroadcasts(action) && //
|
||||||
|
!processTuneUpBroadcasts(action) && //
|
||||||
|
!processDeviceSpecificBroadcasts(action, intent) && //
|
||||||
|
!processApplicationSpecificBroadcasts(action, intent) //
|
||||||
|
) {
|
||||||
|
LOG.error("Unhandled broadcast: action=" + action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void registerBroadcasts() {
|
||||||
|
|
||||||
|
IntentFilter intentFilter = new IntentFilter();
|
||||||
|
|
||||||
|
for (Map.Entry<String, List<String>> stringListEntry : broadcastIdentifiers.entrySet()) {
|
||||||
|
|
||||||
|
for (String intentKey : stringListEntry.getValue()) {
|
||||||
|
System.out.println("Intent: " + intentKey);
|
||||||
|
intentFilter.addAction(intentKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (deviceSpecificPrefix != null) {
|
||||||
|
serviceInstance.registerDeviceSpecificBroadcasts(intentFilter);
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalBroadcastManager.getInstance(context).registerReceiver(this, intentFilter);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private boolean processRileyLinkBroadcasts(String action) {
|
||||||
|
|
||||||
|
if (action.equals(RileyLinkConst.Intents.RileyLinkDisconnected)) {
|
||||||
|
if (BluetoothAdapter.getDefaultAdapter().isEnabled()) {
|
||||||
|
RileyLinkUtil
|
||||||
|
.setServiceState(RileyLinkServiceState.BluetoothReady, RileyLinkError.RileyLinkUnreachable);
|
||||||
|
} else {
|
||||||
|
RileyLinkUtil.setServiceState(RileyLinkServiceState.BluetoothError, RileyLinkError.BluetoothDisabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} else if (action.equals(RileyLinkConst.Intents.RileyLinkReady)) {
|
||||||
|
LOG.warn("MedtronicConst.Intents.RileyLinkReady");
|
||||||
|
// sendIPCNotification(RT2Const.IPC.MSG_note_WakingPump);
|
||||||
|
this.serviceInstance.rileyLinkBLE.enableNotifications();
|
||||||
|
this.serviceInstance.rfspy.startReader(); // call startReader from outside?
|
||||||
|
|
||||||
|
this.serviceInstance.rfspy.initializeRileyLink();
|
||||||
|
String bleVersion = this.serviceInstance.rfspy.getBLEVersionCached();
|
||||||
|
RileyLinkFirmwareVersion rlVersion = this.serviceInstance.rfspy.getRLVersionCached();
|
||||||
|
|
||||||
|
LOG.debug("RfSpy version (BLE113): " + bleVersion);
|
||||||
|
this.serviceInstance.rileyLinkServiceData.versionBLE113 = bleVersion;
|
||||||
|
|
||||||
|
LOG.debug("RfSpy Radio version (CC110): " + rlVersion.name());
|
||||||
|
this.serviceInstance.rileyLinkServiceData.versionCC110 = rlVersion;
|
||||||
|
|
||||||
|
ServiceTask task = new InitializePumpManagerTask(RileyLinkUtil.getTargetDevice());
|
||||||
|
ServiceTaskExecutor.startTask(task);
|
||||||
|
LOG.info("Announcing RileyLink open For business");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} else if (action.equals(RileyLinkConst.Intents.RileyLinkDisconnected)) {
|
||||||
|
if (BluetoothAdapter.getDefaultAdapter().isEnabled()) {
|
||||||
|
RileyLinkUtil
|
||||||
|
.setServiceState(RileyLinkServiceState.BluetoothReady, RileyLinkError.RileyLinkUnreachable);
|
||||||
|
} else {
|
||||||
|
RileyLinkUtil.setServiceState(RileyLinkServiceState.BluetoothError, RileyLinkError.BluetoothDisabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} else if (action.equals(RileyLinkConst.Intents.RileyLinkNewAddressSet)) {
|
||||||
|
String RileylinkBLEAddress = SP.getString(RileyLinkConst.Prefs.RileyLinkAddress, "");
|
||||||
|
if (RileylinkBLEAddress.equals("")) {
|
||||||
|
LOG.error("No Rileylink BLE Address saved in app");
|
||||||
|
} else {
|
||||||
|
// showBusy("Configuring Service", 50);
|
||||||
|
// rileyLinkBLE.findRileyLink(RileylinkBLEAddress);
|
||||||
|
this.serviceInstance.reconfigureRileyLink(RileylinkBLEAddress);
|
||||||
|
// MainApp.getServiceClientConnection().setThisRileylink(RileylinkBLEAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} else if (action.equals(RileyLinkConst.Intents.RileyLinkDisconnect)) {
|
||||||
|
this.serviceInstance.disconnectRileyLink();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public boolean processBluetoothBroadcasts(String action) {
|
||||||
|
|
||||||
|
if (action.equals(RileyLinkConst.Intents.BluetoothConnected)) {
|
||||||
|
LOG.debug("Bluetooth - Connected");
|
||||||
|
// sendIPCNotification(RT2Const.IPC.MSG_note_FindingRileyLink);
|
||||||
|
ServiceTaskExecutor.startTask(new DiscoverGattServicesTask());
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
} else if (action.equals(RileyLinkConst.Intents.BluetoothReconnected)) {
|
||||||
|
LOG.debug("Bluetooth - Reconnecting");
|
||||||
|
// sendIPCNotification(RT2Const.IPC.MSG_note_FindingRileyLink);
|
||||||
|
serviceInstance.bluetoothInit();
|
||||||
|
ServiceTaskExecutor.startTask(new DiscoverGattServicesTask(true));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} else if (action.equals(RileyLinkConst.Intents.BluetoothReconnected)) {
|
||||||
|
LOG.debug("Bluetooth - Reconnected");
|
||||||
|
// sendIPCNotification(RT2Const.IPC.MSG_note_FindingRileyLink);
|
||||||
|
serviceInstance.bluetoothInit();
|
||||||
|
ServiceTaskExecutor.startTask(new DiscoverGattServicesTask(true));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private boolean processTuneUpBroadcasts(String action) {
|
||||||
|
|
||||||
|
if (this.broadcastIdentifiers.get("TuneUp").contains(action)) {
|
||||||
|
if (serviceInstance.getRileyLinkTargetDevice().isTuneUpEnabled()) {
|
||||||
|
ServiceTaskExecutor.startTask(new WakeAndTuneTask());
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public boolean processDeviceSpecificBroadcasts(String action, Intent intent) {
|
||||||
|
|
||||||
|
if (this.deviceSpecificPrefix == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action.startsWith(this.deviceSpecificPrefix)) {
|
||||||
|
return this.serviceInstance.handleDeviceSpecificBroadcasts(intent);
|
||||||
|
} else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public boolean processApplicationSpecificBroadcasts(String action, Intent intent) {
|
||||||
|
// if (action.equals(RT2Const.serviceLocal.ipcBound)) {
|
||||||
|
// // If we still need permission for bluetooth, ask now.
|
||||||
|
// // if (needBluetoothPermission) {
|
||||||
|
// // sendBLERequestForAccess();
|
||||||
|
// // }
|
||||||
|
// return true;
|
||||||
|
// } else if (RT2Const.IPC.MSG_ServiceCommand.equals(action)) {
|
||||||
|
// serviceInstance.handleIncomingServiceTransport(intent);
|
||||||
|
// return true;
|
||||||
|
// } else if (RT2Const.serviceLocal.INTENT_sessionCompleted.equals(action)) {
|
||||||
|
// Bundle bundle = intent.getBundleExtra(RT2Const.IPC.bundleKey);
|
||||||
|
// if (bundle != null) {
|
||||||
|
// ServiceTransport transport = new ServiceTransport(bundle);
|
||||||
|
// rileyLinkIPCConnection.sendTransport(transport, transport.getSenderHashcode());
|
||||||
|
// } else {
|
||||||
|
// LOG.error("sessionCompleted: no bundle!");
|
||||||
|
// }
|
||||||
|
// return true;
|
||||||
|
// } else {
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void sendIPCNotification(String notification) {
|
||||||
|
// rileyLinkIPCConnection.sendNotification(new ServiceNotification(notification), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -48,7 +48,7 @@ public abstract class RileyLinkService extends Service {
|
||||||
protected BluetoothAdapter bluetoothAdapter;
|
protected BluetoothAdapter bluetoothAdapter;
|
||||||
protected RFSpy rfspy; // interface for RL xxx Mhz radio.
|
protected RFSpy rfspy; // interface for RL xxx Mhz radio.
|
||||||
protected Context context;
|
protected Context context;
|
||||||
protected BroadcastReceiver mBroadcastReceiver;
|
protected RileyLinkBroadcastReceiver mBroadcastReceiver;
|
||||||
protected RileyLinkServiceData rileyLinkServiceData;
|
protected RileyLinkServiceData rileyLinkServiceData;
|
||||||
protected RileyLinkTargetFrequency rileyLinkTargetFrequency;
|
protected RileyLinkTargetFrequency rileyLinkTargetFrequency;
|
||||||
|
|
||||||
|
@ -121,173 +121,32 @@ public abstract class RileyLinkService extends Service {
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
LOG.debug("onCreate");
|
LOG.debug("onCreate");
|
||||||
|
|
||||||
// rileyLinkIPCConnection = new RileyLinkIPCConnection(context); // TODO We might be able to remove this -- Andy
|
mBroadcastReceiver = new RileyLinkBroadcastReceiver(this, this.context);
|
||||||
// RileyLinkUtil.setRileyLinkIPCConnection(rileyLinkIPCConnection);
|
mBroadcastReceiver.registerBroadcasts();
|
||||||
|
|
||||||
mBroadcastReceiver = new BroadcastReceiver() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
|
|
||||||
if (intent == null) {
|
|
||||||
LOG.error("onReceive: received null intent");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String action = intent.getAction();
|
|
||||||
if (action == null) {
|
|
||||||
LOG.error("onReceive: null action");
|
|
||||||
} else {
|
|
||||||
|
|
||||||
if (action.equals(RileyLinkConst.Intents.BluetoothConnected)) {
|
|
||||||
// rileyLinkIPCConnection.sendNotification(new
|
|
||||||
// ServiceNotification(RT2Const.IPC.MSG_note_FindingRileyLink), null);
|
|
||||||
ServiceTaskExecutor.startTask(new DiscoverGattServicesTask());
|
|
||||||
} else if (action.equals(RileyLinkConst.Intents.RileyLinkDisconnected)) {
|
|
||||||
if (BluetoothAdapter.getDefaultAdapter().isEnabled()) {
|
|
||||||
RileyLinkUtil.setServiceState(RileyLinkServiceState.RileyLinkError,
|
|
||||||
RileyLinkError.RileyLinkUnreachable);
|
|
||||||
} else {
|
|
||||||
RileyLinkUtil.setServiceState(RileyLinkServiceState.BluetoothError,
|
|
||||||
RileyLinkError.BluetoothDisabled);
|
|
||||||
}
|
|
||||||
} else if (action.equals(RileyLinkConst.Intents.RileyLinkReady)) {
|
|
||||||
LOG.warn("BroadcastReceive: RileyLink Ready");
|
|
||||||
|
|
||||||
rileyLinkBLE.enableNotifications();
|
|
||||||
rfspy.startReader(); // call startReader from outside?
|
|
||||||
|
|
||||||
rfspy.initializeRileyLink();
|
|
||||||
String bleVersion = rfspy.getBLEVersionCached();
|
|
||||||
RileyLinkFirmwareVersion rlVersion = rfspy.getRLVersionCached();
|
|
||||||
|
|
||||||
LOG.debug("RfSpy version (BLE113): " + bleVersion);
|
|
||||||
rileyLinkServiceData.versionBLE113 = bleVersion;
|
|
||||||
|
|
||||||
LOG.debug("RfSpy Radio version (CC110): " + rlVersion.name());
|
|
||||||
rileyLinkServiceData.versionCC110 = rlVersion;
|
|
||||||
|
|
||||||
ServiceTask task = new InitializePumpManagerTask(getRileyLinkTargetDevice());
|
|
||||||
ServiceTaskExecutor.startTask(task);
|
|
||||||
LOG.info("Announcing RileyLink open For business");
|
|
||||||
|
|
||||||
} else if (action.equals(RileyLinkConst.Intents.BluetoothReconnected)) {
|
|
||||||
LOG.debug("BroadcastReceive: Reconnecting Bluetooth");
|
|
||||||
// rileyLinkIPCConnection.sendNotification(new
|
|
||||||
// ServiceNotification(RT2Const.IPC.MSG_note_FindingRileyLink), null);
|
|
||||||
bluetoothInit();
|
|
||||||
ServiceTaskExecutor.startTask(new DiscoverGattServicesTask(true));
|
|
||||||
} else if (action.equals(RileyLinkConst.IPC.MSG_PUMP_tunePump) || //
|
|
||||||
action.equals(RileyLinkConst.IPC.MSG_PUMP_quickTune)) {
|
|
||||||
if (getRileyLinkTargetDevice().isTuneUpEnabled()) {
|
|
||||||
// doTuneUpDevice();
|
|
||||||
ServiceTaskExecutor.startTask(new WakeAndTuneTask());
|
|
||||||
}
|
|
||||||
} else if (action.startsWith("MSG_PUMP_")) {
|
|
||||||
handlePumpSpecificIntents(intent);
|
|
||||||
} else if (RileyLinkConst.IPC.MSG_ServiceCommand.equals(action)) {
|
|
||||||
handleIncomingServiceTransport(intent);
|
|
||||||
} else if (action.equals(RileyLinkConst.Intents.RileyLinkNewAddressSet)) {
|
|
||||||
String RileylinkBLEAddress = SP.getString(RileyLinkConst.Prefs.RileyLinkAddress, "");
|
|
||||||
if (RileylinkBLEAddress.equals("")) {
|
|
||||||
LOG.error("No Rileylink BLE Address saved in app");
|
|
||||||
} else {
|
|
||||||
// showBusy("Configuring Service", 50);
|
|
||||||
// rileyLinkBLE.findRileyLink(RileylinkBLEAddress);
|
|
||||||
reconfigureRileyLink(RileylinkBLEAddress);
|
|
||||||
// MainApp.getServiceClientConnection().setThisRileylink(RileylinkBLEAddress);
|
|
||||||
}
|
|
||||||
} else if (action.equals(RileyLinkConst.Intents.RileyLinkDisconnect)) {
|
|
||||||
disconnectRileyLink();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* else if (RT2Const.serviceLocal.INTENT_sessionCompleted.equals(action)) {
|
|
||||||
* Bundle bundle = intent.getBundleExtra(RT2Const.IPC.bundleKey);
|
|
||||||
* if (bundle != null) {
|
|
||||||
* ServiceTransport transport = new ServiceTransport(bundle);
|
|
||||||
* //rileyLinkIPCConnection.sendTransport(transport, transport.getSenderHashcode());
|
|
||||||
* //RileyLinkUtil.send
|
|
||||||
* } else {
|
|
||||||
* LOG.error("sessionCompleted: no bundle!");
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* else
|
|
||||||
*
|
|
||||||
* if (case RT2Const.local.INTENT_serviceConnected:
|
|
||||||
* case RT2Const.local.INTENT_NEW_rileylinkAddressKey:
|
|
||||||
* showIdle();
|
|
||||||
* /**
|
|
||||||
* Client MUST send a "UseThisRileylink" message because it asserts that
|
|
||||||
* the user has given explicit permission to use bluetooth.
|
|
||||||
*
|
|
||||||
* We can change the format so that it is a simple "bluetooth OK" message,
|
|
||||||
* rather than an explicit address of a Rileylink, and the Service can
|
|
||||||
* use the last known good value. But the kick-off of bluetooth ops must
|
|
||||||
* come from an Activity.
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
* String RileylinkBLEAddress = SP.getString(MedtronicConst.Prefs.RileyLinkAddress, "");
|
|
||||||
* if (RileylinkBLEAddress.equals("")) {
|
|
||||||
* // TODO: 11/07/2016 @TIM UI message for user
|
|
||||||
* Log.e(TAG, "No Rileylink BLE Address saved in app");
|
|
||||||
* } else {
|
|
||||||
* //showBusy("Configuring Service", 50);
|
|
||||||
* MainApp.getServiceClientConnection().setThisRileylink(RileylinkBLEAddress);
|
|
||||||
* }
|
|
||||||
* break;
|
|
||||||
* case RT2Const.local.INTENT_NEW_pumpIDKey:
|
|
||||||
* MainApp.getServiceClientConnection().sendPUMP_useThisDevice(SP.getString(MedtronicConst.Prefs.
|
|
||||||
* PumpSerial, ""));
|
|
||||||
* break;
|
|
||||||
*/
|
|
||||||
|
|
||||||
else {
|
|
||||||
LOG.error("Unhandled broadcast: action=" + action);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
IntentFilter intentFilter = new IntentFilter();
|
|
||||||
intentFilter.addAction(RileyLinkConst.Intents.BluetoothConnected);
|
|
||||||
intentFilter.addAction(RileyLinkConst.Intents.BluetoothDisconnected);
|
|
||||||
intentFilter.addAction(RileyLinkConst.Intents.RileyLinkReady);
|
|
||||||
intentFilter.addAction(RileyLinkConst.Intents.RileyLinkDisconnected);
|
|
||||||
intentFilter.addAction(RileyLinkConst.Intents.BluetoothReconnected);
|
|
||||||
intentFilter.addAction(RileyLinkConst.Intents.RileyLinkNewAddressSet);
|
|
||||||
intentFilter.addAction(RileyLinkConst.Intents.RileyLinkDisconnect);
|
|
||||||
// intentFilter.addAction(RT2Const.serviceLocal.ipcBound);
|
|
||||||
// intentFilter.addAction(RT2Const.IPC.MSG_BLE_accessGranted);
|
|
||||||
// intentFilter.addAction(RT2Const.IPC.MSG_BLE_accessDenied);
|
|
||||||
// intentFilter.addAction(RT2Const.IPC.MSG_BLE_useThisDevice);
|
|
||||||
intentFilter.addAction(RileyLinkConst.IPC.MSG_PUMP_tunePump);
|
|
||||||
// intentFilter.addAction(RT2Const.IPC.MSG_PUMP_useThisAddress);
|
|
||||||
intentFilter.addAction(RileyLinkConst.IPC.MSG_ServiceCommand);
|
|
||||||
// intentFilter.addAction(RileyLinkConst.serviceLocal.INTENT_sessionCompleted);
|
|
||||||
|
|
||||||
addPumpSpecificIntents(intentFilter);
|
|
||||||
|
|
||||||
LocalBroadcastManager.getInstance(context).registerReceiver(mBroadcastReceiver, intentFilter);
|
|
||||||
|
|
||||||
LOG.debug("onCreate(): It's ALIVE!");
|
LOG.debug("onCreate(): It's ALIVE!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prefix for Device specific broadcast identifier prefix (for example MSG_PUMP_ for pump or
|
||||||
|
* MSG_POD_ for Omnipod)
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public abstract String getDeviceSpecificBroadcastsIdentifierPrefix();
|
||||||
|
|
||||||
|
|
||||||
|
public abstract boolean handleDeviceSpecificBroadcasts(Intent intent);
|
||||||
|
|
||||||
|
|
||||||
|
public abstract void registerDeviceSpecificBroadcasts(IntentFilter intentFilter);
|
||||||
|
|
||||||
|
|
||||||
public abstract RileyLinkCommunicationManager getDeviceCommunicationManager();
|
public abstract RileyLinkCommunicationManager getDeviceCommunicationManager();
|
||||||
|
|
||||||
|
|
||||||
public abstract void addPumpSpecificIntents(IntentFilter intentFilter);
|
public abstract boolean handleIncomingServiceTransport(Intent intent);
|
||||||
|
|
||||||
|
|
||||||
public abstract void handlePumpSpecificIntents(Intent intent);
|
|
||||||
|
|
||||||
|
|
||||||
public abstract void handleIncomingServiceTransport(Intent intent);
|
|
||||||
|
|
||||||
|
|
||||||
// Here is where the wake-lock begins:
|
// Here is where the wake-lock begins:
|
||||||
|
@ -300,7 +159,7 @@ public abstract class RileyLinkService extends Service {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private boolean bluetoothInit() {
|
public boolean bluetoothInit() {
|
||||||
LOG.debug("bluetoothInit: attempting to get an adapter");
|
LOG.debug("bluetoothInit: attempting to get an adapter");
|
||||||
RileyLinkUtil.setServiceState(RileyLinkServiceState.BluetoothInitializing);
|
RileyLinkUtil.setServiceState(RileyLinkServiceState.BluetoothInitializing);
|
||||||
|
|
||||||
|
@ -380,13 +239,6 @@ public abstract class RileyLinkService extends Service {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public boolean sendNotification(ServiceNotification notification, Integer clientHashcode) {
|
|
||||||
// return rileyLinkIPCConnection.sendNotification(notification, clientHashcode);
|
|
||||||
LOG.error("sendNotification not implemented.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// FIXME: This needs to be run in a session so that is interruptable, has a separate thread, etc.
|
// FIXME: This needs to be run in a session so that is interruptable, has a separate thread, etc.
|
||||||
public void doTuneUpDevice() {
|
public void doTuneUpDevice() {
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@ public class LocationHelper {
|
||||||
*/
|
*/
|
||||||
public static boolean isLocationEnabled(Context context) {
|
public static boolean isLocationEnabled(Context context) {
|
||||||
LocationManager locationManager = (LocationManager)context.getSystemService(Context.LOCATION_SERVICE);
|
LocationManager locationManager = (LocationManager)context.getSystemService(Context.LOCATION_SERVICE);
|
||||||
|
|
||||||
return (locationManager != null && //
|
return (locationManager != null && //
|
||||||
(locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) || //
|
(locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) || //
|
||||||
locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)));
|
locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)));
|
||||||
|
|
|
@ -5,6 +5,8 @@ import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.joda.time.DateTime;
|
||||||
|
import org.joda.time.Hours;
|
||||||
import org.joda.time.LocalDateTime;
|
import org.joda.time.LocalDateTime;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
@ -39,9 +41,12 @@ import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.defs.RileyLink
|
||||||
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.service.tasks.ServiceTaskExecutor;
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.service.tasks.ServiceTaskExecutor;
|
||||||
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.service.tasks.WakeAndTuneTask;
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.service.tasks.WakeAndTuneTask;
|
||||||
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.MedtronicCommunicationManager;
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.MedtronicCommunicationManager;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.history.pump.PumpHistoryEntry;
|
||||||
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.ui.MedtronicUIComm;
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.ui.MedtronicUIComm;
|
||||||
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.ui.MedtronicUITask;
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.ui.MedtronicUITask;
|
||||||
import info.nightscout.androidaps.plugins.PumpMedtronic.data.MedtronicHistoryData;
|
import info.nightscout.androidaps.plugins.PumpMedtronic.data.MedtronicHistoryData;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.data.dto.BasalProfile;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.data.dto.BasalProfileEntry;
|
||||||
import info.nightscout.androidaps.plugins.PumpMedtronic.data.dto.TempBasalPair;
|
import info.nightscout.androidaps.plugins.PumpMedtronic.data.dto.TempBasalPair;
|
||||||
import info.nightscout.androidaps.plugins.PumpMedtronic.defs.MedtronicCommandType;
|
import info.nightscout.androidaps.plugins.PumpMedtronic.defs.MedtronicCommandType;
|
||||||
import info.nightscout.androidaps.plugins.PumpMedtronic.defs.MedtronicNotificationType;
|
import info.nightscout.androidaps.plugins.PumpMedtronic.defs.MedtronicNotificationType;
|
||||||
|
@ -133,6 +138,11 @@ public class MedtronicPumpPlugin extends PumpPluginAbstract implements PumpInter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private String getLogPrefix() {
|
||||||
|
return "MedtronicPumpPlugin::";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initPumpStatusData() {
|
public void initPumpStatusData() {
|
||||||
|
|
||||||
|
@ -141,6 +151,7 @@ public class MedtronicPumpPlugin extends PumpPluginAbstract implements PumpInter
|
||||||
|
|
||||||
pumpStatusLocal.lastConnection = SP.getLong(RileyLinkConst.Prefs.LastGoodDeviceCommunicationTime, 0L);
|
pumpStatusLocal.lastConnection = SP.getLong(RileyLinkConst.Prefs.LastGoodDeviceCommunicationTime, 0L);
|
||||||
pumpStatusLocal.lastDataTime = new LocalDateTime(pumpStatusLocal.lastConnection);
|
pumpStatusLocal.lastDataTime = new LocalDateTime(pumpStatusLocal.lastConnection);
|
||||||
|
pumpStatusLocal.previousConnection = pumpStatusLocal.lastConnection;
|
||||||
|
|
||||||
pumpStatusLocal.refreshConfiguration();
|
pumpStatusLocal.refreshConfiguration();
|
||||||
|
|
||||||
|
@ -151,10 +162,10 @@ public class MedtronicPumpPlugin extends PumpPluginAbstract implements PumpInter
|
||||||
pumpDescription.maxTempAbsolute = (pumpStatusLocal.maxBasal != null) ? pumpStatusLocal.maxBasal : 35.0d;
|
pumpDescription.maxTempAbsolute = (pumpStatusLocal.maxBasal != null) ? pumpStatusLocal.maxBasal : 35.0d;
|
||||||
|
|
||||||
// needs to be changed in configuration, after all functionalities are done
|
// needs to be changed in configuration, after all functionalities are done
|
||||||
pumpDescription.isBolusCapable = true;
|
pumpDescription.isBolusCapable = true; // WIP
|
||||||
pumpDescription.isTempBasalCapable = true; // WIP
|
pumpDescription.isTempBasalCapable = true; // WIP
|
||||||
pumpDescription.isExtendedBolusCapable = false;
|
pumpDescription.isExtendedBolusCapable = false;
|
||||||
pumpDescription.isSetBasalProfileCapable = false;
|
pumpDescription.isSetBasalProfileCapable = true;
|
||||||
|
|
||||||
// unchangable
|
// unchangable
|
||||||
pumpDescription.tempBasalStyle = PumpDescription.PERCENT;
|
pumpDescription.tempBasalStyle = PumpDescription.PERCENT;
|
||||||
|
@ -852,11 +863,14 @@ public class MedtronicPumpPlugin extends PumpPluginAbstract implements PumpInter
|
||||||
return new PumpEnactResult().success(response).enacted(response);
|
return new PumpEnactResult().success(response).enacted(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Gson gson = new Gson();
|
||||||
|
PumpHistoryEntry lastPumpHistoryEntry;
|
||||||
|
|
||||||
|
|
||||||
private void readPumpHistory() {
|
private void readPumpHistory() {
|
||||||
LOG.error("MedtronicPumpPlugin::readPumpHistory NOT IMPLEMENTED.");
|
LOG.error("MedtronicPumpPlugin::readPumpHistory NOT IMPLEMENTED.");
|
||||||
|
|
||||||
// TODO read History
|
// readPumpHistoryLogic();
|
||||||
|
|
||||||
scheduleNextRefresh(MedtronicStatusRefreshType.PumpHistory);
|
scheduleNextRefresh(MedtronicStatusRefreshType.PumpHistory);
|
||||||
|
|
||||||
|
@ -868,6 +882,50 @@ public class MedtronicPumpPlugin extends PumpPluginAbstract implements PumpInter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void readPumpHistoryLogic() {
|
||||||
|
|
||||||
|
Long lastPumpHistoryEntryTime = null;
|
||||||
|
|
||||||
|
// TODO read History
|
||||||
|
if (lastPumpHistoryEntry == null) {
|
||||||
|
lastPumpHistoryEntryTime = SP.getLong(MedtronicConst.Statistics.LastPumpHistoryEntry, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (firstRun) {
|
||||||
|
DateTime dt = new DateTime();
|
||||||
|
dt.minus(Hours.hours(36));
|
||||||
|
|
||||||
|
if (lastPumpHistoryEntry == null && lastPumpHistoryEntryTime == null) {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// determine if first run, if yes detrmine how much of update do we need
|
||||||
|
// first run:
|
||||||
|
// get last hiostory entry, if not there download 1.5 days of data
|
||||||
|
// - there: check if last entry is older than 1.5 days
|
||||||
|
// - yes: download 1.5 days
|
||||||
|
// - no: download with last entry
|
||||||
|
// - not there: download 1.5 days
|
||||||
|
//
|
||||||
|
// upload all new entries to NightScout (TBR, Bolus)
|
||||||
|
// determine pump status
|
||||||
|
//
|
||||||
|
// save last entry
|
||||||
|
//
|
||||||
|
// not first run:
|
||||||
|
// update to last entry
|
||||||
|
// - save
|
||||||
|
// - determine pump status
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private void scheduleNextRefresh(MedtronicStatusRefreshType refreshType) {
|
private void scheduleNextRefresh(MedtronicStatusRefreshType refreshType) {
|
||||||
scheduleNextRefresh(refreshType, 0);
|
scheduleNextRefresh(refreshType, 0);
|
||||||
}
|
}
|
||||||
|
@ -931,8 +989,19 @@ public class MedtronicPumpPlugin extends PumpPluginAbstract implements PumpInter
|
||||||
@Override
|
@Override
|
||||||
public PumpEnactResult cancelTempBasal(boolean enforceNew) {
|
public PumpEnactResult cancelTempBasal(boolean enforceNew) {
|
||||||
|
|
||||||
LOG.info("cancelTempBasal - started");
|
LOG.info("MedtronicPumpPlugin::cancelTempBasal - started");
|
||||||
|
|
||||||
|
if (isPumpNotReachable()) {
|
||||||
|
|
||||||
|
setRefreshButtonEnabled(true);
|
||||||
|
|
||||||
|
return new PumpEnactResult() //
|
||||||
|
.success(false) //
|
||||||
|
.enacted(false) //
|
||||||
|
.comment(MainApp.gs(R.string.medtronic_pump_status_pump_unreachable));
|
||||||
|
}
|
||||||
|
|
||||||
|
MedtronicUtil.dismissNotification(MedtronicNotificationType.PumpUnreachable);
|
||||||
setRefreshButtonEnabled(false);
|
setRefreshButtonEnabled(false);
|
||||||
|
|
||||||
TempBasalPair tbrCurrent = readTBR();
|
TempBasalPair tbrCurrent = readTBR();
|
||||||
|
@ -969,12 +1038,89 @@ public class MedtronicPumpPlugin extends PumpPluginAbstract implements PumpInter
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PumpEnactResult setNewBasalProfile(Profile profile) {
|
public PumpEnactResult setNewBasalProfile(Profile profile) {
|
||||||
LOG.warn("MedtronicPumpPlugin::setNewBasalProfile NOT IMPLEMENTED YET.");
|
LOG.error(getLogPrefix() + "setNewBasalProfile - WIP.");
|
||||||
|
|
||||||
// TODO implement this
|
setRefreshButtonEnabled(false);
|
||||||
|
|
||||||
return new PumpEnactResult().success(false).enacted(false)
|
if (isPumpNotReachable()) {
|
||||||
.comment(MainApp.gs(R.string.medtronic_cmd_profile_not_set));
|
|
||||||
|
setRefreshButtonEnabled(true);
|
||||||
|
|
||||||
|
return new PumpEnactResult() //
|
||||||
|
.success(false) //
|
||||||
|
.enacted(false) //
|
||||||
|
.comment(MainApp.gs(R.string.medtronic_pump_status_pump_unreachable));
|
||||||
|
}
|
||||||
|
|
||||||
|
MedtronicUtil.dismissNotification(MedtronicNotificationType.PumpUnreachable);
|
||||||
|
|
||||||
|
BasalProfile basalProfile = convertProfileToMedtronicProfile(profile);
|
||||||
|
|
||||||
|
String profileInvalid = isProfileValid(basalProfile);
|
||||||
|
|
||||||
|
if (profileInvalid != null) {
|
||||||
|
return new PumpEnactResult() //
|
||||||
|
.success(false) //
|
||||||
|
.enacted(false) //
|
||||||
|
.comment(MainApp.gs(R.string.medtronic_cmd_set_profile_pattern_overflow, profileInvalid));
|
||||||
|
}
|
||||||
|
|
||||||
|
MedtronicUITask responseTask = medtronicUIComm.executeCommand(MedtronicCommandType.SetBasalProfileSTD,
|
||||||
|
basalProfile);
|
||||||
|
|
||||||
|
Boolean response = (Boolean)responseTask.returnData;
|
||||||
|
|
||||||
|
LOG.info(getLogPrefix() + "Basal Profile was set: " + response);
|
||||||
|
|
||||||
|
return new PumpEnactResult().success(response).enacted(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private String isProfileValid(BasalProfile basalProfile) {
|
||||||
|
|
||||||
|
StringBuilder stringBuilder = new StringBuilder();
|
||||||
|
|
||||||
|
MedtronicPumpStatus pumpStatus = getMDTPumpStatus();
|
||||||
|
|
||||||
|
if (pumpStatusLocal.maxBasal == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
for (BasalProfileEntry profileEntry : basalProfile.getEntries()) {
|
||||||
|
|
||||||
|
if (profileEntry.rate > pumpStatusLocal.maxBasal) {
|
||||||
|
|
||||||
|
stringBuilder.append(profileEntry.startTime.toString("HH:mm") + "=" + profileEntry.rate);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return stringBuilder.length() == 0 ? null : stringBuilder.toString();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private BasalProfile convertProfileToMedtronicProfile(Profile profile) {
|
||||||
|
|
||||||
|
MedtronicPumpStatus pumpStatus = getMDTPumpStatus();
|
||||||
|
|
||||||
|
PumpType pumpType = pumpStatus.pumpType;
|
||||||
|
|
||||||
|
BasalProfile basalProfile = new BasalProfile();
|
||||||
|
|
||||||
|
for (int i = 0; i < 24; i++) {
|
||||||
|
double rate = profile.getBasalTimeFromMidnight(i * 60 * 60);
|
||||||
|
|
||||||
|
double v = pumpType.determineCorrectBasalSize(rate);
|
||||||
|
|
||||||
|
BasalProfileEntry basalEntry = new BasalProfileEntry(v, i, 0);
|
||||||
|
basalProfile.addEntry(basalEntry);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
basalProfile.generateRawDataFromEntries();
|
||||||
|
|
||||||
|
return basalProfile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -343,15 +343,13 @@ public class MedtronicCommunicationManager extends RileyLinkCommunicationManager
|
||||||
LOG.debug("Run command with Frames: Got ACK response for Attention packet");
|
LOG.debug("Run command with Frames: Got ACK response for Attention packet");
|
||||||
}
|
}
|
||||||
|
|
||||||
int start = 0;
|
|
||||||
int frameNr = 1;
|
int frameNr = 1;
|
||||||
int len = 0;
|
|
||||||
|
|
||||||
for (List<Byte> frame : frames) {
|
for (List<Byte> frame : frames) {
|
||||||
|
|
||||||
byte[] frameData = MedtronicUtil.createByteArray(frame);
|
byte[] frameData = MedtronicUtil.createByteArray(frame);
|
||||||
|
|
||||||
LOG.debug("Frame {} data:\n{}", frameNr, ByteUtil.getCompactString(frameData));
|
// LOG.debug("Frame {} data:\n{}", frameNr, ByteUtil.getCompactString(frameData));
|
||||||
|
|
||||||
PumpMessage msg = makePumpMessage(commandType, new CarelinkLongMessageBody(frameData));
|
PumpMessage msg = makePumpMessage(commandType, new CarelinkLongMessageBody(frameData));
|
||||||
|
|
||||||
|
@ -375,10 +373,6 @@ public class MedtronicCommunicationManager extends RileyLinkCommunicationManager
|
||||||
|
|
||||||
public PumpHistoryResult getPumpHistory(PumpHistoryEntry lastEntry, LocalDateTime targetDate) {
|
public PumpHistoryResult getPumpHistory(PumpHistoryEntry lastEntry, LocalDateTime targetDate) {
|
||||||
|
|
||||||
// int pageNumber = 0;
|
|
||||||
|
|
||||||
// TODO multiple history pages
|
|
||||||
|
|
||||||
PumpHistoryResult pumpTotalResult = new PumpHistoryResult(lastEntry, targetDate);
|
PumpHistoryResult pumpTotalResult = new PumpHistoryResult(lastEntry, targetDate);
|
||||||
|
|
||||||
if (doWakeUpBeforeCommand)
|
if (doWakeUpBeforeCommand)
|
||||||
|
@ -480,14 +474,6 @@ public class MedtronicCommunicationManager extends RileyLinkCommunicationManager
|
||||||
|
|
||||||
return pumpTotalResult;
|
return pumpTotalResult;
|
||||||
|
|
||||||
// Page page = new Page();
|
|
||||||
// // page.parseFrom(rval.getData(),PumpModel.MM522);
|
|
||||||
// // FIXME
|
|
||||||
// page.parseFrom(rawHistoryPage.getData(), MedtronicDeviceType.Medtronic_522);
|
|
||||||
//
|
|
||||||
// // return page;
|
|
||||||
//
|
|
||||||
// return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -774,40 +760,6 @@ public class MedtronicCommunicationManager extends RileyLinkCommunicationManager
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// TODO remove not needed - probably
|
|
||||||
// @Deprecated
|
|
||||||
// private void executeSetCommand(MedtronicCommandType commandType, byte[] bodyData) {
|
|
||||||
//
|
|
||||||
// LOG.debug("Executing Set for {} - 1st call", commandType.name());
|
|
||||||
//
|
|
||||||
// // first we send command without paramters and wait for ACK
|
|
||||||
// PumpMessage pumpMessage = sendAndGetACK(commandType, null, 4000);
|
|
||||||
//
|
|
||||||
// // FIXME check if ACK
|
|
||||||
// LOG.debug("Response 1 - {}", HexDump.toHexStringDisplayable(pumpMessage.getRawContent()));
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// LOG.debug("Executing Set for {} - 2nd call", commandType.name());
|
|
||||||
// // second we send command with parameters and full package 64 bits with zeroed empty places
|
|
||||||
//
|
|
||||||
// byte newBodyData[] = new byte[64];
|
|
||||||
// for(int i = 0; i < 64; i++) {
|
|
||||||
// newBodyData[i] = 0x00;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// newBodyData[0] = (byte) bodyData.length;
|
|
||||||
//
|
|
||||||
// for(int i = 0; i < bodyData.length; i++) {
|
|
||||||
// newBodyData[i + 1] = bodyData[i];
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// PumpMessage pumpMessage2 = sendAndGetACK(commandType, newBodyData, 4000);
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// LOG.debug("Response 2 - {}", HexDump.toHexStringDisplayable(pumpMessage.getRawContent()));
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
|
|
||||||
// PUMP SPECIFIC COMMANDS
|
// PUMP SPECIFIC COMMANDS
|
||||||
|
|
||||||
public Float getRemainingInsulin() {
|
public Float getRemainingInsulin() {
|
||||||
|
@ -830,14 +782,6 @@ public class MedtronicCommunicationManager extends RileyLinkCommunicationManager
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public BasalProfile getBasalProfile_Old() {
|
|
||||||
|
|
||||||
Object responseObject = sendAndGetResponseWithCheck(MedtronicCommandType.GetBasalProfileSTD);
|
|
||||||
|
|
||||||
return responseObject == null ? null : (BasalProfile)responseObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public BasalProfile getBasalProfile() {
|
public BasalProfile getBasalProfile() {
|
||||||
|
|
||||||
// wakeUp
|
// wakeUp
|
||||||
|
@ -992,7 +936,6 @@ public class MedtronicCommunicationManager extends RileyLinkCommunicationManager
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// TODO WIP test
|
|
||||||
public boolean setTBR(TempBasalPair tbr) {
|
public boolean setTBR(TempBasalPair tbr) {
|
||||||
|
|
||||||
if (this.doWakeUpBeforeCommand)
|
if (this.doWakeUpBeforeCommand)
|
||||||
|
@ -1028,6 +971,19 @@ public class MedtronicCommunicationManager extends RileyLinkCommunicationManager
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Boolean setBasalProfile(BasalProfile basalProfile) {
|
||||||
|
|
||||||
|
List<List<Byte>> basalProfileFrames = MedtronicUtil.getBasalProfileFrames(basalProfile.getRawData());
|
||||||
|
|
||||||
|
PumpMessage responseMessage = runCommandWithFrames(MedtronicCommandType.SetBasalProfileSTD, basalProfileFrames);
|
||||||
|
|
||||||
|
// LOG.debug("Set Basal Profile: {}", HexDump.toHexStringDisplayable(responseMessage.getRawContent()));
|
||||||
|
|
||||||
|
return responseMessage.commandType == MedtronicCommandType.CommandACK;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// FIXME --- After this line commands in development --- REMOVE THIS COMMANDS
|
// FIXME --- After this line commands in development --- REMOVE THIS COMMANDS
|
||||||
|
|
||||||
// TODO test
|
// TODO test
|
||||||
|
@ -1061,146 +1017,11 @@ public class MedtronicCommunicationManager extends RileyLinkCommunicationManager
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// TODO generateRawData (check if it works correctly) and test
|
|
||||||
public Boolean setBasalProfile(BasalProfile basalProfile) {
|
|
||||||
|
|
||||||
// [RileyLinkDevice] ======================== Save Basal Profile ===========================
|
|
||||||
// [PumpMessageSender] getResponse(PumpMessage(carelink, setBasalProfileStandard, 3 bytes, 1 bytes), 0,
|
|
||||||
// 0.180000, 3)
|
|
||||||
// [PeripheralManager+RileyLink] RL Send: 19050000000000000000b4030000a9659a6b19b199c555b2c000
|
|
||||||
// [PeripheralManager+RileyLink] RL Recv(single): bb
|
|
||||||
// [PeripheralManager+RileyLink] RileyLink response: PacketResponse(code:
|
|
||||||
// RileyLinkBLEKit.ResponseCode.commandInterrupted, packet: nil)
|
|
||||||
// [PeripheralManager+RileyLink] RL Recv(single): dd0dbca9659a6b19b156655534d500
|
|
||||||
// [PumpMessageSender] getResponse(PumpMessage(carelink, setBasalProfileStandard, 3 bytes, 65 bytes), 0,
|
|
||||||
// 0.180000, 3)
|
|
||||||
// [PeripheralManager+RileyLink] RL Send:
|
|
||||||
// 79050000000000000000b4030000a9659a6b19b199c571c9a555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555556000
|
|
||||||
// 2018-06-30 15:03:13.333962-0500 Loop[24609:13622692] [PeripheralManager+RileyLink] RL Recv(single):
|
|
||||||
// dd10bda9659a6b19b156655534d500
|
|
||||||
// 2018-06-30 15:03:13.334927-0500 Loop[24609:13622484] [PumpMessageSender] getResponse(PumpMessage(carelink,
|
|
||||||
// setBasalProfileStandard, 3 bytes, 65 bytes), 0, 0.180000, 3)
|
|
||||||
// 2018-06-30 15:03:13.337923-0500 Loop[24609:13622484] [PeripheralManager+RileyLink] RL Send:
|
|
||||||
// 79050000000000000000b4030000a9659a6b19b199c5725555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555ac000
|
|
||||||
// 2018-06-30 15:03:14.114024-0500 Loop[24609:13622486] [PeripheralManager+RileyLink] RL Recv(single):
|
|
||||||
// dd0ebea9659a6b19b156655534d500
|
|
||||||
// 2018-06-30 15:03:14.115017-0500 Loop[24609:13622484] [PumpMessageSender] getResponse(PumpMessage(carelink,
|
|
||||||
// setBasalProfileStandard, 3 bytes, 65 bytes), 0, 0.180000, 3)
|
|
||||||
// 2018-06-30 15:03:14.117600-0500 Loop[24609:13622484] [PeripheralManager+RileyLink] RL Send:
|
|
||||||
// 79050000000000000000b4030000a9659a6b19b199c6a355555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555570e000
|
|
||||||
// 2018-06-30 15:03:15.644502-0500 Loop[24609:13622692] [PeripheralManager+RileyLink] RL Recv(single):
|
|
||||||
// dd0ebfa9659a6b19b156655534d500
|
|
||||||
// 2018-06-30 15:03:15.645388-0500 Loop[24609:13622484] [RileyLinkDevice] ------------------------ Save Basal
|
|
||||||
// Profile ---------------------------
|
|
||||||
|
|
||||||
// 2018-06-30 15:03:12.167767-0500 Loop[24609:13622484] [RileyLinkDevice] ======================== Save Basal
|
|
||||||
// Profile ===========================
|
|
||||||
// 2018-06-30 15:03:12.168652-0500 Loop[24609:13622484] [PumpMessageSender] getResponse(PumpMessage(carelink,
|
|
||||||
// setBasalProfileStandard, 3 bytes, 1 bytes), 0, 0.180000, 3)
|
|
||||||
// 2018-06-30 15:03:12.169518-0500 Loop[24609:13622484] [PeripheralManager+RileyLink] RL Send:
|
|
||||||
// 19050000000000000000b4030000a9659a6b19b199c555b2c000
|
|
||||||
// 2018-06-30 15:03:12.463546-0500 Loop[24609:13622486] [PeripheralManager+RileyLink] RL Recv(single): bb
|
|
||||||
// 2018-06-30 15:03:12.463954-0500 Loop[24609:13622486] [PeripheralManager+RileyLink] RileyLink response:
|
|
||||||
// PacketResponse(code: RileyLinkBLEKit.ResponseCode.commandInterrupted, packet: nil)
|
|
||||||
// 2018-06-30 15:03:12.554051-0500 Loop[24609:13622486] [PeripheralManager+RileyLink] RL Recv(single):
|
|
||||||
// dd0dbca9659a6b19b156655534d500
|
|
||||||
// 2018-06-30 15:03:12.555175-0500 Loop[24609:13622484] [PumpMessageSender] getResponse(PumpMessage(carelink,
|
|
||||||
// setBasalProfileStandard, 3 bytes, 65 bytes), 0, 0.180000, 3)
|
|
||||||
// 2018-06-30 15:03:12.557953-0500 Loop[24609:13622484] [PeripheralManager+RileyLink] RL Send:
|
|
||||||
// 79050000000000000000b4030000a9659a6b19b199c571c9a555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555556000
|
|
||||||
// 2018-06-30 15:03:13.333962-0500 Loop[24609:13622692] [PeripheralManager+RileyLink] RL Recv(single):
|
|
||||||
// dd10bda9659a6b19b156655534d500
|
|
||||||
// 2018-06-30 15:03:13.334927-0500 Loop[24609:13622484] [PumpMessageSender] getResponse(PumpMessage(carelink,
|
|
||||||
// setBasalProfileStandard, 3 bytes, 65 bytes), 0, 0.180000, 3)
|
|
||||||
// 2018-06-30 15:03:13.337923-0500 Loop[24609:13622484] [PeripheralManager+RileyLink] RL Send:
|
|
||||||
// 79050000000000000000b4030000a9659a6b19b199c5725555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555ac000
|
|
||||||
// 2018-06-30 15:03:14.114024-0500 Loop[24609:13622486] [PeripheralManager+RileyLink] RL Recv(single):
|
|
||||||
// dd0ebea9659a6b19b156655534d500
|
|
||||||
// 2018-06-30 15:03:14.115017-0500 Loop[24609:13622484] [PumpMessageSender] getResponse(PumpMessage(carelink,
|
|
||||||
// setBasalProfileStandard, 3 bytes, 65 bytes), 0, 0.180000, 3)
|
|
||||||
// 2018-06-30 15:03:14.117600-0500 Loop[24609:13622484] [PeripheralManager+RileyLink] RL Send:
|
|
||||||
// 79050000000000000000b4030000a9659a6b19b199c6a355555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555570e000
|
|
||||||
// 2018-06-30 15:03:15.644502-0500 Loop[24609:13622692] [PeripheralManager+RileyLink] RL Recv(single):
|
|
||||||
// dd0ebfa9659a6b19b156655534d500
|
|
||||||
// 2018-06-30 15:03:15.645388-0500 Loop[24609:13622484] [RileyLinkDevice] ------------------------ Save Basal
|
|
||||||
// Profile ---------------------------
|
|
||||||
|
|
||||||
// byte[] body = basalProfile.generateRawData();
|
|
||||||
|
|
||||||
// byte[] body = new byte[] { 32, 0, 0, 38, 0, 13, 44, 0, 19, 38, 0, 28 };
|
|
||||||
|
|
||||||
byte[] body = ByteUtil
|
|
||||||
.createByteArray(
|
|
||||||
"06000052000178050202000304000402000504000602000704000802000904000a02000b04000c02000d02000e02000f040010020011040012020013040014020015040016020017040018020019000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
0);
|
|
||||||
|
|
||||||
List<List<Byte>> basalProfileFrames = MedtronicUtil.getBasalProfileFrames(body);
|
|
||||||
|
|
||||||
PumpMessage responseMessage = runCommandWithFrames(MedtronicCommandType.SetBasalProfileA, basalProfileFrames);
|
|
||||||
|
|
||||||
// PumpMessage responseMessage;
|
|
||||||
//
|
|
||||||
// if (debugSetCommands)
|
|
||||||
// LOG.debug("Set Basal Profile: Body [{}] - {}", body.length, HexDump.toHexStringDisplayable(body));
|
|
||||||
//
|
|
||||||
// for (List<Byte> basalProfileFrame : basalProfileFrames) {
|
|
||||||
//
|
|
||||||
// PumpMessage msg = makePumpMessage(MedtronicCommandType.SetBasalProfileA, //
|
|
||||||
// new CarelinkLongMessageBody(ByteUtil.concat((byte)body.length, body)));
|
|
||||||
//
|
|
||||||
// responseMessage = runCommandWithArgs(msg);
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (body.length <= 64) {
|
|
||||||
//
|
|
||||||
// PumpMessage msg = makePumpMessage(MedtronicCommandType.SetBasalProfileA, //
|
|
||||||
// new CarelinkLongMessageBody(ByteUtil.concat((byte) body.length, body)));
|
|
||||||
//
|
|
||||||
// responseMessage = runCommandWithArgs(msg);
|
|
||||||
// } else
|
|
||||||
// {
|
|
||||||
//
|
|
||||||
// responseMessage = runCommandWithArgsLong(MedtronicCommandType.SetBasalProfileA, body);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (debugSetCommands)
|
|
||||||
// LOG.debug("Set Basal Profile: {}", HexDump.toHexStringDisplayable(responseMessage.getRawContent()));
|
|
||||||
|
|
||||||
LOG.debug("Set Basal Profile: {}", HexDump.toHexStringDisplayable(responseMessage.getRawContent()));
|
|
||||||
|
|
||||||
return responseMessage.commandType == MedtronicCommandType.CommandACK;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// public byte[] getFullMessageBody(byte[] bodyData, int length) {
|
|
||||||
// byte newBodyData[] = getEmptyMessage(length);
|
|
||||||
//
|
|
||||||
// newBodyData[0] = (byte) bodyData.length;
|
|
||||||
//
|
|
||||||
// for (int i = 0; i < bodyData.length; i++) {
|
|
||||||
// newBodyData[i + 1] = bodyData[i];
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return newBodyData;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public byte[] getEmptyMessage(int length) {
|
|
||||||
// byte newBodyData[] = new byte[length];
|
|
||||||
// for (int i = 0; i < length; i++) {
|
|
||||||
// newBodyData[i] = 0x00;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return newBodyData;
|
|
||||||
// }
|
|
||||||
|
|
||||||
public PumpMessage cancelBolus() {
|
public PumpMessage cancelBolus() {
|
||||||
// ? maybe suspend and resume
|
// ? maybe suspend and resume
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Set TBR 100%
|
// Set TBR 100%
|
||||||
// Cancel TBR (set TBR 100%) 100%
|
// Cancel TBR (set TBR 100%) 100%
|
||||||
// Get Status (40%)
|
// Get Status (40%)
|
||||||
|
@ -1215,42 +1036,4 @@ public class MedtronicCommunicationManager extends RileyLinkCommunicationManager
|
||||||
// Read History 60%
|
// Read History 60%
|
||||||
// Load TDD ?
|
// Load TDD ?
|
||||||
|
|
||||||
// FIXME remove - each part needs to be gotten manually
|
|
||||||
public void updatePumpManagerStatus() {
|
|
||||||
// Integer resp = getRemainingBattery();
|
|
||||||
// pumpStatus.batteryRemaining = resp == null ? -1 : resp;
|
|
||||||
|
|
||||||
// pumpStatus.remainUnits = getRemainingInsulin();
|
|
||||||
|
|
||||||
/* current basal */
|
|
||||||
// TempBasalPair basalRate = getCurrentBasalRate();
|
|
||||||
|
|
||||||
// FIXME
|
|
||||||
// byte[] basalRateBytes = resp.getContents();
|
|
||||||
// if (basalRateBytes != null) {
|
|
||||||
// if (basalRateBytes.length == 2) {
|
|
||||||
// /**
|
|
||||||
// * 0x98 0x06
|
|
||||||
// * 0x98 is "basal rate"
|
|
||||||
// * 0x06 is what? Not currently running a temp basal, current basal is "standard" at 0
|
|
||||||
// */
|
|
||||||
// double basalRate = ByteUtil.asUINT8(basalRateBytes[1]);
|
|
||||||
// pumpStatus.currentBasal = basalRate;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// get last bolus amount
|
|
||||||
// get last bolus time
|
|
||||||
// get tempBasalInProgress
|
|
||||||
// get tempBasalRatio
|
|
||||||
// get tempBasalRemainMin
|
|
||||||
// get tempBasalStart
|
|
||||||
// get pump time
|
|
||||||
LocalDateTime clockResult = getPumpTime();
|
|
||||||
if (clockResult != null) {
|
|
||||||
// pumpStatus.time = clockResult.toDate();
|
|
||||||
}
|
|
||||||
// get last sync time
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -119,11 +119,12 @@ public abstract class MedtronicHistoryDecoder {
|
||||||
|
|
||||||
// public abstract List<? extends MedtronicHistoryEntry> processPageAndCreateRecords(RawHistoryPage page,
|
// public abstract List<? extends MedtronicHistoryEntry> processPageAndCreateRecords(RawHistoryPage page,
|
||||||
// boolean partial) throws Exception;
|
// boolean partial) throws Exception;
|
||||||
//
|
|
||||||
//
|
public <E extends MedtronicHistoryEntry> List<E> processPageAndCreateRecords(RawHistoryPage rawHistoryPage,
|
||||||
// public List<? extends MedtronicHistoryEntry> processPageAndCreateRecords(RawHistoryPage page) throws Exception {
|
Class<E> clazz) throws Exception {
|
||||||
// return processPageAndCreateRecords(page, false);
|
return processPageAndCreateRecords(rawHistoryPage, false, clazz);
|
||||||
// }
|
}
|
||||||
|
|
||||||
|
|
||||||
protected void prepareStatistics() {
|
protected void prepareStatistics() {
|
||||||
if (!statisticsEnabled)
|
if (!statisticsEnabled)
|
||||||
|
@ -212,12 +213,6 @@ public abstract class MedtronicHistoryDecoder {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// public <E extends MedtronicHistoryEntry> List<E> processPageAndCreateRecords(RawHistoryPage rawHistoryPage,
|
|
||||||
// boolean partial) {
|
|
||||||
// return (processPageAndCreateRecords(
|
|
||||||
// rawHistoryPage, partial, clazz);
|
|
||||||
// }
|
|
||||||
|
|
||||||
public <E extends MedtronicHistoryEntry> List<E> processPageAndCreateRecords(RawHistoryPage rawHistoryPage,
|
public <E extends MedtronicHistoryEntry> List<E> processPageAndCreateRecords(RawHistoryPage rawHistoryPage,
|
||||||
boolean partial, Class<E> clazz) {
|
boolean partial, Class<E> clazz) {
|
||||||
List<Byte> dataClear = checkPage(rawHistoryPage, partial);
|
List<Byte> dataClear = checkPage(rawHistoryPage, partial);
|
||||||
|
@ -234,4 +229,5 @@ public abstract class MedtronicHistoryDecoder {
|
||||||
|
|
||||||
|
|
||||||
protected abstract <E extends MedtronicHistoryEntry> List<E> createRecords(List<Byte> dataClear, Class<E> clazz);
|
protected abstract <E extends MedtronicHistoryEntry> List<E> createRecords(List<Byte> dataClear, Class<E> clazz);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,17 @@ public class MedtronicUIPostprocessor {
|
||||||
|
|
||||||
switch (uiTask.commandType) {
|
switch (uiTask.commandType) {
|
||||||
|
|
||||||
|
case SetBasalProfileSTD: {
|
||||||
|
Boolean response = (Boolean) uiTask.returnData;
|
||||||
|
|
||||||
|
if (response) {
|
||||||
|
BasalProfile basalProfile = (BasalProfile) uiTask.getParameter(0);
|
||||||
|
|
||||||
|
pumpStatus.basalsByHour = basalProfile.getProfilesByHour();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case GetBasalProfileSTD: {
|
case GetBasalProfileSTD: {
|
||||||
BasalProfile basalProfile = (BasalProfile)uiTask.returnData;
|
BasalProfile basalProfile = (BasalProfile)uiTask.returnData;
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import info.nightscout.androidaps.MainApp;
|
import info.nightscout.androidaps.MainApp;
|
||||||
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.MedtronicCommunicationManager;
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.MedtronicCommunicationManager;
|
||||||
|
import info.nightscout.androidaps.plugins.PumpMedtronic.data.dto.BasalProfile;
|
||||||
import info.nightscout.androidaps.plugins.PumpMedtronic.data.dto.TempBasalPair;
|
import info.nightscout.androidaps.plugins.PumpMedtronic.data.dto.TempBasalPair;
|
||||||
import info.nightscout.androidaps.plugins.PumpMedtronic.defs.MedtronicCommandType;
|
import info.nightscout.androidaps.plugins.PumpMedtronic.defs.MedtronicCommandType;
|
||||||
import info.nightscout.androidaps.plugins.PumpMedtronic.defs.MedtronicUIResponseType;
|
import info.nightscout.androidaps.plugins.PumpMedtronic.defs.MedtronicUIResponseType;
|
||||||
|
@ -118,7 +119,9 @@ public class MedtronicUITask {
|
||||||
|
|
||||||
case SetBasalProfileSTD:
|
case SetBasalProfileSTD:
|
||||||
case SetBasalProfileA: {
|
case SetBasalProfileA: {
|
||||||
// returnData = communicationManager.setBasalProfile(profile);
|
BasalProfile profile = (BasalProfile) parameters[0];
|
||||||
|
|
||||||
|
returnData = communicationManager.setBasalProfile(profile);
|
||||||
// Float amount = getAmount();
|
// Float amount = getAmount();
|
||||||
//
|
//
|
||||||
// if (amount != null) {
|
// if (amount != null) {
|
||||||
|
@ -227,4 +230,8 @@ public class MedtronicUITask {
|
||||||
public boolean hasData() {
|
public boolean hasData() {
|
||||||
return (responseType == MedtronicUIResponseType.Data);
|
return (responseType == MedtronicUIResponseType.Data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Object getParameter(int index) {
|
||||||
|
return parameters[index];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,7 +69,13 @@ public class BasalProfile {
|
||||||
data = MedtronicUtil.createByteArray(data[0], (byte)0, (byte)0);
|
data = MedtronicUtil.createByteArray(data[0], (byte)0, (byte)0);
|
||||||
}
|
}
|
||||||
|
|
||||||
mRawData = data;
|
if (data.length == MAX_RAW_DATA_SIZE) {
|
||||||
|
mRawData = data;
|
||||||
|
} else {
|
||||||
|
int len = Math.min(MAX_RAW_DATA_SIZE, data.length);
|
||||||
|
mRawData = new byte[MAX_RAW_DATA_SIZE];
|
||||||
|
System.arraycopy(data, 0, mRawData, 0, len);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -190,7 +196,7 @@ public class BasalProfile {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public byte[] generateRawData() {
|
public void generateRawDataFromEntries() {
|
||||||
|
|
||||||
List<Byte> outData = new ArrayList<>();
|
List<Byte> outData = new ArrayList<>();
|
||||||
|
|
||||||
|
@ -205,7 +211,7 @@ public class BasalProfile {
|
||||||
|
|
||||||
this.setRawData(MedtronicUtil.createByteArray(outData));
|
this.setRawData(MedtronicUtil.createByteArray(outData));
|
||||||
|
|
||||||
return this.mRawData;
|
// return this.mRawData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,24 @@ public class BasalProfileEntry {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public BasalProfileEntry(double rate, int hour, int minutes) {
|
||||||
|
byte[] data = MedtronicUtil.getBasalStrokes(rate, true);
|
||||||
|
|
||||||
|
rate_raw = new byte[2];
|
||||||
|
rate_raw[0] = data[1];
|
||||||
|
rate_raw[1] = data[0];
|
||||||
|
|
||||||
|
int interval = hour * 2;
|
||||||
|
|
||||||
|
if (minutes == 30) {
|
||||||
|
interval++;
|
||||||
|
}
|
||||||
|
|
||||||
|
startTime_raw = (byte)interval;
|
||||||
|
startTime = new LocalTime(hour, minutes == 30 ? 30 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public BasalProfileEntry(int rateStrokes, int startTimeInterval) {
|
public BasalProfileEntry(int rateStrokes, int startTimeInterval) {
|
||||||
// rateByte is insulin delivery rate, U/hr, in 0.025 U increments
|
// rateByte is insulin delivery rate, U/hr, in 0.025 U increments
|
||||||
// startTimeByte is time-of-day, in 30 minute increments
|
// startTimeByte is time-of-day, in 30 minute increments
|
||||||
|
|
|
@ -1,13 +1,8 @@
|
||||||
package info.nightscout.androidaps.plugins.PumpMedtronic.service;
|
package info.nightscout.androidaps.plugins.PumpMedtronic.service;
|
||||||
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
|
@ -27,12 +22,10 @@ import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.defs.RileyLink
|
||||||
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.defs.RileyLinkTargetDevice;
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.defs.RileyLinkTargetDevice;
|
||||||
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.service.RileyLinkService;
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.service.RileyLinkService;
|
||||||
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.service.RileyLinkServiceData;
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.service.RileyLinkServiceData;
|
||||||
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.service.data.ServiceNotification;
|
|
||||||
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.service.tasks.ServiceTask;
|
import info.nightscout.androidaps.plugins.PumpCommon.hw.rileylink.service.tasks.ServiceTask;
|
||||||
import info.nightscout.androidaps.plugins.PumpCommon.utils.ByteUtil;
|
import info.nightscout.androidaps.plugins.PumpCommon.utils.ByteUtil;
|
||||||
import info.nightscout.androidaps.plugins.PumpMedtronic.MedtronicPumpPlugin;
|
import info.nightscout.androidaps.plugins.PumpMedtronic.MedtronicPumpPlugin;
|
||||||
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.MedtronicCommunicationManager;
|
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.MedtronicCommunicationManager;
|
||||||
import info.nightscout.androidaps.plugins.PumpMedtronic.comm.data.Page;
|
|
||||||
import info.nightscout.androidaps.plugins.PumpMedtronic.defs.PumpDeviceState;
|
import info.nightscout.androidaps.plugins.PumpMedtronic.defs.PumpDeviceState;
|
||||||
import info.nightscout.androidaps.plugins.PumpMedtronic.driver.MedtronicPumpStatus;
|
import info.nightscout.androidaps.plugins.PumpMedtronic.driver.MedtronicPumpStatus;
|
||||||
import info.nightscout.androidaps.plugins.PumpMedtronic.util.MedtronicConst;
|
import info.nightscout.androidaps.plugins.PumpMedtronic.util.MedtronicConst;
|
||||||
|
@ -74,119 +67,6 @@ public class RileyLinkMedtronicService extends RileyLinkService {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void addPumpSpecificIntents(IntentFilter intentFilter) {
|
|
||||||
//intentFilter.addAction(RileyLinkConst.IPC.MSG_PUMP_fetchHistory);
|
|
||||||
//intentFilter.addAction(RileyLinkConst.IPC.MSG_PUMP_fetchSavedHistory);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void handlePumpSpecificIntents(Intent intent) {
|
|
||||||
String action = intent.getAction();
|
|
||||||
|
|
||||||
if (action.equals(RileyLinkConst.IPC.MSG_PUMP_fetchHistory)) {
|
|
||||||
|
|
||||||
// mHistoryPages = medtronicCommunicationManager.getAllHistoryPages();
|
|
||||||
// final boolean savePages = true;
|
|
||||||
// if (savePages) {
|
|
||||||
// for (int i = 0; i < mHistoryPages.size(); i++) {
|
|
||||||
// String filename = "PumpHistoryPage-" + i;
|
|
||||||
// LOG.warn("Saving history page to file " + filename);
|
|
||||||
// FileOutputStream outputStream;
|
|
||||||
// try {
|
|
||||||
// outputStream = openFileOutput(filename, 0);
|
|
||||||
// byte[] rawData = mHistoryPages.get(i).getRawData();
|
|
||||||
// if (rawData != null) {
|
|
||||||
// outputStream.write(rawData);
|
|
||||||
// }
|
|
||||||
// outputStream.close();
|
|
||||||
// } catch (FileNotFoundException fnf) {
|
|
||||||
// fnf.printStackTrace();
|
|
||||||
// } catch (IOException ioe) {
|
|
||||||
// ioe.printStackTrace();
|
|
||||||
// } catch (Exception e) {
|
|
||||||
// e.printStackTrace();
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// Message msg = Message.obtain(null, RT2Const.IPC.MSG_IPC, 0, 0);
|
|
||||||
// // Create a bundle with the data
|
|
||||||
// Bundle bundle = new Bundle();
|
|
||||||
// bundle.putString(RT2Const.IPC.messageKey, RT2Const.IPC.MSG_PUMP_history);
|
|
||||||
// ArrayList<Bundle> packedPages = new ArrayList<>();
|
|
||||||
// for (Page page : mHistoryPages) {
|
|
||||||
// packedPages.add(page.pack());
|
|
||||||
// }
|
|
||||||
// bundle.putParcelableArrayList(RT2Const.IPC.MSG_PUMP_history_key, packedPages);
|
|
||||||
//
|
|
||||||
// // save it to SQL.
|
|
||||||
// pumpHistoryManager.clearDatabase();
|
|
||||||
// pumpHistoryManager.initFromPages(bundle);
|
|
||||||
// // write html page to documents folder
|
|
||||||
// pumpHistoryManager.writeHtmlPage();
|
|
||||||
//
|
|
||||||
// // Set payload
|
|
||||||
// msg.setData(bundle);
|
|
||||||
// //rileyLinkIPCConnection.sendMessage(msg, null/*broadcast*/);
|
|
||||||
// LOG.debug("sendMessage: sent Full history report");
|
|
||||||
} else if (RileyLinkConst.IPC.MSG_PUMP_fetchSavedHistory.equals(action)) {
|
|
||||||
LOG.info("Fetching saved history");
|
|
||||||
// FileInputStream inputStream;
|
|
||||||
// ArrayList<Page> storedHistoryPages = new ArrayList<>();
|
|
||||||
// for (int i = 0; i < 16; i++) {
|
|
||||||
//
|
|
||||||
// String filename = "PumpHistoryPage-" + i;
|
|
||||||
// try {
|
|
||||||
// inputStream = openFileInput(filename);
|
|
||||||
// byte[] buffer = new byte[1024];
|
|
||||||
// int numRead = inputStream.read(buffer, 0, 1024);
|
|
||||||
// if (numRead == 1024) {
|
|
||||||
// Page p = new Page();
|
|
||||||
// //p.parseFrom(buffer, PumpModel.MM522);
|
|
||||||
// // FIXME
|
|
||||||
// p.parseFrom(buffer, PumpModel.MM522);
|
|
||||||
// storedHistoryPages.add(p);
|
|
||||||
// } else {
|
|
||||||
// LOG.error(filename + " error: short file");
|
|
||||||
// }
|
|
||||||
// } catch (FileNotFoundException fnf) {
|
|
||||||
// LOG.error("Failed to open " + filename + " for reading.");
|
|
||||||
// } catch (IOException e) {
|
|
||||||
// LOG.error("Failed to read " + filename);
|
|
||||||
// } catch (Exception e) {
|
|
||||||
// e.printStackTrace();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// mHistoryPages = storedHistoryPages;
|
|
||||||
// if (storedHistoryPages.isEmpty()) {
|
|
||||||
// LOG.error("No stored history pages loaded");
|
|
||||||
// } else {
|
|
||||||
// Message msg = Message.obtain(null, RT2Const.IPC.MSG_IPC, 0, 0);
|
|
||||||
// // Create a bundle with the data
|
|
||||||
// Bundle bundle = new Bundle();
|
|
||||||
// bundle.putString(RT2Const.IPC.messageKey, RT2Const.IPC.MSG_PUMP_history);
|
|
||||||
// ArrayList<Bundle> packedPages = new ArrayList<>();
|
|
||||||
// for (Page page : mHistoryPages) {
|
|
||||||
// packedPages.add(page.pack());
|
|
||||||
// }
|
|
||||||
// bundle.putParcelableArrayList(RT2Const.IPC.MSG_PUMP_history_key, packedPages);
|
|
||||||
//
|
|
||||||
// // save it to SQL.
|
|
||||||
// pumpHistoryManager.clearDatabase();
|
|
||||||
// pumpHistoryManager.initFromPages(bundle);
|
|
||||||
// // write html page to documents folder
|
|
||||||
// pumpHistoryManager.writeHtmlPage();
|
|
||||||
//
|
|
||||||
// // Set payload
|
|
||||||
// msg.setData(bundle);
|
|
||||||
// //rileyLinkIPCConnection.sendMessage(msg, null/*broadcast*/);
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onConfigurationChanged(Configuration newConfig) {
|
public void onConfigurationChanged(Configuration newConfig) {
|
||||||
LOG.warn("onConfigurationChanged");
|
LOG.warn("onConfigurationChanged");
|
||||||
|
@ -286,10 +166,166 @@ public class RileyLinkMedtronicService extends RileyLinkService {
|
||||||
// LOG.info("setPumpIDString: saved pumpID " + idString);
|
// LOG.info("setPumpIDString: saved pumpID " + idString);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class LocalBinder extends Binder {
|
||||||
|
|
||||||
|
public RileyLinkMedtronicService getServiceInstance() {
|
||||||
|
return RileyLinkMedtronicService.this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* private functions */
|
/* private functions */
|
||||||
|
|
||||||
public void handleIncomingServiceTransport(Intent intent) {
|
// PumpInterface - REMOVE
|
||||||
|
|
||||||
|
public boolean isInitialized() {
|
||||||
|
return RileyLinkServiceState.isReady(RileyLinkUtil.getRileyLinkServiceData().serviceState);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public boolean isSuspended() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public boolean isBusy() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public boolean isConnected() {
|
||||||
|
return RileyLinkServiceState.isReady(RileyLinkUtil.getRileyLinkServiceData().serviceState);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public boolean isConnecting() {
|
||||||
|
return !RileyLinkServiceState.isReady(RileyLinkUtil.getRileyLinkServiceData().serviceState);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDeviceSpecificBroadcastsIdentifierPrefix() {
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public boolean handleDeviceSpecificBroadcasts(Intent intent) {
|
||||||
|
// String action = intent.getAction();
|
||||||
|
|
||||||
|
// if (action.equals(RileyLinkConst.IPC.MSG_PUMP_fetchHistory)) {
|
||||||
|
|
||||||
|
// mHistoryPages = medtronicCommunicationManager.getAllHistoryPages();
|
||||||
|
// final boolean savePages = true;
|
||||||
|
// if (savePages) {
|
||||||
|
// for (int i = 0; i < mHistoryPages.size(); i++) {
|
||||||
|
// String filename = "PumpHistoryPage-" + i;
|
||||||
|
// LOG.warn("Saving history page to file " + filename);
|
||||||
|
// FileOutputStream outputStream;
|
||||||
|
// try {
|
||||||
|
// outputStream = openFileOutput(filename, 0);
|
||||||
|
// byte[] rawData = mHistoryPages.get(i).getRawData();
|
||||||
|
// if (rawData != null) {
|
||||||
|
// outputStream.write(rawData);
|
||||||
|
// }
|
||||||
|
// outputStream.close();
|
||||||
|
// } catch (FileNotFoundException fnf) {
|
||||||
|
// fnf.printStackTrace();
|
||||||
|
// } catch (IOException ioe) {
|
||||||
|
// ioe.printStackTrace();
|
||||||
|
// } catch (Exception e) {
|
||||||
|
// e.printStackTrace();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Message msg = Message.obtain(null, RT2Const.IPC.MSG_IPC, 0, 0);
|
||||||
|
// // Create a bundle with the data
|
||||||
|
// Bundle bundle = new Bundle();
|
||||||
|
// bundle.putString(RT2Const.IPC.messageKey, RT2Const.IPC.MSG_PUMP_history);
|
||||||
|
// ArrayList<Bundle> packedPages = new ArrayList<>();
|
||||||
|
// for (Page page : mHistoryPages) {
|
||||||
|
// packedPages.add(page.pack());
|
||||||
|
// }
|
||||||
|
// bundle.putParcelableArrayList(RT2Const.IPC.MSG_PUMP_history_key, packedPages);
|
||||||
|
//
|
||||||
|
// // save it to SQL.
|
||||||
|
// pumpHistoryManager.clearDatabase();
|
||||||
|
// pumpHistoryManager.initFromPages(bundle);
|
||||||
|
// // write html page to documents folder
|
||||||
|
// pumpHistoryManager.writeHtmlPage();
|
||||||
|
//
|
||||||
|
// // Set payload
|
||||||
|
// msg.setData(bundle);
|
||||||
|
// //rileyLinkIPCConnection.sendMessage(msg, null/*broadcast*/);
|
||||||
|
// LOG.debug("sendMessage: sent Full history report");
|
||||||
|
// } else if (RileyLinkConst.IPC.MSG_PUMP_fetchSavedHistory.equals(action)) {
|
||||||
|
// LOG.info("Fetching saved history");
|
||||||
|
// FileInputStream inputStream;
|
||||||
|
// ArrayList<Page> storedHistoryPages = new ArrayList<>();
|
||||||
|
// for (int i = 0; i < 16; i++) {
|
||||||
|
//
|
||||||
|
// String filename = "PumpHistoryPage-" + i;
|
||||||
|
// try {
|
||||||
|
// inputStream = openFileInput(filename);
|
||||||
|
// byte[] buffer = new byte[1024];
|
||||||
|
// int numRead = inputStream.read(buffer, 0, 1024);
|
||||||
|
// if (numRead == 1024) {
|
||||||
|
// Page p = new Page();
|
||||||
|
// //p.parseFrom(buffer, PumpModel.MM522);
|
||||||
|
//
|
||||||
|
// p.parseFrom(buffer, PumpModel.MM522);
|
||||||
|
// storedHistoryPages.add(p);
|
||||||
|
// } else {
|
||||||
|
// LOG.error(filename + " error: short file");
|
||||||
|
// }
|
||||||
|
// } catch (FileNotFoundException fnf) {
|
||||||
|
// LOG.error("Failed to open " + filename + " for reading.");
|
||||||
|
// } catch (IOException e) {
|
||||||
|
// LOG.error("Failed to read " + filename);
|
||||||
|
// } catch (Exception e) {
|
||||||
|
// e.printStackTrace();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// mHistoryPages = storedHistoryPages;
|
||||||
|
// if (storedHistoryPages.isEmpty()) {
|
||||||
|
// LOG.error("No stored history pages loaded");
|
||||||
|
// } else {
|
||||||
|
// Message msg = Message.obtain(null, RT2Const.IPC.MSG_IPC, 0, 0);
|
||||||
|
// // Create a bundle with the data
|
||||||
|
// Bundle bundle = new Bundle();
|
||||||
|
// bundle.putString(RT2Const.IPC.messageKey, RT2Const.IPC.MSG_PUMP_history);
|
||||||
|
// ArrayList<Bundle> packedPages = new ArrayList<>();
|
||||||
|
// for (Page page : mHistoryPages) {
|
||||||
|
// packedPages.add(page.pack());
|
||||||
|
// }
|
||||||
|
// bundle.putParcelableArrayList(RT2Const.IPC.MSG_PUMP_history_key, packedPages);
|
||||||
|
//
|
||||||
|
// // save it to SQL.
|
||||||
|
// pumpHistoryManager.clearDatabase();
|
||||||
|
// pumpHistoryManager.initFromPages(bundle);
|
||||||
|
// // write html page to documents folder
|
||||||
|
// pumpHistoryManager.writeHtmlPage();
|
||||||
|
//
|
||||||
|
// // Set payload
|
||||||
|
// msg.setData(bundle);
|
||||||
|
// //rileyLinkIPCConnection.sendMessage(msg, null/*broadcast*/);
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void registerDeviceSpecificBroadcasts(IntentFilter intentFilter) {
|
||||||
|
// intentFilter.addAction(RT2Const.IPC.MSG_PUMP_fetchHistory);
|
||||||
|
// intentFilter.addAction(RT2Const.IPC.MSG_PUMP_fetchSavedHistory);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public boolean handleIncomingServiceTransport(Intent intent) {
|
||||||
|
|
||||||
// Bundle bundle = intent.getBundleExtra(RT2Const.IPC.bundleKey);
|
// Bundle bundle = intent.getBundleExtra(RT2Const.IPC.bundleKey);
|
||||||
//
|
//
|
||||||
|
@ -368,6 +404,7 @@ public class RileyLinkMedtronicService extends RileyLinkService {
|
||||||
// break;
|
// break;
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -385,62 +422,21 @@ public class RileyLinkMedtronicService extends RileyLinkService {
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// public void saveHistoryPage(int pagenumber, Page page) {
|
||||||
public void saveHistoryPage(int pagenumber, Page page) {
|
// if ((page == null) || (page.getRawData() == null)) {
|
||||||
if ((page == null) || (page.getRawData() == null)) {
|
// return;
|
||||||
return;
|
// }
|
||||||
}
|
// String filename = "history-" + pagenumber;
|
||||||
String filename = "history-" + pagenumber;
|
// FileOutputStream os;
|
||||||
FileOutputStream os;
|
// try {
|
||||||
try {
|
// os = openFileOutput(filename, Context.MODE_PRIVATE);
|
||||||
os = openFileOutput(filename, Context.MODE_PRIVATE);
|
// os.write(page.getRawData());
|
||||||
os.write(page.getRawData());
|
// os.close();
|
||||||
os.close();
|
// } catch (FileNotFoundException e) {
|
||||||
} catch (FileNotFoundException e) {
|
// e.printStackTrace();
|
||||||
e.printStackTrace();
|
// } catch (IOException e) {
|
||||||
} catch (IOException e) {
|
// e.printStackTrace();
|
||||||
e.printStackTrace();
|
// }
|
||||||
}
|
// }
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// PumpInterface - REMOVE
|
|
||||||
|
|
||||||
public boolean isInitialized() {
|
|
||||||
return RileyLinkServiceState.isReady(RileyLinkUtil.getRileyLinkServiceData().serviceState);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public boolean isSuspended() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public boolean isBusy() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public boolean isConnected() {
|
|
||||||
return RileyLinkServiceState.isReady(RileyLinkUtil.getRileyLinkServiceData().serviceState);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public boolean isConnecting() {
|
|
||||||
return !RileyLinkServiceState.isReady(RileyLinkUtil.getRileyLinkServiceData().serviceState);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// FIXME remove
|
|
||||||
public void sendNotification(ServiceNotification serviceNotification, Object o) {
|
|
||||||
LOG.warn("Send Notification has no implementation.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public class LocalBinder extends Binder {
|
|
||||||
|
|
||||||
public RileyLinkMedtronicService getServiceInstance() {
|
|
||||||
return RileyLinkMedtronicService.this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ public class MedtronicConst {
|
||||||
public static final String TBRsSet = StatsPrefix + "tbrs_set";
|
public static final String TBRsSet = StatsPrefix + "tbrs_set";
|
||||||
public static final String StandardBoluses = StatsPrefix + "std_boluses_delivered";
|
public static final String StandardBoluses = StatsPrefix + "std_boluses_delivered";
|
||||||
public static final String SMBBoluses = StatsPrefix + "smb_boluses_delivered";
|
public static final String SMBBoluses = StatsPrefix + "smb_boluses_delivered";
|
||||||
|
public static final String LastPumpHistoryEntry = StatsPrefix + "pump_history_entry";
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -318,8 +318,8 @@ public class MedtronicUtil extends RileyLinkUtil {
|
||||||
if (isEmptyFrame(frameData)) {
|
if (isEmptyFrame(frameData)) {
|
||||||
byte b = (byte)frame;
|
byte b = (byte)frame;
|
||||||
// b |= 0x80;
|
// b |= 0x80;
|
||||||
// b |= 0b1000_0000;
|
b |= 0b1000_0000;
|
||||||
b |= doneBit;
|
// b |= doneBit;
|
||||||
|
|
||||||
frameData.add(0, b);
|
frameData.add(0, b);
|
||||||
|
|
||||||
|
@ -349,8 +349,8 @@ public class MedtronicUtil extends RileyLinkUtil {
|
||||||
List<Byte> frameData = new ArrayList<>();
|
List<Byte> frameData = new ArrayList<>();
|
||||||
|
|
||||||
byte b = (byte)frame;
|
byte b = (byte)frame;
|
||||||
// b |= 0b1000_0000;
|
b |= 0b1000_0000;
|
||||||
b |= doneBit;
|
// b |= doneBit;
|
||||||
|
|
||||||
frameData.add(b);
|
frameData.add(b);
|
||||||
|
|
||||||
|
|
|
@ -1313,10 +1313,12 @@
|
||||||
<string name="medtronic_pump_status_active">Active</string>
|
<string name="medtronic_pump_status_active">Active</string>
|
||||||
<string name="medtronic_pump_status_sleeping">Sleeping</string>
|
<string name="medtronic_pump_status_sleeping">Sleeping</string>
|
||||||
|
|
||||||
<string name="medtronic_cmd_profile_not_set">Remote Basal profile setting is not supported. Please modify Basal profile on your pump manually.</string>
|
<!-- <string name="medtronic_cmd_profile_not_set">Remote Basal profile setting is not supported. Please modify Basal profile on your pump manually.</string> -->
|
||||||
<string name="medtronic_cmd_cancel_bolus_not_supported">Remote cancel of Bolus is not supported. If you wish to cancel bolus, go to pump put it in suspend and then resume. This will cancel the bolus.</string>
|
<string name="medtronic_cmd_cancel_bolus_not_supported">Remote cancel of Bolus is not supported. If you wish to cancel bolus, go to pump put it in suspend and then resume. This will cancel the bolus.</string>
|
||||||
<string name="medtronic_cmd_cant_read_tbr">Could not read current TBR.</string>
|
<string name="medtronic_cmd_cant_read_tbr">Could not read current TBR.</string>
|
||||||
<string name="medtronic_cmd_cant_cancel_tbr">Could not cancel current TBR. Stopping operation.</string>
|
<string name="medtronic_cmd_cant_cancel_tbr">Could not cancel current TBR. Stopping operation.</string>
|
||||||
|
<string name="medtronic_cmd_set_profile_pattern_overflow">Profile set failed, because following patterns, have too big basal rate: %1$s</string>
|
||||||
|
|
||||||
|
|
||||||
<string name="pump_no_connection_h">No connection for %1$d hour(s) %2$d min</string>
|
<string name="pump_no_connection_h">No connection for %1$d hour(s) %2$d min</string>
|
||||||
<string name="pump_no_connection_d">No connection for %1$d day(s) %2$d hours</string>
|
<string name="pump_no_connection_d">No connection for %1$d day(s) %2$d hours</string>
|
||||||
|
|
76
docs/MedtronicRoadmap.md
Normal file
76
docs/MedtronicRoadmap.md
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
#Medtronic AAPS Roadmap
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
## Phase 1: Create AAPS integration point
|
||||||
|
| Functionality | Done % |
|
||||||
|
| ----------------------------------------- |:------:|
|
||||||
|
| Plugin | 100 |
|
||||||
|
| Base configuration (no RileyLink Discovery) | 100 |
|
||||||
|
| Basic pump configuration | 100 |
|
||||||
|
| Display Fragment | 90 |
|
||||||
|
| Pump Configuration | 70 |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Phase 2: Implement commands within Roadtrip2 application
|
||||||
|
1. Base Bolus/Status commands
|
||||||
|
|
||||||
|
| Functionality | Done % |
|
||||||
|
| ----------------------------------------- |:------:|
|
||||||
|
| Set Bolus | 0 |
|
||||||
|
| Cancel Bolus | 0 |
|
||||||
|
| Get Status | 0 |
|
||||||
|
|
||||||
|
2. TBR
|
||||||
|
|
||||||
|
| Functionality | Done % |
|
||||||
|
| --- |:------:|
|
||||||
|
| Set TBR (Absolute) | 0 |
|
||||||
|
| Cancel TBR | 0 |
|
||||||
|
|
||||||
|
|
||||||
|
3. Basal Profile
|
||||||
|
|
||||||
|
| Functionality | Done % |
|
||||||
|
| ----------------------------------------- |:------:|
|
||||||
|
| Get Profile | 0 |
|
||||||
|
| Set Profile | 0 |
|
||||||
|
|
||||||
|
4. History
|
||||||
|
|
||||||
|
| Functionality | Done % |
|
||||||
|
| ----------------------------------------- |:------:|
|
||||||
|
| Get History | 0 |
|
||||||
|
| Decde History | 0 |
|
||||||
|
|
||||||
|
5. Extended Bolus
|
||||||
|
|
||||||
|
| Functionality | Done % |
|
||||||
|
| ----------------------------------------- |:------:|
|
||||||
|
| Set Extended Bolus | 0 |
|
||||||
|
| Cancel Extended Bolus | 0 |
|
||||||
|
|
||||||
|
|
||||||
|
6. Other commands
|
||||||
|
|
||||||
|
| Functionality | Done % |
|
||||||
|
| ----------------------------------------- |:------:|
|
||||||
|
| Load TDD | 0 |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
##Phase 3: Refactor RT2 code to split RileyLink and Medtronic parts
|
||||||
|
| Functionality | Done % |
|
||||||
|
| ----------------------------------------- |:------:|
|
||||||
|
| Refactor: Split RL and Medtronic code | 0 |
|
||||||
|
| Add RL code to AAPS | 0 |
|
||||||
|
| Start adding commands to AAPS | 0 |
|
||||||
|
| CMD: Base Bolus/Status commands | 0 |
|
||||||
|
| CMD: TBR | 0 |
|
||||||
|
| CMD: Basal Profile | 0 |
|
||||||
|
| CMD: History | 0 |
|
||||||
|
| CMD: Extended Bolus | 0 |
|
||||||
|
|
||||||
|
|
||||||
|
|
129
docs/MedtronicToDo.txt
Normal file
129
docs/MedtronicToDo.txt
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
RileyLinkAAPS
|
||||||
|
=============
|
||||||
|
|
||||||
|
|
||||||
|
Medtronic
|
||||||
|
- set basal profile (try to get to work - see new Loop code)
|
||||||
|
- read history
|
||||||
|
|
||||||
|
|
||||||
|
Bugs:
|
||||||
|
|
||||||
|
|
||||||
|
History: - parse pump history
|
||||||
|
|
||||||
|
|
||||||
|
Device search/scan refactor
|
||||||
|
|
||||||
|
|
||||||
|
CGMS [not in plan for now]:
|
||||||
|
- read history
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
AAPS-Medtronic
|
||||||
|
==============
|
||||||
|
|
||||||
|
|
||||||
|
- history: - integrate
|
||||||
|
- basal change code
|
||||||
|
- settings change code
|
||||||
|
|
||||||
|
- foolproofing: - bolus
|
||||||
|
- TBR
|
||||||
|
* connection (!!)
|
||||||
|
|
||||||
|
- bolus: - progress
|
||||||
|
- cancel handling
|
||||||
|
|
||||||
|
- What needs to be fixed: + button when clicked Refresh
|
||||||
|
x basal profile by 1/2 hour (not supported by AAPS)
|
||||||
|
|
||||||
|
* disconnect
|
||||||
|
|
||||||
|
- MedtronicUIResponseType integration
|
||||||
|
|
||||||
|
- add fabric code for following errors
|
||||||
|
|
||||||
|
- tuneup not working correctly
|
||||||
|
|
||||||
|
|
||||||
|
- BT Scan:
|
||||||
|
+ display mac on calling Preference
|
||||||
|
+ fix location problem
|
||||||
|
- fix start/stop of scan:
|
||||||
|
- remove toast button:
|
||||||
|
- Remove Refresh
|
||||||
|
- Add Scan Button
|
||||||
|
- Scan button is Stop button when search is running
|
||||||
|
- after change old error visible
|
||||||
|
|
||||||
|
* Refresh button toggling
|
||||||
|
|
||||||
|
|
||||||
|
Pages (RL Info):
|
||||||
|
- Base: - display firmware version
|
||||||
|
- display current data (it doesn't work ATM)
|
||||||
|
- RL history
|
||||||
|
- Device page: - Add Commands:
|
||||||
|
- get model
|
||||||
|
- tune (with report)
|
||||||
|
- get time
|
||||||
|
- get remaining insulin
|
||||||
|
- Add Statistics:
|
||||||
|
- Bolus,
|
||||||
|
- TBR
|
||||||
|
- Bolus SMB
|
||||||
|
|
||||||
|
- Pump Tab: - fix 3 buttons
|
||||||
|
|
||||||
|
==========================================================================================================
|
||||||
|
|
||||||
|
DONE - AAPS-Medtronic
|
||||||
|
=====================
|
||||||
|
|
||||||
|
+ status time
|
||||||
|
+ status all scheduling
|
||||||
|
+ status config
|
||||||
|
+ status power
|
||||||
|
|
||||||
|
+ Statistics in App:
|
||||||
|
+ Bolus,
|
||||||
|
+ TBR
|
||||||
|
+ Bolus SMB
|
||||||
|
|
||||||
|
+ status error display: + time
|
||||||
|
+ basal profile enabled
|
||||||
|
+ basal profile incorrect
|
||||||
|
+ tbr type set
|
||||||
|
+ max bolus
|
||||||
|
+ max basal
|
||||||
|
+ wrong pump selected
|
||||||
|
|
||||||
|
+ Basal refactoring: - basal is read when history says there was a change
|
||||||
|
+ merge: bugs (3)
|
||||||
|
+ Code to determine number of users:
|
||||||
|
|
||||||
|
|
||||||
|
DONE - RileyLinkAAPS
|
||||||
|
=====================
|
||||||
|
|
||||||
|
RileyLink
|
||||||
|
+ BT Disconnect and reconnect (change in RileyLinkBLE ??)
|
||||||
|
+ RL version
|
||||||
|
|
||||||
|
|
||||||
|
Medtronic
|
||||||
|
x extended bolus
|
||||||
|
|
||||||
|
|
||||||
|
Firmware Support:
|
||||||
|
+ version 2 support: + V1 works
|
||||||
|
+ V2 works
|
||||||
|
|
||||||
|
Bugs:
|
||||||
|
+ Bolus 554
|
||||||
|
+ TBR (554) ?
|
||||||
|
+ Remaining Insulin [554]
|
||||||
|
+ Get Basal profile returns just part of profile
|
||||||
|
+ Set TBR problem (after Bolus fixes)
|
Loading…
Reference in a new issue