    001/**
    002 * Copyright (c) 2014 Digi International Inc.,
    003 * All rights not expressly granted are reserved.
    004 *
    005 * This Source Code Form is subject to the terms of the Mozilla Public
    006 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
    007 * You can obtain one at http://mozilla.org/MPL/2.0/.
    008 *
    009 * Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343
    010 * =======================================================================
    011 */
    012package com.digi.xbee.api.packet;
    013
    014import java.io.ByteArrayOutputStream;
    015import java.io.IOException;
    016import java.util.LinkedHashMap;
    017
    018import org.slf4j.Logger;
    019import org.slf4j.LoggerFactory;
    020
    021import com.digi.xbee.api.utils.HexUtils;
    022
    023/**
    024 * This abstract class provides the basic structure of a ZigBee API frame.
    025 *
    026 * <p>Derived classes should implement their own methods to generate the API
    027 * data and frame ID in case they support it.</p>
    028 *
    029 * <p>Basic operations such as frame type retrieval are performed in this class.
    030 * </p>
    031 *
    032 * @see XBeePacket
    033 */
    034public abstract class XBeeAPIPacket extends XBeePacket {
    035
    036        // Constants.
    037        public final static int NO_FRAME_ID = 9999;
    038
    039        // Variables.
    040        protected int frameID = NO_FRAME_ID;
    041
    042        private APIFrameType frameType = null;
    043
    044        private int frameTypeValue;
    045
    046        private Logger logger;
    047
    048        /**
    049         * Class constructor. Instantiates a new {@code XBeeAPIPacket} object with
    050         * the given API frame type.
    051         *
    052         * @param frameType XBee packet frame type.
    053         *
    054         * @throws NullPointerException if {@code frameType == null}.
    055         *
    056         * @see APIFrameType
    057         */
    058        protected XBeeAPIPacket(APIFrameType frameType) {
    059                super();
    060
    061                if (frameType == null)
    062                        throw new NullPointerException("Frame type cannot be null.");
    063
    064                this.frameType = frameType;
    065                frameTypeValue = frameType.getValue();
    066
    067                this.logger = LoggerFactory.getLogger(XBeeAPIPacket.class);
    068        }
    069
    070        /**
    071         * Class constructor. Instantiates a new {@code XBeeAPIPacket} object with
    072         * the given frame type value.
    073         *
    074         * @param frameTypeValue XBee packet frame type integer value.
    075         *
    076         * @throws IllegalArgumentException if {@code frameTypeValue < 0} or
    077         *                                  if {@code frameTypeValue > 255}.
    078         */
    079        protected XBeeAPIPacket(int frameTypeValue) {
    080                super();
    081
    082                if (frameTypeValue < 0 || frameTypeValue > 255)
    083                        throw new IllegalArgumentException("Frame type value must be between 0 and 255.");
    084
    085                this.frameTypeValue = frameTypeValue;
    086                this.frameType = APIFrameType.get(frameTypeValue);
    087
    088                this.logger = LoggerFactory.getLogger(XBeeAPIPacket.class);
    089        }
    090
    091        /**
    092         * Returns the XBee packet frame type.
    093         *
    094         * @return The XBee packet frame type.
    095         *
    096         * @see APIFrameType
    097         */
    098        public APIFrameType getFrameType() {
    099                return frameType;
    100        }
    101
    102        /**
    103         * Returns the XBee packet frame type integer value.
    104         *
    105         * @return The XBee packet frame type integer value.
    106         */
    107        public int getFrameTypeValue() {
    108                return frameTypeValue;
    109        }
    110
    111        /*
    112         * (non-Javadoc)
    113         * @see com.digi.xbee.api.packet.XBeePacket#getPacketData()
    114         */
    115        @Override
    116        public byte[] getPacketData() {
    117                ByteArrayOutputStream data = new ByteArrayOutputStream();
    118
    119                data.write(frameTypeValue);
    120
    121                byte[] apiData = getAPIData();
    122                if (apiData == null)
    123                        apiData = new byte[0];
    124                if (apiData != null && apiData.length > 0) {
    125                        try {
    126                                data.write(apiData);
    127                        } catch (IOException e) {
    128                                logger.error(e.getMessage(), e);
    129                        }
    130                }
    131
    132                return data.toByteArray();
    133        }
    134
    135        /**
    136         * Returns the XBee API packet data.
    137         *
    138         * <p>This does not include the frame ID if it is needed.</p>
    139         *
    140         * @return The XBee API packet data.
    141         */
    142        public byte[] getAPIData() {
    143                ByteArrayOutputStream data = new ByteArrayOutputStream();
    144
    145                byte[] apiData = getAPIPacketSpecificData();
    146                if (apiData == null)
    147                        apiData = new byte[0];
    148
    149                if (needsAPIFrameID())
    150                        data.write(frameID);
    151
    152                if (apiData != null && apiData.length > 0) {
    153                        try {
    154                                data.write(apiData);
    155                        } catch (IOException e) {
    156                                logger.error(e.getMessage(), e);
    157                        }
    158                }
    159
    160                return data.toByteArray();
    161        }
    162
    163        /**
    164         * Returns the XBee API packet specific data.
    165         *
    166         * <p>This does not include the frame ID if it is needed.</p>
    167         *
    168         * @return The XBee API packet data.
    169         */
    170        protected abstract byte[] getAPIPacketSpecificData();
    171
    172        /**
    173         * Returns whether the API packet needs API Frame ID or not.
    174         *
    175         * @return {@code true} if the packet needs API Frame ID, {@code false}
    176         *         otherwise.
    177         */
    178        public abstract boolean needsAPIFrameID();
    179
    180        /**
    181         * Returns the Frame ID of the API packet.
    182         *
    183         * <p>If the frame ID is not configured or if the API packet does not need
    184         * a Frame ID ({@code if (!needsAPIFrameID())}) this method returns
    185         * {@code NO_FRAME_ID} ({@value #NO_FRAME_ID}).</p>
    186         *
    187         * @return The frame ID.
    188         *
    189         * @see #NO_FRAME_ID
    190         * @see #needsAPIFrameID()
    191         * @see #setFrameID(int)
    192         */
    193        public int getFrameID() {
    194                if (needsAPIFrameID())
    195                        return frameID;
    196                return NO_FRAME_ID;
    197        }
    198
    199        /**
    200         * Sets the frame ID of the API packet.
    201         *
    202         * <p>If the API packet does not need a frame ID
    203         * ({@code if (!needsAPIFrameID())}), this method does nothing.</p>
    204         *
    205         * @param frameID The frame ID to set.
    206         *
    207         * @throws IllegalArgumentException if {@code frameID < 0} or
    208         *                                  if {@code frameID > 255}.
    209         *
    210         * @see #getFrameID()
    211         */
    212        public void setFrameID(int frameID) {
    213                if (frameID < 0 || frameID > 255)
    214                        throw new IllegalArgumentException("Frame ID must be between 0 and 255.");
    215
    216                if (needsAPIFrameID())
    217                        this.frameID = frameID;
    218        }
    219
    220        /**
    221         * Returns whether or not the packet is a broadcast packet.
    222         *
    223         * @return {@code true} if the packet is a broadcast packet, {@code false}
    224         *         otherwise.
    225         */
    226        public boolean isBroadcast() {
    227                return false;
    228        }
    229
    230        /**
    231         * Returns whether the given ID is the current frame ID.
    232         *
    233         * @param id The frame id to check.
    234         *
    235         * @return {@code true} if frame ID is equal to the {@code id} provided,
    236         *         {@code false} otherwise or if the frame does not need an ID.
    237         *
    238         * @see #getFrameID()
    239         * @see #needsAPIFrameID()
    240         * @see #setFrameID(int)
    241         */
    242        public boolean checkFrameID(int id) {
    243                if (needsAPIFrameID() && getFrameID() == id)
    244                        return true;
    245                return false;
    246        }
    247
    248        /*
    249         * (non-Javadoc)
    250         * @see com.digi.xbee.api.packet.XBeePacket#getPacketParameters()
    251         */
    252        @Override
    253        protected LinkedHashMap<String, String> getPacketParameters() {
    254                LinkedHashMap<String, String> parameters = new LinkedHashMap<String, String>();
    255                if (getFrameType() != null)
    256                        parameters.put("Frame type", HexUtils.prettyHexString(HexUtils.integerToHexString(frameTypeValue, 1)) + " (" + getFrameType().getName() + ")");
    257                else
    258                        parameters.put("Frame type", HexUtils.prettyHexString(HexUtils.integerToHexString(frameTypeValue, 1)));
    259
    260                if (needsAPIFrameID()) {
    261                        if (frameID == NO_FRAME_ID)
    262                                parameters.put("Frame ID", "(NO FRAME ID)");
    263                        else
    264                                parameters.put("Frame ID", HexUtils.prettyHexString(HexUtils.integerToHexString(frameID, 1)) + " (" + frameID + ")");
    265                }
    266
    267                LinkedHashMap<String, String> apiParams = getAPIPacketParameters();
    268                if (apiParams != null)
    269                        parameters.putAll(apiParams);
    270                return parameters;
    271        }
    272
    273        /**
    274         * Returns a map with the XBee packet parameters and their values.
    275         *
    276         * @return A sorted map containing the XBee packet parameters with their
    277         *         values.
    278         */
    279        protected abstract LinkedHashMap<String, String> getAPIPacketParameters();
    280}