コード例 #1
0
        public SimulatedPacketForwarder(IPEndPoint networkServerIPEndpoint, Rxpk rxpk = null)
        {
            IPAddress  ip       = IPAddress.Any;
            int        port     = 1681;
            IPEndPoint endPoint = new IPEndPoint(ip, port);

            this.udpClient = new UdpClient(endPoint);
            this.networkServerIPEndpoint = networkServerIPEndpoint;
            this.TimeAtBoot = DateTimeOffset.Now.UtcTicks;
            this.MacAddress = Utility.GetMacAddress();

            this.Rxpk = rxpk ?? new Rxpk()
            {
                Chan = 7,
                Rfch = 1,
                Freq = 903.700000,
                Stat = 1,
                Modu = "LORA",
                Datr = "SF10BW125",
                Codr = "4/5",
                Rssi = -17,
                Lsnr = 12.0f
            };

            TestLogger.Log($"*** Simulated Packed Forwarder created: {ip}:{port} ***");
        }
コード例 #2
0
        /// <summary>
        /// Process a raw message
        /// </summary>
        /// <param name="rxpk"><see cref="Rxpk"/> representing incoming message</param>
        /// <param name="startTimeProcessing">Starting time counting from the moment the message was received</param>
        /// <returns>A <see cref="DownlinkPktFwdMessage"/> if a message has to be sent back to device</returns>
        public async Task <DownlinkPktFwdMessage> ProcessMessageAsync(Rxpk rxpk, DateTime startTimeProcessing)
        {
            if (!LoRaPayload.TryCreateLoRaPayload(rxpk, out LoRaPayload loRaPayload))
            {
                Logger.Log("There was a problem in decoding the Rxpk", LogLevel.Error);
                return(null);
            }

            if (this.loraRegion == null)
            {
                if (!RegionFactory.TryResolveRegion(rxpk))
                {
                    // log is generated in Region factory
                    // move here once V2 goes GA
                    return(null);
                }

                this.loraRegion = RegionFactory.CurrentRegion;
            }

            if (loRaPayload.LoRaMessageType == LoRaMessageType.JoinRequest)
            {
                return(await this.ProcessJoinRequestAsync(rxpk, (LoRaPayloadJoinRequest)loRaPayload, startTimeProcessing));
            }
            else if (loRaPayload.LoRaMessageType == LoRaMessageType.UnconfirmedDataUp || loRaPayload.LoRaMessageType == LoRaMessageType.ConfirmedDataUp)
            {
                return(await this.ProcessDataMessageAsync(rxpk, (LoRaPayloadData)loRaPayload, startTimeProcessing));
            }

            Logger.Log("Unknwon message type in rxpk, message ignored", LogLevel.Error);
            return(null);
        }
コード例 #3
0
        public void When_Creating_From_Json_With_Custom_Elements_Has_Correct_Value()
        {
            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="",
                    ""custom_prop_a"":""a"",
                    ""custom_prop_b"":10
                }]}";

            byte[] physicalUpstreamPyld = new byte[12];
            physicalUpstreamPyld[0] = 2;
            var request = Encoding.Default.GetBytes(jsonUplink);
            var rxpks   = Rxpk.CreateRxpk(physicalUpstreamPyld.Concat(request).ToArray());

            Assert.Single(rxpks);
            Assert.Equal(2, rxpks[0].ExtraData.Count);
            Assert.Contains("custom_prop_a", rxpks[0].ExtraData.Keys);
            Assert.Contains("custom_prop_b", rxpks[0].ExtraData.Keys);
            Assert.Equal("a", rxpks[0].ExtraData["custom_prop_a"]);
            Assert.Equal(10L, rxpks[0].ExtraData["custom_prop_b"]);
        }
