package de.uniluebeck.itm.wsn.drivers.pacemate; import com.google.inject.Inject; import com.google.inject.name.Named; import de.uniluebeck.itm.util.StringUtils; import de.uniluebeck.itm.wsn.drivers.core.Connection; import de.uniluebeck.itm.wsn.drivers.core.exception.InvalidChecksumException; import de.uniluebeck.itm.wsn.drivers.core.exception.TimeoutException; import de.uniluebeck.itm.wsn.drivers.core.exception.UnexpectedResponseException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; public class PacemateHelper { /** * This is the Start Address in the RAM to write data */ public static final long START_ADDRESS_IN_RAM = 1073742336; private static final Logger LOG = LoggerFactory.getLogger(PacemateHelper.class); private static final int TIMEOUT_WAIT_DATA_AVAILABLE = 2000; private static final int ASCII_CR = 13; private static final int ASCII_LF = 10; private static final int ASCII_ZERO = 48; private boolean echo = true; private final Connection connection; @Inject public PacemateHelper(Connection connection) { this.connection = connection; } public boolean isEcho() { return echo; } @Inject(optional = true) public void setEcho(@Named("pacemate.echo") boolean echo) { this.echo = echo; } public void sendBootLoaderMessage(byte[] message) throws IOException { // Allocate buffer for message + CR and LF byte[] data = new byte[message.length + 2]; // Copy message into the buffer System.arraycopy(message, 0, data, 0, message.length); // add CR and LF data[data.length - 2] = 0x0D; // <CR> data[data.length - 1] = 0x0A; // <LF> // Send message final OutputStream outputStream = connection.getOutputStream(); outputStream.write(data); outputStream.flush(); } public void clearStreamData() throws IOException { final InputStream inStream = connection.getInputStream(); // Allocate message buffer max 255 bytes to read byte[] message = new byte[255]; int index = 0; // Read the data boolean a = true; while ((inStream.available() > 0) && (a) && (index < 255)) { try { //System.out.println("************ Reading from stream"); message[index] = (byte) inStream.read(); //System.out.println("************ Done reading from stream"); } catch (IOException e) { LOG.error("" + e, e); } if (message[index] == -1) { a = false; } else { index++; } } } public void configureFlash(int start, int end) throws Exception { LOG.debug("Configuring flash from " + start + " to " + end + "..."); enableFlashErase(); // Send flash configure request sendBootLoaderMessage(Messages.flashConfigureRequestMessage(start, end)); // Read flash configure response receiveBootLoaderReplySuccess(Messages.CMD_SUCCESS); LOG.debug("Flash is configured"); } public void copyRAMToFlash(long flashAddress, long ramAddress, int length) throws Exception { // Send flash program request LOG.debug("Sending program request for address " + ramAddress + " with " + length + " bytes"); sendBootLoaderMessage(Messages.copyRAMToFlashRequestMessage(flashAddress, ramAddress, length)); // Read flash program response receiveBootLoaderReplySuccess(Messages.CMD_SUCCESS); LOG.debug("Copy Ram to Flash ok"); } public void eraseFlash(int start, int end) throws Exception { LOG.debug("Erasing sector from " + start + " to " + end + "..."); sendBootLoaderMessage(Messages.flashEraseRequestMessage(start, end)); receiveBootLoaderReplySuccess(Messages.CMD_SUCCESS); try { receiveBootLoaderReplySuccess(Messages.CMD_SUCCESS); } catch (TimeoutException e) { LOG.debug("one line erase response"); } LOG.debug("Flash erased"); } public void enableFlashErase() throws Exception { LOG.debug("Enabling Erase Flash..."); sendBootLoaderMessage(Messages.Unlock_RequestMessage()); receiveBootLoaderReplySuccess(Messages.CMD_SUCCESS); } /** * Receive the bsl reply message to all request messages with a success answer * * @param type * type of operation * * @return the response code as String * * @throws de.uniluebeck.itm.wsn.drivers.core.exception.TimeoutException * if a timeout occurs * @throws de.uniluebeck.itm.wsn.drivers.core.exception.UnexpectedResponseException * if an error occurs * @throws de.uniluebeck.itm.wsn.drivers.core.exception.InvalidChecksumException * if an error occurs * @throws java.io.IOException * if an error occurs * @throws java.lang.NullPointerException * if an error occurs */ protected String receiveBootLoaderReplySuccess(String type) throws TimeoutException, UnexpectedResponseException, InvalidChecksumException, IOException, NullPointerException { byte[] reply = echo ? readInputStream(3) : readInputStream(2); String replyStr = StringUtils.toASCIIString(reply); // split the lines from the response message String[] parts = replyStr.split("<CR><LF>"); for (final String part : parts) { LOG.trace("BL parts " + part); } // does the node echo all messages or not if (echo) { if (parts.length >= 2) { if (parts[1].compareTo("0") == 0) // 0 = everything is OK { if (parts.length >= 3) { return parts[2]; } else { return ""; } } } } else { if (parts.length >= 1) { if (parts[0].compareTo("0") == 0) // 0 = everything is OK { if (parts.length >= 1) { return parts[1]; } else { return ""; } } } } throw new UnexpectedResponseException("Error in response *" + replyStr + "*", -1, -1); } /** * Receive the BSL reply message for the autobaud / synchronize request * * @param type * type of operation * * @return the BSL reply message * * @throws de.uniluebeck.itm.wsn.drivers.core.exception.TimeoutException * if a timeout occurs * @throws de.uniluebeck.itm.wsn.drivers.core.exception.UnexpectedResponseException * if an error occurs * @throws de.uniluebeck.itm.wsn.drivers.core.exception.InvalidChecksumException * if an error occurs * @throws java.io.IOException * if an error occurs * @throws java.lang.NullPointerException * if an error occurs */ protected byte[] receiveBootLoaderReplySynchronized(String type) throws TimeoutException, UnexpectedResponseException, InvalidChecksumException, IOException, NullPointerException { byte[] reply; if (type.compareTo(Messages.SYNCHRONIZED) == 0) { reply = readInputStream(1); } else { reply = readInputStream(3); } String replyStr = StringUtils.toASCIIString(reply); if ((replyStr.compareTo("Synchronized<CR><LF>") == 0) || (replyStr.compareTo("Synchronized<CR><LF>OK<CR><LF>") == 0)) { return reply; } else if ((type.compareTo(Messages.SYNCHRONIZED_OK) == 0)) { return reply; } throw new UnexpectedResponseException("Wrong response " + StringUtils.toASCIIString(reply) + " and not " + type, -1, -1 ); } /** * Read the echo for a line of data * * @return the echo * * @throws de.uniluebeck.itm.wsn.drivers.core.exception.TimeoutException * if a timeout occurs * @throws de.uniluebeck.itm.wsn.drivers.core.exception.UnexpectedResponseException * if an error occurs * @throws de.uniluebeck.itm.wsn.drivers.core.exception.InvalidChecksumException * if an error occurs * @throws java.io.IOException * if an error occurs * @throws java.lang.NullPointerException * if an error occurs */ protected String receiveBootLoaderReplySendDataEcho() throws TimeoutException, UnexpectedResponseException, InvalidChecksumException, IOException, NullPointerException { byte[] reply; reply = readInputStream(1); return StringUtils.toASCIIString(reply); } /** * Read the requested line from the Flash * * @return requested line from the Flash * * @throws de.uniluebeck.itm.wsn.drivers.core.exception.TimeoutException * if a timeout occurs * @throws de.uniluebeck.itm.wsn.drivers.core.exception.UnexpectedResponseException * if an error occurs * @throws de.uniluebeck.itm.wsn.drivers.core.exception.InvalidChecksumException * if an error occurs * @throws java.io.IOException * if an error occurs * @throws java.lang.NullPointerException * if an error occurs */ protected byte[] receiveBootLoaderReplyReadData() throws TimeoutException, UnexpectedResponseException, InvalidChecksumException, IOException, NullPointerException { byte[] reply; if (this.echo) { reply = readInputStream(4); } else { reply = readInputStream(3); } int i = 0; if (this.echo) { for (i = 0; i < reply.length; i++) { if (reply[i] == 13) { // skip the echo and cr i = i + 2; // and lf as well break; } } } int len = (reply.length - (i + 5)); byte[] lineFromFlash = new byte[len]; if ((i + 3 < reply.length) && (reply[i] == 48)) { // copy the line and skip the answer and cr lf System.arraycopy(reply, i + 3, lineFromFlash, 0, len); //System.out.println(StringUtils.toASCIIString(lineFromFlash)); return lineFromFlash; } throw new UnexpectedResponseException("Error in response *" + StringUtils.toASCIIString(reply) + "*", -1, -1); } /** * Read the response to the CRC message * * @return the response * * @throws de.uniluebeck.itm.wsn.drivers.core.exception.TimeoutException * if a timeout occurs * @throws de.uniluebeck.itm.wsn.drivers.core.exception.UnexpectedResponseException * if an error occurs * @throws de.uniluebeck.itm.wsn.drivers.core.exception.InvalidChecksumException * if an error occurs * @throws java.io.IOException * if an error occurs * @throws java.lang.NullPointerException * if an error occurs */ protected byte[] receiveBootLoaderReplyReadCRCOK() throws TimeoutException, UnexpectedResponseException, InvalidChecksumException, IOException, NullPointerException { byte[] reply; if (this.echo) { reply = readInputStream(2); } else { reply = readInputStream(1); } String replyStr = StringUtils.toASCIIString(reply); // split the lines from the response message String[] parts = replyStr.split("<CR><LF>"); if (this.echo) { if (parts[1].compareTo(Messages.OK) == 0) { return reply; } else { LOG.debug("Received boot loader msg: " + replyStr); throw new InvalidChecksumException("Invalid checksum - resend " + replyStr); } } else { if (parts[0].compareTo(Messages.OK) == 0) { return reply; } else { LOG.debug("Received boot loader msg: " + replyStr); throw new InvalidChecksumException("Invalid checksum - resend " + replyStr); } } } /** * Read from the Input stream from the Pacemate. The length of the expected pacemate reply message is given with * the expected number of <cr><lf> chars. * * @param CRLFCount * expected number of lines * * @return lines from the input stream * * @throws de.uniluebeck.itm.wsn.drivers.core.exception.TimeoutException * if a timeout occurs * @throws java.io.IOException * if an error occurs */ private byte[] readInputStream(int CRLFCount) throws TimeoutException, IOException { final byte[] message = new byte[255]; int index = 0; int counter = 0; int wait = 5; connection.waitDataAvailable(TIMEOUT_WAIT_DATA_AVAILABLE); // Read the message - read CRLFCount lines of response final InputStream inStream = connection.getInputStream(); while ((index < 255) && (counter < CRLFCount)) { if (inStream.available() > 0) { message[index] = (byte) inStream.read(); if (message[index] == 0x0a) { counter++; } if (message[index] != -1) { index++; } } else { // message is smaller then expected // check if the last line was cr lf 0 cr lf == Success message without more infos if (index >= 5 && checkResponseMessage(message, index)) { break; } try { connection.waitDataAvailable(1000); } catch (final TimeoutException e) { // Do nothing } wait--; if (wait == 0) { final byte[] fullMessage = new byte[index]; System.arraycopy(message, 0, fullMessage, 0, index); throw new TimeoutException("Not a complete response message from the node *" + StringUtils .toASCIIString(fullMessage) + "*" ); } } } // copy to real length byte[] fullMessage = new byte[index]; System.arraycopy(message, 0, fullMessage, 0, index); LOG.trace("read lines " + StringUtils.toASCIIString(fullMessage)); return fullMessage; } /** * Check if the last received bytes were <cr><lf>0<cr><lf> == Success message without more * infos. * * @param message * the received message * @param index * the position in the message to check * * @return {@code true} if success, {@code false} otherwise */ private boolean checkResponseMessage(byte[] message, int index) { return (message[index - 5] == ASCII_CR) && (message[index - 4] == ASCII_LF) && (message[index - 3] == ASCII_ZERO) && (message[index - 2] == ASCII_CR) && (message[index - 1] == ASCII_LF); } protected void waitForBootLoader() throws IOException { try { // Send flash read request (in fact, this could be any valid message // to which the // device is supposed to respond) sendBootLoaderMessage(Messages.ReadPartIDRequestMessage()); receiveBootLoaderReplySuccess(Messages.CMD_SUCCESS); LOG.debug("Device connection established"); } catch (Exception error) { LOG.warn("Exception while waiting for connection", error); connection.clear(); throw new IOException(error); } } public boolean autobaud() { try { sendBootLoaderMessage(Messages.AutoBaudRequestMessage()); receiveBootLoaderReplySynchronized(Messages.SYNCHRONIZED); sendBootLoaderMessage(Messages.AutoBaudRequest2Message()); receiveBootLoaderReplySynchronized(Messages.SYNCHRONIZED); sendBootLoaderMessage(Messages.AutoBaudRequest3Message()); receiveBootLoaderReplySynchronized(Messages.SYNCHRONIZED_OK); LOG.debug("Autobaud"); } catch (TimeoutException to) { LOG.debug("Still waiting for a connection."); } catch (Exception error) { LOG.warn("Exception while waiting for connection", error); } return true; } public void writeToRAM(long address, int len) throws Exception { // Send flash program request // LOG.debug("Sending program request for address " + address + " with " + data.length + " bytes"); sendBootLoaderMessage(Messages.writeToRAMRequestMessage(address, len)); //System.out.println("send ready"); // Read flash program response receiveBootLoaderReplySuccess(Messages.CMD_SUCCESS); // LOG.debug("write to RAM ok"); } /** * Writes the byte array to the out stream * * @param dataMessage * the message to write * * @throws NullPointerException * if an error occurs * @throws InvalidChecksumException * if an error occurs * @throws UnexpectedResponseException * if an error occurs * @throws TimeoutException * if an error occurs */ public void sendDataMessage(byte[] dataMessage) throws IOException, TimeoutException, UnexpectedResponseException, InvalidChecksumException, NullPointerException { // Allocate buffer for message + CR and LF int array_length = dataMessage.length + 2; byte[] data = new byte[array_length]; // Copy message into the buffer System.arraycopy(dataMessage, 0, data, 0, dataMessage.length); // add CR and LF data[dataMessage.length] = 0x0D; // <CR> data[dataMessage.length + 1] = 0x0A; // <LF> // Print message // LOG.debug("Sending data msg: " + Tools.toASCIIString(data)); // Send message final OutputStream outputStream = connection.getOutputStream(); outputStream.write(data); outputStream.flush(); receiveBootLoaderReplySendDataEcho(); } public void sendChecksum(long CRC) throws IOException, TimeoutException, UnexpectedResponseException, InvalidChecksumException, NullPointerException { // LOG.debug("Send CRC after 20 Lines or end of Block"); sendBootLoaderMessage(Messages.writeCRCRequestMessage(CRC)); receiveBootLoaderReplyReadCRCOK(); } /** * Writes the CRC to the last two bytes of the flash * * @param crc * CRC value to write * * @return {@code true} if succeeded, {@code false} otherwise * * @throws java.lang.Exception * if an error occurs */ public boolean writeCRCtoFlash(int crc) throws Exception { byte crc_bytes[] = new byte[256]; for (int i = 0; i < 256; i++) { crc_bytes[i] = (byte) 0xff; } crc_bytes[254] = (byte) ((crc & 0xff00) >> 8); crc_bytes[255] = (byte) (crc & 0xff); LOG.trace("CRC = " + crc + " " + crc_bytes[254] + " " + crc_bytes[255]); try { configureFlash(14, 14); } catch (Exception e) { LOG.debug("Error while configure flash!"); return false; } try { eraseFlash(14, 14); } catch (Exception e) { LOG.debug("Error while erasing flash!"); return false; } try { writeToRAM(START_ADDRESS_IN_RAM, 256); } catch (Exception e) { LOG.debug("Error while write to RAM!"); return false; } int counter = 0; int crc_checksum = 0; byte[] line; // each block is sent in parts of 20 lines a 45 bytes while (counter < crc_bytes.length) { int offset = 0; if (counter + 45 < crc_bytes.length) { line = new byte[PacemateBinaryImage.LINESIZE]; // a line with 45 bytes System.arraycopy(crc_bytes, counter, line, 0, PacemateBinaryImage.LINESIZE); counter = counter + PacemateBinaryImage.LINESIZE; } else { if (((crc_bytes.length - counter) % 3) == 1) { offset = 2; } else if (((crc_bytes.length - counter) % 3) == 2) { offset = 1; } line = new byte[crc_bytes.length - counter + offset]; line[line.length - 1] = 0; line[line.length - 2] = 0; System.arraycopy(crc_bytes, counter, line, 0, crc_bytes.length - counter); counter = counter + (crc_bytes.length - counter); } for (int i = 0; i < line.length; i++) { crc_checksum = PacemateBinaryImage.calcCRCChecksum(crc_checksum, line[i]); } if (LOG.isDebugEnabled()) { LOG.debug("Sending data msg: " + StringUtils.toHexString(line)); } sendDataMessage(PacemateBinaryImage.encodeCRCData(line, line.length - offset)); } try { sendChecksum(crc_checksum); } catch (Exception e) { LOG.debug("Error while sending checksum for crc!"); return false; } // if block is completed copy data from RAM to Flash int crc_block_start = 0x3ff00; LOG.trace("Prepare Flash and Copy Ram to Flash 14 14 " + crc_block_start); try { configureFlash(14, 14); copyRAMToFlash(crc_block_start, START_ADDRESS_IN_RAM, 256); } catch (Exception e) { LOG.debug("Error while copy RAM to Flash!"); return false; } return true; } }