예제 #1
0
        public void When_Has_Payload_And_MacCommand_Zero_FPort_Should_Be_Invalid()
        {
            var payloadC2d = new LoRaCloudToDeviceMessage()
            {
                MessageId   = "01",
                Payload     = "hello",
                Fport       = LoRaFPort.MacCommand,
                MacCommands = new MacCommand[]
                {
                    new DevStatusRequest(),
                },
            };

            Assert.False(payloadC2d.IsValid(out var errorMessage));
            Assert.Equal("invalid MAC command fport usage in cloud to device message '01'", errorMessage);

            var rawPayloadC2d = new LoRaCloudToDeviceMessage()
            {
                MessageId   = "02",
                RawPayload  = "AAAA",
                Fport       = LoRaFPort.MacCommand,
                MacCommands = new MacCommand[]
                {
                    new DevStatusRequest(),
                },
            };

            Assert.False(rawPayloadC2d.IsValid(out errorMessage));
            Assert.Equal("invalid MAC command fport usage in cloud to device message '02'", errorMessage);
        }
        public static async Task <bool> SendCloudToDeviceMessage(DevEui devEUI, LoRaCloudToDeviceMessage c2dMessage)
        {
            var path = $"cloudtodevicemessage/{devEUI}";
            var json = JsonConvert.SerializeObject(c2dMessage);

            return(await PostFunctionEndpointAsync(path, json));
        }
예제 #3
0
        public void When_Fport_0_And_Payload_Is_Empty_Should_Be_Invalid()
        {
            var c2d = new LoRaCloudToDeviceMessage();

            Assert.False(c2d.IsValid(out var errorMessage));
            Assert.Equal("invalid MAC command fport usage in cloud to device message ''", errorMessage);
        }
예제 #4
0
        public async Task When_Querying_Devices_And_Finds_Single_Gateway_Class_C_Should_Update_Cache_And_Send_Direct_Method()
        {
            const string devEUI = "0123456789";

            var deviceTwin = new Twin();

            deviceTwin.Properties = new TwinProperties()
            {
                Desired  = new TwinCollection($"{{\"DevAddr\": \"03010101\", \"ClassType\": \"C\", \"GatewayID\":\"mygateway\"}}"),
                Reported = new TwinCollection(),
            };

            var query = new Mock <IQuery>(MockBehavior.Strict);

            query.Setup(x => x.HasMoreResults).Returns(true);
            query.Setup(x => x.GetNextAsTwinAsync())
            .ReturnsAsync(new[] { deviceTwin });

            this.registryManager.Setup(x => x.CreateQuery(It.IsNotNull <string>(), It.IsAny <int?>()))
            .Returns(query.Object);

            var actualMessage = new LoRaCloudToDeviceMessage()
            {
                Fport   = 1,
                Payload = "hello",
            };

            this.serviceClient.Setup(x => x.InvokeDeviceMethodAsync("mygateway", LoraKeysManagerFacadeConstants.NetworkServerModuleId, It.IsNotNull <CloudToDeviceMethod>()))
            .Callback <string, string, CloudToDeviceMethod>((device, methodName, method) =>
            {
                var c2dMessage = JsonConvert.DeserializeObject <LoRaCloudToDeviceMessage>(method.GetPayloadAsJson());
                Assert.Equal(c2dMessage.Fport, actualMessage.Fport);
                Assert.Equal(c2dMessage.Payload, actualMessage.Payload);
            })
            .ReturnsAsync(new CloudToDeviceMethodResult()
            {
                Status = (int)HttpStatusCode.OK
            });

            var actual = await this.sendCloudToDeviceMessage.SendCloudToDeviceMessageImplementationAsync(
                devEUI,
                actualMessage);

            Assert.IsType <OkObjectResult>(actual);
            var responseValue = ((OkObjectResult)actual).Value as SendCloudToDeviceMessageResult;

            Assert.NotNull(responseValue);
            Assert.Equal("C", responseValue.ClassType);
            Assert.Equal(devEUI, responseValue.DevEUI);
            Assert.Null(responseValue.MessageID);

            var cachedPreferredGateway = LoRaDevicePreferredGateway.LoadFromCache(this.cacheStore, devEUI);

            Assert.NotNull(cachedPreferredGateway);
            Assert.Equal("mygateway", cachedPreferredGateway.GatewayID);

            this.serviceClient.VerifyAll();
            this.registryManager.VerifyAll();
            query.VerifyAll();
        }
예제 #5
0
        public void When_Has_Only_Payload_And_Valid_FPort_Should_Be_Valid()
        {
            var payloadC2d = new LoRaCloudToDeviceMessage()
            {
                MessageId   = "01",
                Payload     = "hello",
                MacCommands = new MacCommand[]
                {
                    new DevStatusRequest(),
                },
                Fport = 1,
            };

            Assert.True(payloadC2d.IsValid(out var errorMessage));
            Assert.Null(errorMessage);

            var rawPayloadC2d = new LoRaCloudToDeviceMessage()
            {
                MessageId   = "01",
                RawPayload  = "AAAA",
                MacCommands = new MacCommand[]
                {
                    new DevStatusRequest(),
                },
                Fport = 2,
            };

            Assert.True(rawPayloadC2d.IsValid(out errorMessage));
            Assert.Null(errorMessage);
        }
        /// <summary>
        /// Helper to create a <see cref="Message"/> from a <see cref="LoRaCloudToDeviceMessage"/>
        /// </summary>
        public static Message CreateMessage(this LoRaCloudToDeviceMessage loRaMessage)
        {
            var message = new Message(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(loRaMessage)))
            {
                ContentType = "application/json",
            };

            return(message);
        }
예제 #7
0
        public async Task When_Querying_Devices_And_Finds_Class_A_Should_Send_Message()
        {
            var devEui = new DevEui(123456789);

            var deviceTwin = new Twin
            {
                Properties = new TwinProperties()
                {
                    Desired = new TwinCollection($"{{\"DevAddr\": \"03010101\"}}"),
                }
            };

            var query = new Mock <IQuery>(MockBehavior.Strict);

            query.Setup(x => x.HasMoreResults).Returns(true);
            query.Setup(x => x.GetNextAsTwinAsync())
            .ReturnsAsync(new[] { deviceTwin });

            this.registryManager.Setup(x => x.CreateQuery(It.IsNotNull <string>(), It.IsAny <int?>()))
            .Returns(query.Object);

            var actualMessage = new LoRaCloudToDeviceMessage()
            {
                MessageId = "myMessageId-1234",
                Fport     = TestPort,
                Payload   = "hello",
            };

            LoRaCloudToDeviceMessage     receivedC2DMessage = null;
            IDictionary <string, string> receivedProperties = null;

            this.serviceClient.Setup(x => x.SendAsync(devEui.ToString(), It.IsNotNull <Message>()))
            .Callback((string d, Message m) => (receivedProperties, receivedC2DMessage) = (m.Properties, JsonConvert.DeserializeObject <LoRaCloudToDeviceMessage>(Encoding.UTF8.GetString(m.GetBytes()))))
            .Returns(Task.CompletedTask);

            var actual = await this.sendCloudToDeviceMessage.SendCloudToDeviceMessageImplementationAsync(
                devEui,
                actualMessage);

            Assert.IsType <OkObjectResult>(actual);
            var responseValue = ((OkObjectResult)actual).Value as SendCloudToDeviceMessageResult;

            Assert.NotNull(responseValue);
            Assert.Equal("A", responseValue.ClassType);
            Assert.Equal(devEui, responseValue.DevEui);
            Assert.Equal(actualMessage.MessageId, responseValue.MessageID);

            Assert.Empty(receivedProperties);
            Assert.Equal(receivedC2DMessage.Fport, actualMessage.Fport);
            Assert.Equal(receivedC2DMessage.Payload, actualMessage.Payload);
            Assert.Equal(receivedC2DMessage.MessageId, actualMessage.MessageId);

            this.serviceClient.VerifyAll();
            this.registryManager.VerifyAll();
            query.VerifyAll();
        }
        public async Task Test_ClassC_Send_Message_From_Direct_Method_Should_Be_Received()
        {
            const int MAX_MODULE_DIRECT_METHOD_CALL_TRIES = 3;
            var       device = this.TestFixtureCi.Device24_ABP;

            this.LogTestStart(device);

            await this.ArduinoDevice.setDeviceModeAsync(LoRaArduinoSerial._device_mode_t.LWABP);

            await this.ArduinoDevice.setIdAsync(device.DevAddr, device.DeviceID, null);

            await this.ArduinoDevice.setKeyAsync(device.NwkSKey, device.AppSKey, null);

            await this.ArduinoDevice.SetupLora(this.TestFixtureCi.Configuration.LoraRegion);

            await this.ArduinoDevice.setClassTypeAsync(LoRaArduinoSerial._class_type_t.CLASS_C);

            var c2d = new LoRaCloudToDeviceMessage()
            {
                DevEUI     = device.DeviceID,
                MessageId  = Guid.NewGuid().ToString(),
                Fport      = 23,
                RawPayload = Convert.ToBase64String(new byte[] { 0xFF, 0x00 }),
            };

            TestLogger.Log($"[INFO] Using service client to call direct method to {this.TestFixture.Configuration.LeafDeviceGatewayID}/{this.TestFixture.Configuration.NetworkServerModuleID}");
            TestLogger.Log($"[INFO] {JsonConvert.SerializeObject(c2d, Formatting.None)}");

            for (var i = 0; i < MAX_MODULE_DIRECT_METHOD_CALL_TRIES;)
            {
                i++;

                try
                {
                    await this.TestFixtureCi.InvokeModuleDirectMethodAsync(this.TestFixture.Configuration.LeafDeviceGatewayID, this.TestFixture.Configuration.NetworkServerModuleID, "cloudtodevicemessage", c2d);

                    break;
                }
                catch (Exception ex)
                {
                    if (i == MAX_MODULE_DIRECT_METHOD_CALL_TRIES)
                    {
                        throw;
                    }

                    TestLogger.Log($"[ERR] Failed to call module direct method: {ex.Message}");
                    await Task.Delay(TimeSpan.FromSeconds(5));
                }
            }

            await Task.Delay(Constants.DELAY_BETWEEN_MESSAGES);

            Assert.Contains(this.ArduinoDevice.SerialLogs, (l) => l.StartsWith("+MSG: PORT: 23; RX: \"FF00\""));
            Assert.Contains(this.ArduinoDevice.SerialLogs, (l) => l.StartsWith("+MSG: RXWIN0, RSSI"));
            await AssertUtils.ContainsWithRetriesAsync("+MSG: Done", this.ArduinoDevice.SerialLogs);
        }
        public async Task SendCloudToDeviceMessageAsync(string deviceId, LoRaCloudToDeviceMessage message)
        {
            var msg = new Message(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(message)));

            if (!string.IsNullOrEmpty(message.MessageId))
            {
                msg.MessageId = message.MessageId;
            }

            await this.SendCloudToDeviceMessageAsync(deviceId, msg);
        }
        public void When_Has_Only_MacCommand_And_FPort_0_Should_Be_Valid()
        {
            var c2d = new LoRaCloudToDeviceMessage()
            {
                MessageId   = "01",
                MacCommands = { new DevStatusRequest() },
                Fport       = FramePort.MacCommand,
            };

            Assert.True(c2d.IsValid(out var errorMessage));
            Assert.Null(errorMessage);
        }