コード例 #4
0
        public LoRaDeviceTelemetry(Rxpk rxpk, LoRaPayloadData upstreamPayload, object payloadData, byte[] decryptedPayloadData)
        {
            if (rxpk.ExtraData != null)
            {
                this.ExtraData = new Dictionary <string, object>(rxpk.ExtraData);
            }

            this.Chan    = rxpk.Chan;
            this.Codr    = rxpk.Codr;
            this.Data    = payloadData;
            this.Rawdata = decryptedPayloadData?.Length > 0 ? Convert.ToBase64String(decryptedPayloadData) : string.Empty;
            this.Datr    = rxpk.Datr;
            this.Freq    = rxpk.Freq;
            this.Lsnr    = rxpk.Lsnr;
            this.Modu    = rxpk.Modu;
            this.Rfch    = rxpk.Rfch;
            this.Rssi    = rxpk.Rssi;
            this.Size    = rxpk.Size;
            this.Stat    = rxpk.Stat;
            this.Time    = rxpk.Time;
            this.Tmms    = rxpk.Tmms;
            this.Tmst    = rxpk.Tmst;
            this.Fcnt    = upstreamPayload.GetFcnt();
            this.Port    = upstreamPayload.GetFPort();
        }
コード例 #5
0
        private async Task ProcessRxpkAsync(string remoteIp, Rxpk rxpk, DateTime startTimeProcessing)
        {
            try
            {
                var downstreamMessage = await this.messageProcessor.ProcessMessageAsync(rxpk, startTimeProcessing);

                if (downstreamMessage?.Txpk != null)
                {
                    var jsonMsg     = JsonConvert.SerializeObject(downstreamMessage);
                    var messageByte = Encoding.UTF8.GetBytes(jsonMsg);
                    var token       = await this.GetTokenAsync();

                    PhysicalPayload pyld = new PhysicalPayload(token, PhysicalIdentifier.PULL_RESP, messageByte);
                    if (this.pullAckRemoteLoRaAggregatorPort != 0)
                    {
                        await this.UdpSendMessage(pyld.GetMessage(), remoteIp, this.pullAckRemoteLoRaAggregatorPort);

                        Logger.Log("UDP", $"message sent with ID {ConversionHelper.ByteArrayToString(token)}", LogLevel.Information);
                    }
                    else
                    {
                        Logger.Log(
                            "UDP",
                            "Waiting for first pull_ack message from the packet forwarder. The received message was discarded as the network server is still starting.",
                            LogLevel.Debug);
                    }
                }
            }
            catch (Exception ex)
            {
                Logger.Log($"Error processing the message {ex.Message}, {ex.StackTrace}", LogLevel.Error);
            }
        }
コード例 #6
0
        public void TestJoinRequest()
        {
            byte[] physicalUpstreamPyld = new byte[12];
            physicalUpstreamPyld[0] = 2;
            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=""
                }]}";
            var         joinRequestInput = Encoding.Default.GetBytes(jsonUplink);
            List <Rxpk> rxpk             = Rxpk.CreateRxpk(physicalUpstreamPyld.Concat(joinRequestInput).ToArray());

            TestRxpk(rxpk[0]);
        }
コード例 #7
0
        public void CheckEUValidUpstreamRxpk(double frequency, string datr, string expectedDatr)
        {
            string jsonUplink =
                "{\"rxpk\":[{\"time\":\"2013-03-31T16:21:17.528002Z\"," +
                "\"tmst\":3512348611," +
                "\"chan\":2," +
                "\"rfch\":0," +
                $"\"freq\": {frequency}," +
                "\"stat\":1," +
                "\"modu\":\"LORA\"," +
                $"\"datr\":\"{datr}\"," +
                "\"codr\":\"4/6\"," +
                "\"rssi\":-35," +
                "\"lsnr\":5.1," +
                "\"size\":32," +
                "\"data\":\"AAQDAgEEAwIBBQQDAgUEAwItEGqZDhI=\"," +
                "\"custom_prop_a\":\"a\"," +
                "\"custom_prop_b\":10" +
                " }]}";

            byte[] physicalUpstreamPyld = new byte[12];
            physicalUpstreamPyld[0] = 2;
            var request    = Encoding.Default.GetBytes(jsonUplink);
            var rxpks      = Rxpk.CreateRxpk(physicalUpstreamPyld.Concat(request).ToArray());
            var downstream = RegionManager.EU868.GetDownstreamDR(rxpks[0]);

            Assert.Equal(expectedDatr, downstream);
        }
