All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.rapplogic.xbee.api.PacketParser Maven / Gradle / Ivy

/**
 * Copyright (c) 2008 Andrew Rapp. All rights reserved.
 *  
 * This file is part of XBee-API.
 *  
 * XBee-API is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *  
 * XBee-API 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 General Public License for more details.
 *  
 * You should have received a copy of the GNU General Public License
 * along with XBee-API.  If not, see .
 */

package com.rapplogic.xbee.api;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;

import org.apache.log4j.Logger;

import com.rapplogic.xbee.api.wpan.RxResponse16;
import com.rapplogic.xbee.api.wpan.RxResponse64;
import com.rapplogic.xbee.api.wpan.RxResponseIoSample;
import com.rapplogic.xbee.api.wpan.TxStatusResponse;
import com.rapplogic.xbee.api.zigbee.ZNetExplicitRxResponse;
import com.rapplogic.xbee.api.zigbee.ZNetNodeIdentificationResponse;
import com.rapplogic.xbee.api.zigbee.ZNetRxIoSampleResponse;
import com.rapplogic.xbee.api.zigbee.ZNetRxResponse;
import com.rapplogic.xbee.api.zigbee.ZNetTxStatusResponse;
import com.rapplogic.xbee.util.ByteUtils;
import com.rapplogic.xbee.util.IIntInputStream;
import com.rapplogic.xbee.util.InputStreamWrapper;
import com.rapplogic.xbee.util.IntArrayOutputStream;