예제 #11
0
        public async Task Test_ClassC_Send_Message_Using_Function_Endpoint_Should_Be_Received()
        {
            var device = TestFixtureCi.Device24_ABP;

            LogTestStart(device);

            await ArduinoDevice.setDeviceModeAsync(LoRaArduinoSerial._device_mode_t.LWABP);

            await ArduinoDevice.setIdAsync(device.DevAddr, device.DeviceID, null);

            await ArduinoDevice.setKeyAsync(device.NwkSKey, device.AppSKey, null);

            await ArduinoDevice.SetupLora(TestFixtureCi.Configuration);

            await ArduinoDevice.setClassTypeAsync(LoRaArduinoSerial._class_type_t.CLASS_C);

            // send one confirmed message for ensuring that a basicstation is "bound" to the device
            await Task.Delay(Constants.DELAY_BETWEEN_MESSAGES);

            var msg = PayloadGenerator.Next().ToString(CultureInfo.InvariantCulture);

            Log($"{device.DeviceID}: Sending confirmed '{msg}'");
            await ArduinoDevice.transferPacketWithConfirmedAsync(msg, 10);

            await Task.Delay(Constants.DELAY_BETWEEN_MESSAGES);

            TestFixtureCi.ClearLogs();

            // Now sending a c2d
            var c2d = new LoRaCloudToDeviceMessage()
            {
                DevEUI     = DevEui.Parse(device.DeviceID),
                MessageId  = Guid.NewGuid().ToString(),
                Fport      = FramePorts.App23,
                RawPayload = Convert.ToBase64String(new byte[] { 0xFF, 0x00 }),
            };

            TestLogger.Log($"[INFO] Using service API to send C2D message to device {device.DeviceID}");
            TestLogger.Log($"[INFO] {JsonConvert.SerializeObject(c2d, Formatting.None)}");

            // send message using the SendCloudToDeviceMessage API endpoint
            Assert.True(await LoRaAPIHelper.SendCloudToDeviceMessage(device.DevEui, c2d));

            await Task.Delay(Constants.DELAY_BETWEEN_MESSAGES);

            // 0000000000000024: received cloud to device message from direct method
            await TestFixtureCi.AssertNetworkServerModuleLogStartsWithAsync($"{device.DeviceID}: received cloud to device message from direct method");

            Assert.Contains(ArduinoDevice.SerialLogs, (l) => l.Contains("PORT: 23; RX: \"FF00\"", StringComparison.Ordinal));
            Assert.Contains(ArduinoDevice.SerialLogs, (l) => l.Contains("RXWIN0, RSSI", StringComparison.Ordinal));
            await AssertUtils.ContainsWithRetriesAsync("+MSG: Done", ArduinoDevice.SerialLogs);
        }
예제 #12
0
        public async Task SendCloudToDeviceMessageAsync(string deviceId, LoRaCloudToDeviceMessage message)
        {
            using var msg = new Message(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(message)))
                  {
                      ExpiryTimeUtc = DateTime.UtcNow.AddMinutes(C2dExpiryTime)
                  };

            if (!string.IsNullOrEmpty(message.MessageId))
            {
                msg.MessageId = message.MessageId;
            }

            await SendCloudToDeviceMessageAsync(deviceId, msg);
        }
        private async Task <IActionResult> SendMessageViaDirectMethodAsync(
            string preferredGatewayID,
            DevEui devEUI,
            LoRaCloudToDeviceMessage c2dMessage)
        {
            try
            {
                var method = new CloudToDeviceMethod(LoraKeysManagerFacadeConstants.CloudToDeviceMessageMethodName, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1));
                _ = method.SetPayloadJson(JsonConvert.SerializeObject(c2dMessage));

                var res = await this.serviceClient.InvokeDeviceMethodAsync(preferredGatewayID, LoraKeysManagerFacadeConstants.NetworkServerModuleId, method);

                if (IsSuccessStatusCode(res.Status))
                {
                    this.log.LogInformation("Direct method call to {gatewayID} and {devEUI} succeeded with {statusCode}", preferredGatewayID, devEUI, res.Status);

                    return(new OkObjectResult(new SendCloudToDeviceMessageResult()
                    {
                        DevEui = devEUI,
                        MessageID = c2dMessage.MessageId,
                        ClassType = "C",
                    }));
                }

                this.log.LogError("Direct method call to {gatewayID} failed with {statusCode}. Response: {response}", preferredGatewayID, res.Status, res.GetPayloadAsJson());

                return(new ObjectResult(res.GetPayloadAsJson())
                {
                    StatusCode = res.Status,
                });
            }
            catch (JsonSerializationException ex)
            {
                this.log.LogError(ex, "Failed to serialize C2D message {c2dmessage} to {devEUI}", JsonConvert.SerializeObject(c2dMessage), devEUI);
                return(new ObjectResult("Failed serialize C2D Message")
                {
                    StatusCode = (int)HttpStatusCode.InternalServerError
                });
            }
            catch (IotHubException ex)
            {
                this.log.LogError(ex, "Failed to send message for {devEUI} to the IoT Hub", devEUI);
                return(new ObjectResult("Failed to send message for device to the iot Hub")
                {
                    StatusCode = (int)HttpStatusCode.InternalServerError
                });
            }
        }
예제 #14
0
        public async Task When_Message_Is_Invalid_Should_Return_BadRequest()
        {
            var c2dMessage = new LoRaCloudToDeviceMessage()
            {
                DevEUI  = "0123456789",
                Fport   = LoRaFPort.MacCommand,
                Payload = "hello",
            };

            var actual = await this.sendCloudToDeviceMessage.SendCloudToDeviceMessageImplementationAsync(
                "0123456789",
                c2dMessage);

            Assert.IsType <BadRequestObjectResult>(actual);

            this.serviceClient.VerifyAll();
            this.registryManager.VerifyAll();
        }