コード例 #8
0
        public LoRaDeviceTelemetry(Rxpk rxpk, LoRaPayloadData loRaPayloadData, object payloadData)
        {
            if (rxpk.ExtraData != null)
            {
                this.ExtraData = new Dictionary <string, object>(rxpk.ExtraData);
            }

            this.Chan    = rxpk.Chan;
            this.Codr    = rxpk.Codr;
            this.Data    = payloadData;
            this.Rawdata = rxpk.Data;
            this.Datr    = rxpk.Datr;
            this.Freq    = rxpk.Freq;
            this.Lsnr    = rxpk.Lsnr;
            this.Modu    = rxpk.Modu;
            this.Rfch    = rxpk.Rfch;
            this.Rssi    = rxpk.Rssi;
            this.Size    = rxpk.Size;
            this.Stat    = rxpk.Stat;
            this.Time    = rxpk.Time;
            this.Tmms    = rxpk.Tmms;
            this.Tmst    = rxpk.Tmst;
            this.Fcnt    = loRaPayloadData.GetFcnt();
            this.Port    = loRaPayloadData.GetFPort();
        }
コード例 #9
0
        private static List <Rxpk> GenerateRxpk(string datr, double freq)
        {
            string jsonUplink =
                @"{ ""rxpk"":[
                {
                    ""time"":""2013-03-31T16:21:17.528002Z"",
                    ""tmst"":3512348611,
                    ""chan"":2,
                    ""rfch"":0,
                    ""freq"":" + freq + @",
                    ""stat"":1,
                    ""modu"":""LORA"",
                    ""datr"":""" + datr + @""",
                    ""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());

            return(rxpk);
        }
コード例 #10
0
 public LoRaRequest(
     Rxpk rxpk,
     IPacketForwarder packetForwarder,
     DateTime startTime)
 {
     this.Rxpk            = rxpk;
     this.PacketForwarder = packetForwarder;
     this.StartTime       = startTime;
 }
コード例 #11
0
        public void Multiple_Rxpk_Are_Detected_Correctly()
        {
            string jsonUplink = @"{""rxpk"":[{""tmst"":373051724,""time"":""2020-02-19T04:08:57.265951Z"",""chan"":0,""rfch"":0,""freq"":923.200000,""stat"":1,""modu"":""LORA"",""datr"":""SF9BW125"",""codr"":""4/5"",""lsnr"":12.5,""rssi"":-47,""size"":21,""data"":""gAMAABKgmAAIAvEgIbhjS0LBeM/d""},{""tmst"":373053772,""time"":""2020-02-19T04:08:57.265951Z"",""chan"":6,""rfch"":0,""freq"":923.000000,""stat"":-1,""modu"":""LORA"",""datr"":""SF9BW125"",""codr"":""4/5"",""lsnr"":-13.0,""rssi"":-97,""size"":21,""data"":""gJni7n4+wQBUl/E0sO4vB4gFePx7""}]}";

            byte[] physicalUpstreamPyld = new byte[12];
            physicalUpstreamPyld[0] = 2;
            var request = Encoding.Default.GetBytes(jsonUplink);
            var rxpks   = Rxpk.CreateRxpk(physicalUpstreamPyld.Concat(request).ToArray());

            Assert.Equal(2, rxpks.Count);
        }
コード例 #12
0
ファイル: RegionFactory.cs プロジェクト: zhaozhenhong5/zhao
 static public void Create(Rxpk _rxpk)
 {
     //EU863-870
     if (_rxpk.freq < 870 && _rxpk.freq > 863)
     {
         CurrentRegion = CreateEURegion();
     }//US902-928-->CN470-510
     else if (_rxpk.freq <= 510 && _rxpk.freq >= 470)
     {
         CurrentRegion = CreateUSRegion();
     }
 }
コード例 #13
0
 static public void Create(Rxpk _rxpk)
 {
     //EU863-870
     if (_rxpk.freq < 870 && _rxpk.freq > 863)
     {
         CurrentRegion = CreateEURegion();
     }//US902-928
     else if (_rxpk.freq <= 928 && _rxpk.freq >= 902)
     {
         CurrentRegion = CreateUSRegion();
     }
 }
