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.common;
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.models.XBee16BitAddress;
022import com.digi.xbee.api.models.XBeeDiscoveryStatus;
023import com.digi.xbee.api.models.XBeeTransmitStatus;
024import com.digi.xbee.api.packet.XBeeAPIPacket;
025import com.digi.xbee.api.packet.APIFrameType;
026import com.digi.xbee.api.utils.HexUtils;
027
028/**
029 * This class represents a Transmit Status Packet. Packet is built using the 
030 * parameters of the constructor or providing a valid API payload.
031 * 
032 * <p>When a Transmit Request is completed, the module sends a Transmit Status 
033 * message. This message will indicate if the packet was transmitted 
034 * successfully or if there was a failure.</p>
035 * 
036 * <p>This packet is the response to standard and explicit transmit requests.
037 * </p>
038 * 
039 * @see TransmitPacket
040 */
041public class TransmitStatusPacket extends XBeeAPIPacket {
042        
043        // Constants.
044        private static final int MIN_API_PAYLOAD_LENGTH = 7; // 1 (Frame type) + 1 (frame ID) + 2 (16-bit address) + 1 (retry count) + 1 (delivery status) + 1 (discovery status)
045                
046        // Variables.
047        private final XBee16BitAddress destAddress16;
048        
049        private final int tranmistRetryCount;
050        private final XBeeTransmitStatus transmitStatus;
051        private final XBeeDiscoveryStatus discoveryStatus;
052        
053        private Logger logger;
054        
055        /**
056         * Creates a new {@code TransmitStatusPacket} object from the given payload.
057         * 
058         * @param payload The API frame payload. It must start with the frame type 
059         *                corresponding to a Transmit Status packet ({@code 0x8B}).
060         *                The byte array must be in {@code OperatingMode.API} mode.
061         * 
062         * @return Parsed Transmit Status packet.
063         * 
064         * @throws IllegalArgumentException if {@code payload[0] != APIFrameType.TRANSMIT_STATUS.getValue()} or
065         *                                  if {@code payload.length < }{@value #MIN_API_PAYLOAD_LENGTH} or
066         *                                  if {@code frameID < 0} or
067         *                                  if {@code frameID > 255} or
068         *                                  if {@code tranmistRetryCount < 0} or
069         *                                  if {@code tranmistRetryCount > 255}.
070         * @throws NullPointerException if {@code payload == null}.
071         */
072        public static TransmitStatusPacket createPacket(byte[] payload) {
073                if (payload == null)
074                        throw new NullPointerException("Transmit Status packet payload cannot be null.");
075                
076                // 1 (Frame type) + 1 (frame ID) + 2 (16-bit address) + 1 (retry count) + 1 (delivery status) + 1 (discovery status)
077                if (payload.length < MIN_API_PAYLOAD_LENGTH)
078                        throw new IllegalArgumentException("Incomplete Transmit Status packet.");
079                
080                if ((payload[0] & 0xFF) != APIFrameType.TRANSMIT_STATUS.getValue())
081                        throw new IllegalArgumentException("Payload is not a Transmit Status packet.");
082                
083                // payload[0] is the frame type.
084                int index = 1;
085                
086                // Frame ID byte.
087                int frameID = payload[index] & 0xFF;
088                index = index + 1;
089                
090                // 2 bytes of 16-bit address.
091                XBee16BitAddress address = new XBee16BitAddress(payload[index] & 0xFF, payload[index + 1] & 0xFF);
092                index = index + 2;
093                
094                // Retry count byte.
095                int retryCount = payload[index] & 0xFF;
096                index = index + 1;
097                
098                // Delivery status byte.
099                int deliveryStatus = payload[index] & 0xFF;
100                index = index + 1;
101                
102                // Discovery status byte.
103                int discoveryStatus = payload[index] & 0xFF;
104                
105                // TODO if XBeeTransmitStatus is unknown????
106                return new TransmitStatusPacket(frameID, address, retryCount, 
107                                XBeeTransmitStatus.get(deliveryStatus), XBeeDiscoveryStatus.get(discoveryStatus));
108        }
109        
110        /**
111         * Class constructor. Instantiates a new {@code TransmitStatusPacket} 
112         * object with the given parameters.
113         * 
114         * @param frameID Frame ID.
115         * @param destAddress16 16-bit Network address the packet was delivered to.
116         * @param tranmistRetryCount The number of application transmission retries 
117         *                           that took place.
118         * @param transmitStatus Transmit status.
119         * @param discoveryStatus Discovery status.
120         * 
121         * @throws IllegalArgumentException if {@code frameID < 0} or
122         *                                  if {@code frameID > 255} or
123         *                                  if {@code tranmistRetryCount < 0} or
124         *                                  if {@code tranmistRetryCount > 255}.
125         * @throws NullPointerException if {@code destAddress16 == null} or
126         *                              if {@code transmitStatus == null} or
127         *                              if {@code discoveryStatus == null}.
128         * 
129         * @see com.digi.xbee.api.models.XBeeDiscoveryStatus
130         * @see com.digi.xbee.api.models.XBeeTransmitStatus
131         * @see com.digi.xbee.api.models.XBee16BitAddress
132         */
133        public TransmitStatusPacket(int frameID, XBee16BitAddress destAddress16, int tranmistRetryCount, 
134                        XBeeTransmitStatus transmitStatus, XBeeDiscoveryStatus discoveryStatus) {
135                super(APIFrameType.TRANSMIT_STATUS);
136                
137                if (destAddress16 == null)
138                        throw new NullPointerException("16-bit destination address cannot be null.");
139                if (transmitStatus == null)
140                        throw new NullPointerException("Delivery status cannot be null.");
141                if (discoveryStatus == null)
142                        throw new NullPointerException("Discovery status cannot be null.");
143                if (frameID < 0 || frameID > 255)
144                        throw new IllegalArgumentException("Frame ID must be between 0 and 255.");
145                if (tranmistRetryCount < 0 || tranmistRetryCount > 255)
146                        throw new IllegalArgumentException("Transmit retry count must be between 0 and 255.");
147                
148                this.frameID = frameID;
149                this.destAddress16 = destAddress16;
150                this.tranmistRetryCount = tranmistRetryCount;
151                this.transmitStatus = transmitStatus;
152                this.discoveryStatus = discoveryStatus;
153                this.logger = LoggerFactory.getLogger(TransmitStatusPacket.class);
154        }
155
156        /*
157         * (non-Javadoc)
158         * @see com.digi.xbee.api.packet.XBeeAPIPacket#getAPIPacketSpecificData()
159         */
160        @Override
161        protected byte[] getAPIPacketSpecificData() {
162                ByteArrayOutputStream data = new ByteArrayOutputStream();
163                try {
164                        data.write(destAddress16.getValue());
165                        data.write(tranmistRetryCount);
166                        data.write(transmitStatus.getId());
167                        data.write(discoveryStatus.getId());
168                } catch (IOException e) {
169                        logger.error(e.getMessage(), e);
170                }
171                return data.toByteArray();
172        }
173
174        /*
175         * (non-Javadoc)
176         * @see com.digi.xbee.api.packet.XBeeAPIPacket#needsAPIFrameID()
177         */
178        @Override
179        public boolean needsAPIFrameID() {
180                return true;
181        }
182        
183        /**
184         * Returns the 16-bit destination address. 
185         * 
186         * @return The 16-bit destination address.
187         * 
188         * @see com.digi.xbee.api.models.XBee16BitAddress
189         */
190        public XBee16BitAddress get16bitDestinationAddress() {
191                return destAddress16;
192        }
193        
194        /**
195         * Returns the transmit retry count.
196         * 
197         * @return Transmit retry count.
198         */
199        public int getTransmitRetryCount() {
200                return tranmistRetryCount;
201        }
202        
203        /**
204         * Returns the transmit status.
205         * 
206         * @return Transmit status.
207         * 
208         * @see com.digi.xbee.api.models.XBeeTransmitStatus
209         */
210        public XBeeTransmitStatus getTransmitStatus() {
211                return transmitStatus;
212        }
213        
214        /**
215         * Returns the discovery status.
216         * 
217         * @return Discovery status.
218         * 
219         * @see XBeeDiscoveryStatus
220         */
221        public XBeeDiscoveryStatus getDiscoveryStatus() {
222                return discoveryStatus;
223        }
224        
225        /*
226         * (non-Javadoc)
227         * @see com.digi.xbee.api.packet.XBeeAPIPacket#isBroadcast()
228         */
229        @Override
230        public boolean isBroadcast() {
231                return false;
232        }
233        
234        /*
235         * (non-Javadoc)
236         * @see com.digi.xbee.api.packet.XBeeAPIPacket#getAPIPacketParameters()
237         */
238        @Override
239        public LinkedHashMap<String, String> getAPIPacketParameters() {
240                LinkedHashMap<String, String> parameters = new LinkedHashMap<String, String>();
241                parameters.put("16-bit dest. address", HexUtils.prettyHexString(destAddress16.toString()));
242                parameters.put("Tx. retry count", HexUtils.prettyHexString(HexUtils.integerToHexString(tranmistRetryCount, 1)) + " (" + tranmistRetryCount + ")");
243                parameters.put("Delivery status", HexUtils.prettyHexString(HexUtils.integerToHexString(transmitStatus.getId(), 1)) + " (" + transmitStatus.getDescription() + ")");
244                parameters.put("Discovery status", HexUtils.prettyHexString(HexUtils.integerToHexString(discoveryStatus.getId(), 1)) + " (" + discoveryStatus.getDescription() + ")");
245                return parameters;
246        }
247}