예제 #15
0
        public async Task When_DevEUI_Is_Missing_Should_Return_BadRequest(string devEUI)
        {
            var c2dMessage = new LoRaCloudToDeviceMessage()
            {
                DevEUI = null,
            };

            var request = new DefaultHttpContext().Request;

            request.Body = new MemoryStream(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(c2dMessage)));

            var result = await this.sendCloudToDeviceMessage.Run(request, devEUI);

            Assert.IsType <BadRequestObjectResult>(result);

            this.serviceClient.VerifyAll();
            this.registryManager.VerifyAll();
        }
예제 #16
0
        public async Task When_Sending_Message_Throws_Error_Should_Return_Application_Error()
        {
            var devEui = new DevEui(123456789);

            var deviceTwin = new Twin
            {
                Properties = new TwinProperties()
                {
                    Desired = new TwinCollection($"{{\"DevAddr\": \"03010101\"}}"),
                }
            };

            var query = new Mock <IQuery>(MockBehavior.Strict);

            query.Setup(x => x.HasMoreResults).Returns(true);
            query.Setup(x => x.GetNextAsTwinAsync())
            .ReturnsAsync(new[] { deviceTwin });

            this.registryManager.Setup(x => x.CreateQuery(It.IsNotNull <string>(), It.IsAny <int?>()))
            .Returns(query.Object);

            var actualMessage = new LoRaCloudToDeviceMessage()
            {
                MessageId = "myMessageId-1234",
                Fport     = TestPort,
                Payload   = "hello",
            };

            this.serviceClient.Setup(x => x.SendAsync(devEui.ToString(), It.IsNotNull <Message>()))
            .ThrowsAsync(new IotHubCommunicationException(string.Empty));

            var actual = await this.sendCloudToDeviceMessage.SendCloudToDeviceMessageImplementationAsync(
                devEui,
                actualMessage);

            Assert.IsType <ObjectResult>(actual);
            Assert.Equal((int)HttpStatusCode.InternalServerError, ((ObjectResult)actual).StatusCode);

            this.serviceClient.VerifyAll();
            this.registryManager.VerifyAll();
            query.VerifyAll();
        }
예제 #17
0
        public async Task When_Querying_Devices_And_Finds_No_Gateway_For_Class_C_Should_Return_InternalServerError()
        {
            var devEui     = new DevEui(0123456789);
            var devAddr    = new DevAddr(03010101);
            var deviceTwin = new Twin
            {
                Properties = new TwinProperties()
                {
                    Desired  = new TwinCollection($"{{\"DevAddr\": \"{devAddr}\", \"ClassType\": \"C\"}}"),
                    Reported = new TwinCollection(),
                }
            };

            var query = new Mock <IQuery>(MockBehavior.Strict);

            query.Setup(x => x.HasMoreResults).Returns(true);
            query.Setup(x => x.GetNextAsTwinAsync())
            .ReturnsAsync(new[] { deviceTwin });

            this.registryManager.Setup(x => x.CreateQuery(It.IsNotNull <string>(), It.IsAny <int?>()))
            .Returns(query.Object);

            var actualMessage = new LoRaCloudToDeviceMessage()
            {
                Fport   = TestPort,
                Payload = "hello",
            };

            var actual = await this.sendCloudToDeviceMessage.SendCloudToDeviceMessageImplementationAsync(
                devEui,
                actualMessage);

            var result = Assert.IsType <ObjectResult>(actual);

            Assert.Equal(500, result.StatusCode);
            Assert.Equal("Class C devices must sent at least one message upstream. None has been received", result.Value.ToString());

            this.serviceClient.VerifyAll();
            this.registryManager.VerifyAll();
            query.VerifyAll();
        }
예제 #18
0
        public async Task When_Device_Is_Found_In_Cache_Should_Send_Via_Direct_Method()
        {
            const string devEUI           = "0123456789";
            var          preferredGateway = new LoRaDevicePreferredGateway("gateway1", 100);

            LoRaDevicePreferredGateway.SaveToCache(this.cacheStore, devEUI, preferredGateway);

            var actualMessage = new LoRaCloudToDeviceMessage()
            {
                Fport   = 1,
                Payload = "hello",
            };

            this.serviceClient.Setup(x => x.InvokeDeviceMethodAsync("gateway1", LoraKeysManagerFacadeConstants.NetworkServerModuleId, It.IsNotNull <CloudToDeviceMethod>()))
            .Callback <string, string, CloudToDeviceMethod>((device, methodName, method) =>
            {
                var c2dMessage = JsonConvert.DeserializeObject <LoRaCloudToDeviceMessage>(method.GetPayloadAsJson());
                Assert.Equal(c2dMessage.Fport, actualMessage.Fport);
                Assert.Equal(c2dMessage.Payload, actualMessage.Payload);
            })
            .ReturnsAsync(new CloudToDeviceMethodResult()
            {
                Status = (int)HttpStatusCode.OK
            });

            var actual = await this.sendCloudToDeviceMessage.SendCloudToDeviceMessageImplementationAsync(
                devEUI,
                actualMessage);

            Assert.IsType <OkObjectResult>(actual);
            var responseValue = ((OkObjectResult)actual).Value as SendCloudToDeviceMessageResult;

            Assert.NotNull(responseValue);
            Assert.Equal("C", responseValue.ClassType);
            Assert.Equal(devEUI, responseValue.DevEUI);
            Assert.Null(responseValue.MessageID);

            this.serviceClient.VerifyAll();
            this.registryManager.VerifyAll();
        }
예제 #19
0
        public void When_FPort_Is_Reserved_Should_Be_Invalid()
        {
            var payloadC2d = new LoRaCloudToDeviceMessage()
            {
                MessageId = "01",
                Payload   = "hello",
                Fport     = LoRaFPort.ReservedForFutureAplications,
            };

            Assert.False(payloadC2d.IsValid(out var errorMessage));
            Assert.Equal("invalid fport '224' in cloud to device message '01'", errorMessage);

            var rawPayloadC2d = new LoRaCloudToDeviceMessage()
            {
                MessageId  = "02",
                RawPayload = "AAAA",
                Fport      = LoRaFPort.ReservedForFutureAplications + 1,
            };

            Assert.False(rawPayloadC2d.IsValid(out errorMessage));
            Assert.Equal("invalid fport '225' in cloud to device message '02'", errorMessage);
        }
예제 #20
0
        public void When_Has_Payload_And_MacCommand_Non_Zero_FPort_Should_Be_Valid()
        {
            var payloadC2d = new LoRaCloudToDeviceMessage()
            {
                MessageId = "01",
                Payload   = "hello",
                Fport     = 1,
            };

            Assert.True(payloadC2d.IsValid(out var errorMessage));
            Assert.Null(errorMessage);

            var rawPayloadC2d = new LoRaCloudToDeviceMessage()
            {
                MessageId  = "01",
                RawPayload = "AAAA",
                Fport      = 2,
            };

            Assert.True(rawPayloadC2d.IsValid(out errorMessage));
            Assert.Null(errorMessage);
        }
