package dk.silverbullet.telemed.device.monica; import android.content.Context; import android.util.Log; import dk.silverbullet.telemed.device.DeviceInitialisationException; import dk.silverbullet.telemed.device.DeviceNotFoundException; import dk.silverbullet.telemed.device.monica.packet.*; import dk.silverbullet.telemed.device.monica.packet.PatientStatusMessage.Status; import dk.silverbullet.telemed.questionnaire.R; import dk.silverbullet.telemed.questionnaire.node.monica.DeviceState; import dk.silverbullet.telemed.questionnaire.node.monica.MonicaDeviceCallback; import dk.silverbullet.telemed.utils.DataLogger; import dk.silverbullet.telemed.utils.Util; import java.io.IOException; import java.util.Date; public class MonicaDeviceController extends Thread implements MonicaDevice { private static final String TAG = "MonicaDeviceController"; private final MonicaDeviceCallback monicaCallback; private Context context; private MonicaBluetoothIOController btio; private boolean orange; private boolean white; private boolean green; private boolean black; private boolean yellow; private boolean abort; public MonicaDeviceController(MonicaDeviceCallback monicaCallback, Context context) throws DeviceInitialisationException { this.monicaCallback = monicaCallback; this.context = context; this.abort = false; try { monicaCallback.setState(DeviceState.WAITING_FOR_CONNECTION); btio = new MonicaBluetoothIOController(); start(); Log.d(TAG, "All is well!"); } catch (IOException e) { btio = null; throw new DeviceInitialisationException("Could not initialize connection to device", e); } catch (InterruptedException e) { btio = null; throw new DeviceInitialisationException("Could not initialize connection to device", e); } } @Override public void run() { try { btio.checkDevice(); monicaCallback.setDeviceIdString(btio.getMacAddress()); monicaCallback.setState(DeviceState.CHECKING_STARTING_CONDITION); checkStartState(); monicaCallback.setState(DeviceState.WAITING_FOR_DATA); collectData(); } catch (DeviceNotFoundException dne) { Log.w(TAG, dne); monicaCallback.abort(Util.getString(R.string.monica_device_not_found, context)); } catch (BatteryTooLowException btl) { Log.w(TAG, btl); monicaCallback.done(Util.getString(R.string.monica_battery_low, context)); } catch (DeviceSwitchedOffException off) { Log.w(TAG, off); monicaCallback.done(Util.getString(R.string.monica_switched_off, context)); } catch (UnknownFirmwareVersionException e) { Log.w(TAG, e); monicaCallback.abort(Util.getString(R.string.monica_unknown_firmware, context, e.getVersion())); } catch (DeviceInitialisationException e) { Log.w(TAG, e); monicaCallback.abort(Util.getString(R.string.monica_device_initialisation_error, context)); } catch (IOException e) { Log.w(TAG, e); monicaCallback.done(Util.getString(R.string.monica_communication_error, context)); } catch (InterruptedException e) { Log.w(TAG, e); monicaCallback.done(Util.getString(R.string.monica_reading_interrupted, context)); } catch (MonicaSamplesMissingException e) { Log.w(TAG, e); monicaCallback.done(Util.getString(R.string.monica_missing_sample, context)); } finally { monicaCallback.setState(DeviceState.CLOSING); try { checkEndState(); } catch (IOException ex) { Log.w("IOException while shutting down!", ex); } monicaCallback.setState(DeviceState.PROCESSING_DATA); monicaCallback.done(); close(); Log.d(TAG, "BTIO closed!"); } } private void collectData() throws InterruptedException, IOException, MonicaSamplesMissingException, DeviceSwitchedOffException { btio.clearReadQueue(); btio.writeMessage(MessageFactory.selectContinuousMode()); int count = 0; MessageProcessor mp = new MessageProcessor(monicaCallback); long lastMessageTime = System.currentTimeMillis(); while (count < monicaCallback.getSampleTimeMinutes() * 60 && !abort) { try { if (btio != null && btio.messagesWaiting() == 0) { if (count > 0 && System.currentTimeMillis() - lastMessageTime > 30000) { Log.d(TAG, "Read timeout - no connection for " + (System.currentTimeMillis() - lastMessageTime) + " ms"); mp.flushMessageBuffer(); break; } Date startTime = monicaCallback.getStartTimeValue(); long sampleTime = monicaCallback.getSampleTimeMinutes() * 60 * 1000; if (startTime != null && System.currentTimeMillis() >= startTime.getTime() + sampleTime + 20000) { Log.d(TAG, "Time is up - got all requested data (startTime: " + startTime + ")"); mp.flushMessageBuffer(); break; } sleep(500); } if (btio != null && btio.messagesWaiting() == 0 && count == 0) { Log.d(TAG, "*** Ping...."); btio.writeMessage(MessageFactory.downloadData(), 1, 3000); } MonicaMessage msg = null; if (btio != null && count == 0) msg = btio.readMessage(10000); else if(btio != null) msg = btio.readMessage(3000); if (msg != null && msg instanceof CBlockMessage) { count++; if (msg.getReadTime() == null) lastMessageTime = System.currentTimeMillis(); else lastMessageTime = msg.getReadTime().getTime(); mp.process((CBlockMessage) msg); } else if (msg != null && msg instanceof MmMessage) { mp.process((MmMessage) msg); } else if (msg != null && msg instanceof FetalHeightAndSignalToNoise) { mp.process((FetalHeightAndSignalToNoise) msg); } else if (msg != null && msg instanceof DeviceOffMessage) { btio.close(); btio = null; throw new DeviceSwitchedOffException(); } else if (msg != null && msg instanceof ImpedanceStatus) { // May never be received at this stage updateImpedanceStatus((ImpedanceStatus) msg); } else Log.i(TAG, "Unexpected message: " + msg); } catch (IOException ioe) { monicaCallback.setState(DeviceState.WAITING_FOR_CONNECTION); Log.d(TAG, "Got an IOException while reading!"); } } monicaCallback.setEndTimeValue(new Date()); if (btio != null) btio.writeMessage(MessageFactory.halt()); } private void checkStartState() throws IOException, InterruptedException, BatteryTooLowException, DeviceInitialisationException { Log.d(TAG, "checkStartState"); btio.clearReadQueue(); BatteryVoltageMessage voltage = btio.writeAndRead(MessageFactory.requestBatteryLevel(), BatteryVoltageMessage.class); while (voltage.getVoltage() < 0.01) { Log.d(TAG, "Bad battery voltage message: " + voltage); sleep(500); voltage = btio.writeAndRead(MessageFactory.requestBatteryLevel(), BatteryVoltageMessage.class); } Log.d(TAG, "Battery voltage: " + voltage); monicaCallback.setStartVoltage(voltage.getVoltage()); if (voltage.getVoltage() < 3.85) // Problems observed when voltage is below 3.85 V in firmware 2.1 throw new BatteryTooLowException(); PatientStatusMessage status = btio.writeAndRead(MessageFactory.setPatientStatus(Status.L_AND_D, true), PatientStatusMessage.class); if (status.getStatus() != Status.L_AND_D) { throw new UnexpectedPatientStatus(Status.L_AND_D, status); } // Check electrodes/data status... int okCount = 0; for (int i = 0; i < 60 && okCount < 5; i++) { MonicaMessage msg = btio.writeAndRead(MessageFactory.requestImpedanceStatus1(), MonicaMessage.class, 3, 20000); if (msg instanceof ImpedanceStatus) { ImpedanceStatus imp = (ImpedanceStatus) msg; Log.d(TAG, imp.toString()); updateImpedanceStatus(imp); if (black && green && white && orange && yellow) okCount++; else okCount = 0; if (okCount < 5 && btio.messagesWaiting() == 0) sleep(500); } else if (msg instanceof GotDataMessage) { // Sent without request..!! Log.d(TAG, "Attempting to delete unknown existing data..."); btio.writeMessage(MessageFactory.deleteExistingData()); sleep(500); } else { Log.d(TAG, "Ignoring: " + msg); } } if (okCount < 5) { throw new DeviceInitialisationException(""); } } private void updateImpedanceStatus(ImpedanceStatus imp) { Log.d(TAG, imp.toString()); orange = imp.isOrangeOK(); white = imp.isWhiteOK(); green = imp.isGreenOK(); black = imp.isBlackOK(); yellow = imp.isYellowOK(); monicaCallback.setProbeState(orange, white, green, black, yellow); } private void checkEndState() throws IOException { Log.d(TAG, "checkEndState"); if (btio != null) { btio.clearReadQueue(); BatteryVoltageMessage voltage = btio.writeAndRead(MessageFactory.requestBatteryLevel(), BatteryVoltageMessage.class); monicaCallback.setEndVoltage(voltage.getVoltage()); btio.writeMessage(MessageFactory.deleteDataAndSwitchOff()); btio.close(); } } @Override public void close() { DataLogger.close(); if (btio != null) { try { abort = true; interrupt(); join(10000); //Dont block for more than 10 seconds btio.clearReadQueue(); btio.writeMessage(MessageFactory.deleteDataAndSwitchOff()); btio.close(); btio = null; } catch (IOException ex) { // Ignore } catch (InterruptedException ex) { // Ignore } } } public String getDeviceName() { return btio.getDeviceName(); } }