/// <summary> /// Creates a <see cref="TestDeviceInfo"/> with ABP authentication /// </summary> public static TestDeviceInfo CreateABPDevice(uint deviceID, string prefix = null, string gatewayID = null, string sensorDecoder = "DecoderValueSensor", uint netId = 1, char deviceClassType = 'A', bool supports32BitFcnt = false) { var value8 = deviceID.ToString("00000000"); var value16 = deviceID.ToString("0000000000000000"); var value32 = deviceID.ToString("00000000000000000000000000000000"); if (!string.IsNullOrEmpty(prefix)) { value8 = string.Concat(prefix, value8.Substring(prefix.Length)); value16 = string.Concat(prefix, value16.Substring(prefix.Length)); value32 = string.Concat(prefix, value32.Substring(prefix.Length)); } var devAddrValue = NetIdHelper.SetNwkIdPart(value8, netId); var result = new TestDeviceInfo { DeviceID = value16, GatewayID = gatewayID, SensorDecoder = sensorDecoder, AppSKey = value32, NwkSKey = value32, DevAddr = devAddrValue, ClassType = deviceClassType, Supports32BitFCnt = supports32BitFcnt }; return(result); }
public async Task Join_And_Send_Unconfirmed_And_Confirmed_Messages(string deviceGatewayID, uint initialFcntUp, uint initialFcntDown, uint startingPayloadFcnt, uint netId) { var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateOTAADevice(1, gatewayID: deviceGatewayID)); var joinRequestPayload = simulatedDevice.CreateJoinRequest(); // Create Rxpk var joinRxpk = joinRequestPayload.SerializeUplink(simulatedDevice.LoRaDevice.AppKey).Rxpk[0]; var devNonce = LoRaTools.Utils.ConversionHelper.ByteArrayToString(joinRequestPayload.DevNonce); var devAddr = string.Empty; var devEUI = simulatedDevice.LoRaDevice.DeviceID; var appEUI = simulatedDevice.LoRaDevice.AppEUI; this.ServerConfiguration.NetId = netId; // Device twin will be queried var twin = new Twin(); twin.Properties.Desired[TwinProperty.DevEUI] = devEUI; twin.Properties.Desired[TwinProperty.AppEUI] = simulatedDevice.LoRaDevice.AppEUI; twin.Properties.Desired[TwinProperty.AppKey] = simulatedDevice.LoRaDevice.AppKey; twin.Properties.Desired[TwinProperty.GatewayID] = deviceGatewayID; twin.Properties.Desired[TwinProperty.SensorDecoder] = simulatedDevice.LoRaDevice.SensorDecoder; twin.Properties.Reported[TwinProperty.FCntUp] = initialFcntUp; twin.Properties.Reported[TwinProperty.FCntDown] = initialFcntDown; this.LoRaDeviceClient.Setup(x => x.GetTwinAsync()).ReturnsAsync(twin); // Device twin will be updated string afterJoinAppSKey = null; string afterJoinNwkSKey = null; string afterJoinDevAddr = null; uint afterJoinFcntDown = 0; uint afterJoinFcntUp = 0; this.LoRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>())) .Callback <TwinCollection>((updatedTwin) => { afterJoinAppSKey = updatedTwin[TwinProperty.AppSKey]; afterJoinNwkSKey = updatedTwin[TwinProperty.NwkSKey]; afterJoinDevAddr = updatedTwin[TwinProperty.DevAddr]; afterJoinFcntDown = updatedTwin[TwinProperty.FCntDown]; afterJoinFcntUp = updatedTwin[TwinProperty.FCntUp]; // should not save class C device properties Assert.False(updatedTwin.Contains(TwinProperty.Region)); Assert.False(updatedTwin.Contains(TwinProperty.PreferredGatewayID)); }) .ReturnsAsync(true); // message will be sent var sentTelemetry = new List <LoRaDeviceTelemetry>(); this.LoRaDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null)) .Callback <LoRaDeviceTelemetry, Dictionary <string, string> >((t, _) => sentTelemetry.Add(t)) .ReturnsAsync(true); // C2D message will be checked this.LoRaDeviceClient.Setup(x => x.ReceiveAsync(It.IsNotNull <TimeSpan>())) .ReturnsAsync((Message)null); // Lora device api will be search by devices with matching deveui, this.LoRaDeviceApi.Setup(x => x.SearchAndLockForJoinAsync(this.ServerConfiguration.GatewayID, devEUI, appEUI, devNonce)) .ReturnsAsync(new SearchDevicesResult(new IoTHubDeviceInfo(devAddr, devEUI, "aabb").AsList())); // multi gateway will request a next frame count down from the lora device api, prepare it if (string.IsNullOrEmpty(deviceGatewayID)) { this.LoRaDeviceApi.Setup(x => x.NextFCntDownAsync(devEUI, 0, startingPayloadFcnt + 1, this.ServerConfiguration.GatewayID)) .ReturnsAsync((ushort)1); this.LoRaDeviceApi .Setup(x => x.ExecuteFunctionBundlerAsync(devEUI, It.IsAny <FunctionBundlerRequest>())) .ReturnsAsync(() => new FunctionBundlerResult { AdrResult = new LoRaTools.ADR.LoRaADRResult { CanConfirmToDevice = false, FCntDown = 1, NbRepetition = 1, TxPower = 0 }, NextFCntDown = 1 }); } // using factory to create mock of var memoryCache = new MemoryCache(new MemoryCacheOptions()); var deviceRegistry = new LoRaDeviceRegistry(this.ServerConfiguration, memoryCache, this.LoRaDeviceApi.Object, this.LoRaDeviceFactory); // Send to message processor var messageProcessor = new MessageDispatcher( this.ServerConfiguration, deviceRegistry, this.FrameCounterUpdateStrategyProvider); var joinRequest = this.CreateWaitableRequest(joinRxpk, constantElapsedTime: TimeSpan.FromMilliseconds(300)); messageProcessor.DispatchRequest(joinRequest); Assert.True(await joinRequest.WaitCompleteAsync()); Assert.True(joinRequest.ProcessingSucceeded); Assert.NotNull(joinRequest.ResponseDownlink); Assert.Single(this.PacketForwarder.DownlinkMessages); var downlinkJoinAcceptMessage = this.PacketForwarder.DownlinkMessages[0]; var joinAccept = new LoRaPayloadJoinAccept(Convert.FromBase64String(downlinkJoinAcceptMessage.Txpk.Data), simulatedDevice.LoRaDevice.AppKey); Assert.Equal(joinAccept.DevAddr.ToArray(), ConversionHelper.StringToByteArray(afterJoinDevAddr)); // check that the device is in cache var devicesForDevAddr = deviceRegistry.InternalGetCachedDevicesForDevAddr(afterJoinDevAddr); Assert.Single(devicesForDevAddr); // should have the single device Assert.True(devicesForDevAddr.TryGetValue(devEUI, out var loRaDevice)); Assert.Equal(afterJoinAppSKey, loRaDevice.AppSKey); Assert.Equal(afterJoinNwkSKey, loRaDevice.NwkSKey); Assert.Equal(afterJoinDevAddr, loRaDevice.DevAddr); var netIdBytes = BitConverter.GetBytes(netId); Assert.Equal((uint)(netIdBytes[0] & 0b01111111), NetIdHelper.GetNwkIdPart(afterJoinDevAddr)); if (deviceGatewayID == null) { Assert.Null(loRaDevice.GatewayID); } else { Assert.Equal(deviceGatewayID, loRaDevice.GatewayID); } // fcnt is restarted Assert.Equal(0U, afterJoinFcntDown); Assert.Equal(0U, afterJoinFcntUp); Assert.Equal(0U, loRaDevice.FCntUp); Assert.Equal(0U, loRaDevice.FCntDown); Assert.False(loRaDevice.HasFrameCountChanges); simulatedDevice.LoRaDevice.AppSKey = afterJoinAppSKey; simulatedDevice.LoRaDevice.NwkSKey = afterJoinNwkSKey; simulatedDevice.LoRaDevice.DevAddr = afterJoinDevAddr; // sends unconfirmed message var unconfirmedMessagePayload = simulatedDevice.CreateUnconfirmedDataUpMessage("100", fcnt: startingPayloadFcnt); var unconfirmedRequest = this.CreateWaitableRequest(unconfirmedMessagePayload.SerializeUplink(simulatedDevice.AppSKey, simulatedDevice.NwkSKey).Rxpk[0], constantElapsedTime: TimeSpan.FromMilliseconds(300)); messageProcessor.DispatchRequest(unconfirmedRequest); Assert.True(await unconfirmedRequest.WaitCompleteAsync()); Assert.Null(unconfirmedRequest.ResponseDownlink); Assert.True(unconfirmedRequest.ProcessingSucceeded); // fcnt up was updated Assert.Equal(startingPayloadFcnt, loRaDevice.FCntUp); Assert.Equal(0U, loRaDevice.FCntDown); if (startingPayloadFcnt != 0) { // Frame change flag will be set, only saving every 10 messages Assert.True(loRaDevice.HasFrameCountChanges); } Assert.Single(sentTelemetry); // sends confirmed message var confirmedMessagePayload = simulatedDevice.CreateConfirmedDataUpMessage("200", fcnt: startingPayloadFcnt + 1); var confirmedMessageRxpk = confirmedMessagePayload.SerializeUplink(simulatedDevice.AppSKey, simulatedDevice.NwkSKey).Rxpk[0]; var confirmedRequest = this.CreateWaitableRequest(confirmedMessageRxpk, constantElapsedTime: TimeSpan.FromMilliseconds(300)); messageProcessor.DispatchRequest(confirmedRequest); Assert.True(await confirmedRequest.WaitCompleteAsync()); Assert.True(confirmedRequest.ProcessingSucceeded); Assert.NotNull(confirmedRequest.ResponseDownlink); Assert.NotNull(confirmedRequest.ResponseDownlink.Txpk); Assert.Equal(2, this.PacketForwarder.DownlinkMessages.Count); Assert.Equal(2, sentTelemetry.Count); var downstreamMessage = this.PacketForwarder.DownlinkMessages[1]; // validates txpk according to eu region Assert.True(RegionManager.EU868.TryGetDownstreamChannelFrequency(confirmedMessageRxpk, out double frequency)); Assert.Equal(frequency, downstreamMessage.Txpk.Freq); Assert.Equal("4/5", downstreamMessage.Txpk.Codr); Assert.False(downstreamMessage.Txpk.Imme); Assert.True(downstreamMessage.Txpk.Ipol); Assert.Equal("LORA", downstreamMessage.Txpk.Modu); // fcnt up was updated Assert.Equal(startingPayloadFcnt + 1, loRaDevice.FCntUp); Assert.Equal(1U, loRaDevice.FCntDown); // Frame change flag will be set, only saving every 10 messages Assert.True(loRaDevice.HasFrameCountChanges); // C2D message will be checked twice this.LoRaDeviceClient.Verify(x => x.ReceiveAsync(It.IsNotNull <TimeSpan>()), Times.Exactly(2)); // has telemetry with both fcnt Assert.Single(sentTelemetry, (t) => t.Fcnt == startingPayloadFcnt); Assert.Single(sentTelemetry, (t) => t.Fcnt == (startingPayloadFcnt + 1)); this.LoRaDeviceClient.VerifyAll(); }
public async Task Join_And_Send_Unconfirmed_And_Confirmed_Messages(string deviceGatewayID, int initialFcntUp, int initialFcntDown, int startingPayloadFcnt, uint netId) { var simulatedDevice = new SimulatedDevice(TestDeviceInfo.CreateOTAADevice(1, gatewayID: deviceGatewayID)); var joinRequest = simulatedDevice.CreateJoinRequest(); // Create Rxpk var joinRxpk = joinRequest.SerializeUplink(simulatedDevice.LoRaDevice.AppKey).Rxpk[0]; var devNonce = LoRaTools.Utils.ConversionHelper.ByteArrayToString(joinRequest.DevNonce); var devAddr = string.Empty; var devEUI = simulatedDevice.LoRaDevice.DeviceID; var appEUI = simulatedDevice.LoRaDevice.AppEUI; var loRaDeviceClient = new Mock <ILoRaDeviceClient>(MockBehavior.Strict); this.ServerConfiguration.NetId = netId; // Device twin will be queried var twin = new Twin(); twin.Properties.Desired[TwinProperty.DevEUI] = devEUI; twin.Properties.Desired[TwinProperty.AppEUI] = simulatedDevice.LoRaDevice.AppEUI; twin.Properties.Desired[TwinProperty.AppKey] = simulatedDevice.LoRaDevice.AppKey; twin.Properties.Desired[TwinProperty.GatewayID] = deviceGatewayID; twin.Properties.Desired[TwinProperty.SensorDecoder] = simulatedDevice.LoRaDevice.SensorDecoder; twin.Properties.Reported[TwinProperty.FCntUp] = initialFcntUp; twin.Properties.Reported[TwinProperty.FCntDown] = initialFcntDown; loRaDeviceClient.Setup(x => x.GetTwinAsync()).ReturnsAsync(twin); // Device twin will be updated string afterJoinAppSKey = null; string afterJoinNwkSKey = null; string afterJoinDevAddr = null; int afterJoinFcntDown = -1; int afterJoinFcntUp = -1; loRaDeviceClient.Setup(x => x.UpdateReportedPropertiesAsync(It.IsNotNull <TwinCollection>())) .Callback <TwinCollection>((updatedTwin) => { afterJoinAppSKey = updatedTwin[TwinProperty.AppSKey]; afterJoinNwkSKey = updatedTwin[TwinProperty.NwkSKey]; afterJoinDevAddr = updatedTwin[TwinProperty.DevAddr]; afterJoinFcntDown = updatedTwin[TwinProperty.FCntDown]; afterJoinFcntUp = updatedTwin[TwinProperty.FCntUp]; }) .ReturnsAsync(true); // message will be sent var sentTelemetry = new List <LoRaDeviceTelemetry>(); loRaDeviceClient.Setup(x => x.SendEventAsync(It.IsNotNull <LoRaDeviceTelemetry>(), null)) .Callback <LoRaDeviceTelemetry, Dictionary <string, string> >((t, _) => sentTelemetry.Add(t)) .ReturnsAsync(true); // C2D message will be checked loRaDeviceClient.Setup(x => x.ReceiveAsync(It.IsNotNull <TimeSpan>())) .ReturnsAsync((Message)null); // Lora device api will be search by devices with matching deveui, var loRaDeviceApi = new Mock <LoRaDeviceAPIServiceBase>(MockBehavior.Strict); loRaDeviceApi.Setup(x => x.SearchAndLockForJoinAsync(this.ServerConfiguration.GatewayID, devEUI, appEUI, devNonce)) .ReturnsAsync(new SearchDevicesResult(new IoTHubDeviceInfo(devAddr, devEUI, "aabb").AsList())); // multi gateway will request a next frame count down from the lora device api, prepare it if (string.IsNullOrEmpty(deviceGatewayID)) { loRaDeviceApi.Setup(x => x.NextFCntDownAsync(devEUI, 0, startingPayloadFcnt + 1, this.ServerConfiguration.GatewayID)) .ReturnsAsync((ushort)1); } // using factory to create mock of var loRaDeviceFactory = new TestLoRaDeviceFactory(loRaDeviceClient.Object); var memoryCache = new MemoryCache(new MemoryCacheOptions()); var deviceRegistry = new LoRaDeviceRegistry(this.ServerConfiguration, memoryCache, loRaDeviceApi.Object, loRaDeviceFactory); var frameCounterUpdateStrategyFactory = new LoRaDeviceFrameCounterUpdateStrategyFactory(this.ServerConfiguration.GatewayID, loRaDeviceApi.Object); // Send to message processor var messageProcessor = new MessageProcessor( this.ServerConfiguration, deviceRegistry, frameCounterUpdateStrategyFactory, new LoRaPayloadDecoder()); var downlinkJoinAcceptMessage = await messageProcessor.ProcessMessageAsync(joinRxpk); Assert.NotNull(downlinkJoinAcceptMessage); var joinAccept = new LoRaPayloadJoinAccept(Convert.FromBase64String(downlinkJoinAcceptMessage.Txpk.Data), simulatedDevice.LoRaDevice.AppKey); Assert.Equal(joinAccept.DevAddr.ToArray(), ConversionHelper.StringToByteArray(afterJoinDevAddr)); // check that the device is in cache var devicesForDevAddr = deviceRegistry.InternalGetCachedDevicesForDevAddr(afterJoinDevAddr); Assert.Single(devicesForDevAddr); // should have the single device Assert.True(devicesForDevAddr.TryGetValue(devEUI, out var loRaDevice)); Assert.Equal(afterJoinAppSKey, loRaDevice.AppSKey); Assert.Equal(afterJoinNwkSKey, loRaDevice.NwkSKey); Assert.Equal(afterJoinDevAddr, loRaDevice.DevAddr); var netIdBytes = BitConverter.GetBytes(netId); Assert.Equal((uint)(netIdBytes[0] & 0b01111111), NetIdHelper.GetNwkIdPart(afterJoinDevAddr)); if (deviceGatewayID == null) { Assert.Null(loRaDevice.GatewayID); } else { Assert.Equal(deviceGatewayID, loRaDevice.GatewayID); } // fcnt is restarted Assert.Equal(0, afterJoinFcntDown); Assert.Equal(0, afterJoinFcntUp); Assert.Equal(0, loRaDevice.FCntUp); Assert.Equal(0, loRaDevice.FCntDown); Assert.False(loRaDevice.HasFrameCountChanges); simulatedDevice.LoRaDevice.AppSKey = afterJoinAppSKey; simulatedDevice.LoRaDevice.NwkSKey = afterJoinNwkSKey; simulatedDevice.LoRaDevice.DevAddr = afterJoinDevAddr; // sends unconfirmed message var unconfirmedMessagePayload = simulatedDevice.CreateUnconfirmedDataUpMessage("100", fcnt: startingPayloadFcnt); var unconfirmedMessageResult = await messageProcessor.ProcessMessageAsync(unconfirmedMessagePayload.SerializeUplink(simulatedDevice.AppSKey, simulatedDevice.NwkSKey).Rxpk[0]); Assert.Null(unconfirmedMessageResult); // fcnt up was updated Assert.Equal(startingPayloadFcnt, loRaDevice.FCntUp); Assert.Equal(0, loRaDevice.FCntDown); if (startingPayloadFcnt != 0) { // Frame change flag will be set, only saving every 10 messages Assert.True(loRaDevice.HasFrameCountChanges); } Assert.Single(sentTelemetry); // sends confirmed message var confirmedMessagePayload = simulatedDevice.CreateConfirmedDataUpMessage("200", fcnt: startingPayloadFcnt + 1); var confirmedMessageRxpk = confirmedMessagePayload.SerializeUplink(simulatedDevice.AppSKey, simulatedDevice.NwkSKey).Rxpk[0]; var confirmedMessage = await messageProcessor.ProcessMessageAsync(confirmedMessageRxpk); Assert.NotNull(confirmedMessage); Assert.NotNull(confirmedMessage.Txpk); Assert.Equal(2, sentTelemetry.Count); // validates txpk according to eu region Assert.Equal(RegionFactory.CreateEU868Region().GetDownstreamChannelFrequency(confirmedMessageRxpk), confirmedMessage.Txpk.Freq); Assert.Equal("4/5", confirmedMessage.Txpk.Codr); Assert.False(confirmedMessage.Txpk.Imme); Assert.True(confirmedMessage.Txpk.Ipol); Assert.Equal("LORA", confirmedMessage.Txpk.Modu); // fcnt up was updated Assert.Equal(startingPayloadFcnt + 1, loRaDevice.FCntUp); Assert.Equal(1, loRaDevice.FCntDown); // Frame change flag will be set, only saving every 10 messages Assert.True(loRaDevice.HasFrameCountChanges); // C2D message will be checked twice loRaDeviceClient.Verify(x => x.ReceiveAsync(It.IsNotNull <TimeSpan>()), Times.Exactly(2)); // has telemetry with both fcnt Assert.Single(sentTelemetry, (t) => t.Fcnt == startingPayloadFcnt); Assert.Single(sentTelemetry, (t) => t.Fcnt == (startingPayloadFcnt + 1)); loRaDeviceClient.VerifyAll(); loRaDeviceApi.VerifyAll(); }