コード例 #14
0
        // Sends device telemetry data to IoT Hub
        private async Task <bool> SendDeviceEventAsync(LoRaDevice loRaDevice, Rxpk rxpk, object decodedValue, LoRaPayloadData loRaPayloadData, LoRaOperationTimeWatcher timeWatcher)
        {
            var deviceTelemetry = new LoRaDeviceTelemetry(rxpk, loRaPayloadData, decodedValue)
            {
                DeviceEUI = loRaDevice.DevEUI,
                GatewayID = this.configuration.GatewayID,
                Edgets    = (long)(timeWatcher.Start - DateTime.UnixEpoch).TotalMilliseconds
            };

            Dictionary <string, string> eventProperties = null;

            if (loRaPayloadData.IsUpwardAck())
            {
                eventProperties = new Dictionary <string, string>();
                Logger.Log(loRaDevice.DevEUI, $"Message ack received for C2D message id {loRaDevice.LastConfirmedC2DMessageID}", LogLevel.Information);
                eventProperties.Add(C2D_MSG_PROPERTY_VALUE_NAME, loRaDevice.LastConfirmedC2DMessageID ?? C2D_MSG_ID_PLACEHOLDER);
                loRaDevice.LastConfirmedC2DMessageID = null;
            }

            var macCommand = loRaPayloadData.GetMacCommands();

            if (macCommand.MacCommand.Count > 0)
            {
                eventProperties = eventProperties ?? new Dictionary <string, string>();

                for (int i = 0; i < macCommand.MacCommand.Count; i++)
                {
                    eventProperties[macCommand.MacCommand[i].Cid.ToString()] = JsonConvert.SerializeObject(macCommand.MacCommand[i], 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();
                    }
                }
            }

            if (await loRaDevice.SendEventAsync(deviceTelemetry, eventProperties))
            {
                var payloadAsRaw = deviceTelemetry.Data as string;
                if (payloadAsRaw == null && deviceTelemetry.Data != null)
                {
                    payloadAsRaw = JsonConvert.SerializeObject(deviceTelemetry.Data, Formatting.None);
                }

                Logger.Log(loRaDevice.DevEUI, $"message '{payloadAsRaw}' sent to hub", LogLevel.Information);
                return(true);
            }

            return(false);
        }
コード例 #15
0
        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);
            List <Rxpk> rxpk = Rxpk.CreateRxpk(physicalUpstreamPyld.Concat(jsonUplinkUnconfirmedDataUpBytes).ToArray());

            Assert.True(LoRaPayload.TryCreateLoRaPayload(rxpk[0], out LoRaPayload loRaPayload));

            Assert.Equal(LoRaMessageType.UnconfirmedDataUp, loRaPayload.LoRaMessageType);

            LoRaPayloadData loRaPayloadUplinkObj = (LoRaPayloadData)loRaPayload;

            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)));
        }
コード例 #16
0
        public WaitableLoRaRequest CreateWaitableRequest(Rxpk rxpk, IPacketForwarder packetForwarder = null, TimeSpan?startTimeOffset = null, TimeSpan?constantElapsedTime = null)
        {
            var requestStartTime = startTimeOffset.HasValue ? DateTime.UtcNow.Subtract(startTimeOffset.Value) : DateTime.UtcNow;
            var request          = new WaitableLoRaRequest(rxpk, packetForwarder ?? this.PacketForwarder, requestStartTime);

            if (constantElapsedTime.HasValue)
            {
                Assert.True(RegionManager.TryResolveRegion(rxpk, out var region));
                var timeWatcher = new TestLoRaOperationTimeWatcher(region, constantElapsedTime.Value);
                request.UseTimeWatcher(timeWatcher);
            }

            return(request);
        }
コード例 #17
0
        /// <summary>
        /// Tries the resolve region.
        /// </summary>
        /// <returns><c>true</c>, if a region was resolved, <c>false</c> otherwise.</returns>
        /// <param name="rxpk">Rxpk.</param>
        /// <param name="region">Region.</param>
        public static bool TryResolveRegion(Rxpk rxpk, out Region region)
        {
            region = null;

            // EU863-870
            if (rxpk.Freq < 870 && rxpk.Freq > 863)
            {
                region = EU868;
                return(true);
            }// US902-928 frequency band, upstream messages are between 902 and 915.
            else if (rxpk.Freq <= 915 && rxpk.Freq >= 902)
            {
                region = US915;
                return(true);
            }

            return(false);
        }
コード例 #18
0
 private void DispatchMessages(byte[] buffer, DateTime startTimeProcessing)
 {
     try
     {
         List <Rxpk> messageRxpks = Rxpk.CreateRxpk(buffer);
         if (messageRxpks != null)
         {
             foreach (var rxpk in messageRxpks)
             {
                 this.messageDispatcher.DispatchRequest(new LoRaRequest(rxpk, this, startTimeProcessing));
             }
         }
     }
     catch (Exception ex)
     {
         Logger.Log("UDP", $"failed to dispatch messages: {ex.Message}", LogLevel.Error);
     }
 }
