package dk.silverbullet.telemed.device.continua.protocol; import java.io.IOException; import android.util.Log; import dk.silverbullet.telemed.device.continua.ContinuaPacketTag; import dk.silverbullet.telemed.device.continua.PacketParser; import dk.silverbullet.telemed.device.continua.packet.SystemId; import dk.silverbullet.telemed.device.continua.packet.input.AbortCommunicationPacket; import dk.silverbullet.telemed.device.continua.packet.input.AssociationReleaseRequestPacket; import dk.silverbullet.telemed.device.continua.packet.input.AssociationRequestPacket; import dk.silverbullet.telemed.device.continua.packet.input.ConfirmedMeasurementDataPacket; import dk.silverbullet.telemed.device.continua.packet.output.AssociationReleaseResponsePacket; import dk.silverbullet.telemed.device.continua.packet.output.AssociationResponsePacket; import dk.silverbullet.telemed.device.continua.packet.output.ConfirmedMeasurementResponsePacket; import dk.silverbullet.telemed.utils.Util; public abstract class ProtocolStateController<MeasurementType, ConfirmedMeasurementsType extends ConfirmedMeasurementDataPacket> implements PacketParser { private static final String TAG = Util.getTag(ProtocolStateController.class); private static final SystemId DUMMY_TABLET_SYSTEM_ID = new SystemId("12"); private static final int MAX_RESET_COUNT = 5; private int resetCount = 0; public static enum State { UNASSOCIATED, ASSOCIATED, MEASUREMENT_RECEIVED, DONE }; protected final ProtocolStateListener<MeasurementType> listener; protected State currentState = State.UNASSOCIATED; protected SystemId systemId; public ProtocolStateController(ProtocolStateListener<MeasurementType> listener) { this.listener = listener; } protected abstract ConfirmedMeasurementsType createConfirmedMeasurementsType(byte[] contents) throws IOException; protected abstract void handleConfirmedMeasurements(ConfirmedMeasurementsType confirmedMeasurements); protected abstract void handleAssociationReleaseRequest( AssociationReleaseRequestPacket associationReleaseRequestPacket); public void receive(AssociationRequestPacket associationRequest) { Log.d(TAG, "Received AssociationRequestPacket: " + associationRequest); if (currentState == State.DONE) { Log.w(TAG, "Ignored - in state done!"); } else if (currentState == State.UNASSOCIATED) { systemId = associationRequest.getSystemId(); try { listener.sendPacket(new AssociationResponsePacket(DUMMY_TABLET_SYSTEM_ID)); currentState = State.ASSOCIATED; } catch (IOException ex) { // Ignore! We're ins state UNASSOCIATED anyway! } } else { Log.e(TAG, "Unexpected protocol state (" + currentState + ") - resetting!"); resetProtocol(); } } public void receive(ConfirmedMeasurementsType confirmedMeasurements) { Log.d(TAG, "Received ConfirmedMeasurementData: " + confirmedMeasurements); if (currentState == State.DONE) { Log.w(TAG, "Ignored - in state done!"); } else if (currentState == State.ASSOCIATED) { handleConfirmedMeasurements(confirmedMeasurements); try { listener.sendPacket(new ConfirmedMeasurementResponsePacket(confirmedMeasurements.getInvokeId(), confirmedMeasurements.getEventType())); } catch (IOException ex) { currentState = State.UNASSOCIATED; } } else { Log.e(TAG, "Unexpected protocol state (" + currentState + ") - resetting!"); resetProtocol(); } } public void receive(AssociationReleaseRequestPacket associationReleaseRequestPacket) { Log.d(TAG, "Received AssociationReleaseRequestPacket: " + associationReleaseRequestPacket); if (currentState == State.DONE) { Log.w(TAG, "Ignored - in state done!"); } else if (currentState == State.ASSOCIATED || currentState == State.MEASUREMENT_RECEIVED) { handleAssociationReleaseRequest(associationReleaseRequestPacket); try { listener.sendPacket(new AssociationReleaseResponsePacket()); } catch (IOException e) { Log.w(TAG, "Could not send association release response", e); } currentState = State.DONE; listener.finishNow(); } else { Log.e(TAG, "Unexpected protocol state (" + currentState + ") - resetting!"); resetProtocol(); } } public void receive(AbortCommunicationPacket abortCommunication) { Log.d(TAG, "Received AbortCommunication: " + abortCommunication); resetProtocol(); } @Override public void errorReceived(IOException ex) { Log.e(TAG, "ProtocolStateController received an error", ex); resetProtocol(); } private void resetProtocol() { if (currentState == State.DONE) { return; } resetCount++; Log.w(TAG, "Protocol reset, count=" + resetCount); if (resetCount >= MAX_RESET_COUNT) { listener.tooManyRetries(); currentState = State.DONE; } else { currentState = State.UNASSOCIATED; } } @Override public void reset() { Log.d(TAG, "Received reset!"); currentState = State.UNASSOCIATED; resetCount = 0; } @Override public void handle(ContinuaPacketTag tag, byte[] contents) throws IOException { switch (tag) { case AARQ_APDU: receive(new AssociationRequestPacket(contents)); break; case PRST_APDU: receive(createConfirmedMeasurementsType(contents)); break; case ABRT_APDU: receive(new AbortCommunicationPacket(contents)); break; case RLRQ_APDU: receive(new AssociationReleaseRequestPacket(contents)); break; default: throw new IllegalStateException("Uknonwn packet tag: '" + tag + "'"); } } }