예제 #21
0
        public void When_FPort_Is_For_Mac_Command_Should_Be_Invalid_For_Payload()
        {
            var payloadC2d = new LoRaCloudToDeviceMessage()
            {
                MessageId = "01",
                Payload   = "hello",
                Fport     = LoRaFPort.MacCommand,
            };

            Assert.False(payloadC2d.IsValid(out var errorMessage));
            Assert.Equal("invalid MAC command fport usage in cloud to device message '01'", errorMessage);

            var rawPayloadC2d = new LoRaCloudToDeviceMessage()
            {
                MessageId  = "01",
                RawPayload = "AAAA",
                Fport      = LoRaFPort.MacCommand,
            };

            Assert.False(rawPayloadC2d.IsValid(out errorMessage));
            Assert.Equal("invalid MAC command fport usage in cloud to device message '01'", errorMessage);
        }
        public async Task Test_OTAA_Confirmed_Receives_C2D_Message_With_RX_Delay_2()
        {
            const int messagesToSend     = 10;
            const int warmUpMessageCount = 2;
            var       device             = this.TestFixtureCi.Device9_OTAA;

            this.LogTestStart(device);

            await this.ArduinoDevice.setDeviceModeAsync(LoRaArduinoSerial._device_mode_t.LWOTAA);

            await this.ArduinoDevice.setIdAsync(device.DevAddr, device.DeviceID, device.AppEUI);

            await this.ArduinoDevice.setKeyAsync(device.NwkSKey, device.AppSKey, device.AppKey);

            await this.ArduinoDevice.SetupLora(this.TestFixtureCi.Configuration.LoraRegion);

            var joinSucceeded = await this.ArduinoDevice.setOTAAJoinAsyncWithRetry(LoRaArduinoSerial._otaa_join_cmd_t.JOIN, 20000, 5);

            Assert.True(joinSucceeded, "Join failed");

            // find the gateway that accepted the join
            var joinAccept = await this.TestFixtureCi.SearchNetworkServerModuleAsync((s) => s.IndexOf("JoinAccept", StringComparison.OrdinalIgnoreCase) != -1);

            Assert.NotNull(joinAccept);
            Assert.NotNull(joinAccept.MatchedEvent);

            var targetGw = joinAccept.MatchedEvent.SourceId;

            Assert.Equal(device.GatewayID, targetGw);

            // wait 1 second after joined
            await Task.Delay(Constants.DELAY_FOR_SERIAL_AFTER_JOIN);

            // Sends 2x confirmed messages
            for (var i = 1; i <= warmUpMessageCount; ++i)
            {
                var msg = PayloadGenerator.Next().ToString();
                this.Log($"{device.DeviceID}: Sending confirmed '{msg}' {i}/{messagesToSend}");

                await this.ArduinoDevice.transferPacketWithConfirmedAsync(msg, 10);

                await Task.Delay(Constants.DELAY_BETWEEN_MESSAGES);

                // +CMSG: ACK Received
                await AssertUtils.ContainsWithRetriesAsync("+CMSG: ACK Received", this.ArduinoDevice.SerialLogs);

                this.TestFixtureCi.ClearLogs();
            }

            // sends C2D - between 10 and 99
            var c2dMessageBody = (100 + random.Next(90)).ToString();
            var c2dMessage     = new LoRaCloudToDeviceMessage()
            {
                Payload   = c2dMessageBody,
                Fport     = 1,
                MessageId = Guid.NewGuid().ToString(),
            };

            await this.TestFixtureCi.SendCloudToDeviceMessageAsync(device.DeviceID, c2dMessage);

            this.Log($"Message {c2dMessageBody} sent to device, need to check if it receives");

            var foundC2DMessageCount    = 0;
            var foundReceivePacketCount = 0;
            var expectedRxSerial        = $"+CMSG: PORT: 1; RX: \"{this.ToHexString(c2dMessageBody)}\"";

            this.Log($"Expected C2D received log is: {expectedRxSerial}");

            var c2dLogMessage = $"{device.DeviceID}: done completing cloud to device message, id: {c2dMessage.MessageId}";

            this.Log($"Expected C2D network server log is: {expectedRxSerial}");

            // Sends 8x confirmed messages, stopping if C2D message is found
            for (var i = warmUpMessageCount + 1; i <= messagesToSend; ++i)
            {
                var msg = PayloadGenerator.Next().ToString();
                this.Log($"{device.DeviceID}: Sending confirmed '{msg}' {i}/{messagesToSend}");
                await this.ArduinoDevice.transferPacketWithConfirmedAsync(msg, 10);

                await Task.Delay(TimeSpan.FromSeconds(5));

                // After transferPacketWithConfirmed: Expectation from serial
                // +CMSG: ACK Received
                await AssertUtils.ContainsWithRetriesAsync("+CMSG: ACK Received", this.ArduinoDevice.SerialLogs);

                // Check that RXDelay was correctly used
                if (this.ArduinoDevice.SerialLogs.Where(x => x.StartsWith("+CMSG: RXWIN1", StringComparison.OrdinalIgnoreCase)).Count() > 0)
                {
                    await this.TestFixtureCi.CheckAnswerTimingAsync(device.RXDelay *Constants.CONVERT_TO_PKT_FWD_TIME, false, device.GatewayID);
                }
                else if (this.ArduinoDevice.SerialLogs.Where(x => x.StartsWith("+CMSG: RXWIN2", StringComparison.OrdinalIgnoreCase)).Count() > 0)
                {
                    await this.TestFixtureCi.CheckAnswerTimingAsync(device.RXDelay *Constants.CONVERT_TO_PKT_FWD_TIME, true, device.GatewayID);
                }
                else
                {
                    Assert.True(false, "We were not able to determine in which windows the acknowledgement was submitted");
                }

                // check if c2d message was found
                // 0000000000000009: C2D message: 58
                var searchResults = await this.TestFixtureCi.SearchNetworkServerModuleAsync(
                    (messageBody) =>
                {
                    return(messageBody.StartsWith(c2dLogMessage, StringComparison.OrdinalIgnoreCase));
                },
                    new SearchLogOptions
                {
                    Description = c2dLogMessage,
                    MaxAttempts = 1
                });

                // We should only receive the message once
                if (searchResults.Found)
                {
                    foundC2DMessageCount++;
                    this.Log($"{device.DeviceID}: Found C2D message in log (after sending {i}/{messagesToSend}) {foundC2DMessageCount} times");
                    this.EnsureNotSeenTooManyTimes(foundC2DMessageCount);
                }

                var localFoundCloudToDeviceInSerial = this.ArduinoDevice.SerialLogs.Contains(expectedRxSerial);
                if (localFoundCloudToDeviceInSerial)
                {
                    foundReceivePacketCount++;
                    this.Log($"{device.DeviceID}: Found C2D message in serial logs (after sending {i}/{messagesToSend}) {foundReceivePacketCount} times");
                    this.EnsureNotSeenTooManyTimes(foundReceivePacketCount);
                }

                this.TestFixtureCi.ClearLogs();

                await Task.Delay(Constants.DELAY_BETWEEN_MESSAGES);
            }

            Assert.True(foundC2DMessageCount > 0, $"Did not find '{device.DeviceID}: C2D message: {c2dMessageBody}' in logs");

            // checks if log arrived
            if (foundReceivePacketCount == 0)
            {
                if (this.ArduinoDevice.SerialLogs.Contains(expectedRxSerial))
                {
                    foundReceivePacketCount++;
                }
            }

            Assert.True(foundReceivePacketCount > 0, $"Could not find lora receiving message '{expectedRxSerial}'");
        }
        public async Task C2D_When_Device_Has_Preferred_Windows_2_Should_Receive_In_2nd_Window_With_Custom_DR()
        {
            const int messagesToSend     = 10;
            const int warmUpMessageCount = 2;
            var       device             = this.TestFixtureCi.Device21_ABP;

            this.LogTestStart(device);
            // Setup LoRa device properties
            await this.ArduinoDevice.setDeviceModeAsync(LoRaArduinoSerial._device_mode_t.LWABP);

            await this.ArduinoDevice.setIdAsync(device.DevAddr, device.DeviceID, device.AppEUI);

            await this.ArduinoDevice.setKeyAsync(device.NwkSKey, device.AppSKey, device.AppKey);

            // Setup protocol properties
            await this.ArduinoDevice.SetupLora(this.TestFixture.Configuration.LoraRegion);

            // Sends 2x unconfirmed messages
            for (var i = 1; i <= warmUpMessageCount; ++i)
            {
                var msg = PayloadGenerator.Next().ToString();
                this.Log($"{device.DeviceID}: Sending unconfirmed '{msg}' {i}/{messagesToSend}");

                await this.ArduinoDevice.transferPacketAsync(msg, 10);

                await Task.Delay(Constants.DELAY_BETWEEN_MESSAGES);

                await AssertUtils.ContainsWithRetriesAsync("+MSG: Done", this.ArduinoDevice.SerialLogs);

                this.TestFixture.ClearLogs();
            }

            // sends C2D - between 10 and 99
            var c2dMessageBody = (100 + random.Next(90)).ToString();
            var msgId          = Guid.NewGuid().ToString();
            var c2dMessage     = new LoRaCloudToDeviceMessage()
            {
                Payload   = c2dMessageBody,
                Fport     = 1,
                MessageId = msgId,
                Confirmed = true,
            };

            await this.TestFixtureCi.SendCloudToDeviceMessageAsync(device.DeviceID, c2dMessage);

            this.Log($"Message {c2dMessageBody} sent to device, need to check if it receives");

            var foundC2DMessageCount         = 0;
            var foundReceivePacketCount      = 0;
            var foundReceivePacketInRX2Count = 0;
            var expectedRxSerial1            = $"+MSG: PORT: 1; RX: \"{this.ToHexString(c2dMessageBody)}\"";
            var expectedRxSerial2            = $"+MSG: RXWIN2";
            var expectedUDPMessageV1         = $"{device.DevAddr}: ConfirmedDataDown";
            var expectedUDPMessageV2         = $"{device.DeviceID}: cloud to device message: {this.ToHexString(c2dMessageBody)}, id: {msgId}, fport: 1, confirmed: True";

            this.Log($"Expected C2D received log is: {expectedRxSerial1} and {expectedRxSerial2}");
            this.Log($"Expected UDP log starting with: {expectedUDPMessageV1} or {expectedUDPMessageV2}");

            // Sends 8x confirmed messages, stopping if C2D message is found
            for (var i = warmUpMessageCount + 1; i <= messagesToSend; ++i)
            {
                var msg = PayloadGenerator.Next().ToString();
                this.Log($"{device.DeviceID}: Sending unconfirmed '{msg}' {i}/{messagesToSend}");
                await this.ArduinoDevice.transferPacketAsync(msg, 10);

                await Task.Delay(Constants.DELAY_BETWEEN_MESSAGES);

                await AssertUtils.ContainsWithRetriesAsync("+MSG: Done", this.ArduinoDevice.SerialLogs);

                // check if c2d message was found
                var searchResults = await this.TestFixture.SearchNetworkServerModuleAsync(
                    (messageBody) =>
                {
                    return(messageBody.StartsWith(expectedUDPMessageV1, StringComparison.OrdinalIgnoreCase) || messageBody.StartsWith(expectedUDPMessageV2, StringComparison.OrdinalIgnoreCase));
                },
                    new SearchLogOptions
                {
                    Description = $"{expectedUDPMessageV1} or {expectedUDPMessageV2}",
                    MaxAttempts = 1,
                });

                // We should only receive the message once
                if (searchResults.Found)
                {
                    foundC2DMessageCount++;
                    this.Log($"{device.DeviceID}: Found C2D message in log (after sending {i}/{messagesToSend}) {foundC2DMessageCount} times");
                    this.EnsureNotSeenTooManyTimes(foundC2DMessageCount);
                }

                if (this.ArduinoDevice.SerialLogs.Contains(expectedRxSerial1))
                {
                    foundReceivePacketCount++;
                    this.Log($"{device.DeviceID}: Found C2D message in serial logs (after sending {i}/{messagesToSend}) {foundReceivePacketCount} times");
                    this.EnsureNotSeenTooManyTimes(foundReceivePacketCount);
                }

                if (this.ArduinoDevice.SerialLogs.Any(x => x.StartsWith(expectedRxSerial2, StringComparison.OrdinalIgnoreCase)))
                {
                    foundReceivePacketInRX2Count++;
                    this.Log($"{device.DeviceID}: Found C2D message (rx2) in serial logs (after sending {i}/{messagesToSend}) {foundReceivePacketInRX2Count} times");
                    this.EnsureNotSeenTooManyTimes(foundReceivePacketInRX2Count);
                }

                if (foundReceivePacketCount > 0 && foundReceivePacketInRX2Count > 0 && foundC2DMessageCount > 0)
                {
                    this.Log($"{device.DeviceID}: Found all messages in log (after sending {i}/{messagesToSend})");
                    break;
                }

                this.TestFixture.ClearLogs();

                await Task.Delay(Constants.DELAY_BETWEEN_MESSAGES);
            }

            Assert.True(foundC2DMessageCount > 0, $"Did not find {expectedUDPMessageV1} or {expectedUDPMessageV2} in logs");

            // checks if serial received the message
            if (foundReceivePacketCount == 0)
            {
                if (this.ArduinoDevice.SerialLogs.Contains(expectedRxSerial1))
                {
                    foundReceivePacketCount++;
                }
            }

            Assert.True(foundReceivePacketCount > 0, $"Could not find lora receiving message '{expectedRxSerial1}'");

            // checks if serial received the message in RX2
            if (foundReceivePacketInRX2Count == 0)
            {
                if (this.ArduinoDevice.SerialLogs.Any(x => x.StartsWith(expectedRxSerial2, StringComparison.OrdinalIgnoreCase)))
                {
                    foundReceivePacketInRX2Count++;
                }
            }

            Assert.True(foundReceivePacketInRX2Count > 0, $"Could not find lora receiving message '{expectedRxSerial2}'");
        }
