public byte[] GetUnconfirmedDataUpMessage() { byte[] _mhbr = new byte[] { 0x40 }; byte[] _devAddr = LoRaDevice.GetDevAddr(); Array.Reverse(_devAddr); byte[] _FCtrl = new byte[] { 0x80 }; // byte[] _FCnt = new byte[] { 0x00, 0x00 }; _FCnt[0]++; byte[] _Fopts = null; byte[] _FPort = new byte[] { 0x01 }; // Creating a random number Random random = new Random(); int temp = random.Next(-50, 70); Logger.Log(LoRaDevice.DevAddr, $"Simulated data: {temp.ToString()}", Logger.LoggingLevel.Always); byte[] _payload = Encoding.ASCII.GetBytes(temp.ToString()); Array.Reverse(_payload); // 0 = uplink, 1 = downlink int direction = 0; LoRaPayloadData standardData = new LoRaPayloadData(_mhbr, _devAddr, _FCtrl, _FCnt, _Fopts, _FPort, _payload, direction); // Need to create Fops. If not, then MIC won't be correct standardData.Fopts = new byte[0]; // First encrypt the data standardData.PerformEncryption(LoRaDevice.AppSKey); //"0A501524F8EA5FCBF9BDB5AD7D126F75"); // Now we have the full package, create the MIC standardData.SetMic(LoRaDevice.NwkSKey); //"99D58493D1205B43EFF938F0F66C339E"); return(standardData.GetByteMessage()); }
byte[] CreateUnconfirmedDataUpMessage(string data, byte fport = 1) { byte[] mhbr = new byte[] { 0x40 }; byte[] devAddr = ConversionHelper.StringToByteArray(LoRaDevice.DevAddr); Array.Reverse(devAddr); byte[] fCtrl = new byte[] { 0x80 }; // byte[] _FCnt = new byte[] { 0x00, 0x00 }; fCnt[0]++; byte[] fopts = null; byte[] fPort = new byte[] { fport }; TestLogger.Log($"{LoRaDevice.DeviceID}: Simulated data: {data}"); byte[] payload = Encoding.UTF8.GetBytes(data); Array.Reverse(payload); // 0 = uplink, 1 = downlink int direction = 0; LoRaPayloadData standardData = new LoRaPayloadData((LoRaPayloadData.MType)mhbr[0], devAddr, fCtrl, fCnt, fopts, fPort, payload, direction); // Need to create Fops. If not, then MIC won't be correct standardData.Fopts = new byte[0]; // First encrypt the data standardData.PerformEncryption(LoRaDevice.AppSKey); //"0A501524F8EA5FCBF9BDB5AD7D126F75"); // Now we have the full package, create the MIC standardData.SetMic(LoRaDevice.NwkSKey); //"99D58493D1205B43EFF938F0F66C339E"); return(standardData.GetByteMessage()); }
public byte[] GetUnconfirmedDataUpMessage() { byte[] mhbr = new byte[] { 0x40 }; byte[] devAddr = this.LoRaDevice.GetDevAddr(); Array.Reverse(devAddr); byte[] fCtrl = new byte[] { 0x80 }; // byte[] _FCnt = new byte[] { 0x00, 0x00 }; this._fCnt[0]++; List <MacCommand> fopts = null; byte[] fPort = new byte[] { 0x01 }; // Creating a random number // Random random = new Random(); // int temp = random.Next(-50, 70); this.IncrementalData++; Logger.LogAlways(this.LoRaDevice.DevEUI, $"Simulated data: {this.IncrementalData.ToString()}"); byte[] payload = Encoding.ASCII.GetBytes(this.IncrementalData.ToString()); Array.Reverse(payload); // 0 = uplink, 1 = downlink int direction = 0; LoRaPayloadData standardData = new LoRaPayloadData((LoRaMessageType)mhbr[0], devAddr, fCtrl, this._fCnt, fopts, fPort, payload, direction); // Need to create Fops. If not, then MIC won't be correct standardData.Fopts = new byte[0]; // First encrypt the data standardData.PerformEncryption(this.LoRaDevice.AppSKey); // "0A501524F8EA5FCBF9BDB5AD7D126F75"); // Now we have the full package, create the MIC standardData.SetMic(this.LoRaDevice.NwkSKey); // "99D58493D1205B43EFF938F0F66C339E"); return(standardData.GetByteMessage()); }
public void TestConfirmedDataUp() { byte[] mhdr = new byte[1]; mhdr[0] = 128; byte[] devAddr = new byte[4] { 4, 3, 2, 1 }; byte[] fctrl = new byte[1] { 0 }; byte[] fcnt = new byte[2] { 0, 0 }; byte[] fport = new byte[1] { 10 }; byte[] frmPayload = new byte[4] { 4, 3, 2, 1 }; var nwkkey = new byte[16] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; var appkey = new byte[16] { 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 }; LoRaPayloadData lora = new LoRaPayloadData(MType.ConfirmedDataUp, devAddr, fctrl, fcnt, null, fport, frmPayload, 0); lora.PerformEncryption(ConversionHelper.ByteArrayToString(appkey)); byte[] testEncrypt = new byte[4] { 226, 100, 212, 247 }; Assert.Equal(testEncrypt, lora.Frmpayload.ToArray()); lora.SetMic(ConversionHelper.ByteArrayToString(nwkkey)); byte[] testMic = new byte[4] { 181, 106, 14, 117 }; Assert.Equal(testMic, lora.Mic.ToArray()); var mess = lora.GetByteMessage(); lora.ChangeEndianess(); Assert.Equal(mess, lora.RawMessage); //TODO }
public void TestUnconfirmedUplink() { string jsonUplinkUnconfirmedDataUp = @"{ ""rxpk"":[ { ""time"":""2013-03-31T16:21:17.528002Z"", ""tmst"":3512348611, ""chan"":2, ""rfch"":0, ""freq"":866.349812, ""stat"":1, ""modu"":""LORA"", ""datr"":""SF7BW125"", ""codr"":""4/6"", ""rssi"":-35, ""lsnr"":5.1, ""size"":32, ""data"":""QAQDAgGAAQABppRkJhXWw7WC"" }]}"; byte[] physicalUpstreamPyld = new byte[12]; physicalUpstreamPyld[0] = 2; var jsonUplinkUnconfirmedDataUpBytes = Encoding.Default.GetBytes(jsonUplinkUnconfirmedDataUp); LoRaMessageWrapper jsonUplinkUnconfirmedMessage = new LoRaMessageWrapper(physicalUpstreamPyld.Concat(jsonUplinkUnconfirmedDataUpBytes).ToArray()); Assert.Equal(LoRaMessageType.UnconfirmedDataUp, jsonUplinkUnconfirmedMessage.LoRaMessageType); LoRaPayloadData loRaPayloadUplinkObj = (LoRaPayloadData)jsonUplinkUnconfirmedMessage.LoRaPayloadMessage; Assert.True(loRaPayloadUplinkObj.Fcnt.Span.SequenceEqual(new byte[2] { 1, 0 })); Assert.True(loRaPayloadUplinkObj.DevAddr.Span.SequenceEqual(new byte[4] { 1, 2, 3, 4 })); byte[] LoRaPayloadUplinkNwkKey = new byte[16] { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }; Assert.True(loRaPayloadUplinkObj.CheckMic(ConversionHelper.ByteArrayToString(LoRaPayloadUplinkNwkKey))); byte[] LoRaPayloadUplinkAppKey = new byte[16] { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; var key = ConversionHelper.ByteArrayToString(LoRaPayloadUplinkAppKey); Assert.Equal("hello", Encoding.ASCII.GetString((loRaPayloadUplinkObj.PerformEncryption(key)))); }
private async Task <byte[]> ProcessLoraMessage(LoRaMessageWrapper loraMessage) { bool validFrameCounter = false; byte[] udpMsgForPktForwarder = new byte[0]; string devAddr = ConversionHelper.ByteArrayToString(loraMessage.LoRaPayloadMessage.DevAddr.ToArray()); Message c2dMsg = null; Cache.TryGetValue(devAddr, out LoraDeviceInfo loraDeviceInfo); if (loraDeviceInfo == null || !loraDeviceInfo.IsOurDevice) { loraDeviceInfo = await LoraDeviceInfoManager.GetLoraDeviceInfoAsync(devAddr, GatewayID); if (loraDeviceInfo.DevEUI != null) { Logger.Log(loraDeviceInfo.DevEUI, $"processing message, device not in cache", Logger.LoggingLevel.Info); } else { Logger.Log(devAddr, $"processing message, device not in cache", Logger.LoggingLevel.Info); } Cache.AddToCache(devAddr, loraDeviceInfo); } else { Logger.Log(loraDeviceInfo.DevEUI, $"processing message, device in cache", Logger.LoggingLevel.Info); } if (loraDeviceInfo != null && loraDeviceInfo.IsOurDevice) { //either there is no gateway linked to the device or the gateway is the one that the code is running if (String.IsNullOrEmpty(loraDeviceInfo.GatewayID) || loraDeviceInfo.GatewayID.ToUpper() == GatewayID.ToUpper()) { if (loraMessage.CheckMic(loraDeviceInfo.NwkSKey)) { if (loraDeviceInfo.HubSender == null) { loraDeviceInfo.HubSender = new IoTHubConnector(loraDeviceInfo.DevEUI, loraDeviceInfo.PrimaryKey); } UInt16 fcntup = BitConverter.ToUInt16(loraMessage.LoRaPayloadMessage.GetLoRaMessage().Fcnt.ToArray(), 0); byte[] linkCheckCmdResponse = null; //check if the frame counter is valid: either is above the server one or is an ABP device resetting the counter (relaxed seqno checking) if (fcntup > loraDeviceInfo.FCntUp || (fcntup == 0 && loraDeviceInfo.FCntUp == 0) || (fcntup == 1 && String.IsNullOrEmpty(loraDeviceInfo.AppEUI))) { //save the reset fcnt for ABP (relaxed seqno checking) if (fcntup == 1 && String.IsNullOrEmpty(loraDeviceInfo.AppEUI)) { _ = loraDeviceInfo.HubSender.UpdateFcntAsync(fcntup, 0, true); //if the device is not attached to a gateway we need to reset the abp fcnt server side cache if (String.IsNullOrEmpty(loraDeviceInfo.GatewayID)) { bool rit = await LoraDeviceInfoManager.ABPFcntCacheReset(loraDeviceInfo.DevEUI); } } validFrameCounter = true; Logger.Log(loraDeviceInfo.DevEUI, $"valid frame counter, msg: {fcntup} server: {loraDeviceInfo.FCntUp}", Logger.LoggingLevel.Info); byte[] decryptedMessage = null; try { decryptedMessage = loraMessage.DecryptPayload(loraDeviceInfo.AppSKey); } catch (Exception ex) { Logger.Log(loraDeviceInfo.DevEUI, $"failed to decrypt message: {ex.Message}", Logger.LoggingLevel.Error); } Rxpk rxPk = loraMessage.PktFwdPayload.GetPktFwdMessage().Rxpks[0]; dynamic fullPayload = JObject.FromObject(rxPk); string jsonDataPayload = ""; uint fportUp = 0; bool isAckFromDevice = false; if (loraMessage.LoRaPayloadMessage.GetLoRaMessage().Fport.Span.Length > 0) { fportUp = (uint)loraMessage.LoRaPayloadMessage.GetLoRaMessage().Fport.Span[0]; } else // this is an acknowledgment sent from the device { isAckFromDevice = true; fullPayload.deviceAck = true; } fullPayload.port = fportUp; fullPayload.fcnt = fcntup; if (isAckFromDevice) { jsonDataPayload = Convert.ToBase64String(decryptedMessage); fullPayload.data = jsonDataPayload; } else { Logger.Log(loraDeviceInfo.DevEUI, $"decoding with: {loraDeviceInfo.SensorDecoder} port: {fportUp}", Logger.LoggingLevel.Info); fullPayload.data = await LoraDecoders.DecodeMessage(decryptedMessage, fportUp, loraDeviceInfo.SensorDecoder); } fullPayload.eui = loraDeviceInfo.DevEUI; fullPayload.gatewayid = GatewayID; //Edge timestamp fullPayload.edgets = (long)((startTimeProcessing - new DateTime(1970, 1, 1)).TotalMilliseconds); List <KeyValuePair <String, String> > messageProperties = new List <KeyValuePair <String, String> >(); //Parsing MacCommands and add them as property of the message to be sent to the IoT Hub. var macCommand = ((LoRaPayloadData)loraMessage.LoRaPayloadMessage).GetMacCommands(); if (macCommand.macCommand.Count > 0) { for (int i = 0; i < macCommand.macCommand.Count; i++) { messageProperties.Add(new KeyValuePair <string, string>(macCommand.macCommand[i].Cid.ToString(), value: JsonConvert.SerializeObject(macCommand.macCommand[i], Newtonsoft.Json.Formatting.None))); //in case it is a link check mac, we need to send it downstream. if (macCommand.macCommand[i].Cid == CidEnum.LinkCheckCmd) { linkCheckCmdResponse = new LinkCheckCmd(rxPk.GetModulationMargin(), 1).ToBytes(); } } } string iotHubMsg = fullPayload.ToString(Newtonsoft.Json.Formatting.None); await loraDeviceInfo.HubSender.SendMessageAsync(iotHubMsg, messageProperties); if (isAckFromDevice) { Logger.Log(loraDeviceInfo.DevEUI, $"ack from device sent to hub", Logger.LoggingLevel.Info); } else { var fullPayloadAsString = fullPayload.data as string; if (fullPayloadAsString == null) { fullPayloadAsString = ((JObject)fullPayload.data).ToString(Formatting.None); } Logger.Log(loraDeviceInfo.DevEUI, $"message '{fullPayloadAsString}' sent to hub", Logger.LoggingLevel.Info); } loraDeviceInfo.FCntUp = fcntup; } else { validFrameCounter = false; Logger.Log(loraDeviceInfo.DevEUI, $"invalid frame counter, msg: {fcntup} server: {loraDeviceInfo.FCntUp}", Logger.LoggingLevel.Info); } //we lock as fast as possible and get the down fcnt for multi gateway support for confirmed message if (loraMessage.LoRaMessageType == LoRaMessageType.ConfirmedDataUp && String.IsNullOrEmpty(loraDeviceInfo.GatewayID)) { ushort newFCntDown = await LoraDeviceInfoManager.NextFCntDown(loraDeviceInfo.DevEUI, loraDeviceInfo.FCntDown, fcntup, GatewayID); //ok to send down ack or msg if (newFCntDown > 0) { loraDeviceInfo.FCntDown = newFCntDown; } //another gateway was first with this message we simply drop else { PhysicalPayload pushAck = new PhysicalPayload(loraMessage.PhysicalPayload.token, PhysicalIdentifier.PUSH_ACK, null); udpMsgForPktForwarder = pushAck.GetMessage(); Logger.Log(loraDeviceInfo.DevEUI, $"another gateway has already sent ack or downlink msg", Logger.LoggingLevel.Info); Logger.Log(loraDeviceInfo.DevEUI, $"processing time: {DateTime.UtcNow - startTimeProcessing}", Logger.LoggingLevel.Info); return(udpMsgForPktForwarder); } } //start checking for new c2d message, we do it even if the fcnt is invalid so we support replying to the ConfirmedDataUp //todo ronnie should we wait up to 900 msec? c2dMsg = await loraDeviceInfo.HubSender.ReceiveAsync(TimeSpan.FromMilliseconds(20)); byte[] bytesC2dMsg = null; byte[] fport = null; //Todo revamp fctrl byte[] fctrl = new byte[1] { 32 }; //check if we got a c2d message to be added in the ack message and prepare the message if (c2dMsg != null) { ////check if there is another message var secondC2dMsg = await loraDeviceInfo.HubSender.ReceiveAsync(TimeSpan.FromMilliseconds(20)); if (secondC2dMsg != null) { //put it back to the queue for the next pickup //todo ronnie check abbandon logic especially in case of mqtt _ = await loraDeviceInfo.HubSender.AbandonAsync(secondC2dMsg); //set the fpending flag so the lora device will call us back for the next message fctrl = new byte[1] { 48 }; } bytesC2dMsg = c2dMsg.GetBytes(); fport = new byte[1] { 1 }; if (bytesC2dMsg != null) { Logger.Log(loraDeviceInfo.DevEUI, $"C2D message: {Encoding.UTF8.GetString(bytesC2dMsg)}", Logger.LoggingLevel.Info); } //todo ronnie implement a better max payload size by datarate //cut to the max payload of lora for any EU datarate if (bytesC2dMsg.Length > 51) { Array.Resize(ref bytesC2dMsg, 51); } Array.Reverse(bytesC2dMsg); } //if confirmation or cloud to device msg send down the message if (loraMessage.LoRaMessageType == LoRaMessageType.ConfirmedDataUp || c2dMsg != null) { //check if we are not too late for the second receive windows if ((DateTime.UtcNow - startTimeProcessing) <= TimeSpan.FromMilliseconds(RegionFactory.CurrentRegion.receive_delay2 * 1000 - 100)) { //if running in multigateway we need to use redis to sync the down fcnt if (!String.IsNullOrEmpty(loraDeviceInfo.GatewayID)) { loraDeviceInfo.FCntDown++; } else if (loraMessage.LoRaMessageType == LoRaMessageType.UnconfirmedDataUp) { ushort newFCntDown = await LoraDeviceInfoManager.NextFCntDown(loraDeviceInfo.DevEUI, loraDeviceInfo.FCntDown, fcntup, GatewayID); //ok to send down ack or msg if (newFCntDown > 0) { loraDeviceInfo.FCntDown = newFCntDown; } //another gateway was first with this message we simply drop else { PhysicalPayload pushAck = new PhysicalPayload(loraMessage.PhysicalPayload.token, PhysicalIdentifier.PUSH_ACK, null); udpMsgForPktForwarder = pushAck.GetMessage(); Logger.Log(loraDeviceInfo.DevEUI, $"another gateway has already sent ack or downlink msg", Logger.LoggingLevel.Info); Logger.Log(loraDeviceInfo.DevEUI, $"processing time: {DateTime.UtcNow - startTimeProcessing}", Logger.LoggingLevel.Info); return(udpMsgForPktForwarder); } } Logger.Log(loraDeviceInfo.DevEUI, $"down frame counter: {loraDeviceInfo.FCntDown}", Logger.LoggingLevel.Info); //Saving both fcnts to twins _ = loraDeviceInfo.HubSender.UpdateFcntAsync(loraDeviceInfo.FCntUp, loraDeviceInfo.FCntDown); //todo need implementation of current configuation to implement this as this depends on RX1DROffset //var _datr = ((UplinkPktFwdMessage)loraMessage.LoraMetadata.FullPayload).rxpk[0].datr; var datr = RegionFactory.CurrentRegion.GetDownstreamDR(loraMessage.PktFwdPayload.GetPktFwdMessage().Rxpks[0]); //todo should discuss about the logic in case of multi channel gateway. uint rfch = loraMessage.PktFwdPayload.GetPktFwdMessage().Rxpks[0].rfch; //todo should discuss about the logic in case of multi channel gateway double freq = RegionFactory.CurrentRegion.GetDownstreamChannel(loraMessage.PktFwdPayload.GetPktFwdMessage().Rxpks[0]); //if we are already longer than 900 mssecond move to the 2 second window //uncomment the following line to force second windows usage TODO change this to a proper expression? //Thread.Sleep(901 long tmst = loraMessage.PktFwdPayload.GetPktFwdMessage().Rxpks[0].tmst + RegionFactory.CurrentRegion.receive_delay1 * 1000000; if ((DateTime.UtcNow - startTimeProcessing) > TimeSpan.FromMilliseconds(RegionFactory.CurrentRegion.receive_delay1 * 1000 - 100)) { fctrl = new byte[1] { 32 }; if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable("RX2_DATR"))) { Logger.Log(loraDeviceInfo.DevEUI, $"using standard second receive windows", Logger.LoggingLevel.Info); tmst = loraMessage.PktFwdPayload.GetPktFwdMessage().Rxpks[0].tmst + RegionFactory.CurrentRegion.receive_delay2 * 1000000; freq = RegionFactory.CurrentRegion.RX2DefaultReceiveWindows.frequency; datr = RegionFactory.CurrentRegion.DRtoConfiguration[RegionFactory.CurrentRegion.RX2DefaultReceiveWindows.dr].configuration; } //if specific twins are set, specify second channel to be as specified else { freq = double.Parse(Environment.GetEnvironmentVariable("RX2_FREQ")); datr = Environment.GetEnvironmentVariable("RX2_DATR"); Logger.Log(loraDeviceInfo.DevEUI, $"using custom DR second receive windows freq : {freq}, datr:{datr}", Logger.LoggingLevel.Info); } } Byte[] devAddrCorrect = new byte[4]; Array.Copy(loraMessage.LoRaPayloadMessage.DevAddr.ToArray(), devAddrCorrect, 4); Array.Reverse(devAddrCorrect); bool requestForConfirmedResponse = false; //check if the c2d message has a mac command byte[] macbytes = null; if (c2dMsg != null) { var macCmd = c2dMsg.Properties.Where(o => o.Key == "CidType"); if (macCmd.Count() != 0) { MacCommandHolder macCommandHolder = new MacCommandHolder(Convert.ToByte(macCmd.First().Value)); macbytes = macCommandHolder.macCommand[0].ToBytes(); } var confirmCmd = c2dMsg.Properties.Where(o => o.Key == "Confirmed"); if (confirmCmd.Count() != 0) { requestForConfirmedResponse = true; } } if (requestForConfirmedResponse) { fctrl[0] += 16; } if (macbytes != null && linkCheckCmdResponse != null) { macbytes = macbytes.Concat(linkCheckCmdResponse).ToArray(); } LoRaPayloadData ackLoRaMessage = new LoRaPayloadData( requestForConfirmedResponse ? MType.ConfirmedDataDown : MType.UnconfirmedDataDown, //ConversionHelper.StringToByteArray(requestForConfirmedResponse?"A0":"60"), devAddrCorrect, fctrl, BitConverter.GetBytes(loraDeviceInfo.FCntDown), macbytes, fport, bytesC2dMsg, 1); ackLoRaMessage.PerformEncryption(loraDeviceInfo.AppSKey); ackLoRaMessage.SetMic(loraDeviceInfo.NwkSKey); byte[] rndToken = new byte[2]; Random rnd = new Random(); rnd.NextBytes(rndToken); //todo ronnie should check the device twin preference if using confirmed or unconfirmed down LoRaMessageWrapper ackMessage = new LoRaMessageWrapper(ackLoRaMessage, LoRaMessageType.UnconfirmedDataDown, rndToken, datr, 0, freq, tmst); udpMsgForPktForwarder = ackMessage.PhysicalPayload.GetMessage(); linkCheckCmdResponse = null; //confirm the message to iot hub only if we are in time for a delivery if (c2dMsg != null) { //todo ronnie check if it is ok to do async so we make it in time to send the message _ = loraDeviceInfo.HubSender.CompleteAsync(c2dMsg); //bool rit = await loraDeviceInfo.HubSender.CompleteAsync(c2dMsg); //if (rit) // Logger.Log(loraDeviceInfo.DevEUI, $"completed the c2d msg to IoT Hub", Logger.LoggingLevel.Info); //else //{ // //we could not complete the msg so we send only a pushAck // PhysicalPayload pushAck = new PhysicalPayload(loraMessage.PhysicalPayload.token, PhysicalIdentifier.PUSH_ACK, null); // udpMsgForPktForwarder = pushAck.GetMessage(); // Logger.Log(loraDeviceInfo.DevEUI, $"could not complete the c2d msg to IoT Hub", Logger.LoggingLevel.Info); //} } } else { PhysicalPayload pushAck = new PhysicalPayload(loraMessage.PhysicalPayload.token, PhysicalIdentifier.PUSH_ACK, null); udpMsgForPktForwarder = pushAck.GetMessage(); //put back the c2d message to the queue for the next round //todo ronnie check abbandon logic especially in case of mqtt if (c2dMsg != null) { _ = await loraDeviceInfo.HubSender.AbandonAsync(c2dMsg); } Logger.Log(loraDeviceInfo.DevEUI, $"too late for down message, sending only ACK to gateway", Logger.LoggingLevel.Info); _ = loraDeviceInfo.HubSender.UpdateFcntAsync(loraDeviceInfo.FCntUp, null); } } //No ack requested and no c2d message we send the udp ack only to the gateway else if (loraMessage.LoRaMessageType == LoRaMessageType.UnconfirmedDataUp && c2dMsg == null) { PhysicalPayload pushAck = new PhysicalPayload(loraMessage.PhysicalPayload.token, PhysicalIdentifier.PUSH_ACK, null); udpMsgForPktForwarder = pushAck.GetMessage(); ////if ABP and 1 we reset the counter (loose frame counter) with force, if not we update normally //if (fcntup == 1 && String.IsNullOrEmpty(loraDeviceInfo.AppEUI)) // _ = loraDeviceInfo.HubSender.UpdateFcntAsync(fcntup, null, true); if (validFrameCounter) { _ = loraDeviceInfo.HubSender.UpdateFcntAsync(loraDeviceInfo.FCntUp, null); } } //If there is a link check command waiting if (linkCheckCmdResponse != null) { Byte[] devAddrCorrect = new byte[4]; Array.Copy(loraMessage.LoRaPayloadMessage.DevAddr.ToArray(), devAddrCorrect, 4); byte[] fctrl2 = new byte[1] { 32 }; Array.Reverse(devAddrCorrect); LoRaPayloadData macReply = new LoRaPayloadData(MType.ConfirmedDataDown, devAddrCorrect, fctrl2, BitConverter.GetBytes(loraDeviceInfo.FCntDown), null, new byte[1] { 0 }, linkCheckCmdResponse, 1); macReply.PerformEncryption(loraDeviceInfo.AppSKey); macReply.SetMic(loraDeviceInfo.NwkSKey); byte[] rndToken = new byte[2]; Random rnd = new Random(); rnd.NextBytes(rndToken); var datr = RegionFactory.CurrentRegion.GetDownstreamDR(loraMessage.PktFwdPayload.GetPktFwdMessage().Rxpks[0]); //todo should discuss about the logic in case of multi channel gateway. uint rfch = loraMessage.PktFwdPayload.GetPktFwdMessage().Rxpks[0].rfch; double freq = RegionFactory.CurrentRegion.GetDownstreamChannel(loraMessage.PktFwdPayload.GetPktFwdMessage().Rxpks[0]); long tmst = loraMessage.PktFwdPayload.GetPktFwdMessage().Rxpks[0].tmst + RegionFactory.CurrentRegion.receive_delay1 * 1000000; //todo ronnie should check the device twin preference if using confirmed or unconfirmed down LoRaMessageWrapper ackMessage = new LoRaMessageWrapper(macReply, LoRaMessageType.UnconfirmedDataDown, rndToken, datr, 0, freq, tmst); udpMsgForPktForwarder = ackMessage.PhysicalPayload.GetMessage(); } } else { Logger.Log(loraDeviceInfo.DevEUI, $"with devAddr {devAddr} check MIC failed. Device will be ignored from now on", Logger.LoggingLevel.Info); loraDeviceInfo.IsOurDevice = false; } } else { Logger.Log(loraDeviceInfo.DevEUI, $"ignore message because is not linked to this GatewayID", Logger.LoggingLevel.Info); } } else { Logger.Log(devAddr, $"device is not our device, ignore message", Logger.LoggingLevel.Info); } Logger.Log(loraDeviceInfo.DevEUI, $"processing time: {DateTime.UtcNow - startTimeProcessing}", Logger.LoggingLevel.Info); return(udpMsgForPktForwarder); }
public async Task Validate_Limits( uint payloadFcntUp, uint?deviceFcntUp, uint?deviceFcntDown, uint?startFcntUp, uint?startFcntDown, uint expectedFcntUp, uint expectedFcntDown, bool abpRelaxed, bool confirmed, bool supports32Bit = false, LoRaDeviceRequestFailedReason?failedReason = null) { var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateABPDevice(1, gatewayID: this.ServerConfiguration.GatewayID, supports32BitFcnt: supports32Bit)); var devEUI = simulatedDevice.LoRaDevice.DeviceID; var devAddr = simulatedDevice.LoRaDevice.DevAddr; this.LoRaDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null)).ReturnsAsync(true); var initialTwin = SetupTwins(deviceFcntUp, deviceFcntDown, startFcntUp, startFcntDown, abpRelaxed, supports32Bit, simulatedDevice, devEUI, devAddr); this.LoRaDeviceClient .Setup(x => x.GetTwinAsync()).Returns(() => { return(Task.FromResult(initialTwin)); }); uint?fcntUpSavedInTwin = null; uint?fcntDownSavedInTwin = null; this.LoRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>())) .Returns <TwinCollection>((t) => { fcntUpSavedInTwin = (uint)t[TwinProperty.FCntUp]; fcntDownSavedInTwin = (uint)t[TwinProperty.FCntDown]; return(Task.FromResult(true)); }); this.LoRaDeviceClient .Setup(x => x.ReceiveAsync(It.IsAny <TimeSpan>())) .ReturnsAsync((Message)null); var shouldReset = payloadFcntUp == 0 && abpRelaxed; if (shouldReset) { this.LoRaDeviceApi.Setup(x => x.ABPFcntCacheResetAsync(devEUI, It.IsAny <uint>(), It.IsNotNull <string>())).ReturnsAsync(true); } this.LoRaDeviceApi.Setup(x => x.SearchByDevAddrAsync(devAddr)) .ReturnsAsync(new SearchDevicesResult(new IoTHubDeviceInfo(devAddr, devEUI, "abc").AsList())); var memoryCache = new MemoryCache(new MemoryCacheOptions()); var deviceRegistry = new LoRaDeviceRegistry(this.ServerConfiguration, memoryCache, this.LoRaDeviceApi.Object, this.LoRaDeviceFactory); // Send to message processor var messageDispatcher = new MessageDispatcher( this.ServerConfiguration, deviceRegistry, this.FrameCounterUpdateStrategyProvider); WaitableLoRaRequest req = null; if (confirmed) { var payload = simulatedDevice.CreateConfirmedDataUpMessage("1234", fcnt: (uint)payloadFcntUp); var rxpk = payload.SerializeUplink(simulatedDevice.AppSKey, simulatedDevice.NwkSKey).Rxpk[0]; req = this.CreateWaitableRequest(rxpk); } else { var rxpk = simulatedDevice.CreateUnconfirmedMessageUplink("1234", fcnt: (uint)payloadFcntUp).Rxpk[0]; req = new WaitableLoRaRequest(rxpk, this.PacketForwarder); } messageDispatcher.DispatchRequest(req); Assert.True(await req.WaitCompleteAsync(-1)); if (failedReason.HasValue) { Assert.Equal(failedReason.Value, req.ProcessingFailedReason); } else { Assert.True(req.ProcessingSucceeded); Assert.True(this.LoRaDeviceFactory.TryGetLoRaDevice(devEUI, out var loRaDevice)); if (confirmed) { Assert.NotNull(req.ResponseDownlink); Assert.True(req.ProcessingSucceeded); Assert.Single(this.PacketForwarder.DownlinkMessages); var downlinkMessage = this.PacketForwarder.DownlinkMessages[0]; var payloadDataDown = new LoRaPayloadData(Convert.FromBase64String(downlinkMessage.Txpk.Data)); payloadDataDown.PerformEncryption(simulatedDevice.AppSKey); Assert.Equal(expectedFcntDown, payloadDataDown.GetFcnt()); } Assert.Equal(expectedFcntUp, loRaDevice.FCntUp); } }
public async Task Should_Reject( bool isConfirmed, bool hasMacInUpstream, bool hasMacInC2D, [CombinatorialValues("SF10BW125", "SF9BW125", "SF8BW125", "SF7BW125")] string datr) { const int InitialDeviceFcntUp = 9; const int InitialDeviceFcntDown = 20; var simulatedDevice = new SimulatedDevice( TestDeviceInfo.CreateABPDevice(1, gatewayID: this.ServerConfiguration.GatewayID), frmCntUp: InitialDeviceFcntUp, frmCntDown: InitialDeviceFcntDown); var loraDevice = this.CreateLoRaDevice(simulatedDevice); Rxpk rxpk = this.CreateUpstreamRxpk(isConfirmed, hasMacInUpstream, datr, simulatedDevice); if (!hasMacInUpstream) { this.LoRaDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null)) .ReturnsAsync(true); } var euRegion = RegionManager.EU868; var c2dMessageMacCommand = new DevStatusRequest(); var c2dMessageMacCommandSize = hasMacInC2D ? c2dMessageMacCommand.Length : 0; var upstreamMessageMacCommandSize = 0; string expectedDownlinkDatr; if (hasMacInUpstream) { upstreamMessageMacCommandSize = new LinkCheckAnswer(1, 1).Length; } expectedDownlinkDatr = datr; var c2dPayloadSize = euRegion.GetMaxPayloadSize(expectedDownlinkDatr) - c2dMessageMacCommandSize + 1 // make message too long on purpose - Constants.LORA_PROTOCOL_OVERHEAD_SIZE; var c2dMessagePayload = TestUtils.GeneratePayload("123457890", (int)c2dPayloadSize); var c2dMessage = new ReceivedLoRaCloudToDeviceMessage() { Payload = c2dMessagePayload, Fport = 1, }; if (hasMacInC2D) { c2dMessage.MacCommands = new[] { c2dMessageMacCommand }; } var cloudToDeviceMessage = c2dMessage.CreateMessage(); this.LoRaDeviceClient.Setup(x => x.ReceiveAsync(It.IsAny <TimeSpan>())) .ReturnsAsync(cloudToDeviceMessage); this.LoRaDeviceClient.Setup(x => x.RejectAsync(cloudToDeviceMessage)) .ReturnsAsync(true); var deviceRegistry = new LoRaDeviceRegistry(this.ServerConfiguration, this.NewNonEmptyCache(loraDevice), this.LoRaDeviceApi.Object, this.LoRaDeviceFactory); // Send to message processor var messageProcessor = new MessageDispatcher( this.ServerConfiguration, deviceRegistry, this.FrameCounterUpdateStrategyProvider); var request = this.CreateWaitableRequest(rxpk); messageProcessor.DispatchRequest(request); // Expectations // 1. Message was sent to IoT Hub Assert.True(await request.WaitCompleteAsync()); Assert.True(request.ProcessingSucceeded); var shouldHaveADownlink = isConfirmed || hasMacInUpstream; // 2. Return is downstream message ONLY // if is confirmed or had Mac commands in upstream message if (shouldHaveADownlink) { Assert.NotNull(request.ResponseDownlink); Assert.Equal(expectedDownlinkDatr, request.ResponseDownlink.Txpk.Datr); var downlinkMessage = this.PacketForwarder.DownlinkMessages[0]; var payloadDataDown = new LoRaPayloadData(Convert.FromBase64String(downlinkMessage.Txpk.Data)); payloadDataDown.PerformEncryption(loraDevice.AppSKey); Assert.Equal(payloadDataDown.DevAddr.ToArray(), LoRaTools.Utils.ConversionHelper.StringToByteArray(loraDevice.DevAddr)); Assert.Equal(LoRaMessageType.UnconfirmedDataDown, payloadDataDown.LoRaMessageType); if (hasMacInUpstream) { Assert.Equal(new LinkCheckAnswer(1, 1).Length, payloadDataDown.Frmpayload.Length); Assert.Equal(0, payloadDataDown.GetFPort()); } } else { Assert.Null(request.ResponseDownlink); } this.LoRaDeviceClient.VerifyAll(); this.LoRaDeviceApi.VerifyAll(); }
public async Task Should_Abandon( bool isConfirmed, bool hasMacInUpstream, bool hasMacInC2D, [CombinatorialValues("SF9BW125", "SF8BW125", "SF7BW125")] string datr) { const int InitialDeviceFcntUp = 9; const int InitialDeviceFcntDown = 20; var simulatedDevice = new SimulatedDevice( TestDeviceInfo.CreateABPDevice(1, gatewayID: this.ServerConfiguration.GatewayID), frmCntUp: InitialDeviceFcntUp, frmCntDown: InitialDeviceFcntDown); var loraDevice = this.CreateLoRaDevice(simulatedDevice); Rxpk rxpk = this.CreateUpstreamRxpk(isConfirmed, hasMacInUpstream, datr, simulatedDevice); if (!hasMacInUpstream) { this.LoRaDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null)) .ReturnsAsync(true); } var euRegion = RegionManager.EU868; var c2dMessageMacCommand = new DevStatusRequest(); var c2dMessageMacCommandSize = hasMacInC2D ? c2dMessageMacCommand.Length : 0; var upstreamMessageMacCommandSize = 0; string expectedDownlinkDatr; if (hasMacInUpstream) { upstreamMessageMacCommandSize = new LinkCheckAnswer(1, 1).Length; } expectedDownlinkDatr = euRegion.DRtoConfiguration[euRegion.RX2DefaultReceiveWindows.dr].configuration; var c2dPayloadSize = euRegion.GetMaxPayloadSize(expectedDownlinkDatr) - c2dMessageMacCommandSize + 1 // make message too long on purpose - Constants.LORA_PROTOCOL_OVERHEAD_SIZE; var c2dMessagePayload = TestUtils.GeneratePayload("123457890", (int)c2dPayloadSize); var c2dMessage = new ReceivedLoRaCloudToDeviceMessage() { Payload = c2dMessagePayload, Fport = 1, }; if (hasMacInC2D) { c2dMessage.MacCommands = new[] { c2dMessageMacCommand }; } var cloudToDeviceMessage = c2dMessage.CreateMessage(); this.LoRaDeviceClient.Setup(x => x.ReceiveAsync(It.IsAny <TimeSpan>())) .Callback <TimeSpan>((_) => { this.LoRaDeviceClient.Setup(x => x.ReceiveAsync(It.IsAny <TimeSpan>())) .ReturnsAsync((Message)null); }) .ReturnsAsync(cloudToDeviceMessage); this.LoRaDeviceClient.Setup(x => x.AbandonAsync(cloudToDeviceMessage)) .ReturnsAsync(true); var deviceRegistry = new LoRaDeviceRegistry(this.ServerConfiguration, this.NewNonEmptyCache(loraDevice), this.LoRaDeviceApi.Object, this.LoRaDeviceFactory); // Send to message processor var messageProcessor = new MessageDispatcher( this.ServerConfiguration, deviceRegistry, this.FrameCounterUpdateStrategyProvider); var request = this.CreateWaitableRequest(rxpk, startTimeOffset: TestUtils.GetStartTimeOffsetForSecondWindow(), constantElapsedTime: TimeSpan.FromMilliseconds(1002)); messageProcessor.DispatchRequest(request); // Expectations // 1. Message was sent to IoT Hub Assert.True(await request.WaitCompleteAsync()); Assert.True(request.ProcessingSucceeded); // 2. Return is downstream message Assert.NotNull(request.ResponseDownlink); Assert.Equal(expectedDownlinkDatr, request.ResponseDownlink.Txpk.Datr); var downlinkMessage = this.PacketForwarder.DownlinkMessages[0]; var payloadDataDown = new LoRaPayloadData(Convert.FromBase64String(downlinkMessage.Txpk.Data)); payloadDataDown.PerformEncryption(loraDevice.AppSKey); // 3. Fpending flag is set Assert.Equal((byte)FctrlEnum.FpendingOrClassB, payloadDataDown.Fctrl.Span[0] & (byte)FctrlEnum.FpendingOrClassB); Assert.Equal(payloadDataDown.DevAddr.ToArray(), LoRaTools.Utils.ConversionHelper.StringToByteArray(loraDevice.DevAddr)); Assert.Equal(LoRaMessageType.UnconfirmedDataDown, payloadDataDown.LoRaMessageType); // Expected Mac command is present if (hasMacInUpstream) { // Possible problem: manually casting frmPayload to array. No reversal. var frmPayload = payloadDataDown.Frmpayload.ToArray(); var macCommands = MacCommand.CreateServerMacCommandFromBytes(simulatedDevice.DevEUI, frmPayload); Assert.Single(macCommands); Assert.IsType <LinkCheckAnswer>(macCommands.First()); } else { Assert.Null(payloadDataDown.MacCommands); } this.LoRaDeviceClient.VerifyAll(); this.LoRaDeviceApi.VerifyAll(); }
public async Task Perform_TXPower_Adaptation_When_Needed() { uint deviceId = 44; int messageCount = 21; uint payloadFcnt = 0; const uint InitialDeviceFcntUp = 9; const uint InitialDeviceFcntDown = 0; var simulatedDevice = new SimulatedDevice( TestDeviceInfo.CreateABPDevice(deviceId, gatewayID: this.ServerConfiguration.GatewayID), frmCntUp: InitialDeviceFcntUp); var loraDevice = this.CreateLoRaDevice(simulatedDevice); this.LoRaDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null)) .ReturnsAsync(true); this.LoRaDeviceClient.Setup(x => x.ReceiveAsync(It.IsNotNull <TimeSpan>())) .ReturnsAsync((Message)null); int reportedDR = 0; int reportedTxPower = 0; this.LoRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>())) .Callback <TwinCollection>((t) => { if (t.Contains(TwinProperty.DataRate)) { reportedDR = t[TwinProperty.DataRate].Value; } if (t.Contains(TwinProperty.TxPower)) { reportedTxPower = t[TwinProperty.TxPower].Value; } }) .ReturnsAsync(true); var deviceRegistry = new LoRaDeviceRegistry(this.ServerConfiguration, this.NewNonEmptyCache(loraDevice), this.LoRaDeviceApi.Object, this.LoRaDeviceFactory); // Send to message processor var messageProcessor = new MessageDispatcher( this.ServerConfiguration, deviceRegistry, this.FrameCounterUpdateStrategyProvider); payloadFcnt = await this.InitializeCacheToDefaultValuesAsync(payloadFcnt, simulatedDevice, messageProcessor); // **************************************************** // First part gives very good connectivity and ensure we set power to minimum and DR increase to 5 // **************************************************** // set to very good lsnr and DR3 float currentLsnr = 20; string currentDR = "SF9BW125"; // todo add case without buffer for (int i = 0; i < messageCount; i++) { payloadFcnt = await this.SendMessage(currentLsnr, currentDR, payloadFcnt, simulatedDevice, messageProcessor, (byte)((int)LoRaTools.LoRaMessage.FctrlEnum.ADR)); } var payload = simulatedDevice.CreateUnconfirmedDataUpMessage("1234", fcnt: payloadFcnt, fctrl: (byte)((int)LoRaTools.LoRaMessage.FctrlEnum.ADRAckReq + (int)LoRaTools.LoRaMessage.FctrlEnum.ADR)); var rxpk = payload.SerializeUplink(simulatedDevice.AppSKey, simulatedDevice.NwkSKey, lsnr: currentLsnr, datr: currentDR).Rxpk[0]; var request = this.CreateWaitableRequest(rxpk); messageProcessor.DispatchRequest(request); Assert.True(await request.WaitCompleteAsync()); Assert.NotNull(request.ResponseDownlink); Assert.Equal(2, this.PacketForwarder.DownlinkMessages.Count); var downlinkMessage = this.PacketForwarder.DownlinkMessages[1]; var payloadDataDown = new LoRaPayloadData(Convert.FromBase64String(downlinkMessage.Txpk.Data)); // We expect a mac command in the payload Assert.Equal(5, payloadDataDown.Frmpayload.Span.Length); var decryptedPayload = payloadDataDown.PerformEncryption(simulatedDevice.NwkSKey); Assert.Equal(0, payloadDataDown.Fport.Span[0]); Assert.Equal((byte)CidEnum.LinkADRCmd, decryptedPayload[0]); var linkAdr = new LinkADRRequest(decryptedPayload); Assert.Equal(5, linkAdr.DataRate); Assert.Equal(5, loraDevice.DataRate); Assert.Equal(5, reportedDR); Assert.Equal(7, linkAdr.TxPower); Assert.Equal(7, loraDevice.TxPower); Assert.Equal(7, reportedTxPower); Assert.Equal(payloadDataDown.DevAddr.ToArray(), LoRaTools.Utils.ConversionHelper.StringToByteArray(loraDevice.DevAddr)); Assert.False(payloadDataDown.IsConfirmed); Assert.Equal(LoRaMessageType.UnconfirmedDataDown, payloadDataDown.LoRaMessageType); // 4. Frame counter up was updated Assert.Equal(payloadFcnt, loraDevice.FCntUp); // 5. Frame counter down is updated Assert.Equal(InitialDeviceFcntDown + 1, loraDevice.FCntDown); Assert.Equal(InitialDeviceFcntDown + 1, payloadDataDown.GetFcnt()); // **************************************************** // Second part reduce connectivity and verify the DR stay to 5 and power set to max // **************************************************** currentLsnr = -50; // DR5 currentDR = "SF7BW125"; // todo add case without buffer for (int i = 0; i < messageCount; i++) { payloadFcnt = await this.SendMessage(currentLsnr, currentDR, payloadFcnt, simulatedDevice, messageProcessor, (byte)((int)LoRaTools.LoRaMessage.FctrlEnum.ADR)); } payload = simulatedDevice.CreateUnconfirmedDataUpMessage("1234", fcnt: payloadFcnt, fctrl: (byte)((int)LoRaTools.LoRaMessage.FctrlEnum.ADRAckReq + (int)LoRaTools.LoRaMessage.FctrlEnum.ADR)); rxpk = payload.SerializeUplink(simulatedDevice.AppSKey, simulatedDevice.NwkSKey, lsnr: currentLsnr, datr: currentDR).Rxpk[0]; request = this.CreateWaitableRequest(rxpk); messageProcessor.DispatchRequest(request); Assert.True(await request.WaitCompleteAsync()); Assert.NotNull(request.ResponseDownlink); Assert.Equal(3, this.PacketForwarder.DownlinkMessages.Count); downlinkMessage = this.PacketForwarder.DownlinkMessages[2]; payloadDataDown = new LoRaPayloadData(Convert.FromBase64String(downlinkMessage.Txpk.Data)); // We expect a mac command in the payload Assert.Equal(5, payloadDataDown.Frmpayload.Span.Length); decryptedPayload = payloadDataDown.PerformEncryption(simulatedDevice.NwkSKey); Assert.Equal(0, payloadDataDown.Fport.Span[0]); Assert.Equal((byte)CidEnum.LinkADRCmd, decryptedPayload[0]); linkAdr = new LinkADRRequest(decryptedPayload); Assert.Equal(5, linkAdr.DataRate); Assert.Equal(5, loraDevice.DataRate); Assert.Equal(5, reportedDR); Assert.Equal(0, linkAdr.TxPower); Assert.Equal(0, loraDevice.TxPower); Assert.Equal(0, reportedTxPower); Assert.Equal(payloadDataDown.DevAddr.ToArray(), LoRaTools.Utils.ConversionHelper.StringToByteArray(loraDevice.DevAddr)); Assert.False(payloadDataDown.IsConfirmed); Assert.Equal(LoRaMessageType.UnconfirmedDataDown, payloadDataDown.LoRaMessageType); // 4. Frame counter up was updated Assert.Equal(payloadFcnt, loraDevice.FCntUp); // 5. Frame counter down is updated Assert.Equal(InitialDeviceFcntDown + 2, loraDevice.FCntDown); Assert.Equal(InitialDeviceFcntDown + 2, payloadDataDown.GetFcnt()); // Expectations // 1. Message was sent to IoT Hub this.LoRaDeviceClient.VerifyAll(); this.LoRaDeviceApi.VerifyAll(); Assert.True(request.ProcessingSucceeded); }
public void TestMultipleRxpks() { // create multiple Rxpk message string jsonUplink = @"{""rxpk"":[ { ""time"":""2013-03-31T16:21:17.528002Z"", ""tmst"":3512348611, ""chan"":2, ""rfch"":0, ""freq"":866.349812, ""stat"":1, ""modu"":""LORA"", ""datr"":""SF7BW125"", ""codr"":""4/6"", ""rssi"":-35, ""lsnr"":5.1, ""size"":32, ""data"":""AAQDAgEEAwIBBQQDAgUEAwItEGqZDhI="" }, { ""time"":""2013-03-31T16:21:17.528002Z"", ""tmst"":3512348611, ""chan"":2, ""rfch"":0, ""freq"":866.349812, ""stat"":1, ""modu"":""LORA"", ""datr"":""SF7BW125"", ""codr"":""4/6"", ""rssi"":-35, ""lsnr"":5.1, ""size"":32, ""data"":""QAQDAgGAAQABppRkJhXWw7WC"" },{ ""time"":""2013-03-31T16:21:17.528002Z"", ""tmst"":3512348611, ""chan"":2, ""rfch"":0, ""freq"":866.349812, ""stat"":1, ""modu"":""LORA"", ""datr"":""SF7BW125"", ""codr"":""4/6"", ""rssi"":-35, ""lsnr"":5.1, ""size"":32, ""data"":""AAQDAgEEAwIBBQQDAgUEAwItEGqZDhI="" } ]}"; var multiRxpkInput = Encoding.Default.GetBytes(jsonUplink); byte[] physicalUpstreamPyld = new byte[12]; physicalUpstreamPyld[0] = 2; List <Rxpk> rxpk = Rxpk.CreateRxpk(physicalUpstreamPyld.Concat(multiRxpkInput).ToArray()); Assert.Equal(3, rxpk.Count); TestRxpk(rxpk[0]); TestRxpk(rxpk[2]); Assert.True(LoRaPayload.TryCreateLoRaPayload(rxpk[1], out LoRaPayload jsonUplinkUnconfirmedMessage)); Assert.Equal(LoRaMessageType.UnconfirmedDataUp, jsonUplinkUnconfirmedMessage.LoRaMessageType); LoRaPayloadData loRaPayloadUplinkObj = (LoRaPayloadData)jsonUplinkUnconfirmedMessage; Assert.True(loRaPayloadUplinkObj.Fcnt.Span.SequenceEqual(new byte[2] { 1, 0 })); Assert.True(loRaPayloadUplinkObj.DevAddr.Span.SequenceEqual(new byte[4] { 1, 2, 3, 4 })); byte[] loRaPayloadUplinkNwkKey = new byte[16] { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }; Assert.True(loRaPayloadUplinkObj.CheckMic(ConversionHelper.ByteArrayToString(loRaPayloadUplinkNwkKey))); byte[] loRaPayloadUplinkAppKey = new byte[16] { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; var key = ConversionHelper.ByteArrayToString(loRaPayloadUplinkAppKey); Assert.Equal("hello", Encoding.ASCII.GetString(loRaPayloadUplinkObj.PerformEncryption(key))); }
public async Task When_Device_Prefers_Second_Window_Should_Send_Downstream_In_Second_Window() { const int PayloadFcnt = 10; const int InitialDeviceFcntUp = 9; const int InitialDeviceFcntDown = 20; var simulatedDevice = new SimulatedDevice( TestDeviceInfo.CreateABPDevice(1, gatewayID: this.ServerConfiguration.GatewayID), frmCntUp: InitialDeviceFcntUp, frmCntDown: InitialDeviceFcntDown); var devAddr = simulatedDevice.DevAddr; var devEUI = simulatedDevice.DevEUI; var loraDeviceClient = new Mock <ILoRaDeviceClient>(MockBehavior.Strict); // Will get twin to initialize LoRaDevice var deviceTwin = TestUtils.CreateABPTwin( simulatedDevice, desiredProperties: new Dictionary <string, object> { { TwinProperty.PreferredWindow, 2 } }); loraDeviceClient.Setup(x => x.GetTwinAsync()).ReturnsAsync(deviceTwin); loraDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null)) .ReturnsAsync(true); loraDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>())) .ReturnsAsync(true); var cloudToDeviceMessage = new Message(Encoding.UTF8.GetBytes("c2d")); cloudToDeviceMessage.Properties[MessageProcessor.FPORT_MSG_PROPERTY_KEY] = "1"; loraDeviceClient.SetupSequence(x => x.ReceiveAsync(It.IsAny <TimeSpan>())) .ReturnsAsync(cloudToDeviceMessage) .Returns(this.EmptyAdditionalMessageReceiveAsync); // 2nd cloud to device message does not return anything loraDeviceClient.Setup(x => x.CompleteAsync(cloudToDeviceMessage)) .ReturnsAsync(true); var payloadDecoder = new Mock <ILoRaPayloadDecoder>(); var loRaDeviceAPI = new Mock <LoRaDeviceAPIServiceBase>(); loRaDeviceAPI.Setup(x => x.SearchByDevAddrAsync(devAddr)) .ReturnsAsync(new SearchDevicesResult(new IoTHubDeviceInfo(devAddr, devEUI, "adad").AsList())); var loRaDeviceRegistry = new LoRaDeviceRegistry(this.ServerConfiguration, new MemoryCache(new MemoryCacheOptions()), loRaDeviceAPI.Object, new TestLoRaDeviceFactory(loraDeviceClient.Object)); // Send to message processor var messageProcessor = new MessageProcessor( this.ServerConfiguration, loRaDeviceRegistry, new LoRaDeviceFrameCounterUpdateStrategyFactory(this.ServerConfiguration.GatewayID, loRaDeviceAPI.Object), new LoRaPayloadDecoder()); var payload = simulatedDevice.CreateUnconfirmedDataUpMessage("1234", fcnt: PayloadFcnt); var rxpk = payload.SerializeUplink(simulatedDevice.AppSKey, simulatedDevice.NwkSKey).Rxpk[0]; var actual = await messageProcessor.ProcessMessageAsync(rxpk); // Expectations // 1. Message was sent to IoT Hub loraDeviceClient.VerifyAll(); // 2. Single gateway frame counter strategy was used this.FrameCounterUpdateStrategyFactory.VerifyAll(); // 3. Return is downstream message Assert.NotNull(actual); Assert.IsType <DownlinkPktFwdMessage>(actual); var downlinkMessage = (DownlinkPktFwdMessage)actual; var euRegion = RegionFactory.CreateEU868Region(); // Ensure we are using second window frequency Assert.Equal(euRegion.RX2DefaultReceiveWindows.frequency, actual.Txpk.Freq); // Ensure we are using second window datr Assert.Equal(euRegion.DRtoConfiguration[euRegion.RX2DefaultReceiveWindows.dr].configuration, actual.Txpk.Datr); // Ensure tmst was computed to 2 seconds (2 windows in Europe) Assert.Equal(2000000, actual.Txpk.Tmst); // Get the device from registry var deviceDictionary = loRaDeviceRegistry.InternalGetCachedDevicesForDevAddr(devAddr); Assert.True(deviceDictionary.TryGetValue(simulatedDevice.DevEUI, out var loRaDevice)); var payloadDataDown = new LoRaPayloadData(Convert.FromBase64String(downlinkMessage.Txpk.Data)); payloadDataDown.PerformEncryption(loRaDevice.AppSKey); Assert.Equal(payloadDataDown.DevAddr.ToArray(), ConversionHelper.StringToByteArray(loRaDevice.DevAddr)); Assert.False(payloadDataDown.IsConfirmed()); Assert.Equal(LoRaMessageType.UnconfirmedDataDown, payloadDataDown.LoRaMessageType); // 4. Frame counter up was updated Assert.Equal(PayloadFcnt, loRaDevice.FCntUp); // 5. Frame counter down is updated var expectedFcntDown = InitialDeviceFcntDown + 10 + 1; // adding 10 as buffer when creating a new device instance Assert.Equal(expectedFcntDown, loRaDevice.FCntDown); Assert.Equal(expectedFcntDown, payloadDataDown.GetFcnt()); // 6. Frame count has no pending changes Assert.False(loRaDevice.HasFrameCountChanges); }
public async Task OTAA_Confirmed_With_Cloud_To_Device_Message_Returns_Downstream_Message() { const int PayloadFcnt = 10; const int InitialDeviceFcntUp = 9; const int InitialDeviceFcntDown = 20; var simulatedDevice = new SimulatedDevice( TestDeviceInfo.CreateABPDevice(1, gatewayID: this.ServerConfiguration.GatewayID), frmCntUp: InitialDeviceFcntUp, frmCntDown: InitialDeviceFcntDown); var loraDeviceClient = new Mock <ILoRaDeviceClient>(MockBehavior.Strict); var loraDevice = TestUtils.CreateFromSimulatedDevice(simulatedDevice, loraDeviceClient.Object); loraDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null)) .ReturnsAsync(true); loraDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>())) .ReturnsAsync(true); var cloudToDeviceMessage = new Message(Encoding.UTF8.GetBytes("c2d")); cloudToDeviceMessage.Properties[MessageProcessor.FPORT_MSG_PROPERTY_KEY] = "1"; loraDeviceClient.SetupSequence(x => x.ReceiveAsync(It.IsAny <TimeSpan>())) .ReturnsAsync(cloudToDeviceMessage) .ReturnsAsync((Message)null); // 2nd cloud to device message does not return anything loraDeviceClient.Setup(x => x.CompleteAsync(cloudToDeviceMessage)) .ReturnsAsync(true); var payloadDecoder = new Mock <ILoRaPayloadDecoder>(); this.LoRaDeviceRegistry.Setup(x => x.GetDeviceForPayloadAsync(It.IsAny <LoRaTools.LoRaMessage.LoRaPayloadData>())) .ReturnsAsync(loraDevice); // Setup frame counter strategy this.FrameCounterUpdateStrategyFactory.Setup(x => x.GetSingleGatewayStrategy()) .Returns(new SingleGatewayFrameCounterUpdateStrategy()); // Send to message processor var messageProcessor = new MessageProcessor( this.ServerConfiguration, this.LoRaDeviceRegistry.Object, this.FrameCounterUpdateStrategyFactory.Object, payloadDecoder.Object); var payload = simulatedDevice.CreateConfirmedDataUpMessage("1234", fcnt: PayloadFcnt); var rxpk = payload.SerializeUplink(simulatedDevice.AppSKey, simulatedDevice.NwkSKey).Rxpk[0]; var actual = await messageProcessor.ProcessMessageAsync(rxpk); // Expectations // 1. Message was sent to IoT Hub loraDeviceClient.VerifyAll(); // 2. Single gateway frame counter strategy was used this.FrameCounterUpdateStrategyFactory.VerifyAll(); // 3. Return is downstream message Assert.NotNull(actual); Assert.IsType <DownlinkPktFwdMessage>(actual); var downlinkMessage = (DownlinkPktFwdMessage)actual; var payloadDataDown = new LoRaPayloadData(Convert.FromBase64String(downlinkMessage.Txpk.Data)); payloadDataDown.PerformEncryption(loraDevice.AppSKey); Assert.Equal(payloadDataDown.DevAddr.ToArray(), LoRaTools.Utils.ConversionHelper.StringToByteArray(loraDevice.DevAddr)); Assert.False(payloadDataDown.IsConfirmed()); Assert.Equal(LoRaMessageType.UnconfirmedDataDown, payloadDataDown.LoRaMessageType); // 4. Frame counter up was updated Assert.Equal(PayloadFcnt, loraDevice.FCntUp); // 5. Frame counter down is updated Assert.Equal(InitialDeviceFcntDown + 1, loraDevice.FCntDown); Assert.Equal(InitialDeviceFcntDown + 1, payloadDataDown.GetFcnt()); // 6. Frame count has no pending changes Assert.False(loraDevice.HasFrameCountChanges); }
public async Task MessageProcessor_End2End_NoDep_ClassC_CloudToDeviceMessage_SizeLimit_Should_Accept( bool hasMacInC2D) { var simulatedDevice = new SimulatedDevice( TestDeviceInfo.CreateABPDevice( 1, deviceClassType: 'c', gatewayID: this.serverConfiguration.GatewayID)); var devEUI = simulatedDevice.DevEUI; this.deviceApi.Setup(x => x.SearchByDevEUIAsync(devEUI)) .ReturnsAsync(new SearchDevicesResult( new IoTHubDeviceInfo(string.Empty, devEUI, "123").AsList())); var twin = simulatedDevice.CreateABPTwin(reportedProperties: new Dictionary <string, object> { { TwinProperty.Region, LoRaRegionType.EU868.ToString() } }); this.deviceClient.Setup(x => x.GetTwinAsync()) .ReturnsAsync(twin); var c2dMessageMacCommand = new DevStatusRequest(); var c2dMessageMacCommandSize = hasMacInC2D ? c2dMessageMacCommand.Length : 0; var datr = this.loRaRegion.DRtoConfiguration[this.loRaRegion.RX2DefaultReceiveWindows.dr].configuration; var c2dPayloadSize = this.loRaRegion.GetMaxPayloadSize(datr) - c2dMessageMacCommandSize - Constants.LORA_PROTOCOL_OVERHEAD_SIZE; var c2dMsgPayload = this.GeneratePayload("123457890", (int)c2dPayloadSize); var c2d = new ReceivedLoRaCloudToDeviceMessage() { DevEUI = devEUI, Payload = c2dMsgPayload, Fport = 1, }; if (hasMacInC2D) { c2d.MacCommands = new[] { c2dMessageMacCommand }; } var cloudToDeviceMessage = c2d.CreateMessage(); var target = new DefaultClassCDevicesMessageSender( this.serverConfiguration, this.loRaDeviceRegistry, this.PacketForwarder, this.frameCounterStrategyProvider); // Expectations // Verify that C2D message is sent Assert.True(await target.SendAsync(c2d)); // Verify that exactly one C2D message was received Assert.Single(this.PacketForwarder.DownlinkMessages); // Verify donwlink message is correct this.EnsureDownlinkIsCorrect( this.PacketForwarder.DownlinkMessages.First(), simulatedDevice, c2d); // Get C2D message payload var downlinkMessage = this.PacketForwarder.DownlinkMessages[0]; var payloadDataDown = new LoRaPayloadData( Convert.FromBase64String(downlinkMessage.Txpk.Data)); payloadDataDown.PerformEncryption(simulatedDevice.AppSKey); // Verify that expected Mac commands are present var expectedMacCommandsCount = 0; if (hasMacInC2D) { expectedMacCommandsCount++; } if (expectedMacCommandsCount > 0) { var macCommands = MacCommand.CreateServerMacCommandFromBytes( simulatedDevice.DevEUI, payloadDataDown.Fopts); Assert.Equal(expectedMacCommandsCount, macCommands.Count); } else { Assert.Null(payloadDataDown.MacCommands); } this.deviceApi.VerifyAll(); this.deviceClient.VerifyAll(); }
public async Task Perform_NbRep_Adaptation_When_Needed() { uint deviceId = 31; string currentDR = "SF8BW125"; int currentLsnr = -20; int messageCount = 20; uint payloadFcnt = 0; const uint InitialDeviceFcntUp = 1; const uint ExpectedDeviceFcntDown = 4; var simulatedDevice = new SimulatedDevice( TestDeviceInfo.CreateABPDevice(deviceId, gatewayID: this.ServerConfiguration.GatewayID), frmCntUp: InitialDeviceFcntUp); var loraDevice = this.CreateLoRaDevice(simulatedDevice); this.LoRaDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null)) .ReturnsAsync(true); this.LoRaDeviceClient.Setup(x => x.ReceiveAsync(It.IsNotNull <TimeSpan>())) .ReturnsAsync((Message)null); int reportedNbRep = 0; this.LoRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>())) .Callback <TwinCollection>((t) => { if (t.Contains(TwinProperty.NbRep)) { reportedNbRep = (int)t[TwinProperty.NbRep]; } }) .ReturnsAsync(true); var deviceRegistry = new LoRaDeviceRegistry(this.ServerConfiguration, this.NewNonEmptyCache(loraDevice), this.LoRaDeviceApi.Object, this.LoRaDeviceFactory); // Send to message processor var messageProcessor = new MessageDispatcher( this.ServerConfiguration, deviceRegistry, this.FrameCounterUpdateStrategyProvider); payloadFcnt = await this.InitializeCacheToDefaultValuesAsync(payloadFcnt, simulatedDevice, messageProcessor); // **************************************************** // First part send messages with spaces in fcnt to simulate wrong network connectivity // **************************************************** // send a message with a fcnt every 4. for (int i = 0; i < messageCount; i++) { payloadFcnt = await this.SendMessage(currentLsnr, currentDR, payloadFcnt + 3, simulatedDevice, messageProcessor, (byte)((int)LoRaTools.LoRaMessage.FctrlEnum.ADR)); } var payload = simulatedDevice.CreateUnconfirmedDataUpMessage("1234", fcnt: payloadFcnt, fctrl: (byte)((int)LoRaTools.LoRaMessage.FctrlEnum.ADRAckReq + (int)LoRaTools.LoRaMessage.FctrlEnum.ADR)); var rxpk = payload.SerializeUplink(simulatedDevice.AppSKey, simulatedDevice.NwkSKey, lsnr: currentLsnr, datr: currentDR).Rxpk[0]; var request = this.CreateWaitableRequest(rxpk); messageProcessor.DispatchRequest(request); Assert.True(await request.WaitCompleteAsync()); // Expectations // 1. Message was sent to IoT Hub this.LoRaDeviceClient.VerifyAll(); this.LoRaDeviceApi.VerifyAll(); Assert.True(request.ProcessingSucceeded); Assert.NotNull(request.ResponseDownlink); Assert.Equal(2, this.PacketForwarder.DownlinkMessages.Count); var downlinkMessage = this.PacketForwarder.DownlinkMessages[1]; var payloadDataDown = new LoRaPayloadData(Convert.FromBase64String(downlinkMessage.Txpk.Data)); // We expect a mac command in the payload Assert.Equal(5, payloadDataDown.Frmpayload.Span.Length); var decryptedPayload = payloadDataDown.PerformEncryption(simulatedDevice.NwkSKey); Array.Reverse(decryptedPayload); Assert.Equal(0, payloadDataDown.Fport.Span[0]); Assert.Equal((byte)CidEnum.LinkADRCmd, decryptedPayload[0]); var linkAdr = new LinkADRRequest(decryptedPayload); Assert.Equal(3, reportedNbRep); Assert.Equal(3, linkAdr.NbRep); Assert.Equal(3, loraDevice.NbRep); // **************************************************** // Second part send normal messages to decrease NbRep // **************************************************** // send a message with a fcnt every 1 for (int i = 0; i < messageCount; i++) { payloadFcnt = await this.SendMessage(currentLsnr, currentDR, payloadFcnt, simulatedDevice, messageProcessor, (byte)((int)LoRaTools.LoRaMessage.FctrlEnum.ADR)); } payload = simulatedDevice.CreateUnconfirmedDataUpMessage("1234", fcnt: payloadFcnt, fctrl: (byte)((int)LoRaTools.LoRaMessage.FctrlEnum.ADRAckReq + (int)LoRaTools.LoRaMessage.FctrlEnum.ADR)); rxpk = payload.SerializeUplink(simulatedDevice.AppSKey, simulatedDevice.NwkSKey, lsnr: currentLsnr, datr: currentDR).Rxpk[0]; request = this.CreateWaitableRequest(rxpk); messageProcessor.DispatchRequest(request); Assert.True(await request.WaitCompleteAsync()); // Expectations // 1. Message was sent to IoT Hub this.LoRaDeviceClient.VerifyAll(); this.LoRaDeviceApi.VerifyAll(); Assert.True(request.ProcessingSucceeded); Assert.NotNull(request.ResponseDownlink); Assert.Equal(3, this.PacketForwarder.DownlinkMessages.Count); downlinkMessage = this.PacketForwarder.DownlinkMessages[2]; payloadDataDown = new LoRaPayloadData(Convert.FromBase64String(downlinkMessage.Txpk.Data)); // We expect a mac command in the payload Assert.Equal(5, payloadDataDown.Frmpayload.Span.Length); decryptedPayload = payloadDataDown.PerformEncryption(simulatedDevice.NwkSKey); Assert.Equal(0, payloadDataDown.Fport.Span[0]); Assert.Equal((byte)CidEnum.LinkADRCmd, decryptedPayload[0]); linkAdr = new LinkADRRequest(decryptedPayload); Assert.Equal(2, reportedNbRep); Assert.Equal(2, linkAdr.NbRep); Assert.Equal(2, loraDevice.NbRep); // in case no payload the mac is in the FRMPayload and is decrypted with NwkSKey Assert.Equal(payloadDataDown.DevAddr.ToArray(), LoRaTools.Utils.ConversionHelper.StringToByteArray(loraDevice.DevAddr)); Assert.False(payloadDataDown.IsConfirmed); Assert.Equal(LoRaMessageType.UnconfirmedDataDown, payloadDataDown.LoRaMessageType); // 4. Frame counter up was updated Assert.Equal(payloadFcnt, loraDevice.FCntUp); // **************************************************** // Third part send normal messages to decrease NbRep to 1 // **************************************************** // send a message with a fcnt every 1 for (int i = 0; i < messageCount; i++) { payloadFcnt = await this.SendMessage(currentLsnr, currentDR, payloadFcnt, simulatedDevice, messageProcessor, (byte)((int)LoRaTools.LoRaMessage.FctrlEnum.ADR)); } payload = simulatedDevice.CreateUnconfirmedDataUpMessage("1234", fcnt: payloadFcnt, fctrl: (byte)((int)LoRaTools.LoRaMessage.FctrlEnum.ADRAckReq + (int)LoRaTools.LoRaMessage.FctrlEnum.ADR)); rxpk = payload.SerializeUplink(simulatedDevice.AppSKey, simulatedDevice.NwkSKey, lsnr: currentLsnr, datr: currentDR).Rxpk[0]; request = this.CreateWaitableRequest(rxpk); messageProcessor.DispatchRequest(request); Assert.True(await request.WaitCompleteAsync()); // Expectations // 1. Message was sent to IoT Hub this.LoRaDeviceClient.VerifyAll(); this.LoRaDeviceApi.VerifyAll(); Assert.True(request.ProcessingSucceeded); Assert.NotNull(request.ResponseDownlink); Assert.Equal(4, this.PacketForwarder.DownlinkMessages.Count); downlinkMessage = this.PacketForwarder.DownlinkMessages[3]; payloadDataDown = new LoRaPayloadData(Convert.FromBase64String(downlinkMessage.Txpk.Data)); // We expect a mac command in the payload Assert.Equal(5, payloadDataDown.Frmpayload.Span.Length); decryptedPayload = payloadDataDown.PerformEncryption(simulatedDevice.NwkSKey); Assert.Equal(0, payloadDataDown.Fport.Span[0]); Assert.Equal((byte)CidEnum.LinkADRCmd, decryptedPayload[0]); linkAdr = new LinkADRRequest(decryptedPayload); Assert.Equal(1, reportedNbRep); Assert.Equal(1, linkAdr.NbRep); Assert.Equal(1, loraDevice.NbRep); // 5. Frame counter down is updated Assert.Equal(ExpectedDeviceFcntDown, loraDevice.FCntDown); Assert.Equal(ExpectedDeviceFcntDown, payloadDataDown.GetFcnt()); }
public async Task Perform_Rate_Adapatation_When_Possible(uint deviceId, int count, int expectedDR, int expectedTXPower, int expectedNbRep, uint initialDeviceFcntUp) { uint payloadFcnt = 0; const uint InitialDeviceFcntDown = 0; var simulatedDevice = new SimulatedDevice( TestDeviceInfo.CreateABPDevice(deviceId, gatewayID: this.ServerConfiguration.GatewayID), frmCntUp: initialDeviceFcntUp); var loraDevice = this.CreateLoRaDevice(simulatedDevice); this.LoRaDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null)) .ReturnsAsync(true); this.LoRaDeviceClient.Setup(x => x.ReceiveAsync(It.IsNotNull <TimeSpan>())) .ReturnsAsync((Message)null); this.LoRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>())) .ReturnsAsync(true); var deviceRegistry = new LoRaDeviceRegistry(this.ServerConfiguration, this.NewNonEmptyCache(loraDevice), this.LoRaDeviceApi.Object, this.LoRaDeviceFactory); // Send to message processor var messageProcessor = new MessageDispatcher( this.ServerConfiguration, deviceRegistry, this.FrameCounterUpdateStrategyProvider); // In this case we want to simulate a cache failure, so we don't initialize the cache. if (deviceId != 12) { payloadFcnt = await this.InitializeCacheToDefaultValuesAsync(payloadFcnt, simulatedDevice, messageProcessor); } else { var payloadInt = simulatedDevice.CreateConfirmedDataUpMessage("1234", fcnt: payloadFcnt); var rxpkInt = payloadInt.SerializeUplink(simulatedDevice.AppSKey, simulatedDevice.NwkSKey).Rxpk[0]; var requestInt = this.CreateWaitableRequest(rxpkInt); messageProcessor.DispatchRequest(requestInt); Assert.True(await requestInt.WaitCompleteAsync(-1)); payloadFcnt++; } for (int i = 0; i < count; i++) { var payloadInt = simulatedDevice.CreateUnconfirmedDataUpMessage("1234", fcnt: payloadFcnt, fctrl: (byte)((int)LoRaTools.LoRaMessage.FctrlEnum.ADR)); var rxpkInt = payloadInt.SerializeUplink(simulatedDevice.AppSKey, simulatedDevice.NwkSKey).Rxpk[0]; var requestInt = this.CreateWaitableRequest(rxpkInt); messageProcessor.DispatchRequest(requestInt); Assert.True(await requestInt.WaitCompleteAsync(-1)); payloadFcnt++; } var payload = simulatedDevice.CreateUnconfirmedDataUpMessage("1234", fcnt: payloadFcnt, fctrl: (byte)((int)LoRaTools.LoRaMessage.FctrlEnum.ADRAckReq + (int)LoRaTools.LoRaMessage.FctrlEnum.ADR)); var rxpk = payload.SerializeUplink(simulatedDevice.AppSKey, simulatedDevice.NwkSKey).Rxpk[0]; var request = this.CreateWaitableRequest(rxpk); messageProcessor.DispatchRequest(request); Assert.True(await request.WaitCompleteAsync()); // Expectations // 1. Message was sent to IoT Hub this.LoRaDeviceClient.VerifyAll(); this.LoRaDeviceApi.VerifyAll(); Assert.NotNull(request.ResponseDownlink); Assert.True(request.ProcessingSucceeded); Assert.Equal(2, this.PacketForwarder.DownlinkMessages.Count); var downlinkMessage = this.PacketForwarder.DownlinkMessages[1]; var payloadDataDown = new LoRaPayloadData(Convert.FromBase64String(downlinkMessage.Txpk.Data)); // in this case we expect a null payload if (deviceId == 11) { Assert.Equal(0, payloadDataDown.Frmpayload.Span.Length); } else { // We expect a mac command in the payload Assert.Equal(5, payloadDataDown.Frmpayload.Span.Length); var decryptedPayload = payloadDataDown.PerformEncryption(simulatedDevice.NwkSKey); Assert.Equal(0, payloadDataDown.Fport.Span[0]); Assert.Equal((byte)CidEnum.LinkADRCmd, decryptedPayload[0]); var linkAdr = new LinkADRRequest(decryptedPayload); Assert.Equal(expectedDR, linkAdr.DataRate); Assert.Equal(expectedDR, loraDevice.DataRate); Assert.Equal(expectedTXPower, linkAdr.TxPower); Assert.Equal(expectedTXPower, loraDevice.TxPower); Assert.Equal(expectedNbRep, linkAdr.NbRep); Assert.Equal(expectedNbRep, loraDevice.NbRep); } // in case no payload the mac is in the FRMPayload and is decrypted with NwkSKey Assert.Equal(payloadDataDown.DevAddr.ToArray(), LoRaTools.Utils.ConversionHelper.StringToByteArray(loraDevice.DevAddr)); Assert.False(payloadDataDown.IsConfirmed); Assert.Equal(LoRaMessageType.UnconfirmedDataDown, payloadDataDown.LoRaMessageType); // 4. Frame counter up was updated Assert.Equal(payloadFcnt, loraDevice.FCntUp); // 5. Frame counter down is updated Assert.Equal(InitialDeviceFcntDown + 1, loraDevice.FCntDown); Assert.Equal(InitialDeviceFcntDown + 1, payloadDataDown.GetFcnt()); }
public async Task Should_Accept( bool isConfirmed, bool hasMacInUpstream, bool hasMacInC2D, bool isTooLongForUpstreamMacCommandInAnswer, bool isSendingInRx2, [CombinatorialValues("SF10BW125", "SF9BW125", "SF8BW125", "SF7BW125")] string datr) { const int InitialDeviceFcntUp = 9; const int InitialDeviceFcntDown = 20; // This scenario makes no sense if (hasMacInUpstream && isTooLongForUpstreamMacCommandInAnswer) { return; } var simulatedDevice = new SimulatedDevice( TestDeviceInfo.CreateABPDevice(1, gatewayID: this.ServerConfiguration.GatewayID), frmCntUp: InitialDeviceFcntUp, frmCntDown: InitialDeviceFcntDown); var loraDevice = this.CreateLoRaDevice(simulatedDevice); Rxpk rxpk = this.CreateUpstreamRxpk(isConfirmed, hasMacInUpstream, datr, simulatedDevice); if (!hasMacInUpstream) { this.LoRaDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null)) .ReturnsAsync(true); } var euRegion = RegionManager.EU868; var c2dMessageMacCommand = new DevStatusRequest(); var c2dMessageMacCommandSize = hasMacInC2D ? c2dMessageMacCommand.Length : 0; var upstreamMessageMacCommandSize = 0; string expectedDownlinkDatr; if (hasMacInUpstream && !isTooLongForUpstreamMacCommandInAnswer) { upstreamMessageMacCommandSize = new LinkCheckAnswer(1, 1).Length; } if (isSendingInRx2) { expectedDownlinkDatr = euRegion.DRtoConfiguration[euRegion.RX2DefaultReceiveWindows.dr].configuration; } else { expectedDownlinkDatr = datr; } var c2dPayloadSize = euRegion.GetMaxPayloadSize(expectedDownlinkDatr) - c2dMessageMacCommandSize - upstreamMessageMacCommandSize - Constants.LORA_PROTOCOL_OVERHEAD_SIZE; var c2dMessagePayload = TestUtils.GeneratePayload("123457890", (int)c2dPayloadSize); var c2dMessage = new ReceivedLoRaCloudToDeviceMessage() { Payload = c2dMessagePayload, Fport = 1, }; if (hasMacInC2D) { c2dMessage.MacCommands = new[] { c2dMessageMacCommand }; } var cloudToDeviceMessage = c2dMessage.CreateMessage(); this.LoRaDeviceClient.SetupSequence(x => x.ReceiveAsync(It.IsAny <TimeSpan>())) .ReturnsAsync(cloudToDeviceMessage) .ReturnsAsync((Message)null); this.LoRaDeviceClient.Setup(x => x.CompleteAsync(cloudToDeviceMessage)) .ReturnsAsync(true); var deviceRegistry = new LoRaDeviceRegistry(this.ServerConfiguration, this.NewNonEmptyCache(loraDevice), this.LoRaDeviceApi.Object, this.LoRaDeviceFactory); // Send to message processor var messageProcessor = new MessageDispatcher( this.ServerConfiguration, deviceRegistry, this.FrameCounterUpdateStrategyProvider); var startTimeOffset = isSendingInRx2 ? TestUtils.GetStartTimeOffsetForSecondWindow() : TimeSpan.Zero; var request = this.CreateWaitableRequest(rxpk, startTimeOffset: startTimeOffset); messageProcessor.DispatchRequest(request); // Expectations // 1. Message was sent to IoT Hub Assert.True(await request.WaitCompleteAsync()); Assert.True(request.ProcessingSucceeded); // 2. Return is downstream message Assert.NotNull(request.ResponseDownlink); Assert.Equal(expectedDownlinkDatr, request.ResponseDownlink.Txpk.Datr); // Get downlink message var downlinkMessage = this.PacketForwarder.DownlinkMessages[0]; var payloadDataDown = new LoRaPayloadData(Convert.FromBase64String(downlinkMessage.Txpk.Data)); payloadDataDown.PerformEncryption(loraDevice.AppSKey); // 3. downlink message payload contains expected message type and DevAddr Assert.Equal(payloadDataDown.DevAddr.ToArray(), LoRaTools.Utils.ConversionHelper.StringToByteArray(loraDevice.DevAddr)); Assert.Equal(LoRaMessageType.UnconfirmedDataDown, payloadDataDown.LoRaMessageType); // 4. Expected Mac commands are present var expectedMacCommandsCount = 0; if (hasMacInC2D) { expectedMacCommandsCount++; } if (hasMacInUpstream && !isTooLongForUpstreamMacCommandInAnswer) { expectedMacCommandsCount++; } if (expectedMacCommandsCount > 0) { // Possible problem: Manually casting payloadDataDown.Fopts to array and reversing it var macCommands = MacCommand.CreateServerMacCommandFromBytes(simulatedDevice.DevEUI, payloadDataDown.Fopts.ToArray().Reverse().ToArray()); Assert.Equal(expectedMacCommandsCount, macCommands.Count); } else { Assert.Null(payloadDataDown.MacCommands); } this.LoRaDeviceClient.VerifyAll(); this.LoRaDeviceApi.VerifyAll(); }
public async Task Perform_DR_Adaptation_When_Needed(uint deviceId, float currentLsnr, string currentDR, int expectedDR, int expectedTxPower) { int messageCount = 21; uint payloadFcnt = 0; const uint InitialDeviceFcntUp = 9; const uint ExpectedDeviceFcntDown = 0; var simulatedDevice = new SimulatedDevice( TestDeviceInfo.CreateABPDevice(deviceId, gatewayID: this.ServerConfiguration.GatewayID), frmCntUp: InitialDeviceFcntUp); var loraDevice = this.CreateLoRaDevice(simulatedDevice); this.LoRaDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null)) .ReturnsAsync(true); this.LoRaDeviceClient.Setup(x => x.ReceiveAsync(It.IsNotNull <TimeSpan>())) .ReturnsAsync((Message)null); int twinDR = 0; int twinTxPower = 0; this.LoRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>())) .Callback <TwinCollection>((t) => { if (t.Contains(TwinProperty.DataRate)) { twinDR = t[TwinProperty.DataRate]; } if (t.Contains(TwinProperty.TxPower)) { twinTxPower = (int)t[TwinProperty.TxPower]; } }) .ReturnsAsync(true); var deviceRegistry = new LoRaDeviceRegistry(this.ServerConfiguration, this.NewNonEmptyCache(loraDevice), this.LoRaDeviceApi.Object, this.LoRaDeviceFactory); // Send to message processor var messageProcessor = new MessageDispatcher( this.ServerConfiguration, deviceRegistry, this.FrameCounterUpdateStrategyProvider); payloadFcnt = await this.InitializeCacheToDefaultValuesAsync(payloadFcnt, simulatedDevice, messageProcessor); for (int i = 0; i < messageCount; i++) { payloadFcnt = await this.SendMessage(currentLsnr, currentDR, payloadFcnt, simulatedDevice, messageProcessor, (byte)((int)LoRaTools.LoRaMessage.FctrlEnum.ADR)); } var payload = simulatedDevice.CreateUnconfirmedDataUpMessage("1234", fcnt: payloadFcnt, fctrl: (byte)((int)LoRaTools.LoRaMessage.FctrlEnum.ADRAckReq + (int)LoRaTools.LoRaMessage.FctrlEnum.ADR)); var rxpk = payload.SerializeUplink(simulatedDevice.AppSKey, simulatedDevice.NwkSKey, lsnr: currentLsnr, datr: currentDR).Rxpk[0]; var request = this.CreateWaitableRequest(rxpk); messageProcessor.DispatchRequest(request); Assert.True(await request.WaitCompleteAsync()); // Expectations // 1. Message was sent to IoT Hub this.LoRaDeviceClient.VerifyAll(); this.LoRaDeviceApi.VerifyAll(); Assert.True(request.ProcessingSucceeded); Assert.NotNull(request.ResponseDownlink); var downlinkMessage = this.PacketForwarder.DownlinkMessages[1]; var payloadDataDown = new LoRaPayloadData(Convert.FromBase64String(downlinkMessage.Txpk.Data)); // We expect a mac command in the payload if (deviceId == 221) { // In this case, no ADR adaptation is performed, so the message should be empty Assert.Equal(0, payloadDataDown.Frmpayload.Span.Length); Assert.Equal(0, payloadDataDown.Fopts.Span.Length); } else { Assert.Equal(5, payloadDataDown.Frmpayload.Span.Length); var decryptedPayload = payloadDataDown.PerformEncryption(simulatedDevice.NwkSKey); Assert.Equal(0, payloadDataDown.Fport.Span[0]); Assert.Equal((byte)CidEnum.LinkADRCmd, decryptedPayload[0]); var linkAdr = new LinkADRRequest(decryptedPayload); Assert.Equal(expectedDR, linkAdr.DataRate); Assert.Equal(expectedDR, loraDevice.DataRate); Assert.Equal(expectedDR, twinDR); Assert.Equal(expectedTxPower, linkAdr.TxPower); Assert.Equal(expectedTxPower, loraDevice.TxPower); Assert.Equal(expectedTxPower, twinTxPower); // in case no payload the mac is in the FRMPayload and is decrypted with NwkSKey Assert.Equal(payloadDataDown.DevAddr.ToArray(), LoRaTools.Utils.ConversionHelper.StringToByteArray(loraDevice.DevAddr)); Assert.False(payloadDataDown.IsConfirmed); Assert.Equal(LoRaMessageType.UnconfirmedDataDown, payloadDataDown.LoRaMessageType); // 4. Frame counter up was updated Assert.Equal(payloadFcnt, loraDevice.FCntUp); // 5. Frame counter down is updated Assert.Equal(ExpectedDeviceFcntDown + 1, loraDevice.FCntDown); Assert.Equal(ExpectedDeviceFcntDown + 1, payloadDataDown.GetFcnt()); } }