コード例 #19
0
        /// <summary>
        /// Tries the resolve region.
        /// </summary>
        /// <returns><c>true</c>, if a region was resolved, <c>false</c> otherwise.</returns>
        /// <param name="rxpk">Rxpk.</param>
        /// <param name="region">Region.</param>
        public static bool TryResolveRegion(Rxpk rxpk, out Region region)
        {
            region = null;

            // EU863-870
            if (rxpk.Freq < 870 && rxpk.Freq > 863)
            {
                region = EU868;
                return(true);
            }// US902-928
            else if (rxpk.Freq <= 928 && rxpk.Freq >= 902)
            {
                region = US915;
                return(true);
            }

            return(false);
        }
コード例 #20
0
 public static bool TryResolveRegion(Rxpk rxpk)
 {
     // EU863-870
     if (rxpk.Freq < 870 && rxpk.Freq > 863)
     {
         CurrentRegion = CreateEU868Region();
         return(true);
     }// US902-928
     else if (rxpk.Freq <= 928 && rxpk.Freq >= 902)
     {
         CurrentRegion = CreateUS915Region();
         return(true);
     }
     else
     {
         Logger.Log("RegionFactory", "The current frequency plan is not supported. Currently only EU868 and US915 frequency bands are supported.", LogLevel.Error);
         return(false);
     }
 }
        public Rxpk CreateUpstreamRxpk(bool isConfirmed, bool hasMacInUpstream, string datr, SimulatedDevice simulatedDevice)
        {
            Rxpk   rxpk       = null;
            string msgPayload = null;

            if (isConfirmed)
            {
                if (hasMacInUpstream)
                {
                    // Cofirmed message with Mac command in upstream
                    msgPayload = "02";
                    var confirmedMessagePayload = simulatedDevice.CreateConfirmedDataUpMessage(msgPayload, isHexPayload: true, fport: 0);
                    rxpk = confirmedMessagePayload.SerializeUplink(simulatedDevice.AppSKey, simulatedDevice.NwkSKey, datr: datr).Rxpk[0];
                }
                else
                {
                    // Cofirmed message without Mac command in upstream
                    msgPayload = "1234567890";
                    var confirmedMessagePayload = simulatedDevice.CreateConfirmedDataUpMessage(msgPayload);
                    rxpk = confirmedMessagePayload.SerializeUplink(simulatedDevice.AppSKey, simulatedDevice.NwkSKey, datr: datr).Rxpk[0];
                }
            }
            else
            {
                if (hasMacInUpstream)
                {
                    // Uncofirmed message with Mac command in upstream
                    msgPayload = "02";
                    var unconfirmedMessagePayload = simulatedDevice.CreateUnconfirmedDataUpMessage(msgPayload, isHexPayload: true, fport: 0);
                    rxpk = unconfirmedMessagePayload.SerializeUplink(simulatedDevice.AppSKey, simulatedDevice.NwkSKey, datr: datr).Rxpk[0];
                }
                else
                {
                    // Uncofirmed message without Mac command in upstream
                    msgPayload = "1234567890";
                    var unconfirmedMessagePayload = simulatedDevice.CreateUnconfirmedDataUpMessage(msgPayload);
                    rxpk = unconfirmedMessagePayload.SerializeUplink(simulatedDevice.AppSKey, simulatedDevice.NwkSKey, datr: datr).Rxpk[0];
                }
            }

            return(rxpk);
        }
コード例 #22
0
        public SimulatedPacketForwarder(IPEndPoint networkServerIPEndpoint, Rxpk rxpk = null)
        {
            IPEndPoint endPoint = new IPEndPoint(IPAddress.Any, 1681);

            this.udpClient = new UdpClient(endPoint);
            this.networkServerIPEndpoint = networkServerIPEndpoint;
            TimeAtBoot      = DateTimeOffset.Now.UtcTicks;
            this.MacAddress = Utility.GetMacAddress();

            this.rxpk = rxpk ?? new Rxpk()
            {
                chan = 7,
                rfch = 1,
                freq = 903.700000,
                stat = 1,
                modu = "LORA",
                datr = "SF10BW125",
                codr = "4/5",
                rssi = -17,
                lsnr = 12.0f
            };
        }