/**
 * Reads a packet from the input stream, verifies checksum and creates an XBeeResponse object
 * 

* Notes: *

* Escaped bytes increase packet length but packet stated length only indicates un-escaped bytes. * Stated length includes all bytes after Length bytes, not including the checksum *

* @author Andrew Rapp * */ public class PacketParser implements IIntInputStream, IPacketParser { private final static Logger log = Logger.getLogger(PacketParser.class); private IIntInputStream in; // size of packet after special bytes have been escaped private XBeePacketLength length; private Checksum checksum = new Checksum(); private boolean done = false; private int bytesRead; private int escapeBytes; private XBeeResponse response; private ApiId apiId; private int intApiId; private static Map> handlerMap = new HashMap>(); // TODO reuse this object for all packets // experiment to preserve original byte array for transfer over network (Starts with length) private IntArrayOutputStream rawBytes = new IntArrayOutputStream(); static { // TODO put all response handlers in specific packet and load all. implement static handlesApi method handlerMap.put(ApiId.AT_RESPONSE.getValue(), AtCommandResponse.class); handlerMap.put(ApiId.MODEM_STATUS_RESPONSE.getValue(), ModemStatusResponse.class); handlerMap.put(ApiId.REMOTE_AT_RESPONSE.getValue(), RemoteAtResponse.class); handlerMap.put(ApiId.RX_16_IO_RESPONSE.getValue(), RxResponseIoSample.class); handlerMap.put(ApiId.RX_64_IO_RESPONSE.getValue(), RxResponseIoSample.class); handlerMap.put(ApiId.RX_16_RESPONSE.getValue(), RxResponse16.class); handlerMap.put(ApiId.RX_64_RESPONSE.getValue(), RxResponse64.class); handlerMap.put(ApiId.TX_STATUS_RESPONSE.getValue(), TxStatusResponse.class); handlerMap.put(ApiId.ZNET_EXPLICIT_RX_RESPONSE.getValue(), ZNetExplicitRxResponse.class); handlerMap.put(ApiId.ZNET_IO_NODE_IDENTIFIER_RESPONSE.getValue(), ZNetNodeIdentificationResponse.class); handlerMap.put(ApiId.ZNET_IO_SAMPLE_RESPONSE.getValue(), ZNetRxIoSampleResponse.class); handlerMap.put(ApiId.ZNET_RX_RESPONSE.getValue(), ZNetRxResponse.class); handlerMap.put(ApiId.ZNET_TX_STATUS_RESPONSE.getValue(), ZNetTxStatusResponse.class); } static void registerResponseHandler(int apiId, Class clazz) { if (handlerMap.get(apiId) == null) { log.info("Registering response handler " + clazz.getCanonicalName() + " for apiId: " + apiId); } else { log.warn("Overriding existing implementation: " + handlerMap.get(apiId).getCanonicalName() + ", with " + clazz.getCanonicalName() + " for apiId: " + apiId); } handlerMap.put(apiId, clazz); } static void unRegisterResponseHandler(int apiId) { if (handlerMap.get(apiId) != null) { log.info("Unregistering response handler " + handlerMap.get(apiId).getCanonicalName() + " for apiId: " + apiId); handlerMap.remove(apiId); } else { throw new IllegalArgumentException("No response handler for: " + apiId); } } public PacketParser(InputStream in) { this.in = new InputStreamWrapper(in); } // for parsing a packet from a byte array public PacketParser(IIntInputStream in) { this.in = in; } /** * This method is guaranteed (unless I screwed up) to return an instance of XBeeResponse and should never throw an exception * If an exception occurs, it will be packaged and returned as an ErrorResponse. * * @return */ public XBeeResponse parsePacket() { Exception exception = null; try { // BTW, length doesn't account for escaped bytes int msbLength = this.read("Length MSB"); int lsbLength = this.read("Length LSB"); // length of api structure, starting here (not including start byte or length bytes, or checksum) this.length = new XBeePacketLength(msbLength, lsbLength); log.debug("packet length is " + String.format("[0x%03X]", length.getLength())); // total packet length = stated length + 1 start byte + 1 checksum byte + 2 length bytes intApiId = this.read("API ID"); this.apiId = ApiId.get(intApiId); if (apiId == null) { this.apiId = ApiId.UNKNOWN; } log.info("Handling ApiId: " + apiId); // TODO parse I/O data page 12. 82 API Identifier Byte for 64 bit address A/D data (83 is for 16bit A/D data) for (Integer handlerApiId : handlerMap.keySet()) { if (intApiId == handlerApiId) { log.debug("Found response handler for apiId [" + ByteUtils.toBase16(intApiId) + "]: " + handlerMap.get(handlerApiId).getCanonicalName()); response = (XBeeResponse) handlerMap.get(handlerApiId).newInstance(); response.parse(this); break; } } if (response == null) { log.info("Did not find a response handler for ApiId [" + ByteUtils.toBase16(intApiId) + "]. Returning GenericResponse"); response = new GenericResponse(); response.parse(this); } response.setChecksum(this.read("Checksum")); if (!this.isDone()) { throw new XBeeParseException("There are remaining bytes according to stated packet length but we have read all the bytes we thought were required for this packet (if that makes sense)"); } response.finish(); } catch (Exception e) { // added bytes read for troubleshooting log.error("Failed due to exception. Returning ErrorResponse. bytes read: " + ByteUtils.toBase16(rawBytes.getIntArray()), e); exception = e; response = new ErrorResponse(); ((ErrorResponse)response).setErrorMsg(exception.getMessage()); // but this isn't ((ErrorResponse)response).setException(e); } if (response != null) { response.setLength(length); response.setApiId(apiId); // preserve original byte array for transfer over networks response.setRawPacketBytes(rawBytes.getIntArray()); } return response; } /** * Same as read() but logs the context of the byte being read. useful for debugging */ public int read(String context) throws IOException { int b = this.read(); log.debug("Read " + context + " byte, val is " + ByteUtils.formatByte(b)); return b; } /** * This method should only be called by read() * * @throws IOException */ private int readFromStream() throws IOException { int b = in.read(); // save raw bytes to transfer via network rawBytes.write(b); return b; } /** * This method reads bytes from the underlying input stream and performs the following tasks: * 1. Keeps track of how many bytes we've read * 2. Un-escapes bytes if necessary and verifies the checksum. */ public int read() throws IOException { if (done) { throw new XBeeParseException("Packet has read all of its bytes"); } int b = this.readFromStream(); if (b == -1) { throw new XBeeParseException("Read -1 from input stream while reading packet!"); } if (XBeePacket.isSpecialByte(b)) { log.debug("Read special byte that needs to be unescaped"); if (b == XBeePacket.SpecialByte.ESCAPE.getValue()) { log.debug("found escape byte"); // read next byte b = this.readFromStream(); log.debug("next byte is " + ByteUtils.formatByte(b)); b = 0x20 ^ b; log.debug("unescaped (xor) byte is " + ByteUtils.formatByte(b)); escapeBytes++; } else { // TODO some responses such as AT Response for node discover do not escape the bytes?? shouldn't occur if AP mode is 2? // while reading remote at response Found unescaped special byte base10=19,base16=0x13,base2=00010011 at position 5 log.warn("Found unescaped special byte " + ByteUtils.formatByte(b) + " at position " + bytesRead); } } bytesRead++; // do this only after reading length bytes if (bytesRead > 2) { // when verifying checksum you must add the checksum that we are verifying // checksum should only include unescaped bytes!!!! // when computing checksum, do not include start byte, length, or checksum; when verifying, include checksum checksum.addByte(b); log.debug("Read byte " + ByteUtils.formatByte(b) + " at position " + bytesRead + ", packet length is " + this.length.get16BitValue() + ", #escapeBytes is " + escapeBytes + ", remaining bytes is " + this.getRemainingBytes()); // escape bytes are not included in the stated packet length if (this.getFrameDataBytesRead() >= (length.get16BitValue() + 1)) { // this is checksum and final byte of packet done = true; log.debug("Checksum byte is " + b); if (!checksum.verify()) { throw new XBeeParseException("Checksum is incorrect. Expected 0xff, but got " + checksum.getChecksum()); } } } return b; } /** * Reads all remaining bytes except for checksum * @return * @throws IOException */ public int[] readRemainingBytes() throws IOException { // minus one since we don't read the checksum int[] value = new int[this.getRemainingBytes() - 1]; log.debug("There are " + value.length + " remaining bytes"); for (int i = 0; i < value.length; i++) { value[i] = this.read("Remaining bytes " + i); } return value; } public XBeeAddress64 parseAddress64() throws IOException { XBeeAddress64 addr = new XBeeAddress64(); for (int i = 0; i < 8; i++) { addr.getAddress()[i] = this.read("64-bit Address byte " + i); } return addr; } public XBeeAddress16 parseAddress16() throws IOException { XBeeAddress16 addr16 = new XBeeAddress16(); addr16.setMsb(this.read("Address 16 MSB")); addr16.setLsb(this.read("Address 16 LSB")); return addr16; } /** * Returns number of bytes remaining, relative to the stated packet length (not including checksum). * @return */ public int getFrameDataBytesRead() { // subtract out the 2 length bytes return this.getBytesRead() - 2; } /** * Number of bytes remaining to be read, including the checksum * @return */ public int getRemainingBytes() { // add one for checksum byte (not included) in packet length return this.length.get16BitValue() - this.getFrameDataBytesRead() + 1; } // get unescaped packet length // get escaped packet length /** * Does not include any escape bytes * @return */ public int getBytesRead() { return bytesRead; } public void setBytesRead(int bytesRead) { this.bytesRead = bytesRead; } public boolean isDone() { return done; } public void setDone(boolean done) { this.done = done; } public int getChecksum() { return checksum.getChecksum(); } public XBeePacketLength getLength() { return length; } public ApiId getApiId() { return apiId; } public int getIntApiId() { return this.intApiId; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy