package info.nightscout.androidaps.plugins.pump.danaR; import android.bluetooth.BluetoothSocket; import android.os.SystemClock; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import info.nightscout.androidaps.logging.L; import info.nightscout.androidaps.plugins.pump.danaR.comm.MessageBase; import info.nightscout.androidaps.plugins.pump.danaR.comm.MessageHashTableBase; import info.nightscout.androidaps.plugins.pump.danaR.services.AbstractSerialIOThread; import info.nightscout.androidaps.utils.CRC; /** * Created by mike on 17.07.2016. */ public class SerialIOThread extends AbstractSerialIOThread { private static Logger log = LoggerFactory.getLogger(L.PUMPBTCOMM); private InputStream mInputStream = null; private OutputStream mOutputStream = null; private BluetoothSocket mRfCommSocket; private boolean mKeepRunning = true; private byte[] mReadBuff = new byte[0]; private MessageBase processedMessage; private MessageHashTableBase hashTable; public SerialIOThread(BluetoothSocket rfcommSocket, MessageHashTableBase hashTable) { super(); this.hashTable = hashTable; mRfCommSocket = rfcommSocket; try { mOutputStream = mRfCommSocket.getOutputStream(); mInputStream = mRfCommSocket.getInputStream(); } catch (IOException e) { log.error("Unhandled exception", e); } this.start(); } @Override public final void run() { try { while (mKeepRunning) { int availableBytes = mInputStream.available(); // Ask for 1024 byte (or more if available) byte[] newData = new byte[Math.max(1024, availableBytes)]; int gotBytes = mInputStream.read(newData); // When we are here there is some new data available appendToBuffer(newData, gotBytes); // process all messages we already got while (mReadBuff.length > 3) { // 3rd byte is packet size. continue only if we an determine packet size byte[] extractedBuff = cutMessageFromBuffer(); if (extractedBuff == null) break; // message is not complete in buffer (wrong packet calls disconnection) int command = (extractedBuff[5] & 0xFF) | ((extractedBuff[4] << 8) & 0xFF00); MessageBase message; if (processedMessage != null && processedMessage.getCommand() == command) { message = processedMessage; } else { // get it from hash table message = hashTable.findMessage(command); } if (L.isEnabled(L.PUMPBTCOMM)) log.debug("<<<<< " + message.getMessageName() + " " + MessageBase.toHexString(extractedBuff)); // process the message content message.received = true; message.handleMessage(extractedBuff); synchronized (message) { message.notify(); } } } } catch (Exception e) { if (!e.getMessage().contains("bt socket closed")) log.error("Thread exception: ", e); mKeepRunning = false; } disconnect("EndOfLoop"); } private void appendToBuffer(byte[] newData, int gotBytes) { // add newData to mReadBuff byte[] newReadBuff = new byte[mReadBuff.length + gotBytes]; System.arraycopy(mReadBuff, 0, newReadBuff, 0, mReadBuff.length); System.arraycopy(newData, 0, newReadBuff, mReadBuff.length, gotBytes); mReadBuff = newReadBuff; } private byte[] cutMessageFromBuffer() { if (mReadBuff[0] == (byte) 0x7E && mReadBuff[1] == (byte) 0x7E) { int length = (mReadBuff[2] & 0xFF) + 7; // Check if we have enough data if (mReadBuff.length < length) { return null; } if (mReadBuff[length - 2] != (byte) 0x2E || mReadBuff[length - 1] != (byte) 0x2E) { log.error("wrong packet lenght=" + length + " data " + MessageBase.toHexString(mReadBuff)); disconnect("wrong packet"); return null; } short crc = CRC.getCrc16(mReadBuff, 3, length - 7); byte crcByte0 = (byte) (crc >> 8 & 0xFF); byte crcByte1 = (byte) (crc & 0xFF); byte crcByte0received = mReadBuff[length - 4]; byte crcByte1received = mReadBuff[length - 3]; if (crcByte0 != crcByte0received || crcByte1 != crcByte1received) { log.error("CRC Error" + String.format("%02x ", crcByte0) + String.format("%02x ", crcByte1) + String.format("%02x ", crcByte0received) + String.format("%02x ", crcByte1received)); disconnect("crc error"); return null; } // Packet is verified here. extract data byte[] extractedBuff = new byte[length]; System.arraycopy(mReadBuff, 0, extractedBuff, 0, length); // remove extracted data from read buffer byte[] unprocessedData = new byte[mReadBuff.length - length]; System.arraycopy(mReadBuff, length, unprocessedData, 0, unprocessedData.length); mReadBuff = unprocessedData; return extractedBuff; } else { log.error("Wrong beginning of packet len=" + mReadBuff.length + " " + MessageBase.toHexString(mReadBuff)); disconnect("Wrong beginning of packet"); return null; } } @Override public synchronized void sendMessage(MessageBase message) { if (!mRfCommSocket.isConnected()) { log.error("Socket not connected on sendMessage"); return; } processedMessage = message; byte[] messageBytes = message.getRawMessageBytes(); if (L.isEnabled(L.PUMPBTCOMM)) log.debug(">>>>> " + message.getMessageName() + " " + MessageBase.toHexString(messageBytes)); try { mOutputStream.write(messageBytes); } catch (Exception e) { log.error("sendMessage write exception: ", e); } synchronized (message) { try { message.wait(5000); } catch (InterruptedException e) { log.error("sendMessage InterruptedException", e); } } SystemClock.sleep(200); if (!message.isReceived()) { message.handleMessageNotReceived(); if (L.isEnabled(L.PUMPBTCOMM)) log.error("Reply not received " + message.getMessageName()); if (message.getCommand() == 0xF0F1) { DanaRPump.getInstance().isNewPump = false; if (L.isEnabled(L.PUMPCOMM)) log.debug("Old firmware detected"); } } } @Override public void disconnect(String reason) { mKeepRunning = false; try { mInputStream.close(); } catch (Exception e) { if (L.isEnabled(L.PUMPBTCOMM)) log.debug(e.getMessage()); } try { mOutputStream.close(); } catch (Exception e) { if (L.isEnabled(L.PUMPBTCOMM)) log.debug(e.getMessage()); } try { mRfCommSocket.close(); } catch (Exception e) { if (L.isEnabled(L.PUMPBTCOMM)) log.debug(e.getMessage()); } try { System.runFinalization(); } catch (Exception e) { if (L.isEnabled(L.PUMPBTCOMM)) log.debug(e.getMessage()); } if (L.isEnabled(L.PUMPBTCOMM)) log.debug("Disconnected: " + reason); } }