コード例 #23
0
        private static void TestRxpk(Rxpk rxpk)
        {
            Assert.True(LoRaPayload.TryCreateLoRaPayload(rxpk, out LoRaPayload loRaPayload));
            Assert.Equal(LoRaMessageType.JoinRequest, loRaPayload.LoRaMessageType);
            LoRaPayloadJoinRequest joinRequestMessage = (LoRaPayloadJoinRequest)loRaPayload;

            byte[] joinRequestAppKey = new byte[16]
            {
                1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
            };
            var joinRequestBool = joinRequestMessage.CheckMic(ConversionHelper.ByteArrayToString(joinRequestAppKey));

            if (!joinRequestBool)
            {
                Console.WriteLine("Join Request type was not computed correclty");
            }

            byte[] joinRequestAppEui = new byte[8]
            {
                1, 2, 3, 4, 1, 2, 3, 4
            };

            byte[] joinRequestDevEUI = new byte[8]
            {
                2, 3, 4, 5, 2, 3, 4, 5
            };
            byte[] joinRequestDevNonce = new byte[2]
            {
                16, 45
            };

            Array.Reverse(joinRequestAppEui);
            Array.Reverse(joinRequestDevEUI);
            Array.Reverse(joinRequestDevNonce);
            Assert.True(joinRequestMessage.AppEUI.ToArray().SequenceEqual(joinRequestAppEui));
            Assert.True(joinRequestMessage.DevEUI.ToArray().SequenceEqual(joinRequestDevEUI));
            Assert.True(joinRequestMessage.DevNonce.ToArray().SequenceEqual(joinRequestDevNonce));
        }
コード例 #24
0
        public static bool TryCreateLoRaPayload(Rxpk rxpk, out LoRaPayload loRaPayloadMessage)
        {
            byte[] convertedInputMessage = Convert.FromBase64String(rxpk.Data);
            var    messageType           = convertedInputMessage[0];

            switch (messageType)
            {
            case (int)LoRaMessageType.UnconfirmedDataUp:
            case (int)LoRaMessageType.ConfirmedDataUp:
                loRaPayloadMessage = new LoRaPayloadData(convertedInputMessage);
                break;

            case (int)LoRaMessageType.JoinRequest:
                loRaPayloadMessage = new LoRaPayloadJoinRequest(convertedInputMessage);
                break;

            default:
                loRaPayloadMessage = null;
                return(false);
            }

            loRaPayloadMessage.LoRaMessageType = (LoRaMessageType)messageType;
            return(true);
        }
コード例 #25
0
        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();
        }
