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)); }
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); }
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(); }
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); }
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); }
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); }
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 }); } }
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(); }
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(); }
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(); }
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(); }
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(); }
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); }
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); }
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}'"); }
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}'"); }
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")); }
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}'"); }
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}'"); }