package org.reprap.comms.snap; import java.io.IOException; public class SNAPPacket { private final int offset_sync = 0; private final int offset_hdb2 = 1; private final int offset_hdb1 = 2; private final int offset_dab = 3; private final int offset_sab = 4; private final int offset_payload = 5; private final byte syncMarker = 0x54; private final int maxSize = 64; // Full raw packet contents including all headers private byte [] buffer; private int receiveLength = 0; private boolean complete = false; public SNAPPacket() { buffer = new byte[maxSize]; } public SNAPPacket(SNAPAddress srcAddress, SNAPAddress destAddress, byte [] payload) { buffer = new byte[payload.length + offset_payload + 1]; buffer[offset_sync] = syncMarker; buffer[offset_hdb2] = 0x51; buffer[offset_hdb1] = 0x30; buffer[offset_dab] = (byte)destAddress.getAddress(); buffer[offset_sab] = (byte)srcAddress.getAddress(); setLength(payload.length); for(int i = 0; i < payload.length; i++) buffer[i + offset_payload] = payload[i]; generateChecksum(); complete = true; } private void generateChecksum() { int length = getLength() + offset_payload; SNAPChecksum crc = new SNAPChecksum(); for(int i = 1; i < length; i++) crc.addData(buffer[i]); buffer[length] = crc.getResult(); } public byte getPacketType() { return buffer[0]; // TODO fix offset } public byte [] getPayload() { int length = getLength(); byte [] payload = new byte[length]; for(int i = 0; i < length; i++) payload[i] = buffer[i + offset_payload]; return payload; } public byte [] getRawData() { return buffer; } /** * * @param data * @return true is the packet is now complete, otherwise false * @throws IOException */ public boolean receiveByte(byte data) throws IOException { if (complete) throw new IOException("Received data beyond end of packet"); if (receiveLength >= maxSize) throw new IOException("Received too much data"); buffer[receiveLength++] = data; if (receiveLength > 4) { int expectedLength = getLength() + offset_payload + 1; if (receiveLength >= expectedLength) return true; } return false; } public boolean validate() { if (receiveLength < offset_payload) return false; int expectedLength = getLength() + offset_payload + 1; if (receiveLength != expectedLength) return false; SNAPChecksum crc = new SNAPChecksum(); for(int i = offset_hdb2; i < receiveLength - 1; i++) crc.addData(buffer[i]); byte expectedCRC = buffer[receiveLength - 1]; return crc.getResult() == expectedCRC; } public SNAPAddress getSourceAddress() { return new SNAPAddress(buffer[offset_sab]); } public SNAPAddress getDestinationAddress() { return new SNAPAddress(buffer[offset_dab]); } private void setLength(int length) { buffer[offset_hdb1] = (byte)((buffer[offset_hdb1] & 0xf0) | (length > 7 ? 8 : length)); } public int getLength() { int l = buffer[offset_hdb1] & 0x0f; if ((l & 8) != 0) return 8 << (l & 7); return l; } public SNAPPacket generateNAK() { SNAPPacket resp = new SNAPPacket(getDestinationAddress(), getSourceAddress(), new byte [] {}); resp.buffer[offset_hdb2] = (byte)((resp.buffer[offset_hdb2] & 0xfc) | 3); resp.generateChecksum(); return resp; } public SNAPPacket generateACK() { SNAPPacket resp = new SNAPPacket(getDestinationAddress(), getSourceAddress(), new byte [] {}); resp.buffer[offset_hdb2] = (byte)((resp.buffer[offset_hdb2] & 0xfc) | 2); resp.generateChecksum(); return resp; } /** * @return true if the packet represents an ACK */ public boolean isAck() { return ((buffer[offset_hdb2] & 3) == 2); } /** * @return true if the packet represents a NAK */ public boolean isNak() { return ((buffer[offset_hdb2] & 3) == 3); } }