예제 #24
0
        public async Task Data_Rate_Is_Updated_When_C2D_With_LinkADRCmd_Received()
        {
            const int messagesToSend     = 10;
            const int warmUpMessageCount = 2;

            var device = TestFixtureCi.Device32_ABP;

            LogTestStart(device);

            // Setup LoRa device properties
            await ArduinoDevice.setDeviceModeAsync(LoRaArduinoSerial._device_mode_t.LWABP);

            await ArduinoDevice.setIdAsync(device.DevAddr, device.DeviceID, device.AppEui);

            await ArduinoDevice.setKeyAsync(device.NwkSKey, device.AppSKey, device.AppKey);

            // Setup protocol properties
            // Start with DR5
            await ArduinoDevice.SetupLora(TestFixtureCi.Configuration.LoraRegion, LoRaArduinoSerial._data_rate_t.DR5, 4, true);

            await TestFixture.CleanupC2DDeviceQueueAsync(device.DeviceID);

            // Sends 2x unconfirmed messages
            for (var i = 1; i <= warmUpMessageCount; ++i)
            {
                var msg = PayloadGenerator.Next().ToString(CultureInfo.InvariantCulture);
                Log($"{device.DeviceID}: Sending unconfirmed '{msg}' {i}/{messagesToSend}");
                await ArduinoDevice.transferPacketAsync(msg, 10);

                await Task.Delay(Constants.DELAY_BETWEEN_MESSAGES);

                await AssertUtils.ContainsWithRetriesAsync("+MSG: Done", ArduinoDevice.SerialLogs);

                TestFixture.ClearLogs();
            }

            var c2dMessage = new LoRaCloudToDeviceMessage()
            {
                Fport       = FramePort.MacCommand,
                Payload     = String.Empty,
                MacCommands = { new LinkADRRequest(datarate: 3, txPower: 4, chMask: 25, chMaskCntl: 0, nbTrans: 1) } // Update data rate to DR3
            };

            await TestFixtureCi.SendCloudToDeviceMessageAsync(device.DeviceID, c2dMessage);

            Log($"C2D Message sent to device, need to check if it receives");

            var foundC2DMessage      = false;
            var foundLinkADRCmd      = false;
            var foundChangedDataRate = false;

            // Sends 8x unconfirmed messages, stopping if C2D message is found and data rate is updated
            for (var i = warmUpMessageCount + 1; i <= messagesToSend; ++i)
            {
                var msg = PayloadGenerator.Next().ToString(CultureInfo.InvariantCulture);
                Log($"{device.DeviceID}: Sending unconfirmed '{msg}' {i}/{messagesToSend}");
                await ArduinoDevice.transferPacketAsync(msg, 10);

                await Task.Delay(Constants.DELAY_BETWEEN_MESSAGES);

                // After transferPacket: Expectation from serial
                // +MSG: Done
                await AssertUtils.ContainsWithRetriesAsync("+MSG: Done", ArduinoDevice.SerialLogs);

                // check if C2D message was found
                if (await SearchMessageAsync($"{device.DeviceID}: cloud to device MAC command LinkADRCmd received Type: LinkADRCmd Answer, datarate: 3"))
                {
                    foundC2DMessage = true;
                }

                // check if LinkADRCmd MAC Command was detected
                if (await SearchMessageAsync("LinkADRCmd mac command detected in upstream payload: Type: LinkADRCmd Answer, power: changed, data rate: changed"))
                {
                    foundLinkADRCmd = true;
                }

                // check if the data rate was changed to DR3
                if (await SearchMessageAsync("\"datr\":\"SF9BW125\""))
                {
                    foundChangedDataRate = true;
                }

                async Task <bool> SearchMessageAsync(string message)
                {
                    var searchResult =
                        await TestFixtureCi.SearchNetworkServerModuleAsync(messageBody => messageBody.Contains(message, StringComparison.OrdinalIgnoreCase),
                                                                           new SearchLogOptions(message)
                    {
                        MaxAttempts = 1
                    });

                    return(searchResult.Found);
                }

                TestFixture.ClearLogs();
                await Task.Delay(Constants.DELAY_BETWEEN_MESSAGES);

                if (foundC2DMessage && foundLinkADRCmd && foundChangedDataRate)
                {
                    break;
                }
            }

            Assert.True(foundC2DMessage, $"Could not find C2D message with MAC command LinkADRCmd in LNS log");
            Assert.True(foundLinkADRCmd, $"Could not find LinkADRCmd MAC Command in LNS log");
            Assert.True(foundChangedDataRate, $"Could not find updated data rate in LNS log");
        }
        // Ensures that C2D messages are received when working with unconfirmed messages
        // Uses Device10_OTAA
        private async Task Test_OTAA_Unconfirmed_Receives_Confirmed_C2D_Message(string devicePropertyName)
        {
            const int messagesToSend     = 10;
            const int warmUpMessageCount = 2;
            var       device             = this.TestFixtureCi.GetDeviceByPropertyName(devicePropertyName);

            this.LogTestStart(device);
            await this.ArduinoDevice.setDeviceModeAsync(LoRaArduinoSerial._device_mode_t.LWOTAA);

            await this.ArduinoDevice.setIdAsync(device.DevAddr, device.DeviceID, device.AppEUI);

            await this.ArduinoDevice.setKeyAsync(device.NwkSKey, device.AppSKey, device.AppKey);

            await this.ArduinoDevice.SetupLora(this.TestFixtureCi.Configuration.LoraRegion);

            var joinSucceeded = await this.ArduinoDevice.setOTAAJoinAsyncWithRetry(LoRaArduinoSerial._otaa_join_cmd_t.JOIN, 20000, 5);

            Assert.True(joinSucceeded, "Join failed");

            // wait 1 second after joined
            await Task.Delay(Constants.DELAY_FOR_SERIAL_AFTER_JOIN);

            if (device.IsMultiGw)
            {
                await this.TestFixtureCi.WaitForTwinSyncAfterJoinAsync(this.ArduinoDevice.SerialLogs, device.DeviceID);
            }

            // Sends 2x unconfirmed messages
            for (var i = 1; i <= warmUpMessageCount; ++i)
            {
                var msg = PayloadGenerator.Next().ToString();
                this.Log($"{device.DeviceID}: Sending unconfirmed '{msg}' {i}/{messagesToSend}");

                await this.ArduinoDevice.transferPacketAsync(msg, 10);

                await Task.Delay(Constants.DELAY_BETWEEN_MESSAGES);

                await AssertUtils.ContainsWithRetriesAsync("+MSG: Done", this.ArduinoDevice.SerialLogs);

                this.TestFixtureCi.ClearLogs();
            }

            // sends C2D - between 10 and 99
            var c2dMessageBody = (100 + random.Next(90)).ToString();
            var msgId          = Guid.NewGuid().ToString();
            var c2dMessage     = new LoRaCloudToDeviceMessage()
            {
                Payload   = c2dMessageBody,
                Fport     = 1,
                MessageId = msgId,
                Confirmed = true,
            };

            await this.TestFixtureCi.SendCloudToDeviceMessageAsync(device.DeviceID, c2dMessage);

            this.Log($"Message {c2dMessageBody} sent to device, need to check if it receives");

            var foundC2DMessageCount    = 0;
            var foundReceivePacketCount = 0;
            var expectedRxSerial        = $"+MSG: PORT: 1; RX: \"{this.ToHexString(c2dMessageBody)}\"";
            var expectedUDPMessageV1    = $"{device.DevAddr}: ConfirmedDataDown";
            var expectedUDPMessageV2    = $"{device.DeviceID}: cloud to device message: {this.ToHexString(c2dMessageBody)}, id: {msgId}, fport: 1, confirmed: True";

            this.Log($"Expected C2D received log is: {expectedRxSerial}");
            this.Log($"Expected UDP log starting with: {expectedUDPMessageV1} or {expectedUDPMessageV2}");

            // Sends 8x unconfirmed messages, stopping if C2D message is found
            for (var i = warmUpMessageCount + 1; i <= messagesToSend; ++i)
            {
                var msg = PayloadGenerator.Next().ToString();
                this.Log($"{device.DeviceID}: Sending unconfirmed '{msg}' {i}/{messagesToSend}");
                await this.ArduinoDevice.transferPacketAsync(msg, 10);

                await Task.Delay(Constants.DELAY_FOR_SERIAL_AFTER_SENDING_PACKET);

                await AssertUtils.ContainsWithRetriesAsync("+MSG: Done", this.ArduinoDevice.SerialLogs);

                // check if c2d message was found
                var searchResults = await this.TestFixtureCi.SearchNetworkServerModuleAsync(
                    (messageBody) =>
                {
                    return(messageBody.StartsWith(expectedUDPMessageV1, StringComparison.OrdinalIgnoreCase) || messageBody.StartsWith(expectedUDPMessageV2, StringComparison.OrdinalIgnoreCase));
                },
                    new SearchLogOptions
                {
                    Description = $"{expectedUDPMessageV1} or {expectedUDPMessageV2}",
                    MaxAttempts = 1
                });

                // We should only receive the message once
                if (searchResults.Found)
                {
                    foundC2DMessageCount++;
                    this.Log($"{device.DeviceID}: Found C2D message in log (after sending {i}/{messagesToSend}) {foundC2DMessageCount} times");
                    this.EnsureNotSeenTooManyTimes(foundC2DMessageCount);
                }

                var localFoundCloudToDeviceInSerial = this.ArduinoDevice.SerialLogs.Contains(expectedRxSerial);
                if (localFoundCloudToDeviceInSerial)
                {
                    foundReceivePacketCount++;
                    this.Log($"{device.DeviceID}: Found C2D message in serial logs (after sending {i}/{messagesToSend}) {foundReceivePacketCount} times");
                    this.EnsureNotSeenTooManyTimes(foundReceivePacketCount);
                }

                this.TestFixtureCi.ClearLogs();

                await Task.Delay(Constants.DELAY_BETWEEN_MESSAGES);
            }

            Assert.True(foundC2DMessageCount > 0, $"Did not find {expectedUDPMessageV1} or {expectedUDPMessageV2} in logs");

            // checks if log arrived
            if (foundReceivePacketCount == 0)
            {
                if (this.ArduinoDevice.SerialLogs.Contains(expectedRxSerial))
                {
                    foundReceivePacketCount++;
                }
            }

            Assert.True(foundReceivePacketCount > 0, $"Could not find lora receiving message '{expectedRxSerial}'");
        }