コード例 #26
0
        private async Task <byte[]> ProcessLoraMessage(LoRaMessage loraMessage)
        {
            bool validFrameCounter = false;

            byte[]  udpMsgForPktForwarder = new byte[0];
            string  devAddr = BitConverter.ToString(loraMessage.payloadMessage.devAddr).Replace("-", "");
            Message c2dMsg  = null;



            Cache.TryGetValue(devAddr, out LoraDeviceInfo loraDeviceInfo);



            if (loraDeviceInfo == null)
            {
                loraDeviceInfo = await LoraDeviceInfoManager.GetLoraDeviceInfoAsync(devAddr);

                Logger.Log(loraDeviceInfo.DevEUI, $"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.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 IoTHubSender(loraDeviceInfo.DevEUI, loraDeviceInfo.PrimaryKey);
                        }


                        UInt16 fcntup = BitConverter.ToUInt16(((LoRaPayloadStandardData)loraMessage.payloadMessage).fcnt, 0);


                        //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 == 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);
                            }

                            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 = ((UplinkPktFwdMessage)loraMessage.loraMetadata.fullPayload).rxpk[0];


                            dynamic fullPayload = JObject.FromObject(rxPk);

                            string jsonDataPayload = "";

                            uint fportUp = (uint)((LoRaPayloadStandardData)loraMessage.payloadMessage).fport[0];

                            fullPayload.port = fportUp;

                            fullPayload.fcnt = fcntup;

                            if (String.IsNullOrEmpty(loraDeviceInfo.SensorDecoder))
                            {
                                jsonDataPayload  = Convert.ToBase64String(decryptedMessage);
                                fullPayload.data = jsonDataPayload;
                            }
                            else
                            {
                                Logger.Log(loraDeviceInfo.DevEUI, $"decoding with: {loraDeviceInfo.SensorDecoder} port: {fportUp}", Logger.LoggingLevel.Info);
                                jsonDataPayload  = LoraDecoders.DecodeMessage(decryptedMessage, fportUp, loraDeviceInfo.SensorDecoder);
                                fullPayload.data = JObject.Parse(jsonDataPayload);
                            }


                            fullPayload.eui       = loraDeviceInfo.DevEUI;
                            fullPayload.gatewayid = GatewayID;

                            //todo check what the other ts are if milliseconds or seconds
                            fullPayload.edgets = (long)((startTimeProcessing - new DateTime(1970, 1, 1)).TotalMilliseconds);


                            string iotHubMsg = fullPayload.ToString(Newtonsoft.Json.Formatting.None);


                            await loraDeviceInfo.HubSender.SendMessageAsync(iotHubMsg);

                            Logger.Log(loraDeviceInfo.DevEUI, $"sent message '{jsonDataPayload}' 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);
                        }

                        //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;
                        byte[] fctl        = new byte[1] {
                            32
                        };

                        //check if we got a c2d message to be added in the ack message and preprare 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
                                _ = loraDeviceInfo.HubSender.AbandonAsync(secondC2dMsg);

                                //set the fpending flag so the lora device will call us back for the next message
                                fctl = 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 1 and 2 window
                            if (((DateTime.UtcNow - startTimeProcessing) <= TimeSpan.FromMilliseconds(1900)))
                            {
                                //increase the fcnt down and save it to iot hub twins
                                loraDeviceInfo.FCntDown++;

                                Logger.Log(loraDeviceInfo.DevEUI, $"down frame counter: {loraDeviceInfo.FCntDown}", Logger.LoggingLevel.Info);


                                //Saving both fcnts to twins
                                _ = loraDeviceInfo.HubSender.UpdateFcntAsync(loraDeviceInfo.FCntUp, loraDeviceInfo.FCntDown);

                                var _datr = ((UplinkPktFwdMessage)loraMessage.loraMetadata.fullPayload).rxpk[0].datr;

                                uint _rfch = ((UplinkPktFwdMessage)loraMessage.loraMetadata.fullPayload).rxpk[0].rfch;

                                double _freq = ((UplinkPktFwdMessage)loraMessage.loraMetadata.fullPayload).rxpk[0].freq;

                                uint txDelay = 0;


                                //if we are already longer than 900 mssecond move to the 2 second window
                                //uncomment to force second windows usage
                                //Thread.Sleep(901);
                                if ((DateTime.UtcNow - startTimeProcessing) > TimeSpan.FromMilliseconds(900))
                                {
                                    if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("RX2_DATR")))
                                    {
                                        Logger.Log(loraDeviceInfo.DevEUI, $"using standard second receive windows", Logger.LoggingLevel.Info);

                                        //using EU fix DR for RX2
                                        _freq = 869.525;
                                        _datr = "SF12BW125";
                                    }
                                    //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);
                                    }

                                    txDelay = 1000000;
                                }


                                long _tmst = ((UplinkPktFwdMessage)loraMessage.loraMetadata.fullPayload).rxpk[0].tmst + txDelay;


                                Byte[] devAddrCorrect = new byte[4];
                                Array.Copy(loraMessage.payloadMessage.devAddr, devAddrCorrect, 4);
                                Array.Reverse(devAddrCorrect);

                                //todo mik check what is the A0
                                LoRaPayloadStandardData ackLoRaMessage = new LoRaPayloadStandardData(StringToByteArray("A0"),
                                                                                                     devAddrCorrect,
                                                                                                     fctl,
                                                                                                     BitConverter.GetBytes(loraDeviceInfo.FCntDown),
                                                                                                     null,
                                                                                                     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
                                LoRaMessage ackMessage = new LoRaMessage(ackLoRaMessage, LoRaMessageType.ConfirmedDataDown, rndToken, _datr, 0, _freq, _tmst);

                                udpMsgForPktForwarder = ackMessage.physicalPayload.GetMessage();


                                //confirm the message to iot hub only if we are in time for a delivery
                                if (c2dMsg != null)
                                {
                                    _ = loraDeviceInfo.HubSender.CompleteAsync(c2dMsg);
                                    Logger.Log(loraDeviceInfo.DevEUI, $"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();


                                _ = loraDeviceInfo.HubSender.UpdateFcntAsync(loraDeviceInfo.FCntUp, null);


                                //put back the c2d message to the queue for the next round
                                _ = loraDeviceInfo.HubSender.AbandonAsync(c2dMsg);

                                Logger.Log(loraDeviceInfo.DevEUI, $"too late for down message, sending only ACK to gateway", Logger.LoggingLevel.Info);
                            }
                        }
                        //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 (validFrameCounter)
                            {
                                _ = loraDeviceInfo.HubSender.UpdateFcntAsync(loraDeviceInfo.FCntUp, null);
                            }
                        }
                    }
                    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 with devAddr {devAddr} is not our device, ignore message", Logger.LoggingLevel.Info);
            }

            Logger.Log(loraDeviceInfo.DevEUI, $"processing time: {DateTime.UtcNow - startTimeProcessing}", Logger.LoggingLevel.Info);

            return(udpMsgForPktForwarder);
        }
