/***************************************************************** SPINE - Signal Processing In-Node Environment is a framework that allows dynamic on node configuration for feature extraction and a OtA protocol for the management for WSN Copyright (C) 2007 Telecom Italia S.p.A. � GNU Lesser General Public License � This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, version 2.1 of the License. � This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.� See the GNU Lesser General Public License for more details. � You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA� 02111-1307, USA. *****************************************************************/ /** * * Implementation for Android Message interface. * * Note that this class is only used internally at the framework. * * @author Alessia Salmeri * @author Scott Coleman * * @version 1.0 */ package t2.spine.communication.android; import net.tinyos.message.Message; import spine.Properties; import spine.SPINEPacketsConstants; import spine.SPINESupportedPlatforms; /** * This is the encapsulation of messages send to/from sensor nodes * for Android. * * @author scott.coleman * */ public class AndroidMessage extends Message { // private static final String TINYOS_URL_PREFIX = Properties.getDefaultProperties().getProperty(SPINESupportedPlatforms.TINYOS + "_" + Properties.URL_PREFIX_KEY); private static final String TINYOS_URL_PREFIX = Properties.getDefaultProperties().getProperty(SPINESupportedPlatforms.ANDROID + "_" + Properties.URL_PREFIX_KEY); private static final int DEFAULT_MESSAGE_SIZE = 0; // it represents a variable-size array and // does not check the corresponding array index private static final int AM_TYPE = SPINEPacketsConstants.AM_SPINE; // TODO: This will change back to 8 when we switch to real shimmer hardware // Currently we are using a bluetooth embedded controller to simulate sensors // these controllers don't currently send the AM Header protected static final int AM_HEADER_SIZE = 0; // protected static final int AM_HEADER_SIZE = 8; protected SPINEHeader header = null; protected byte[] payloadBuf = null; protected AndroidMessage() { super(DEFAULT_MESSAGE_SIZE); this.amTypeSet(AM_TYPE); } /** * Constructs an Android message with the given parameters * @param pktType * @param groupID * @param sourceID * @param destID * @param sequenceNumber * @param fragmentNr * @param totalFragments * @param payload */ protected AndroidMessage(byte pktType, byte groupID, int sourceID, int destID, byte sequenceNumber, byte fragmentNr, byte totalFragments, byte[] payload) { super(SPINEPacketsConstants.SPINE_HEADER_SIZE + payload.length); this.amTypeSet(AM_TYPE); this.header = new SPINEHeader(SPINEPacketsConstants.CURRENT_SPINE_VERSION, false, pktType, groupID, sourceID, destID, sequenceNumber, fragmentNr, totalFragments); this.payloadBuf = payload; byte[] msgBuf = new byte[SPINEPacketsConstants.SPINE_HEADER_SIZE + payload.length]; System.arraycopy(header.build(), 0, msgBuf, 0, SPINEPacketsConstants.SPINE_HEADER_SIZE); System.arraycopy(payload, 0, msgBuf, SPINEPacketsConstants.SPINE_HEADER_SIZE, payload.length); this.dataSet(msgBuf); } /** * Construct a SpineTOSMessage from a raw SERIAL ACTIVE MESSAGE from * the AndroidBTService (which looks like a Serial Forwarder to us). * * @param rawsfmessage raw message bytes from the AndroidBTService */ public static AndroidMessage Construct(byte[] rawsfmessage) { byte[] payload; SPINEHeader h; byte[] sh_bytes = new byte[SPINEPacketsConstants.SPINE_HEADER_SIZE]; System.arraycopy(rawsfmessage, AM_HEADER_SIZE, sh_bytes, 0, sh_bytes.length); try { h = new SPINEHeader(sh_bytes); } catch (IllegalSpineHeaderSizeException ishse) { return null; } payload = new byte[rawsfmessage.length - SPINEPacketsConstants.SPINE_HEADER_SIZE - AM_HEADER_SIZE]; System.arraycopy(rawsfmessage, AM_HEADER_SIZE+SPINEPacketsConstants.SPINE_HEADER_SIZE, payload, 0, payload.length); return new AndroidMessage(h.getPktType(), h.getGroupID(), h.getSourceID(), h.getDestID(), h.getSequenceNumber(), h.getFragmentNumber(), h.getTotalFragments(), payload); } /** * Gets the header associated with the current AndroidMessage * @return Current header * @throws IllegalSpineHeaderSizeException */ protected SPINEHeader getHeader() throws IllegalSpineHeaderSizeException { byte[] msgBuf = this.dataGet(); if (msgBuf.length < SPINEPacketsConstants.SPINE_HEADER_SIZE) throw new IllegalSpineHeaderSizeException(SPINEPacketsConstants.SPINE_HEADER_SIZE, msgBuf.length); byte[] headerBuf = new byte[SPINEPacketsConstants.SPINE_HEADER_SIZE]; System.arraycopy(msgBuf, 0, headerBuf, 0, SPINEPacketsConstants.SPINE_HEADER_SIZE); return new SPINEHeader(headerBuf); } /** * Returns the raw payload buffer for current AndroidMessage * @return byte array payload buffer */ protected byte[] getRawPayload() { if (this.payloadBuf == null) { this.payloadBuf = new byte[this.dataGet().length - SPINEPacketsConstants.SPINE_HEADER_SIZE]; System.arraycopy(this.dataGet(), SPINEPacketsConstants.SPINE_HEADER_SIZE, this.payloadBuf, 0, this.payloadBuf.length); } return this.payloadBuf; } /** * Sets the raw payload buffer for this AndroidMessage * @param payload Byte array of bayload buffer */ protected void setRawPayload(byte[] payload) { this.payloadBuf = payload; } /** * Parses the current AndroidMessage into a TOS message for * forwarding to the Spine server * * @return TOS formatted message * @throws IllegalSpineHeaderSizeException */ protected TOSMessage parse() throws IllegalSpineHeaderSizeException { TOSMessage msg = new TOSMessage(); byte[] msgBuf = this.dataGet(); byte[] headerBuf = new byte[SPINEPacketsConstants.SPINE_HEADER_SIZE]; System.arraycopy(msgBuf, 0, headerBuf, 0, SPINEPacketsConstants.SPINE_HEADER_SIZE); if(this.payloadBuf == null) { this.payloadBuf = new byte[msgBuf.length - SPINEPacketsConstants.SPINE_HEADER_SIZE]; System.arraycopy(msgBuf, SPINEPacketsConstants.SPINE_HEADER_SIZE, this.payloadBuf, 0, this.payloadBuf.length); } SPINEHeader header = new SPINEHeader(headerBuf); msg.setClusterId(header.getPktType()); msg.setProfileId(header.getGroupID()); msg.setSourceURL(TINYOS_URL_PREFIX + header.getSourceID()); msg.setDestinationURL(TINYOS_URL_PREFIX + header.getDestID()); msg.setSeqNo(header.getSequenceNumber()); short[] payloadBufShort = new short[this.payloadBuf.length]; for (int i = 0; i<this.payloadBuf.length; i++) payloadBufShort[i] = payloadBuf[i]; msg.setPayload(payloadBufShort); return msg; } /* (non-Javadoc) * @see java.lang.Object#toString() */ public String toString() { String s = null; try { s = getHeader() + " (hex: "; int len = 0; for (int i = 0; i<getHeader().getHeaderBuf().length; i++) { short b = getHeader().getHeaderBuf()[i]; if (b<0) b += 256; s += Integer.toHexString(b).toUpperCase() + " "; len++; } s += ") - Payload (hex) { "; //int len = 0; if(payloadBuf != null && payloadBuf.length > 0) for (int i = 0; i<payloadBuf.length; i++) { short b = payloadBuf[i]; if (b<0) b += 256; s += Integer.toHexString(b).toUpperCase() + " "; len++; } else if(this.dataGet() != null && this.dataGet().length > SPINEPacketsConstants.SPINE_HEADER_SIZE) for (int i = SPINEPacketsConstants.SPINE_HEADER_SIZE; i<this.dataGet().length; i++) { short b = this.dataGet()[i]; if (b<0) b += 256; s += Integer.toHexString(b).toUpperCase() + " "; len++; } else s += "empty payload "; s += "} [msg len=" + len + "]"; } catch (IllegalSpineHeaderSizeException e) { return e.getMessage(); } return s; } } /** * Class encapsulating a generic Spine header * * @author scott.coleman * */ class SPINEHeader { private byte headerBuf[] = new byte[SPINEPacketsConstants.SPINE_HEADER_SIZE]; private boolean canParse = false; private boolean canBuild = false; private byte vers; // 2 bits private boolean ext; // 1 bit private byte pktT; // 5 bits private byte grpID; // 8 bits private int srcID; // 16 bits private int dstID; // 16 bits private byte seqNr; // 8 bits private byte fragNr; // 8 bits private byte totFrags; // 8 bits protected SPINEHeader (byte version, boolean extension, byte pktType, byte groupID, int sourceID, int destID, byte sequenceNumber, byte fragmentNr, byte totalFragments) { this.vers = version; this.ext = extension; this.pktT = pktType; this.grpID = groupID; this.srcID = sourceID; this.dstID = destID; this.seqNr = sequenceNumber; this.fragNr = fragmentNr; this.totFrags = totalFragments; this.canBuild = true; } protected SPINEHeader(byte[] header) throws IllegalSpineHeaderSizeException { if (header.length != SPINEPacketsConstants.SPINE_HEADER_SIZE) throw new IllegalSpineHeaderSizeException(SPINEPacketsConstants.SPINE_HEADER_SIZE, header.length); else { this.headerBuf = header; this.canParse = true; parse(); } } /** * Builds a Spine header for the current AndroidMessage * @return Spine header for current AndroidMessage */ protected byte[] build() { if (!canBuild) return null; byte e = (this.ext)? (byte)1: (byte)0; headerBuf[0] = (byte)((this.vers<<6) | (e<<5) | this.pktT); headerBuf[1] = this.grpID; headerBuf[2] = (byte)(this.srcID>>8); headerBuf[3] = (byte)this.srcID; headerBuf[4] = (byte)(this.dstID>>8); headerBuf[5] = (byte)this.dstID; headerBuf[6] = this.seqNr; headerBuf[7] = this.fragNr; headerBuf[8] = this.totFrags; return headerBuf; } /** * Parses current header buffer into this classes members * @return */ private boolean parse() { if (!canParse) return false; vers = (byte)((headerBuf[0] & 0xC0)>>6); // 0xC0 = 11000000 binary ext = ((byte)((headerBuf[0] & 0x20)>>5) == 1); // 0x20 = 00100000 binary pktT = (byte)(headerBuf[0] & 0x1F); // 0x1F = 00011111 binary grpID = headerBuf[1]; srcID = headerBuf[2]; // check srcID = ((srcID<<8) | headerBuf[3]); dstID = headerBuf[4]; // check dstID = ((dstID<<8) | headerBuf[5]); seqNr = headerBuf[6]; fragNr = headerBuf[7]; totFrags = headerBuf[8]; return true; } protected byte getVersion() { return vers; } protected boolean isExtended() { return ext; } protected byte getPktType() { return pktT; } protected byte getGroupID() { return grpID; } protected int getSourceID() { return srcID; } protected int getDestID() { return dstID; } protected byte getSequenceNumber() { return seqNr; } protected byte getFragmentNumber() { return fragNr; } protected byte getTotalFragments() { return totFrags; } protected byte[] getHeaderBuf() { return headerBuf; } /* (non-Javadoc) * @see java.lang.Object#toString() */ public String toString() { String grp = (grpID<0)? Integer.toHexString(grpID+256): Integer.toHexString(grpID); String seq = (seqNr<0)? ""+(seqNr+256): ""+seqNr; String dst = (dstID==-1)? "BROADCAST": (dstID==0)? "BASESTATION": ""+dstID; String s = "Spine Header {"; s += "ver: 1." + vers + ", ext:" + ext + ", pktType:" + SPINEPacketsConstants.packetTypeToString(pktT).toUpperCase() + ", groupID:" + grp.toUpperCase() + ", srcID:" + srcID + ", dstID:" + dst + ", seqNr:" + seq + ", fragNr:" + fragNr + ", totFrags:" + totFrags + "}"; return s; } }