예제 #26
0
        private async Task Test_OTAA_Unconfirmed_Send_And_Receive_C2D_Mac_CommandsImplAsync(TestDeviceInfo device, string c2dMessageBody)
        {
            const int MaxAttempts         = 5;
            const int UnconfirmedMsgCount = 2;

            // Sends 2x unconfirmed messages
            for (var i = 1; i <= UnconfirmedMsgCount; ++i)
            {
                var msg = PayloadGenerator.Next().ToString(CultureInfo.InvariantCulture);
                Log($"{device.DeviceID}: Sending unconfirmed '{msg}' {i}/{UnconfirmedMsgCount}");

                await ArduinoDevice.transferPacketAsync(msg, 10);

                await Task.Delay(Constants.DELAY_BETWEEN_MESSAGES);

                await AssertUtils.ContainsWithRetriesAsync("+MSG: Done", ArduinoDevice.SerialLogs);

                TestFixtureCi.ClearLogs();
            }

            var c2dMessage = new LoRaCloudToDeviceMessage()
            {
                Fport       = FramePorts.App1,
                Payload     = c2dMessageBody,
                MacCommands = { new DevStatusRequest() }
            };

            await TestFixtureCi.SendCloudToDeviceMessageAsync(device.DeviceID, c2dMessage);

            Log($"Message {c2dMessageBody} sent to device, need to check if it receives");

            var macCommandReceivedMsg      = $"{device.DeviceID}: cloud to device MAC command DevStatusCmd received";
            var foundMacCommandReceivedMsg = false;

            var deviceMacCommandResponseMsg      = $": DevStatusCmd mac command detected in upstream payload: Type: DevStatusCmd Answer, Battery Level:";
            var foundDeviceMacCommandResponseMsg = false;

            // Sends 5x unconfirmed messages, stopping if assertions succeeded
            for (var i = 1; i <= MaxAttempts; ++i)
            {
                var msg = PayloadGenerator.Next().ToString(CultureInfo.InvariantCulture);
                Log($"{device.DeviceID}: Sending unconfirmed '{msg}' {i}/{MaxAttempts}");
                await ArduinoDevice.transferPacketAsync(msg, 10);

                await Task.Delay(Constants.DELAY_BETWEEN_MESSAGES);

                await AssertUtils.ContainsWithRetriesAsync("+MSG: Done", ArduinoDevice.SerialLogs);

                // check if c2d message was found
                if (!foundMacCommandReceivedMsg)
                {
                    var searchResults = await TestFixtureCi.SearchNetworkServerModuleAsync(
                        (messageBody) =>
                    {
                        return(messageBody.StartsWith(macCommandReceivedMsg, StringComparison.Ordinal));
                    },
                        new SearchLogOptions(macCommandReceivedMsg)
                    {
                        MaxAttempts = 1
                    });

                    // We should only receive the message once
                    if (searchResults.Found)
                    {
                        foundMacCommandReceivedMsg = true;
                        Log($"{device.DeviceID}: Found Mac C2D message in log (after sending {i}/{MaxAttempts}) ? {foundMacCommandReceivedMsg}");
                    }
                }

                if (!foundDeviceMacCommandResponseMsg)
                {
                    var macSearchResults = await TestFixtureCi.SearchNetworkServerModuleAsync(
                        (messageBody) =>
                    {
                        return(messageBody.Contains(deviceMacCommandResponseMsg, StringComparison.InvariantCultureIgnoreCase));
                    },
                        new SearchLogOptions(deviceMacCommandResponseMsg)
                    {
                        MaxAttempts = 1
                    });

                    // We should only receive the message once
                    if (macSearchResults.Found)
                    {
                        foundDeviceMacCommandResponseMsg = true;
                        Log($"{device.DeviceID}: Found Mac Command reply in log (after sending {i}/{MaxAttempts}) ? {foundDeviceMacCommandResponseMsg}");
                    }
                }

                TestFixtureCi.ClearLogs();
                await Task.Delay(Constants.DELAY_BETWEEN_MESSAGES);

                if (foundDeviceMacCommandResponseMsg && foundMacCommandReceivedMsg)
                {
                    break;
                }
            }

            Assert.True(foundMacCommandReceivedMsg, $"Did not find in network server logs: '{macCommandReceivedMsg}'");
            Assert.True(foundDeviceMacCommandResponseMsg, $"Did not find in network server logs: '{deviceMacCommandResponseMsg}'");
        }
        async Task <IActionResult> SendMessageViaCloudToDeviceMessageAsync(string devEUI, LoRaCloudToDeviceMessage c2dMessage)
        {
            try
            {
                var message = new Message(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(c2dMessage)));
                message.MessageId = string.IsNullOrEmpty(c2dMessage.MessageId) ? Guid.NewGuid().ToString() : c2dMessage.MessageId;
                await this.serviceClient.SendAsync(devEUI, message);

                this.log.LogInformation("Sending cloud to device message to {devEUI} succeeded", devEUI);

                return(new OkObjectResult(new SendCloudToDeviceMessageResult()
                {
                    DevEUI = devEUI,
                    MessageID = message.MessageId,
                    ClassType = "A",
                }));
            }
            catch (Exception ex)
            {
                this.log.LogError(ex, "Failed to send message to {devEUI}", devEUI);
                return(new ObjectResult("Failed to send message to device")
                {
                    StatusCode = (int)HttpStatusCode.InternalServerError
                });
            }
        }
        public async Task <IActionResult> SendCloudToDeviceMessageImplementationAsync(string devEUI, LoRaCloudToDeviceMessage c2dMessage)
        {
            if (string.IsNullOrEmpty(devEUI))
            {
                return(new BadRequestObjectResult($"Missing {nameof(devEUI)} value"));
            }

            if (c2dMessage == null)
            {
                return(new BadRequestObjectResult("Missing cloud to device message"));
            }

            if (!c2dMessage.IsValid(out var errorMessage))
            {
                return(new BadRequestObjectResult(errorMessage));
            }

            var cachedPreferredGateway = LoRaDevicePreferredGateway.LoadFromCache(this.cacheStore, devEUI);

            if (cachedPreferredGateway != null && !string.IsNullOrEmpty(cachedPreferredGateway.GatewayID))
            {
                return(await this.SendMessageViaDirectMethodAsync(cachedPreferredGateway.GatewayID, devEUI, c2dMessage));
            }

            var queryText = $"SELECT * FROM devices WHERE deviceId = '{devEUI}'";
            var query     = this.registryManager.CreateQuery(queryText, 1);

            if (query.HasMoreResults)
            {
                IEnumerable <Twin> deviceTwins;
                try
                {
                    deviceTwins = await query.GetNextAsTwinAsync();
                }
                catch (Exception ex)
                {
                    this.log.LogError(ex, "Failed to query devices with {query}", queryText);
                    return(new ObjectResult("Failed to query devices")
                    {
                        StatusCode = (int)HttpStatusCode.InternalServerError
                    });
                }

                var twin = deviceTwins.FirstOrDefault();

                if (twin != null)
                {
                    // the device must have a DevAddr
                    if (twin.Properties?.Desired?.GetTwinPropertyStringSafe(TwinProperty_DevAddr).Length == 0 && twin.Properties?.Reported?.GetTwinPropertyStringSafe(TwinProperty_DevAddr).Length == 0)
                    {
                        return(new BadRequestObjectResult("Device DevAddr is unknown. Ensure the device has been correctly setup as a LoRa device and that it has connected to network at least once."));
                    }

                    if (string.Equals("c", twin.Properties?.Desired?.GetTwinPropertyStringSafe(TwinProperty_ClassType), StringComparison.InvariantCultureIgnoreCase))
                    {
                        var gatewayID = twin.Properties?.Reported?.GetTwinPropertyStringSafe(TwinProperty_PreferredGatewayID);
                        if (string.IsNullOrEmpty(gatewayID))
                        {
                            gatewayID = twin.Properties?.Desired?.GetTwinPropertyStringSafe(TwinProperty_GatewayID);
                        }

                        if (!string.IsNullOrEmpty(gatewayID))
                        {
                            // add it to cache (if it does not exist)
                            var preferredGateway = new LoRaDevicePreferredGateway(gatewayID, 0);
                            LoRaDevicePreferredGateway.SaveToCache(this.cacheStore, devEUI, preferredGateway, onlyIfNotExists: true);

                            return(await this.SendMessageViaDirectMethodAsync(gatewayID, devEUI, c2dMessage));
                        }

                        // class c device that did not send a single upstream message
                        return(new BadRequestObjectResult("Class C devices must sent at least one message upstream. None has been received"));
                    }

                    // Not a class C device? Send message using sdk/queue
                    return(await this.SendMessageViaCloudToDeviceMessageAsync(devEUI, c2dMessage));
                }
            }

            return(new NotFoundObjectResult($"Device '{devEUI}' was not found"));
        }