コード例 #27
0
        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);
        }
コード例 #28
0
        /// <summary>
        /// Prepare the Mac Commands to be sent in the downstream message.
        /// </summary>
        static ICollection <MacCommand> PrepareMacCommandAnswer(
            string devEUI,
            IEnumerable <MacCommand> requestedMacCommands,
            IEnumerable <MacCommand> serverMacCommands,
            Rxpk rxpk,
            LoRaADRResult loRaADRResult)
        {
            var macCommands = new Dictionary <int, MacCommand>();

            if (requestedMacCommands != null)
            {
                foreach (var requestedMacCommand in requestedMacCommands)
                {
                    switch (requestedMacCommand.Cid)
                    {
                    case CidEnum.LinkCheckCmd:
                    {
                        if (rxpk != null)
                        {
                            var linkCheckAnswer = new LinkCheckAnswer(rxpk.GetModulationMargin(), 1);
                            if (macCommands.TryAdd((int)CidEnum.LinkCheckCmd, linkCheckAnswer))
                            {
                                Logger.Log(devEUI, $"answering to a MAC command request {linkCheckAnswer.ToString()}", LogLevel.Information);
                            }
                        }

                        break;
                    }
                    }
                }
            }

            if (serverMacCommands != null)
            {
                foreach (var macCmd in serverMacCommands)
                {
                    if (macCmd != null)
                    {
                        try
                        {
                            if (!macCommands.TryAdd((int)macCmd.Cid, macCmd))
                            {
                                Logger.Log(devEUI, $"could not send the cloud to device MAC command {macCmd.Cid}, as such a property was already present in the message. Please resend the cloud to device message", LogLevel.Error);
                            }

                            Logger.Log(devEUI, $"cloud to device MAC command {macCmd.Cid} received {macCmd}", LogLevel.Information);
                        }
                        catch (MacCommandException ex)
                        {
                            Logger.Log(devEUI, ex.ToString(), LogLevel.Error);
                        }
                    }
                }
            }

            // ADR Part.
            // Currently only replying on ADR Req
            if (loRaADRResult?.CanConfirmToDevice == true)
            {
                const int      placeholderChannel = 25;
                LinkADRRequest linkADR            = new LinkADRRequest((byte)loRaADRResult.DataRate, (byte)loRaADRResult.TxPower, placeholderChannel, 0, (byte)loRaADRResult.NbRepetition);
                macCommands.Add((int)CidEnum.LinkADRCmd, linkADR);
                Logger.Log(devEUI, $"performing a rate adaptation: DR {loRaADRResult.DataRate}, transmit power {loRaADRResult.TxPower}, #repetition {loRaADRResult.NbRepetition}", LogLevel.Information);
            }

            return(macCommands.Values);
        }
コード例 #29
0
 public WaitableLoRaRequest(Rxpk rxpk, IPacketForwarder packetForwarder)
     : this(rxpk, packetForwarder, DateTime.UtcNow)
 {
 }
コード例 #30
0
 public WaitableLoRaRequest(Rxpk rxpk, IPacketForwarder packetForwarder, DateTime startTime)
     : base(rxpk, packetForwarder, startTime)
 {
     this.complete = new SemaphoreSlim(0);
 }