/// <summary> /// constructor for message received (upstream) /// </summary> /// <param name="input"></param> public MacCommandHolder(byte[] input) { int pointer = 0; macCommand = new List <GenericMACCommand>(); while (pointer < input.Length) { CidEnum cid = (CidEnum)input[pointer]; switch (cid) { case CidEnum.LinkCheckCmd: Logger.Log("mac command detected : LinkCheckCmd", Logger.LoggingLevel.Info); LinkCheckCmd linkCheck = new LinkCheckCmd(); pointer += linkCheck.Length; macCommand.Add(linkCheck); break; case CidEnum.LinkADRCmd: Logger.Log("mac command detected : LinkADRCmd", Logger.LoggingLevel.Info); break; case CidEnum.DutyCycleCmd: Logger.Log("mac command detected : DutyCycleCmd", Logger.LoggingLevel.Info); DutyCycleCmd dutyCycle = new DutyCycleCmd(); pointer += dutyCycle.Length; macCommand.Add(dutyCycle); break; case CidEnum.RXParamCmd: Logger.Log("mac command detected : RXParamCmd", Logger.LoggingLevel.Info); break; case CidEnum.DevStatusCmd: Logger.Log("mac command detected : DevStatusCmd", Logger.LoggingLevel.Info); DevStatusCmd devStatus = new DevStatusCmd(input[pointer + 1], input[pointer + 2]); pointer += devStatus.Length; macCommand.Add(devStatus); break; case CidEnum.NewChannelCmd: Logger.Log("mac command detected : NewChannelCmd", Logger.LoggingLevel.Info); NewChannelAns newChannel = new NewChannelAns(Convert.ToBoolean(input[pointer + 1] & 1), Convert.ToBoolean(input[pointer + 1] & 2)); pointer += newChannel.Length; macCommand.Add(newChannel); break; case CidEnum.RXTimingCmd: Logger.Log("mac command detected : RXTimingCmd", Logger.LoggingLevel.Info); RXTimingSetupCmd rXTimingSetup = new RXTimingSetupCmd(); pointer += rXTimingSetup.Length; macCommand.Add(rXTimingSetup); break; } } }
public MacCommandHolder(byte input) { macCommand = new List <GenericMACCommand>(); CidEnum cid = (CidEnum)input; switch (cid) { case CidEnum.LinkCheckCmd: LinkCheckCmd linkCheck = new LinkCheckCmd(); macCommand.Add(linkCheck); break; case CidEnum.LinkADRCmd: Logger.Log("mac command detected : LinkADRCmd", Logger.LoggingLevel.Info); break; case CidEnum.DutyCycleCmd: DutyCycleCmd dutyCycle = new DutyCycleCmd(); macCommand.Add(dutyCycle); break; case CidEnum.RXParamCmd: Logger.Log("mac command detected : RXParamCmd", Logger.LoggingLevel.Info); break; case CidEnum.DevStatusCmd: Logger.Log("mac command detected : DevStatusCmd", Logger.LoggingLevel.Info); DevStatusCmd devStatus = new DevStatusCmd(); macCommand.Add(devStatus); break; case CidEnum.NewChannelCmd: //NewChannelReq newChannel = new NewChannelReq(); //macCommand.Add(newChannel); break; case CidEnum.RXTimingCmd: RXTimingSetupCmd rXTimingSetup = new RXTimingSetupCmd(); macCommand.Add(rXTimingSetup); break; } }
/// <summary> /// Create a List of Mac commands from server based on a sequence of bytes. /// </summary> public static List <MacCommand> CreateServerMacCommandFromBytes(string deviceId, ReadOnlyMemory <byte> input) { int pointer = 0; var macCommands = new List <MacCommand>(3); while (pointer < input.Length) { try { CidEnum cid = (CidEnum)input.Span[pointer]; switch (cid) { case CidEnum.LinkCheckCmd: var linkCheck = new LinkCheckAnswer(input.Span.Slice(pointer)); pointer += linkCheck.Length; macCommands.Add(linkCheck); break; case CidEnum.DevStatusCmd: var devStatusRequest = new DevStatusRequest(); pointer += devStatusRequest.Length; macCommands.Add(devStatusRequest); break; default: Logger.Log(deviceId, $"a Mac command transmitted from the server, value ${input.Span[pointer]} was not from a supported type. Aborting Mac Command processing", LogLevel.Error); return(null); } MacCommand addedMacCommand = macCommands[macCommands.Count - 1]; Logger.Log(deviceId, $"{addedMacCommand.Cid} mac command detected in upstream payload: {addedMacCommand.ToString()}", LogLevel.Debug); } catch (MacCommandException ex) { Logger.Log(deviceId, ex.ToString(), LogLevel.Error); } } return(macCommands); }
internal static DownlinkMessageBuilderResponse CreateDownlinkMessage( NetworkServerConfiguration configuration, LoRaDevice loRaDevice, Region loRaRegion, IReceivedLoRaCloudToDeviceMessage cloudToDeviceMessage, uint fcntDown) { var fcntDownToSend = ValidateAndConvert16bitFCnt(fcntDown); // default fport byte fctrl = 0; CidEnum macCommandType = CidEnum.Zero; byte[] rndToken = new byte[2]; lock (RndLock) { RndDownlinkMessageBuilder.NextBytes(rndToken); } bool isMessageTooLong = false; // Class C always uses RX2 string datr; double freq; var tmst = 0; // immediate mode // Class C always use RX2 (freq, datr) = loRaRegion.GetDownstreamRX2DRAndFreq(loRaDevice.DevEUI, configuration.Rx2DataRate, configuration.Rx2DataFrequency, loRaDevice.ReportedRX2DataRate); // get max. payload size based on data rate from LoRaRegion var maxPayloadSize = loRaRegion.GetMaxPayloadSize(datr); // Deduct 8 bytes from max payload size. maxPayloadSize -= Constants.LORA_PROTOCOL_OVERHEAD_SIZE; var availablePayloadSize = maxPayloadSize; var macCommands = PrepareMacCommandAnswer(loRaDevice.DevEUI, null, cloudToDeviceMessage.MacCommands, null, null); // Calculate total C2D payload size var totalC2dSize = cloudToDeviceMessage.GetPayload()?.Length ?? 0; totalC2dSize += macCommands?.Sum(x => x.Length) ?? 0; // Total C2D payload will NOT fit if (availablePayloadSize < totalC2dSize) { isMessageTooLong = true; return(new DownlinkMessageBuilderResponse(null, isMessageTooLong)); } if (macCommands?.Count > 0) { macCommandType = macCommands.First().Cid; } if (cloudToDeviceMessage.Confirmed) { loRaDevice.LastConfirmedC2DMessageID = cloudToDeviceMessage.MessageId ?? Constants.C2D_MSG_ID_PLACEHOLDER; } var frmPayload = cloudToDeviceMessage.GetPayload(); if (Logger.LoggerLevel >= LogLevel.Information) { Logger.Log(loRaDevice.DevEUI, $"cloud to device message: {ConversionHelper.ByteArrayToString(frmPayload)}, id: {cloudToDeviceMessage.MessageId ?? "undefined"}, fport: {cloudToDeviceMessage.Fport}, confirmed: {cloudToDeviceMessage.Confirmed}, cidType: {macCommandType}", LogLevel.Information); Logger.Log(loRaDevice.DevEUI, $"sending a downstream message with ID {ConversionHelper.ByteArrayToString(rndToken)}", LogLevel.Information); } Array.Reverse(frmPayload); var payloadDevAddr = ConversionHelper.StringToByteArray(loRaDevice.DevAddr); var reversedDevAddr = new byte[payloadDevAddr.Length]; for (int i = reversedDevAddr.Length - 1; i >= 0; --i) { reversedDevAddr[i] = payloadDevAddr[payloadDevAddr.Length - (1 + i)]; } var msgType = cloudToDeviceMessage.Confirmed ? LoRaMessageType.ConfirmedDataDown : LoRaMessageType.UnconfirmedDataDown; var ackLoRaMessage = new LoRaPayloadData( msgType, reversedDevAddr, new byte[] { fctrl }, BitConverter.GetBytes(fcntDownToSend), macCommands, new byte[] { cloudToDeviceMessage.Fport }, frmPayload, 1, loRaDevice.Supports32BitFCnt ? fcntDown : (uint?)null); return(new DownlinkMessageBuilderResponse( ackLoRaMessage.Serialize(loRaDevice.AppSKey, loRaDevice.NwkSKey, datr, freq, tmst, loRaDevice.DevEUI), isMessageTooLong)); }
/// <summary> /// Create a List of Mac commands from client based on a sequence of bytes. /// </summary> public static List <MacCommand> CreateMacCommandFromBytes(string deviceId, ReadOnlyMemory <byte> input) { int pointer = 0; var macCommands = new List <MacCommand>(3); while (pointer < input.Length) { try { CidEnum cid = (CidEnum)input.Span[pointer]; switch (cid) { case CidEnum.LinkCheckCmd: var linkCheck = new LinkCheckRequest(); pointer += linkCheck.Length; macCommands.Add(linkCheck); break; case CidEnum.LinkADRCmd: var linkAdrAnswer = new LinkADRAnswer(input.Span.Slice(pointer)); pointer += linkAdrAnswer.Length; macCommands.Add(linkAdrAnswer); break; case CidEnum.DutyCycleCmd: var dutyCycle = new DutyCycleAnswer(); pointer += dutyCycle.Length; macCommands.Add(dutyCycle); break; case CidEnum.RXParamCmd: var rxParamSetup = new RXParamSetupAnswer(input.Span.Slice(pointer)); pointer += rxParamSetup.Length; macCommands.Add(rxParamSetup); break; case CidEnum.DevStatusCmd: // Added this case to enable unit testing if (input.Length == 1) { var devStatusRequest = new DevStatusRequest(); pointer += devStatusRequest.Length; macCommands.Add(devStatusRequest); } else { DevStatusAnswer devStatus = new DevStatusAnswer(input.Span.Slice(pointer)); pointer += devStatus.Length; macCommands.Add(devStatus); } break; case CidEnum.NewChannelCmd: NewChannelAnswer newChannel = new NewChannelAnswer(input.Span.Slice(pointer)); pointer += newChannel.Length; macCommands.Add(newChannel); break; case CidEnum.RXTimingCmd: RXTimingSetupAnswer rxTimingSetup = new RXTimingSetupAnswer(); pointer += rxTimingSetup.Length; macCommands.Add(rxTimingSetup); break; default: Logger.Log(deviceId, $"a transmitted Mac Command value ${input.Span[pointer]} was not from a supported type. Aborting Mac Command processing", LogLevel.Error); return(null); } MacCommand addedMacCommand = macCommands[macCommands.Count - 1]; Logger.Log(deviceId, $"{addedMacCommand.Cid} mac command detected in upstream payload: {addedMacCommand.ToString()}", LogLevel.Debug); } catch (MacCommandException ex) { Logger.Log(deviceId, ex.ToString(), LogLevel.Error); } } return(macCommands); }
/// <summary> /// Initializes a new instance of the <see cref="MacCommandHolder"/> class. /// constructor for message received (upstream) /// </summary> public MacCommandHolder(byte[] input) { int pointer = 0; this.MacCommand = new List <GenericMACCommand>(); while (pointer < input.Length) { CidEnum cid = (CidEnum)input[pointer]; switch (cid) { case CidEnum.LinkCheckCmd: Logger.Log("mac command detected : LinkCheckCmd", LogLevel.Information); LinkCheckCmd linkCheck = new LinkCheckCmd(); pointer += linkCheck.Length; this.MacCommand.Add(linkCheck); break; case CidEnum.LinkADRCmd: LinkADRCmd linkADRCmd = new LinkADRCmd(); pointer += linkADRCmd.Length; this.MacCommand.Add(linkADRCmd); Logger.Log("mac command detected : LinkADRCmd", LogLevel.Information); break; case CidEnum.DutyCycleCmd: Logger.Log("mac command detected : DutyCycleCmd", LogLevel.Information); DutyCycleCmd dutyCycle = new DutyCycleCmd(); pointer += dutyCycle.Length; this.MacCommand.Add(dutyCycle); break; case CidEnum.RXParamCmd: Logger.Log("mac command detected : RXParamCmd", LogLevel.Information); RXParamSetupCmd rxParamSetupCmd = new RXParamSetupCmd(); pointer += rxParamSetupCmd.Length; this.MacCommand.Add(rxParamSetupCmd); break; case CidEnum.DevStatusCmd: Logger.Log("mac command detected : DevStatusCmd", LogLevel.Information); DevStatusCmd devStatus = new DevStatusCmd(input[pointer + 1], input[pointer + 2]); pointer += devStatus.Length; this.MacCommand.Add(devStatus); break; case CidEnum.NewChannelCmd: Logger.Log("mac command detected : NewChannelCmd", LogLevel.Information); NewChannelAns newChannel = new NewChannelAns(Convert.ToBoolean(input[pointer + 1] & 1), Convert.ToBoolean(input[pointer + 1] & 2)); pointer += newChannel.Length; this.MacCommand.Add(newChannel); break; case CidEnum.RXTimingCmd: Logger.Log("mac command detected : RXTimingCmd", LogLevel.Information); RXTimingSetupCmd rXTimingSetup = new RXTimingSetupCmd(); pointer += rXTimingSetup.Length; this.MacCommand.Add(rXTimingSetup); break; default: Logger.Log($"value {input[pointer]} as MAC command type is not recognized, aborting Mac command parsing", LogLevel.Error); return; } } }