예제 #29
0
        public async Task Test_OTAA_Unconfirmed_Receives_Confirmed_C2D_Message()
        {
            var device = this.TestFixtureCi.Device14_OTAA;

            this.LogTestStart(device);
            await this.ArduinoDevice.setDeviceModeAsync(LoRaArduinoSerial._device_mode_t.LWOTAA);

            await this.ArduinoDevice.setIdAsync(device.DevAddr, device.DeviceID, device.AppEUI);

            await this.ArduinoDevice.setKeyAsync(device.NwkSKey, device.AppSKey, device.AppKey);

            await this.ArduinoDevice.SetupLora(this.TestFixtureCi.Configuration.LoraRegion);

            var joinSucceeded = await this.ArduinoDevice.setOTAAJoinAsyncWithRetry(LoRaArduinoSerial._otaa_join_cmd_t.JOIN, 20000, 5);

            Assert.True(joinSucceeded, "Join failed");

            // wait 1 second after joined
            await Task.Delay(Constants.DELAY_FOR_SERIAL_AFTER_JOIN);

            // Sends 2x unconfirmed messages
            for (var i = 1; i <= 2; ++i)
            {
                var msg = PayloadGenerator.Next().ToString();
                this.Log($"{device.DeviceID}: Sending unconfirmed '{msg}' {i}/10");

                await this.ArduinoDevice.transferPacketAsync(msg, 10);

                await Task.Delay(Constants.DELAY_BETWEEN_MESSAGES);

                await AssertUtils.ContainsWithRetriesAsync("+MSG: Done", this.ArduinoDevice.SerialLogs);

                this.TestFixtureCi.ClearLogs();
            }

            // sends C2D - between 10 and 99
            var c2dMessageBody = (100 + random.Next(90)).ToString();
            var msgId          = Guid.NewGuid().ToString();
            var c2dMessage     = new LoRaCloudToDeviceMessage()
            {
                Payload   = c2dMessageBody,
                Fport     = 1,
                MessageId = msgId,
                Confirmed = true,
            };

            await this.TestFixtureCi.SendCloudToDeviceMessageAsync(device.DeviceID, c2dMessage);

            this.Log($"Message {c2dMessageBody} sent to device, need to check if it receives");

            var foundC2DMessage      = false;
            var foundReceivePacket   = false;
            var expectedRxSerial     = $"+MSG: PORT: 1; RX: \"{this.ToHexString(c2dMessageBody)}\"";
            var expectedUDPMessageV1 = $"{device.DevAddr}: ConfirmedDataDown";
            var expectedUDPMessageV2 = $"{device.DeviceID}: cloud to device message: {this.ToHexString(c2dMessageBody)}, id: {msgId}, fport: 1, confirmed: True";

            this.Log($"Expected C2D received log is: {expectedRxSerial}");
            this.Log($"Expected UDP log starting with: {expectedUDPMessageV1} or {expectedUDPMessageV2}");

            // Sends 8x confirmed messages, stopping if C2D message is found
            for (var i = 3; i <= 10; ++i)
            {
                var msg = PayloadGenerator.Next().ToString();
                this.Log($"{device.DeviceID}: Sending unconfirmed '{msg}' {i}/10");
                await this.ArduinoDevice.transferPacketAsync(msg, 10);

                await Task.Delay(Constants.DELAY_BETWEEN_MESSAGES);

                await AssertUtils.ContainsWithRetriesAsync("+MSG: Done", this.ArduinoDevice.SerialLogs);

                // check if c2d message was found
                var searchResults = await this.TestFixtureCi.SearchNetworkServerModuleAsync(
                    (messageBody) =>
                {
                    return(messageBody.StartsWith(expectedUDPMessageV1) || messageBody.StartsWith(expectedUDPMessageV2));
                },
                    new SearchLogOptions
                {
                    Description = $"{expectedUDPMessageV1} or {expectedUDPMessageV2}",
                    MaxAttempts = 1
                });

                // We should only receive the message once
                if (searchResults.Found)
                {
                    this.Log($"{device.DeviceID}: Found C2D message in log (after sending {i}/10) ? {foundC2DMessage}");
                    Assert.False(foundC2DMessage, "Cloud to Device message should have been detected in Network Service module only once");
                    foundC2DMessage = true;
                }

                var localFoundCloudToDeviceInSerial = this.ArduinoDevice.SerialLogs.Contains(expectedRxSerial);
                if (localFoundCloudToDeviceInSerial)
                {
                    Assert.False(foundReceivePacket, "Cloud to device message should have been received only once");
                    foundReceivePacket = true;
                }

                this.TestFixtureCi.ClearLogs();

                await Task.Delay(Constants.DELAY_BETWEEN_MESSAGES);
            }

            Assert.True(foundC2DMessage, $"Did not find {expectedUDPMessageV1} or {expectedUDPMessageV2} in logs");

            // checks if log arrived
            if (!foundReceivePacket)
            {
                foundReceivePacket = this.ArduinoDevice.SerialLogs.Contains(expectedRxSerial);
            }

            Assert.True(foundReceivePacket, $"Could not find lora receiving message '{expectedRxSerial}'");
        }
