/**
  * Class constructor. Instantiates a new {@code RemoteDigiPointDevice} object
  * with the given local {@code XBeeDevice} which contains the connection
  * interface to be used.
  *
  * @param localXBeeDevice The local XBee device that will behave as
  *                        connection interface to communicate with this
  *                        remote point-to-multipoint device.
  * @param addr64 The 64-bit address to identify this remote point-to-multipoint
  *               device.
  *
  * @throws ArgumentException if {@code localXBeeDevice.isRemote() == true} or
  *                                  if {@code localXBeeDevice.getXBeeProtocol() != XBeeProtocol.DIGI_POINT}.
  * @throws ArgumentNullException if {@code localXBeeDevice == null} or
  *                              if {@code addr64 == null}.
  *
  * @see com.digi.xbee.api.models.XBee64BitAddress
  */
 public RemoteDigiPointDevice(XBeeDevice localXBeeDevice, XBee64BitAddress addr64)
     : base(localXBeeDevice, addr64)
 {
     // Verify the local device has point-to-multipoint protocol.
     if (localXBeeDevice.XBeeProtocol != XBeeProtocol.DIGI_POINT)
         throw new ArgumentException("The protocol of the local XBee device is not " + XBeeProtocol.DIGI_POINT.GetDescription() + ".");
 }
 /**
  * Class constructor. Instantiates a new {@code RemoteZigBeeDevice} object
  * with the given local {@code XBeeDevice} which contains the connection
  * interface to be used.
  *
  * @param localXBeeDevice The local XBee device that will behave as
  *                        connection interface to communicate with this
  *                        remote ZigBee device.
  * @param addr64 The 64-bit address to identify this remote ZigBee device.
  *
  * @throws ArgumentException if {@code localXBeeDevice.isRemote() == true} or
  *                                  if {@code localXBeeDevice.getXBeeProtocol() != XBeeProtocol.ZIGBEE}.
  * @throws ArgumentNullException if {@code localXBeeDevice == null} or
  *                              if {@code addr64 == null}.
  *
  * @see com.digi.xbee.api.models.XBee64BitAddress
  */
 public RemoteZigBeeDevice(XBeeDevice localXBeeDevice, XBee64BitAddress addr64)
     : base(localXBeeDevice, addr64)
 {
     // Verify the local device has ZigBee protocol.
     if (localXBeeDevice.XBeeProtocol != XBeeProtocol.ZIGBEE)
         throw new ArgumentException("The protocol of the local XBee device is not " + XBeeProtocol.ZIGBEE.GetDescription() + ".");
 }
 /**
  * Class constructor. Instantiates a new {@code RemoteDigiMeshDevice} object
  * with the given local {@code XBeeDevice} which contains the connection
  * interface to be used.
  *
  * @param localXBeeDevice The local XBee device that will behave as
  *                        connection interface to communicate with this
  *                        remote DigiMesh device.
  * @param addr64 The 64-bit address to identify this remote DigiMesh device.
  * @param id The node identifier of this remote DigiMesh device. It might
  *           be {@code null}.
  *
  * @throws ArgumentException if {@code localXBeeDevice.isRemote() == true} or
  *                                  if {@code localXBeeDevice.getXBeeProtocol() != XBeeProtocol.DIGI_MESH}.
  * @throws ArgumentNullException if {@code localXBeeDevice == null} or
  *                              if {@code addr64 == null}.
  *
  * @see com.digi.xbee.api.models.XBee64BitAddress
  */
 public RemoteDigiMeshDevice(XBeeDevice localXBeeDevice, XBee64BitAddress addr64, string id)
     : base(localXBeeDevice, addr64, null, id)
 {
     // Verify the local device has DigiMesh protocol.
     if (localXBeeDevice.XBeeProtocol != Models.XBeeProtocol.DIGI_MESH)
         throw new ArgumentException("The protocol of the local XBee device is not " + XBeeProtocol.DIGI_MESH.GetDescription() + ".");
 }
 /**
  * Class constructor. Instantiates a new {@code RemoteRaw802Device} object
  * with the given local {@code XBeeDevice} which contains the connection
  * interface to be used.
  *
  * @param localXBeeDevice The local XBee device that will behave as
  *                        connection interface to communicate with this
  *                        remote 802.15.4 device.
  * @param addr64 The 64-bit address to identify this remote 802.15.4 device.
  * @param addr16 The 16-bit address to identify this remote 802.15.4 device.
  *               It might be {@code null}.
  * @param id The node identifier of this remote 802.15.4 device. It might be
  *           {@code null}.
  *
  * @throws ArgumentException if {@code localXBeeDevice.isRemote() == true} or
  *                                  if {@code localXBeeDevice.getXBeeProtocol() != XBeeProtocol.RAW_802_15_4}
  * @throws ArgumentNullException if {@code localXBeeDevice == null} or
  *                              if {@code addr64 == null}.
  *
  * @see com.digi.xbee.api.models.XBee16BitAddress
  * @see com.digi.xbee.api.models.XBee64BitAddress
  */
 public RemoteRaw802Device(XBeeDevice localXBeeDevice, XBee64BitAddress addr64,
     XBee16BitAddress addr16, string id)
     : base(localXBeeDevice, addr64, addr16, id)
 {
     // Verify the local device has 802.15.4 protocol.
     if (localXBeeDevice.XBeeProtocol != XBeeProtocol.RAW_802_15_4)
         throw new ArgumentException("The protocol of the local XBee device is not " + XBeeProtocol.RAW_802_15_4.GetDescription() + ".");
 }
        /// <summary>
        /// Initializes a new instance of <see cref="XBeeNetwork"/>.
        /// </summary>
        /// <param name="device">A local XBee device to get the network from.</param>
        public XBeeNetwork(XBeeDevice device)
        {
            if (device == null)
                throw new ArgumentNullException("Local XBee device cannot be null.");

            localDevice = device;
            remotesBy64BitAddr = new ConcurrentDictionary<XBee64BitAddress, RemoteXBeeDevice>();
            remotesBy16BitAddr = new ConcurrentDictionary<XBee16BitAddress, RemoteXBeeDevice>();
            nodeDiscovery = new NodeDiscovery(localDevice);

            logger = LogManager.GetLogger(this.GetType());
        }
		/**
		 * Instantiates a new {@code NodeDiscovery} object.
		 * 
		 * @param xbeeDevice XBee Device to perform the discovery operation.
		 * 
		 * @throws ArgumentNullException If {@code xbeeDevice == null}.
		 * 
		 * @see XBeeDevice
		 */
		public NodeDiscovery(XBeeDevice xbeeDevice)
		{
			if (xbeeDevice == null)
				throw new ArgumentNullException("Local XBee device cannot be null.");

			this.xbeeDevice = xbeeDevice;

			frameID = globalFrameID;
			globalFrameID++;
			if (globalFrameID == 0xFF)
				globalFrameID = 1;

			logger = LogManager.GetLogger(this.GetType());
		}
		/**
		 * Parses the given node discovery API data to create and return a remote 
		 * XBee Device.
		 * 
		 * @param data Byte array with the data to parse.
		 * @param localDevice The local device that received the remote XBee data.
		 * 
		 * @return Discovered XBee device.
		 */
		private async Task<RemoteXBeeDevice> ParseDiscoveryAPIData(byte[] data, XBeeDevice localDevice)
		{
			if (data == null)
				return null;

			RemoteXBeeDevice device = null;
			XBee16BitAddress addr16 = null;
			XBee64BitAddress addr64 = null;
			String id = null;
			// TODO role of the device: coordinator, router, end device or unknown.
			//XBeeDeviceType role = XBeeDeviceType.UNKNOWN;
			int signalStrength = 0;
			byte[] profileID = null;
			byte[] manufacturerID = null;

			using (var inputStream = new MemoryStream(data))
			{
				// Read 16 bit address.
				addr16 = new XBee16BitAddress(await ByteUtils.ReadBytes(2, inputStream));
				// Read 64 bit address.
				addr64 = new XBee64BitAddress(await ByteUtils.ReadBytes(8, inputStream));


				switch (localDevice.XBeeProtocol)
				{
					case XBeeProtocol.ZIGBEE:
					case XBeeProtocol.DIGI_MESH:
					case XBeeProtocol.ZNET:
					case XBeeProtocol.DIGI_POINT:
					case XBeeProtocol.XLR:
					// TODO [XLR_DM] The next version of the XLR will add DigiMesh support.
					// For the moment only point-to-multipoint is supported in this kind of devices.
					case XBeeProtocol.XLR_DM:
						// Read node identifier.
						id = ByteUtils.ReadString(inputStream);
						// Read parent address.
						XBee16BitAddress parentAddress = new XBee16BitAddress(await ByteUtils.ReadBytes(2, inputStream));
						// TODO Read device type.
						//role = XBeeDeviceType.get(inputStream.read());
						// Consume status byte, it is not used yet.
						await ByteUtils.ReadBytes(1, inputStream);
						// Read profile ID.
						profileID = await ByteUtils.ReadBytes(2, inputStream);
						// Read manufacturer ID.
						manufacturerID = await ByteUtils.ReadBytes(2, inputStream);

						logger.DebugFormat("{0}Discovered {1} device: 16-bit[{2}], 64-bit[{3}], id[{4}], parent[{5}], profile[{6}], manufacturer[{7}].",
								xbeeDevice.ToString(), localDevice.XBeeProtocol.GetDescription(), addr16,
								addr64, id, parentAddress, HexUtils.ByteArrayToHexString(profileID),
								HexUtils.ByteArrayToHexString(manufacturerID));

						break;
					case XBeeProtocol.RAW_802_15_4:
						// Read strength signal byte.
						signalStrength = inputStream.ReadByte();
						// Read node identifier.
						id = ByteUtils.ReadString(inputStream);

						logger.DebugFormat("{0}Discovered {1} device: 16-bit[{2}], 64-bit[{3}], id[{4}], rssi[{5}].",
								xbeeDevice.ToString(), localDevice.XBeeProtocol.GetDescription(), addr16, addr64, id, signalStrength);

						break;
					case XBeeProtocol.UNKNOWN:
					default:
						logger.DebugFormat("{0}Discovered {1} device: 16-bit[{2}], 64-bit[{3}].",
								xbeeDevice.ToString(), localDevice.XBeeProtocol.GetDescription(), addr16, addr64);
						break;
				}

				// Create device and fill with parameters.
				switch (localDevice.XBeeProtocol)
				{
					case XBeeProtocol.ZIGBEE:
						device = new RemoteZigBeeDevice(localDevice, addr64, addr16, id/*, role*/);
						// TODO profileID and manufacturerID
						break;
					case XBeeProtocol.DIGI_MESH:
						device = new RemoteDigiMeshDevice(localDevice, addr64, id/*, role*/);
						// TODO profileID and manufacturerID
						break;
					case XBeeProtocol.DIGI_POINT:
						device = new RemoteDigiPointDevice(localDevice, addr64, id/*, role*/);
						// TODO profileID and manufacturerID
						break;
					case XBeeProtocol.RAW_802_15_4:
						device = new RemoteRaw802Device(localDevice, addr64, addr16, id/*, role*/);
						// TODO signalStrength
						break;
					default:
						device = new RemoteXBeeDevice(localDevice, addr64, addr16, id/*, role*/);
						break;
				}
			}

			return device;
		}
 /**
  * Class constructor. Instantiates a new {@code RemoteXBeeDevice} object
  * with the given local {@code XBeeDevice} which contains the connection
  * interface to be used.
  *
  * @param localXBeeDevice The local XBee device that will behave as
  *                        connection interface to communicate with this
  *                        remote XBee device.
  * @param addr64 The 64-bit address to identify this remote XBee device.
  * @param addr16 The 16-bit address to identify this remote XBee device. It
  *               might be {@code null}.
  * @param ni The node identifier of this remote XBee device. It might be
  *           {@code null}.
  *
  * @throws ArgumentException if {@code localXBeeDevice.isRemote() == true}.
  * @throws ArgumentNullException if {@code localXBeeDevice == null} or
  *                              if {@code addr64 == null}.
  *
  * @see com.digi.xbee.api.models.XBee16BitAddress
  * @see com.digi.xbee.api.models.XBee64BitAddress
  */
 public RemoteXBeeDevice(XBeeDevice localXBeeDevice, XBee64BitAddress addr64,
     XBee16BitAddress addr16, string ni)
     : base(localXBeeDevice, addr64, addr16, ni)
 {
 }
 /**
  * Class constructor. Instantiates a new {@code RemoteXBeeDevice} object
  * with the given local {@code XBeeDevice} which contains the connection
  * interface to be used.
  *
  * @param localXBeeDevice The local XBee device that will behave as
  *                        connection interface to communicate with this
  *                        remote XBee device.
  * @param addr64 The 64-bit address to identify this remote XBee device.
  *
  * @throws ArgumentException if {@code localXBeeDevice.isRemote() == true}.
  * @throws ArgumentNullException if {@code localXBeeDevice == null} or
  *                              if {@code addr64 == null}.
  *
  * @see com.digi.xbee.api.models.XBee64BitAddress
  */
 public RemoteXBeeDevice(XBeeDevice localXBeeDevice, XBee64BitAddress addr64)
     : base(localXBeeDevice, addr64)
 {
 }
 public CustomModemStatusReceiveListener(XBeeDevice device)
 {
     _device = device;
 }
		/**
		 * Class constructor. Instantiates a new {@code RemoteXBeeDevice} object 
		 * with the given local {@code XBeeDevice} which contains the connection 
		 * interface to be used.
		 * 
		 * @param localXBeeDevice The local XBee device that will behave as 
		 *                        connection interface to communicate with this 
		 *                        remote XBee device.
		 * @param addr64 The 64-bit address to identify this XBee device.
		 * @param addr16 The 16-bit address to identify this XBee device. It might 
		 *               be {@code null}.
		 * @param id The node identifier of this XBee device. It might be 
		 *           {@code null}.
		 * 
		 * @throws ArgumentException If {@code localXBeeDevice.isRemote() == true}.
		 * @throws ArgumentNullException If {@code localXBeeDevice == null} or
		 *                              if {@code addr64 == null}.
		 * 
		 * @see #AbstractXBeeDevice(IConnectionInterface)
		 * @see #AbstractXBeeDevice(String, int)
		 * @see #AbstractXBeeDevice(String, SerialPortParameters)
		 * @see #AbstractXBeeDevice(XBeeDevice, XBee64BitAddress)
		 * @see #AbstractXBeeDevice(String, int, int, int, int, int)
		 * @see com.digi.xbee.api.models.XBee16BitAddress
		 * @see com.digi.xbee.api.models.XBee64BitAddress
		 */
		public AbstractXBeeDevice(XBeeDevice localXBeeDevice, XBee64BitAddress addr64,
				XBee16BitAddress addr16, String id)
		{
			if (localXBeeDevice == null)
				throw new ArgumentNullException("Local XBee device cannot be null.");
			if (addr64 == null)
				throw new ArgumentNullException("XBee 64-bit address of the device cannot be null.");
			if (localXBeeDevice.IsRemote)
				throw new ArgumentException("The given local XBee device is remote.");

			XBeeProtocol = XBeeProtocol.UNKNOWN;
			this.localXBeeDevice = localXBeeDevice;
			this.connectionInterface = localXBeeDevice.GetConnectionInterface();
			this.xbee64BitAddress = addr64;
			this.xbee16BitAddress = addr16;
			if (addr16 == null)
				xbee16BitAddress = XBee16BitAddress.UNKNOWN_ADDRESS;
			this.nodeID = id;
			this.logger = LogManager.GetLogger(this.GetType());
			logger.DebugFormat(ToString() + "Using the connection interface {0}.",
					connectionInterface.GetType().Name);
			this.IOPacketReceiveListener = new CustomPacketReceiveListener(this);
		}
		/**
		 * Class constructor. Instantiates a new {@code RemoteXBeeDevice} object 
		 * with the given local {@code XBeeDevice} which contains the connection 
		 * interface to be used.
		 * 
		 * @param localXBeeDevice The local XBee device that will behave as 
		 *                        connection interface to communicate with this 
		 *                        remote XBee device.
		 * @param addr64 The 64-bit address to identify this XBee device.
		 * 
		 * @throws ArgumentException If {@code localXBeeDevice.isRemote() == true}.
		 * @throws ArgumentNullException if {@code localXBeeDevice == null} or
		 *                              if {@code addr64 == null}.
		 * 
		 * @see #AbstractXBeeDevice(IConnectionInterface)
		 * @see #AbstractXBeeDevice(String, int)
		 * @see #AbstractXBeeDevice(String, SerialPortParameters)
		 * @see #AbstractXBeeDevice(XBeeDevice, XBee64BitAddress, XBee16BitAddress, String)
		 * @see #AbstractXBeeDevice(String, int, int, int, int, int)
		 * @see com.digi.xbee.api.models.XBee16BitAddress
		 */
		public AbstractXBeeDevice(XBeeDevice localXBeeDevice, XBee64BitAddress addr64)
			: this(localXBeeDevice, addr64, null, null)
		{
		}
        /**
         * Class constructor. Instantiates a new {@code RemoteRaw802Device} object
         * interface to be used.
         *
         * @param localXBeeDevice The local 802.15.4 device that will behave as
         *                        connection interface to communicate with this
         *                        remote 802.15.4 device.
         * @param addr16 The 16-bit address to identify this remote 802.15.4
         *               device.
         *
         * @throws ArgumentException if {@code localXBeeDevice.getXBeeProtocol() != XBeeProtocol.RAW_802_15_4}.
         * @throws ArgumentNullException if {@code localXBeeDevice == null} or
         *                              if {@code addr16 == null}.
         *
         * @see com.digi.xbee.api.models.XBee16BitAddress
         */
        public RemoteRaw802Device(XBeeDevice localXBeeDevice, XBee16BitAddress addr16)
            : base(localXBeeDevice, XBee64BitAddress.UNKNOWN_ADDRESS)
        {
            // Verify the local device has 802.15.4 protocol.
            if (localXBeeDevice.XBeeProtocol != XBeeProtocol.RAW_802_15_4)
                throw new ArgumentException("The protocol of the local XBee device is not " + XBeeProtocol.RAW_802_15_4.GetDescription() + ".");

            this.xbee16BitAddress = addr16;
        }