예제 #30
0
        public async Task Test_OTAA_Confirmed_Receives_C2D_Message_With_RX_Delay_2()
        {
            var device = this.TestFixtureCi.Device9_OTAA;

            this.LogTestStart(device);

            await this.ArduinoDevice.setDeviceModeAsync(LoRaArduinoSerial._device_mode_t.LWOTAA);

            await this.ArduinoDevice.setIdAsync(device.DevAddr, device.DeviceID, device.AppEUI);

            await this.ArduinoDevice.setKeyAsync(device.NwkSKey, device.AppSKey, device.AppKey);

            await this.ArduinoDevice.SetupLora(this.TestFixtureCi.Configuration.LoraRegion);

            var joinSucceeded = await this.ArduinoDevice.setOTAAJoinAsyncWithRetry(LoRaArduinoSerial._otaa_join_cmd_t.JOIN, 20000, 5);

            Assert.True(joinSucceeded, "Join failed");

            // wait 1 second after joined
            await Task.Delay(Constants.DELAY_FOR_SERIAL_AFTER_JOIN);

            // Sends 2x confirmed messages
            for (var i = 1; i <= 2; ++i)
            {
                var msg = PayloadGenerator.Next().ToString();
                this.Log($"{device.DeviceID}: Sending confirmed '{msg}' {i}/10");

                await this.ArduinoDevice.transferPacketWithConfirmedAsync(msg, 10);

                await Task.Delay(Constants.DELAY_BETWEEN_MESSAGES);

                // +CMSG: ACK Received
                await AssertUtils.ContainsWithRetriesAsync("+CMSG: ACK Received", this.ArduinoDevice.SerialLogs);

                this.TestFixtureCi.ClearLogs();
            }

            // sends C2D - between 10 and 99
            var c2dMessageBody = (100 + random.Next(90)).ToString();
            var c2dMessage     = new LoRaCloudToDeviceMessage()
            {
                Payload   = c2dMessageBody,
                Fport     = 1,
                MessageId = Guid.NewGuid().ToString(),
            };

            await this.TestFixtureCi.SendCloudToDeviceMessageAsync(device.DeviceID, c2dMessage);

            this.Log($"Message {c2dMessageBody} sent to device, need to check if it receives");

            var foundC2DMessage    = false;
            var foundReceivePacket = false;
            var expectedRxSerial   = $"+CMSG: PORT: 1; RX: \"{this.ToHexString(c2dMessageBody)}\"";

            this.Log($"Expected C2D received log is: {expectedRxSerial}");

            // Sends 8x confirmed messages, stopping if C2D message is found
            for (var i = 3; i <= 10; ++i)
            {
                var msg = PayloadGenerator.Next().ToString();
                this.Log($"{device.DeviceID}: Sending confirmed '{msg}' {i}/10");
                await this.ArduinoDevice.transferPacketWithConfirmedAsync(msg, 10);

                await Task.Delay(TimeSpan.FromSeconds(5));

                // After transferPacketWithConfirmed: Expectation from serial
                // +CMSG: ACK Received
                await AssertUtils.ContainsWithRetriesAsync("+CMSG: ACK Received", this.ArduinoDevice.SerialLogs);

                // Check that RXDelay was correctly used
                if (this.ArduinoDevice.SerialLogs.Where(x => x.StartsWith("+CMSG: RXWIN1")).Count() > 0)
                {
                    await this.TestFixtureCi.CheckAnswerTimingAsync(device.RXDelay *Constants.CONVERT_TO_PKT_FWD_TIME, false);
                }
                else if (this.ArduinoDevice.SerialLogs.Where(x => x.StartsWith("+CMSG: RXWIN2")).Count() > 0)
                {
                    await this.TestFixtureCi.CheckAnswerTimingAsync(device.RXDelay *Constants.CONVERT_TO_PKT_FWD_TIME, true);
                }
                else
                {
                    Assert.True(false, "We were not able to determine in which windows the acknowledgement was submitted");
                }

                // check if c2d message was found
                // 0000000000000009: C2D message: 58
                var c2dLogMessage = $"{device.DeviceID}: cloud to device message: {this.ToHexString(c2dMessageBody)}";
                var searchResults = await this.TestFixtureCi.SearchNetworkServerModuleAsync(
                    (messageBody) =>
                {
                    return(messageBody.StartsWith(c2dLogMessage));
                },
                    new SearchLogOptions
                {
                    Description = c2dLogMessage,
                    MaxAttempts = 1
                });

                // We should only receive the message once
                if (searchResults.Found)
                {
                    this.Log($"{device.DeviceID}: Found C2D message in log (after sending {i}/10) ? {foundC2DMessage}");
                    Assert.False(foundC2DMessage, "Cloud to Device message should have been detected in Network Service module only once");
                    foundC2DMessage = true;
                }

                var localFoundCloudToDeviceInSerial = this.ArduinoDevice.SerialLogs.Contains(expectedRxSerial);
                if (localFoundCloudToDeviceInSerial)
                {
                    Assert.False(foundReceivePacket, "Cloud to device message should have been received only once");
                    foundReceivePacket = true;
                }

                this.TestFixtureCi.ClearLogs();

                await Task.Delay(Constants.DELAY_BETWEEN_MESSAGES);
            }

            Assert.True(foundC2DMessage, $"Did not find '{device.DeviceID}: C2D message: {c2dMessageBody}' in logs");

            // checks if log arrived
            if (!foundReceivePacket)
            {
                foundReceivePacket = this.ArduinoDevice.SerialLogs.Contains(expectedRxSerial);
            }

            Assert.True(foundReceivePacket, $"Could not find lora receiving message '{expectedRxSerial}'");
        }