Exemple #1
0
        private bool CheckRegisterBounds(ModbusFunctionCode functionCode, ushort address, ushort maxStartingAddress, ushort quantityOfRegisters, ushort maxQuantityOfRegisters)
        {
            if (this.ModbusServer.RequestValidator != null)
            {
                var result = this.ModbusServer.RequestValidator(functionCode, address, quantityOfRegisters);

                if (result > ModbusExceptionCode.OK)
                {
                    this.WriteExceptionResponse(functionCode, result);
                    return(false);
                }
            }

            if (address < 0 || address + quantityOfRegisters > maxStartingAddress)
            {
                this.WriteExceptionResponse(functionCode, ModbusExceptionCode.IllegalDataAddress);
                return(false);
            }

            if (quantityOfRegisters <= 0 || quantityOfRegisters > maxQuantityOfRegisters)
            {
                this.WriteExceptionResponse(functionCode, ModbusExceptionCode.IllegalDataValue);
                return(false);
            }

            return(true);
        }
Exemple #2
0
        public string GetParament()
        {
            ParaPack para = new ParaPack();

            if (cbo_functioncode.SelectedItem != null)
            {
                ModbusFunctionCode funCode = (ModbusFunctionCode)cbo_functioncode.SelectedItem;
                para.AddItem("内存区", funCode.Code);
            }
            para.AddItem("偏置", this.ndOffset.Text.Trim());
            if (cbo_StoreType.SelectedItem != null)
            {
                ModbusDataType datatype = (ModbusDataType)cbo_StoreType.SelectedItem;
                para.AddItem("数据类型", datatype.DataType.ToString());
            }
            if (cbo_datatype.SelectedItem != null)
            {
                string datatype = (string)cbo_datatype.SelectedItem;
                para.AddItem("存储位置", datatype.ToString());
            }
            para.AddItem("字节长度", this.ndCharSize.Text.Trim() == ""?"0": this.ndCharSize.Text.Trim());
            para.AddItem("按位存取", this.cbPosition.Checked?"1":"0");
            para.AddItem("数据位", this.ndPosition.Text.Trim() == ""?"0": this.ndPosition.Text.Trim());

            return(para.ToString());
        }
 public WriteMultipleFunction(ModbusFunctionCode functionCode, ushort startAddress, int[] commandValues, CommandOriginType commandOrigin)
     : base(functionCode)
 {
     StartAddress  = startAddress;
     CommandValues = commandValues;
     CommandOrigin = commandOrigin;
 }
 public WriteSingleFunction(ModbusFunctionCode functionCode, ushort outputAddress, int commandValue, CommandOriginType commandOrigin)
     : base(functionCode)
 {
     OutputAddress = outputAddress;
     CommandValue  = commandValue;
     CommandOrigin = commandOrigin;
 }
Exemple #5
0
        /// <summary>
        /// Sends a modbus telegram over the interface and waits for a response
        /// </summary>
        /// <param name="deviceAddress">Modbus device address.</param>
        /// <param name="fc">Function code.</param>
        /// <param name="timeout">Timeout in Milli seconds.</param>
        /// <param name="telegramLength">Total length of the telegram in bytes</param>
        /// <param name="desiredDataLength">Length of the desired telegram data (without fc, cs, ...) of the response in bytes. -1 for unknown.</param>
        /// <param name="telegramContext">Interface specific context of the telegram</param>
        /// <param name="dataPos">Index of the response data in the buffer.</param>
        /// <returns></returns>
        protected virtual short SendReceive(byte deviceAddress, ModbusFunctionCode fc, int timeout, short telegramLength, short desiredDataLength,
                                            object telegramContext, ref short dataPos)
        {
            lock (_SyncObject)
            {
                try
                {
                    _Interface.SendTelegram(_Buffer, telegramLength);
                    _Interface.PrepareRead();

                    if (deviceAddress == ModbusConst.BroadcastAddress)
                    {
                        return(0);
                    }

                    byte  responseDeviceAddress;
                    byte  responseFc = 0;
                    short dataLength = 0;

                    while (timeout > 0)
                    {
                        var ts = DateTime.Now.Ticks;

                        if (!_Interface.ReceiveTelegram(_Buffer, desiredDataLength, timeout, out telegramLength))
                        {
                            throw new ModbusException(ModbusErrorCode.Timeout);
                        }

                        timeout -= (int)((DateTime.Now.Ticks - ts) / 10000);

                        // if this is not the response we are waiting for wait again until time runs out
                        if (_Interface.ParseTelegram(_Buffer, telegramLength, true, ref telegramContext,
                                                     out responseDeviceAddress,
                                                     out responseFc,
                                                     out dataPos, out dataLength) &&
                            responseDeviceAddress == deviceAddress &&
                            (responseFc & 0x7f) == (byte)fc)
                        {
                            break;
                        }
                        if (timeout <= 0)
                        {
                            throw new ModbusException(ModbusErrorCode.Timeout);
                        }
                    }

                    if ((responseFc & 0x80) != 0)
                    {
                        // error response
                        throw new ModbusException((ModbusErrorCode)_Buffer[dataPos]);
                    }
                    return(dataLength);
                }
                finally
                {
                    _Interface.PrepareWrite();
                }
            }
        }
        /// <summary>
        /// Handles a received message.
        /// </summary>
        /// <param name="intf">Interface by which te message was received.</param>
        /// <param name="deviceAddress">Address of the target device</param>
        /// <param name="isBroadcast">true if the message is a broadcast. For broadcast messages no reponse is sent.</param>
        /// <param name="telegramLength">Length of the message in bytes.</param>
        /// <param name="telegramContext">Interface specific message context.</param>
        /// <param name="fc">Function code.</param>
        /// <param name="dataPos">Index of function code specific data.</param>
        /// <param name="dataLength">Length of the function code specific data in bytes.</param>
        protected override void OnHandleTelegram(IModbusInterface intf, byte deviceAddress, bool isBroadcast, short telegramLength,
                                                 object telegramContext, ModbusFunctionCode fc, short dataPos, short dataLength)
        {
            var master = (GatewayMaster)_masterMap[deviceAddress];

            if (master != null)
            {
                short  masterTelegramLength;
                short  masterDataPos;
                object masterTelegramContext = null;

                master.MasterInterface.CreateTelegram(master.TargetAddress, (byte)fc, dataLength, master.Buffer, out masterTelegramLength, out masterDataPos, false, ref masterTelegramContext);
                Array.Copy(Buffer, dataPos, master.Buffer, masterDataPos, dataLength);

                try
                {
                    var masterDataLength = SendReceive(master.MasterInterface, master.Buffer, master.TargetAddress, fc, master.Timeout,
                                                       masterTelegramLength, -1, masterTelegramContext, ref masterDataPos);

                    intf.CreateTelegram(deviceAddress, (byte)fc, masterDataLength, Buffer, out telegramLength, out dataPos,
                                        true, ref telegramContext);
                    Array.Copy(master.Buffer, masterDataPos, Buffer, dataPos, masterDataLength);
                    intf.SendTelegram(Buffer, telegramLength);
                }
                catch (ModbusException ex)
                {
                    try
                    {
                        if (ex.ErrorCode == ModbusErrorCode.Timeout)
                        {
                            SendErrorResult(intf, false, deviceAddress, telegramContext, fc, ModbusErrorCode.GatewayTargetDeviceFailedToRespond);
                        }
                        else if (((ushort)ex.ErrorCode & 0xff00) != 0)
                        {
                            SendErrorResult(intf, false, deviceAddress, telegramContext, fc, ModbusErrorCode.GatewayTargetDeviceFailedToRespond);
                        }
                        else
                        {
                            SendErrorResult(intf, false, deviceAddress, telegramContext, fc, ex.ErrorCode);
                        }
                    }
                    // ReSharper disable once EmptyGeneralCatchClause
                    catch { }
                }
                catch
                {
                    try
                    {
                        SendErrorResult(intf, false, deviceAddress, telegramContext, fc, ModbusErrorCode.GatewayPathUnavailable);
                    }
                    // ReSharper disable once EmptyGeneralCatchClause
                    catch { }
                }
            }
            else
            {
                base.OnHandleTelegram(intf, deviceAddress, isBroadcast, telegramLength, telegramContext, fc, dataPos, dataLength);
            }
        }
Exemple #7
0
      /// <summary>
      /// Sends a modbus telegram over the interface and waits for a response
      /// </summary>
      /// <param name="deviceAddress">Modbus device address.</param>
      /// <param name="fc">Function code.</param>
      /// <param name="timeout">Timeout in Milli seconds.</param>
      /// <param name="telegramLength">Total length of the telegram in bytes</param>
      /// <param name="desiredDataLength">Length of the desired telegram data (without fc, cs, ...) of the response in bytes. -1 for unknown.</param>
      /// <param name="telegramContext">Interface specific context of the telegram</param>
      /// <param name="dataPos">Index of the response data in the buffer.</param>
      /// <returns></returns>
      protected virtual short SendReceive(byte deviceAddress, ModbusFunctionCode fc, int timeout, short telegramLength, short desiredDataLength,
                                          object telegramContext, ref short dataPos)
      {
         lock (_SyncObject)
         {
            try
            {
               _Interface.SendTelegram(_Buffer, telegramLength);
               _Interface.PrepareRead();

               if (deviceAddress == ModbusConst.BroadcastAddress)
               {
                  return 0;
               }

               byte responseDeviceAddress;
               byte responseFc = 0;
               short dataLength = 0;

               while (timeout > 0)
               {
                  var ts = DateTime.Now.Ticks;
                  if (!_Interface.ReceiveTelegram(_Buffer, desiredDataLength, timeout, out telegramLength))
                  {
                     throw new ModbusException(ModbusErrorCode.Timeout);
                  }
                  timeout -= (int) ((DateTime.Now.Ticks - ts)/10000);

                  // if this is not the response we are waiting for wait again until time runs out
                  if (_Interface.ParseTelegram(_Buffer, telegramLength, true, ref telegramContext,
                     out responseDeviceAddress,
                     out responseFc,
                     out dataPos, out dataLength)
                      && responseDeviceAddress == deviceAddress
                      && (responseFc & 0x7f) == (byte) fc)
                  {
                     break;
                  }
                  if (timeout <= 0)
                  {
                     throw new ModbusException(ModbusErrorCode.Timeout);
                  }
               }
               if ((responseFc & 0x80) != 0)
               {
                  // error response
                  throw new ModbusException((ModbusErrorCode) _Buffer[dataPos]);
               }
               return dataLength;
            }
            finally
            {
               _Interface.PrepareWrite();
            }
         }
      }
        private T Write <T>(ModbusDevice device, ModbusFunctionCode functionCode, int zeroBasedOffset, Action <IMessageBufferWriter> writeAction, Func <IMessageBufferReader, T> readValueFunc)
        {
            var requestContext = new ModbusTransportContext()
            {
                TransactionIdentifier = GetTransactionIdentifier()
            };

            ModbusTransport.SendMessage(requestContext,
                                        (writer) =>
            {
                writer.Push(device.Address);
                writer.Push((byte)functionCode);
                writer.Push((byte)((zeroBasedOffset >> 8) & 0xFF));
                writer.Push((byte)((zeroBasedOffset >> 0) & 0xFF));
                writeAction(writer);
            });

            T returnedValue = default;

            var responseContext = ModbusTransport.ReceiveMessage(
                (reader) =>
            {
                if (reader.PushByteFromStream() != device.Address)
                {
                    throw new InvalidOperationException();
                }

                byte receivedFunctionCode = reader.PushByteFromStream();

                if (receivedFunctionCode == ((byte)functionCode | 0x80))
                {
                    var exceptionCode = (ModbusExceptionCode)reader.PushByteFromStream();
                    throw new ModbusException(exceptionCode);
                }

                if (receivedFunctionCode != (byte)functionCode)
                {
                    throw new InvalidOperationException();
                }

                if ((ushort)((reader.PushByteFromStream() << 8) + (reader.PushByteFromStream() << 0)) != zeroBasedOffset)
                {
                    throw new InvalidOperationException();
                }

                returnedValue = readValueFunc(reader);
            });

            if (requestContext.TransactionIdentifier != responseContext.TransactionIdentifier)
            {
                throw new InvalidOperationException();
            }

            return(returnedValue !);
        }
        private async Task <T> WriteAsync <T>(ModbusDevice device, ModbusFunctionCode functionCode, int zeroBasedOffset, Action <IMessageBufferWriter> writeAction, Func <IMessageBufferReader, Task <T> > readValueFunc, CancellationToken cancellationToken)
        {
            var requestContext = new ModbusTransportContext()
            {
                TransactionIdentifier = GetTransactionIdentifier()
            };

            await ModbusTransport.SendMessageAsync(requestContext,
                                                   (writer) =>
            {
                writer.Push(device.Address);
                writer.Push((byte)functionCode);
                writer.Push((byte)((zeroBasedOffset >> 8) & 0xFF));
                writer.Push((byte)((zeroBasedOffset >> 0) & 0xFF));
                writeAction(writer);
            }, cancellationToken);

            T returnedValue = default;

            var responseContext = await ModbusTransport.ReceiveMessageAsync(
                async (reader) =>
            {
                if (await reader.PushByteFromStreamAsync(cancellationToken) != device.Address)
                {
                    throw new InvalidOperationException();
                }

                byte receivedFunctionCode = await reader.PushByteFromStreamAsync(cancellationToken);

                if (receivedFunctionCode == ((byte)functionCode | 0x80))
                {
                    var exceptionCode = (ModbusExceptionCode)await reader.PushByteFromStreamAsync(cancellationToken);
                    throw new ModbusException(exceptionCode);
                }

                if (receivedFunctionCode != (byte)functionCode)
                {
                    throw new InvalidOperationException();
                }

                if (await reader.PushByteFromStreamAsync(cancellationToken) != zeroBasedOffset)
                {
                    throw new InvalidOperationException();
                }

                returnedValue = await readValueFunc(reader);
            }, cancellationToken);

            if (requestContext.TransactionIdentifier != responseContext.TransactionIdentifier)
            {
                throw new InvalidOperationException();
            }

            return(returnedValue !);
        }
        private async Task ReadAsync(ModbusDevice device, ModbusFunctionCode functionCode, int zeroBasedOffset, int count, Action <MessageBufferSpan> actionWithReturnedBuffer, CancellationToken cancellationToken)
        {
            var requestContext = new ModbusTransportContext()
            {
                TransactionIdentifier = GetTransactionIdentifier()
            };

            await ModbusTransport.SendMessageAsync(requestContext,
                                                   (writer) =>
            {
                writer.Push(device.Address);
                writer.Push((byte)functionCode);
                writer.Push((byte)((zeroBasedOffset >> 8) & 0xFF));
                writer.Push((byte)((zeroBasedOffset >> 0) & 0xFF));
                writer.Push((byte)((count >> 8) & 0xFF));
                writer.Push((byte)((count >> 0) & 0xFF));
            }, cancellationToken);

            MessageBufferSpan?receivedBuffer = null;

            var responseContext = await ModbusTransport.ReceiveMessageAsync(
                async (reader) =>
            {
                if (await reader.PushByteFromStreamAsync(cancellationToken) != device.Address)
                {
                    throw new InvalidOperationException();
                }

                byte receivedFunctionCode = await reader.PushByteFromStreamAsync(cancellationToken);

                if (receivedFunctionCode == ((byte)functionCode | 0x80))
                {
                    var exceptionCode = (ModbusExceptionCode)await reader.PushByteFromStreamAsync(cancellationToken);
                    throw new ModbusException(exceptionCode);
                }

                if (receivedFunctionCode != (byte)functionCode)
                {
                    throw new InvalidOperationException();
                }

                var byteCount = await reader.PushByteFromStreamAsync(cancellationToken);

                await reader.PushFromStreamAsync(byteCount, cancellationToken);

                receivedBuffer = new MessageBufferSpan(reader.Buffer, (ushort)(reader.Buffer.Length - byteCount), byteCount);
            }, cancellationToken);

            if (responseContext.TransactionIdentifier != requestContext.TransactionIdentifier)
            {
                throw new InvalidOperationException();
            }

            actionWithReturnedBuffer(receivedBuffer !);
        }
Exemple #11
0
        internal void ProcessError(ModbusFunctionCode functionCode, ModbusExceptionCode exceptionCode)
        {
            switch (exceptionCode)
            {
            case ModbusExceptionCode.IllegalFunction:
                throw new ModbusException(exceptionCode, ErrorMessage.ModbusClient_0x01_IllegalFunction);

            case ModbusExceptionCode.IllegalDataAddress:
                throw new ModbusException(exceptionCode, ErrorMessage.ModbusClient_0x02_IllegalDataAddress);

            case ModbusExceptionCode.IllegalDataValue:

                switch (functionCode)
                {
                case ModbusFunctionCode.WriteMultipleRegisters:
                    throw new ModbusException(exceptionCode, ErrorMessage.ModbusClient_0x03_IllegalDataValue_0x7B);

                case ModbusFunctionCode.ReadHoldingRegisters:
                case ModbusFunctionCode.ReadInputRegisters:
                    throw new ModbusException(exceptionCode, ErrorMessage.ModbusClient_0x03_IllegalDataValue_0x7D);

                case ModbusFunctionCode.ReadCoils:
                case ModbusFunctionCode.ReadDiscreteInputs:
                    throw new ModbusException(exceptionCode, ErrorMessage.ModbusClient_0x03_IllegalDataValue_0x7D0);

                default:
                    throw new ModbusException(exceptionCode, ErrorMessage.ModbusClient_0x03_IllegalDataValue);
                }

            case ModbusExceptionCode.ServerDeviceFailure:
                throw new ModbusException(exceptionCode, ErrorMessage.ModbusClient_0x04_ServerDeviceFailure);

            case ModbusExceptionCode.Acknowledge:
                throw new ModbusException(exceptionCode, ErrorMessage.ModbusClient_0x05_Acknowledge);

            case ModbusExceptionCode.ServerDeviceBusy:
                throw new ModbusException(exceptionCode, ErrorMessage.ModbusClient_0x06_ServerDeviceBusy);

            case ModbusExceptionCode.MemoryParityError:
                throw new ModbusException(exceptionCode, ErrorMessage.ModbusClient_0x08_MemoryParityError);

            case ModbusExceptionCode.GatewayPathUnavailable:
                throw new ModbusException(exceptionCode, ErrorMessage.ModbusClient_0x0A_GatewayPathUnavailable);

            case ModbusExceptionCode.GatewayTargetDeviceFailedToRespond:
                throw new ModbusException(exceptionCode, ErrorMessage.ModbusClient_0x0B_GatewayTargetDeviceFailedToRespond);

            default:
                throw new ArgumentOutOfRangeException(ErrorMessage.ModbusClient_InvalidExceptionCode);
            }
        }
Exemple #12
0
        //不同内存区域显示不同的类型
        private void Cbo_functioncode_SelectedIndexChanged(object sender, EventArgs e)
        {
            groupStored.Visible   = false;
            groupposition.Visible = false;

            if (cbo_functioncode.SelectedItem != null)
            {
                ModbusFunctionCode func = (ModbusFunctionCode)cbo_functioncode.SelectedItem;
                switch (func.Code)
                {
                case "01":
                {
                    groupStored.Visible = false;


                    rb_rw.Checked = true;
                }
                break;

                case "02":
                {
                    groupStored.Visible = false;


                    rb_r.Checked = true;
                }
                break;

                case "03":
                {
                    groupStored.Visible = true;


                    rb_rw.Checked = true;
                }
                break;

                case "04":
                {
                    groupStored.Visible = true;


                    rb_r.Checked = true;
                }
                break;
                }
            }
        }
        private async Task ExecuteReadCommand(IReadModbusFunction readCommand)
        {
            string verboseMessage = $"{baseLogString} entering ExecuteReadCommand method, FunctionCode: {readCommand.FunctionCode}, StartAddress: {readCommand.StartAddress}, Quantity: {readCommand.Quantity}.";

            Logger.LogVerbose(verboseMessage);

            ModbusFunctionCode functionCode = readCommand.FunctionCode;
            ushort             startAddress = readCommand.StartAddress;
            ushort             quantity     = readCommand.Quantity;

            if (quantity <= 0)
            {
                string message = $"{baseLogString} ExecuteReadCommand => Reading Quantity: {quantity} does not make sense.";
                Logger.LogError(message);
                throw new ArgumentException(message);
            }

            if (startAddress + quantity >= ushort.MaxValue || startAddress + quantity == ushort.MinValue || startAddress == ushort.MinValue)
            {
                string message = $"{baseLogString} ExecuteReadCommand => Address is out of bound. Start address: {startAddress}, Quantity: {quantity}";
                Logger.LogError(message);
                throw new ArgumentException(message);
            }

            if (functionCode == ModbusFunctionCode.READ_COILS || functionCode == ModbusFunctionCode.READ_DISCRETE_INPUTS)
            {
                verboseMessage = $"{baseLogString} ExecuteReadCommand => ExecuteDiscreteReadCommand about to be called.";
                Logger.LogVerbose(verboseMessage);

                //LOGIC
                await ExecuteDiscreteReadCommand(functionCode, startAddress, quantity);
            }
            else if (functionCode == ModbusFunctionCode.READ_HOLDING_REGISTERS || functionCode == ModbusFunctionCode.READ_INPUT_REGISTERS)
            {
                verboseMessage = $"{baseLogString} ExecuteReadCommand => ExecuteAnalogReadCommand about to be called.";
                Logger.LogVerbose(verboseMessage);

                //LOGIC
                await ExecuteAnalogReadCommand(functionCode, startAddress, quantity);
            }
            else
            {
                string errorMessage = $"{baseLogString} ExecuteWriteSingleCommand => function code hase value: {functionCode}, but one of these was required: {ModbusFunctionCode.READ_COILS}, {ModbusFunctionCode.READ_DISCRETE_INPUTS}, {ModbusFunctionCode.READ_HOLDING_REGISTERS}, {ModbusFunctionCode.READ_INPUT_REGISTERS}.";
                Logger.LogError(errorMessage);
                throw new ArgumentException(errorMessage);
            }
        }
        private bool CheckRegisterBounds(ModbusFunctionCode functionCode, int startingAddress, int maxStartingAddress, int quantityOfRegisters, int maxQuantityOfRegisters)
        {
            if (startingAddress < 0 || startingAddress + quantityOfRegisters > maxStartingAddress)
            {
                this.WriteExceptionResponse(functionCode, ModbusExceptionCode.IllegalDataAddress);

                return(false);
            }

            if (quantityOfRegisters <= 0 || quantityOfRegisters > maxQuantityOfRegisters)
            {
                this.WriteExceptionResponse(functionCode, ModbusExceptionCode.IllegalDataValue);

                return(false);
            }

            return(true);
        }
Exemple #15
0
        public void SetParament(string parastr)
        {
            ParaPack para = new ParaPack(parastr);

            if (para.Count > 0)
            {
                ///内存区
                for (int i = 0; i < cbo_functioncode.Items.Count; i++)
                {
                    ModbusFunctionCode funCode = (ModbusFunctionCode)cbo_functioncode.Items[i];
                    if (funCode.Code == para.GetValue("内存区"))
                    {
                        cbo_functioncode.SelectedIndex = i;
                        break;
                    }
                }
                //数据类型
                for (int i = 0; i < cbo_StoreType.Items.Count; i++)
                {
                    ModbusDataType datatype = (ModbusDataType)cbo_StoreType.Items[i];
                    if (datatype.ToString() == para.GetValue("数据类型"))
                    {
                        cbo_StoreType.SelectedIndex = i;
                        break;
                    }
                }
                //偏置
                this.ndOffset.Value   = para.GetValue("偏置") == "" ? 0: Convert.ToDecimal(para.GetValue("偏置"));
                this.ndCharSize.Value = para.GetValue("字节长度") == "" ? 0 : Convert.ToDecimal(para.GetValue("字节长度"));
                this.ndPosition.Value = para.GetValue("数据位") == "" ? 0 : Convert.ToDecimal(para.GetValue("数据位"));
                //存储位置
                for (int i = 0; i < cbo_datatype.Items.Count; i++)
                {
                    string datatype = (string)cbo_datatype.Items[i];
                    if (datatype.ToString() == para.GetValue("存储位置"))
                    {
                        cbo_datatype.SelectedIndex = i;
                        break;
                    }
                }
                //按位存取
                this.cbPosition.Checked = para.GetValue("按位存取") == "1" ? true : false;
            }
        }
        private void WriteMultipleRegisters(IModbusInterface intf, bool isBroadcast, object telegramContext, ModbusFunctionCode fc, short dataPos, short dataLength)
        {
            if (dataLength < 5)
            {
                SendErrorResult(intf, isBroadcast, _deviceAddress, telegramContext, fc, ModbusErrorCode.IllegalDataValue);
            }
            else
            {
                ushort startAddress  = ModbusUtils.ExtractUShort(_buffer, dataPos);
                ushort registerCount = ModbusUtils.ExtractUShort(_buffer, dataPos + 2);
                //byte byteCount = _Buffer[dataPos + 4];
                ushort[] registers = new ushort[registerCount];
                for (int i = 0; i < registerCount; i++)
                {
                    registers[i] = ModbusUtils.ExtractUShort(_buffer, dataPos + 5 + 2 * i);
                }

                var err = OnWriteMultipleRegisters(isBroadcast, startAddress, registers);
                if (isBroadcast)
                {
                    return;
                }
                if (err != ModbusErrorCode.NoError)
                {
                    SendErrorResult(intf, false, _deviceAddress, telegramContext, fc, err);
                }
                else
                {
                    short telegramLength;
                    intf.CreateTelegram(_deviceAddress, (byte)fc, 4, _buffer, out telegramLength, out dataPos, true, ref telegramContext);
                    ModbusUtils.InsertUShort(_buffer, dataPos, startAddress);
                    ModbusUtils.InsertUShort(_buffer, dataPos + 2, registerCount);
                    intf.SendTelegram(_buffer, telegramLength);
                }
            }
        }
        private void WriteMultipleCoils(IModbusInterface intf, bool isBroadcast, object telegramContext, ModbusFunctionCode fc, short dataPos, short dataLength)
        {
            if (dataLength < 5)
            {
                SendErrorResult(intf, isBroadcast, _deviceAddress, telegramContext, fc, ModbusErrorCode.IllegalDataValue);
            }
            else
            {
                ushort startAddress = ModbusUtils.ExtractUShort(_buffer, dataPos);
                ushort outputCount  = ModbusUtils.ExtractUShort(_buffer, dataPos + 2);
                byte   byteCount    = _buffer[dataPos + 4];
                byte[] values       = new byte[byteCount];
                Array.Copy(_buffer, dataPos + 5, values, 0, values.Length);

                var err = OnWriteMultipleCoils(isBroadcast, startAddress, outputCount, values);
                if (isBroadcast)
                {
                    return;
                }
                if (err != ModbusErrorCode.NoError)
                {
                    SendErrorResult(intf, false, _deviceAddress, telegramContext, fc, err);
                }
                else
                {
                    short telegramLength;
                    intf.CreateTelegram(_deviceAddress, (byte)fc, 4, _buffer, out telegramLength, out dataPos, true, ref telegramContext);
                    ModbusUtils.InsertUShort(_buffer, dataPos, startAddress);
                    ModbusUtils.InsertUShort(_buffer, dataPos + 2, outputCount);
                    intf.SendTelegram(_buffer, telegramLength);
                }
            }
        }
Exemple #18
0
 /// <summary>
 /// Sends a error result message.
 /// </summary>
 /// <param name="intf">Interface to send message to.</param>
 /// <param name="isBroadcast">true if the message is a broadcast. For broadcast messages no reponse is sent.</param>
 /// <param name="deviceAddress">Device address for response</param>
 /// <param name="telegramContext">Interface specific telegram context.</param>
 /// <param name="fc">Function code. The msg is automatically set.</param>
 /// <param name="modbusErrorCode">Modbus error code to send.</param>
 protected virtual void SendErrorResult(IModbusInterface intf, bool isBroadcast, byte deviceAddress, object telegramContext, ModbusFunctionCode fc, ModbusErrorCode modbusErrorCode)
 {
    if (isBroadcast)
    {
       return;
    }
    short telegramLength;
    short dataPos;
    intf.CreateTelegram(deviceAddress, (byte)((byte)fc | 0x80), 1, _buffer, out telegramLength, out dataPos, true, ref telegramContext);
    _buffer[dataPos] = (byte)modbusErrorCode;
    intf.SendTelegram(_buffer, telegramLength);
 }
        private protected override async Task <Memory <byte> > TransceiveFrameAsync(byte unitIdentifier, ModbusFunctionCode functionCode, Action <ExtendedBinaryWriter> extendFrame, CancellationToken cancellationToken = default)
        {
            int    frameLength;
            byte   rawFunctionCode;
            ushort crc;

            // build request
            if (!(0 <= unitIdentifier && unitIdentifier <= 247))
            {
                throw new ModbusException(ErrorMessage.ModbusClient_InvalidUnitIdentifier);
            }

            // special case: broadcast (only for write commands)
            if (unitIdentifier == 0)
            {
                switch (functionCode)
                {
                case ModbusFunctionCode.WriteMultipleRegisters:
                case ModbusFunctionCode.WriteSingleCoil:
                case ModbusFunctionCode.WriteSingleRegister:
                case ModbusFunctionCode.WriteMultipleCoils:
                case ModbusFunctionCode.WriteFileRecord:
                case ModbusFunctionCode.MaskWriteRegister:
                    break;

                default:
                    throw new ModbusException(ErrorMessage.Modbus_InvalidUseOfBroadcast);
                }
            }

            _frameBuffer.Writer.Seek(0, SeekOrigin.Begin);
            _frameBuffer.Writer.Write(unitIdentifier);                                      // 00     Unit Identifier
            extendFrame(_frameBuffer.Writer);
            frameLength = (int)_frameBuffer.Writer.BaseStream.Position;

            // add CRC
            crc = ModbusUtils.CalculateCRC(_frameBuffer.Buffer.AsMemory().Slice(0, frameLength));
            _frameBuffer.Writer.Write(crc);
            frameLength = (int)_frameBuffer.Writer.BaseStream.Position;

            // send request
            await _serialPort.WriteAsync(_frameBuffer.Buffer, 0, frameLength, cancellationToken).ConfigureAwait(false);

            // special case: broadcast (only for write commands)
            if (unitIdentifier == 0)
            {
                return(_frameBuffer.Buffer.AsMemory(0, 0));
            }

            // wait for and process response
            frameLength = 0;
            _frameBuffer.Reader.BaseStream.Seek(0, SeekOrigin.Begin);

            while (true)
            {
                frameLength += await _serialPort.ReadAsync(_frameBuffer.Buffer, frameLength, _frameBuffer.Buffer.Length - frameLength, cancellationToken).ConfigureAwait(false);

                if (ModbusUtils.DetectFrame(unitIdentifier, _frameBuffer.Buffer.AsMemory().Slice(0, frameLength)))
                {
                    break;
                }
                else
                {
                    // reset length because one or more chunks of data were received and written to
                    // the buffer, but no valid Modbus frame could be detected and now the buffer is full
                    if (frameLength == _frameBuffer.Buffer.Length)
                    {
                        frameLength = 0;
                    }
                }
            }

            unitIdentifier  = _frameBuffer.Reader.ReadByte();
            rawFunctionCode = _frameBuffer.Reader.ReadByte();

            if (rawFunctionCode == (byte)ModbusFunctionCode.Error + (byte)functionCode)
            {
                this.ProcessError(functionCode, (ModbusExceptionCode)_frameBuffer.Buffer[2]);
            }

            else if (rawFunctionCode != (byte)functionCode)
            {
                throw new ModbusException(ErrorMessage.ModbusClient_InvalidResponseFunctionCode);
            }

            return(_frameBuffer.Buffer.AsMemory(1, frameLength - 3));
        }
Exemple #20
0
 private protected abstract Span <byte> TransceiveFrame(byte unitIdentifier, ModbusFunctionCode functionCode, Action <ExtendedBinaryWriter> extendFrame);
Exemple #21
0
      private void WriteSingleRegister(IModbusInterface intf, bool isBroadcast, object telegramContext, ModbusFunctionCode fc, short dataPos, short dataLength)
      {
         if (dataLength < 4)
         {
            SendErrorResult(intf, isBroadcast, _deviceAddress, telegramContext, fc, ModbusErrorCode.IllegalDataValue);
         }
         else
         {
            ushort address = ModbusUtils.ExtractUShort(_buffer, dataPos);
            ushort value = ModbusUtils.ExtractUShort(_buffer, dataPos + 2);

            var err = OnWriteSingleRegister(isBroadcast, address, value);
            if (isBroadcast)
            {
               return;
            }
            if (err != ModbusErrorCode.NoError)
            {
               SendErrorResult(intf, false, _deviceAddress, telegramContext, fc, err);
            }
            else
            {
               short telegramLength;
               intf.CreateTelegram(_deviceAddress, (byte)fc, 4, _buffer, out telegramLength, out dataPos, true, ref telegramContext);
               ModbusUtils.InsertUShort(_buffer, dataPos, address);
               ModbusUtils.InsertUShort(_buffer, dataPos + 2, value);
               intf.SendTelegram(_buffer, telegramLength);
            }
         }
      }
 /// <summary>
 /// OnCustomTelegram is called for any function code which is not explicitly handeled by a On"FunctionCode" methos.
 /// </summary>
 /// <param name="intf">Interface which sent the request.</param>
 /// <param name="isBroadcast">true if the request is a broadcast</param>
 /// <param name="buffer">Buffer containing the message.</param>
 /// <param name="telegramLength">Total length of message in bytes.</param>
 /// <param name="telegramContext">Interface specific message context.</param>
 /// <param name="fc">Function code.</param>
 /// <param name="dataPos">Index of the function code specific data.</param>
 /// <param name="dataLength">Length of the function code specific data in bytes.</param>
 /// <returns></returns>
 /// <remarks>
 /// Look at http://www.modbus.org/docs/Modbus_Application_Protocol_V1_1b3.pdf for more details about function codes.
 /// </remarks>
 protected virtual bool OnCustomTelegram(IModbusInterface intf, bool isBroadcast, byte[] buffer, short telegramLength, object telegramContext, ModbusFunctionCode fc, short dataPos, short dataLength)
 {
     return(false);
 }
Exemple #23
0
 /// <summary>
 /// Is called when ever a modbus message was received, no matter if it was for this device or not.
 /// </summary>
 /// <param name="modbusInterface">Interface by which the message was received</param>
 /// <param name="deviceAddress">Address to which device the message was sent</param>
 /// <param name="functionCode">Function code</param>
 protected virtual void OnMessageReeived(IModbusInterface modbusInterface, byte deviceAddress, ModbusFunctionCode functionCode)
 { }
Exemple #24
0
      private void WriteMultipleCoils(IModbusInterface intf, bool isBroadcast, object telegramContext, ModbusFunctionCode fc, short dataPos, short dataLength)
      {
         if (dataLength < 5)
         {
            SendErrorResult(intf, isBroadcast, _deviceAddress, telegramContext, fc, ModbusErrorCode.IllegalDataValue);
         }
         else
         {
            ushort startAddress = ModbusUtils.ExtractUShort(_buffer, dataPos);
            ushort outputCount = ModbusUtils.ExtractUShort(_buffer, dataPos + 2);
            byte byteCount = _buffer[dataPos + 4];
            byte[] values = new byte[byteCount];
            Array.Copy(_buffer, dataPos + 5, values, 0, values.Length);

            var err = OnWriteMultipleCoils(isBroadcast, startAddress, outputCount, values);
            if (isBroadcast)
            {
               return;
            }
            if (err != ModbusErrorCode.NoError)
            {
               SendErrorResult(intf, false, _deviceAddress, telegramContext, fc, err);
            }
            else
            {
               short telegramLength;
               intf.CreateTelegram(_deviceAddress, (byte)fc, 4, _buffer, out telegramLength, out dataPos, true, ref telegramContext);
               ModbusUtils.InsertUShort(_buffer, dataPos, startAddress);
               ModbusUtils.InsertUShort(_buffer, dataPos + 2, outputCount);
               intf.SendTelegram(_buffer, telegramLength);
            }
         }
      }
Exemple #25
0
      /// <summary>
      /// Handles a received message.
      /// </summary>
      /// <param name="intf">Interface by which te message was received.</param>
      /// <param name="deviceAddress">Address of the target device</param>
      /// <param name="isBroadcast">true if the message is a broadcast. For broadcast messages no reponse is sent.</param>
      /// <param name="telegramLength">Length of the message in bytes.</param>
      /// <param name="telegramContext">Interface specific message context.</param>
      /// <param name="fc">Function code.</param>
      /// <param name="dataPos">Index of function code specific data.</param>
      /// <param name="dataLength">Length of the function code specific data in bytes.</param>
      protected override void OnHandleTelegram(IModbusInterface intf, byte deviceAddress, bool isBroadcast, short telegramLength,
         object telegramContext, ModbusFunctionCode fc, short dataPos, short dataLength)
      {
         var master = (GatewayMaster)_masterMap[deviceAddress];
         if (master != null)
         {
            short masterTelegramLength;
            short masterDataPos;
            object masterTelegramContext = null;

            master.MasterInterface.CreateTelegram(master.TargetAddress, (byte)fc, dataLength, master.Buffer, out masterTelegramLength, out masterDataPos, false, ref masterTelegramContext);
            Array.Copy(Buffer, dataPos, master.Buffer, masterDataPos, dataLength);

            try
            {
               var masterDataLength = SendReceive(master.MasterInterface, master.Buffer, master.TargetAddress, fc, master.Timeout,
                  masterTelegramLength, -1, masterTelegramContext, ref masterDataPos);

               intf.CreateTelegram(deviceAddress, (byte)fc, masterDataLength, Buffer, out telegramLength, out dataPos,
                  true, ref telegramContext);
               Array.Copy(master.Buffer, masterDataPos, Buffer, dataPos, masterDataLength);
               intf.SendTelegram(Buffer, telegramLength);
            }
            catch (ModbusException ex)
            {
               try
               {
                  if (ex.ErrorCode == ModbusErrorCode.Timeout)
                  {
                     SendErrorResult(intf, false, deviceAddress, telegramContext, fc, ModbusErrorCode.GatewayTargetDeviceFailedToRespond);
                  }
                  else if (((ushort)ex.ErrorCode & 0xff00) != 0)
                  {
                     SendErrorResult(intf, false, deviceAddress, telegramContext, fc, ModbusErrorCode.GatewayTargetDeviceFailedToRespond);
                  }
                  else
                  {
                     SendErrorResult(intf, false, deviceAddress, telegramContext, fc, ex.ErrorCode);
                  }
               }
               // ReSharper disable once EmptyGeneralCatchClause
               catch { }
            }
            catch
            {
               try
               {
                  SendErrorResult(intf, false, deviceAddress, telegramContext, fc, ModbusErrorCode.GatewayPathUnavailable);
               }
               // ReSharper disable once EmptyGeneralCatchClause
               catch { }
            }
         }
         else
         {
            base.OnHandleTelegram(intf, deviceAddress, isBroadcast, telegramLength, telegramContext, fc, dataPos, dataLength);
         }
      }
Exemple #26
0
      private void WriteMultipleRegisters(IModbusInterface intf, bool isBroadcast, object telegramContext, ModbusFunctionCode fc, short dataPos, short dataLength)
      {
         if (dataLength < 5)
         {
            SendErrorResult(intf, isBroadcast, _deviceAddress, telegramContext, fc, ModbusErrorCode.IllegalDataValue);
         }
         else
         {
            ushort startAddress = ModbusUtils.ExtractUShort(_buffer, dataPos);
            ushort registerCount = ModbusUtils.ExtractUShort(_buffer, dataPos + 2);
            //byte byteCount = _Buffer[dataPos + 4];
            ushort[] registers = new ushort[registerCount];
            for (int i = 0; i < registerCount; i++)
            {
               registers[i] = ModbusUtils.ExtractUShort(_buffer, dataPos + 5 + 2 * i);
            }

            var err = OnWriteMultipleRegisters(isBroadcast, startAddress, registers);
            if (isBroadcast)
            {
               return;
            }
            if (err != ModbusErrorCode.NoError)
            {
               SendErrorResult(intf, false, _deviceAddress, telegramContext, fc, err);
            }
            else
            {
               short telegramLength;
               intf.CreateTelegram(_deviceAddress, (byte)fc, 4, _buffer, out telegramLength, out dataPos, true, ref telegramContext);
               ModbusUtils.InsertUShort(_buffer, dataPos, startAddress);
               ModbusUtils.InsertUShort(_buffer, dataPos + 2, registerCount);
               intf.SendTelegram(_buffer, telegramLength);
            }
         }
      }
        private async Task ExecuteAnalogReadCommand(ModbusFunctionCode functionCode, ushort startAddress, ushort quantity)
        {
            string verboseMessage = $"{baseLogString} entering ExecuteAnalogReadCommand method, command's functionCode: {functionCode}, startAddress: {startAddress}, quantity:{quantity}.";

            Logger.LogVerbose(verboseMessage);

            int[]     data;
            PointType pointType;

            if (functionCode == ModbusFunctionCode.READ_HOLDING_REGISTERS)
            {
                verboseMessage = $"{baseLogString} ExecuteAnalogReadCommand => about to call ModbusClient.ReadHoldingRegisters({startAddress - 1}, {quantity}) method.";
                Logger.LogVerbose(verboseMessage);

                //KEY LOGIC
                pointType = PointType.ANALOG_OUTPUT;
                data      = modbusClient.ReadHoldingRegisters(startAddress - 1, quantity);

                verboseMessage = $"{baseLogString} ExecuteAnalogReadCommand => ModbusClient.ReadHoldingRegisters({startAddress - 1}, {quantity}) method SUCCESSFULLY executed. Resulting data count: {data.Length}.";
                Logger.LogVerbose(verboseMessage);
            }
            else if (functionCode == ModbusFunctionCode.READ_INPUT_REGISTERS)
            {
                verboseMessage = $"{baseLogString} ExecuteAnalogReadCommand => about to call ModbusClient.ReadInputRegisters({startAddress - 1}, {quantity}) method.";
                Logger.LogVerbose(verboseMessage);

                //KEY LOGIC
                pointType = PointType.ANALOG_INPUT;
                data      = modbusClient.ReadInputRegisters(startAddress - 1, quantity);

                verboseMessage = $"{baseLogString} ExecuteAnalogReadCommand => ModbusClient.ReadInputRegisters({startAddress - 1}, {quantity}) method SUCCESSFULLY executed. Resulting data count: {data.Length}.";
                Logger.LogVerbose(verboseMessage);
            }
            else
            {
                string message = $"{baseLogString} ExecuteAnalogReadCommand => function code is neither ModbusFunctionCode.READ_HOLDING_REGISTERS nor ModbusFunctionCode.READ_INPUT_REGISTERS";
                Logger.LogError(message);
                throw new ArgumentException(message);
            }

            //this.analogMeasurementCache = new Dictionary<long, AnalogModbusData>(data.Length);
            this.analogMeasurementCache.Clear();

            var modelReadAccessClient   = ScadaModelReadAccessClient.CreateClient();
            var modelUpdateAccessClient = ScadaModelUpdateAccessClient.CreateClient();

            var gidToPointItemMap = await modelReadAccessClient.GetGidToPointItemMap();

            var addressToGidMap = await modelReadAccessClient.GetAddressToGidMap();

            var commandDescriptionCache = await modelReadAccessClient.GetCommandDescriptionCache();

            for (ushort i = 0; i < data.Length; i++)
            {
                ushort address  = (ushort)(startAddress + i);
                int    rawValue = data[i];

                if (!addressToGidMap.ContainsKey((short)pointType))
                {
                    Logger.LogWarning($"{baseLogString} ExecuteAnalogReadCommand => Point type: {pointType} is not in the current addressToGidMap.");
                    continue;
                }

                //for commands enqueued during model update, that are not valid
                if (!addressToGidMap[(short)pointType].ContainsKey(address))
                {
                    Logger.LogWarning($"{baseLogString} ExecuteAnalogReadCommand => trying to read value on address {address}, Point type: {pointType}, which is not in the current addressToGidMap.");
                    continue;
                }

                long gid = addressToGidMap[(short)pointType][address];

                //for commands enqueued during model update, that are not valid
                if (!gidToPointItemMap.ContainsKey(gid))
                {
                    Logger.LogWarning($"{baseLogString} ExecuteAnalogReadCommand => trying to read value for measurement with gid: 0x{gid:X16}, which is not in the current SCADA Model.");
                    continue;
                }

                if (!(gidToPointItemMap[gid] is IAnalogPointItem pointItem))
                {
                    string message = $"{baseLogString} ExecuteAnalogReadCommand => PointItem [Gid: 0x{gid:X16}] does not implement {typeof(IAnalogPointItem)}.";
                    Logger.LogError(message);
                    throw new Exception(message);
                }

                //KEY LOGIC
                if (pointItem.CurrentRawValue != rawValue)
                {
                    pointItem = (IAnalogPointItem)(await modelUpdateAccessClient.UpdatePointItemRawValue(pointItem.Gid, rawValue));
                    Logger.LogInformation($"{baseLogString} ExecuteAnalogReadCommand => Alarm for Point [Gid: 0x{pointItem.Gid:X16}, Address: {pointItem.Address}] set to {pointItem.Alarm}.");
                }

                //LOGIC
                CommandOriginType commandOrigin = CommandOriginType.UNKNOWN_ORIGIN;

                if (commandDescriptionCache.ContainsKey(gid) && commandDescriptionCache[gid].Value == pointItem.CurrentRawValue)
                {
                    commandOrigin = commandDescriptionCache[gid].CommandOrigin;
                    await modelUpdateAccessClient.RemoveCommandDescription(gid);

                    Logger.LogDebug($"{baseLogString} ExecuteAnalogReadCommand => Command origin of command address: {pointItem.Address} is set to {commandOrigin}.");

                    //LOGIC
                    AnalogModbusData analogData = new AnalogModbusData(pointItem.CurrentEguValue, pointItem.Alarm, gid, commandOrigin);
                    this.analogMeasurementCache.Add(gid, analogData);

                    verboseMessage = $"{baseLogString} ExecuteAnalogReadCommand => AnalogModbusData added to measurementCache. MeasurementGid: {analogData.MeasurementGid:X16}, Value: {analogData.Value}, Alarm: {analogData.Alarm}, CommandOrigin: {analogData.CommandOrigin} .";
                    Logger.LogVerbose(verboseMessage);
                }
            }

            //LOGIC
            await modelUpdateAccessClient.MakeAnalogEntryToMeasurementCache(this.analogMeasurementCache, true);

            verboseMessage = $"{baseLogString} ExecuteAnalogReadCommand => MakeAnalogEntryToMeasurementCache method called. measurementCache count: {this.analogMeasurementCache.Count}.";
            Logger.LogVerbose(verboseMessage);
        }
Exemple #28
0
      private void ReadWriteMultipleRegisters(IModbusInterface intf, bool isBroadcast, object telegramContext, ModbusFunctionCode fc, short dataPos, short dataLength)
      {
         if (dataLength < 9)
         {
            SendErrorResult(intf, isBroadcast, _deviceAddress, telegramContext, fc, ModbusErrorCode.IllegalDataValue);
         }
         else
         {
            ushort readStartAddress = ModbusUtils.ExtractUShort(_buffer, dataPos);
            ushort readCount = ModbusUtils.ExtractUShort(_buffer, dataPos + 2);
            ushort writeStartAddress = ModbusUtils.ExtractUShort(_buffer, dataPos + 4);
            ushort writeCount = ModbusUtils.ExtractUShort(_buffer, dataPos + 6);
            //byte byteCount = _Buffer[dataPos + 8];
            ushort[] writeRegisters = new ushort[writeCount];
            for (int i = 0; i < writeCount; i++)
            {
               writeRegisters[i] = ModbusUtils.ExtractUShort(_buffer, dataPos + 5 + 2 * i);
            }
            ushort[] readRegisters = new ushort[readCount];

            var err = OnReadWriteMultipleRegisters(isBroadcast, writeStartAddress, writeRegisters, readStartAddress, readRegisters);
            if (isBroadcast)
            {
               return;
            }
            if (err != ModbusErrorCode.NoError)
            {
               SendErrorResult(intf, false, _deviceAddress, telegramContext, fc, err);
            }
            else
            {
               short telegramLength;
               intf.CreateTelegram(_deviceAddress, (byte)fc, (short)(1 + 2 * readRegisters.Length), _buffer, out telegramLength, out dataPos, true, ref telegramContext);
               _buffer[dataPos] = (byte)(2 * readRegisters.Length);
               for (int i = 0; i < readCount; i++)
               {
                  ModbusUtils.InsertUShort(_buffer, dataPos + 1 + 2 * i, readRegisters[i]);
               }
               intf.SendTelegram(_buffer, telegramLength);
            }
         }
      }
Exemple #29
0
      /// <summary>
      /// Is called when the device identification of this devuice is requested.
      /// </summary>
      /// <param name="intf">Interface from wich the requst was received</param>
      /// <param name="isBroadcast">true if request is a broadcast</param>
      /// <param name="telegramContext">Conext of the telegram</param>
      /// <param name="fc">Function code</param>
      /// <param name="dataPos">Posittion (offset) of the data in the buffer</param>
      /// <param name="dataLength">Length of the data in the buffer</param>
      protected virtual void ReadDeviceIdentification(IModbusInterface intf, bool isBroadcast, object telegramContext, ModbusFunctionCode fc, short dataPos, short dataLength)
      {
         if (isBroadcast)
         {
            return;
         }
         if (dataLength < 3)
         {
            SendErrorResult(intf, false, _deviceAddress, telegramContext, fc, ModbusErrorCode.IllegalDataValue);
         }
         else
         {
            byte deviceIdCode = _buffer[dataPos + 1];
            if (deviceIdCode < 1 || deviceIdCode > 4)
            {
               SendErrorResult(intf, false, _deviceAddress, telegramContext, fc, ModbusErrorCode.IllegalDataValue);
               return;
            }
            byte objectId = _buffer[dataPos + 2];
            byte lastObjectId;
            switch (deviceIdCode)
            {
               case 0x00:
                  lastObjectId = 0x02; // basic
                  break;

               case 0x01:
                  lastObjectId = 0x7f; // regular
                  break;

               case 0x02:
                  lastObjectId = 0xff; // extended
                  break;

               default:
                  lastObjectId = objectId; // specific
                  break;
            }

            byte[] values = new byte[intf.MaxTelegramLength - 6];
            byte objectCount = 0;
            short valuePos = 0;
            bool moreFolows = false;
            byte nextId = 0;
            for (short id = objectId; id <= lastObjectId; ++id)
            {
               var value = OnGetDeviceIdentification((ModbusObjectId)id);
               if (value == null)
               {
                  // no more values
                  break;
               }
               if (values.Length - (valuePos + 2) >= value.Length)
               {
                  ++objectCount;
                  values[valuePos++] = (byte)id;
                  values[valuePos++] = (byte)value.Length;
                  for (int c = 0; c < value.Length; c++)
                  {
                     values[valuePos++] = (byte)value[c];
                  }
               }
               else
               {
                  // more to come
                  moreFolows = true;
                  nextId = (byte)(id + 1);
                  break;
               }
            }

            short telegramLength;
            intf.CreateTelegram(_deviceAddress, (byte)fc, (short)(6 + valuePos), _buffer, out telegramLength, out dataPos, true, ref telegramContext);
            _buffer[dataPos] = 0x0e;
            _buffer[dataPos + 1] = deviceIdCode;
            _buffer[dataPos + 2] = (byte)((byte)GetConformityLevel() & 0x80);
            _buffer[dataPos + 3] = (byte)(moreFolows ? 0xff : 0x00);
            _buffer[dataPos + 4] = nextId;
            _buffer[dataPos + 5] = objectCount;
            Array.Copy(values, 0, _buffer, dataPos + 6, valuePos);
            intf.SendTelegram(_buffer, telegramLength);
         }
      }
        private void ReadWriteMultipleRegisters(IModbusInterface intf, bool isBroadcast, object telegramContext, ModbusFunctionCode fc, short dataPos, short dataLength)
        {
            if (dataLength < 9)
            {
                SendErrorResult(intf, isBroadcast, _deviceAddress, telegramContext, fc, ModbusErrorCode.IllegalDataValue);
            }
            else
            {
                ushort readStartAddress  = ModbusUtils.ExtractUShort(_buffer, dataPos);
                ushort readCount         = ModbusUtils.ExtractUShort(_buffer, dataPos + 2);
                ushort writeStartAddress = ModbusUtils.ExtractUShort(_buffer, dataPos + 4);
                ushort writeCount        = ModbusUtils.ExtractUShort(_buffer, dataPos + 6);
                //byte byteCount = _Buffer[dataPos + 8];
                ushort[] writeRegisters = new ushort[writeCount];
                for (int i = 0; i < writeCount; i++)
                {
                    writeRegisters[i] = ModbusUtils.ExtractUShort(_buffer, dataPos + 5 + 2 * i);
                }
                ushort[] readRegisters = new ushort[readCount];

                var err = OnReadWriteMultipleRegisters(isBroadcast, writeStartAddress, writeRegisters, readStartAddress, readRegisters);
                if (isBroadcast)
                {
                    return;
                }
                if (err != ModbusErrorCode.NoError)
                {
                    SendErrorResult(intf, false, _deviceAddress, telegramContext, fc, err);
                }
                else
                {
                    short telegramLength;
                    intf.CreateTelegram(_deviceAddress, (byte)fc, (short)(1 + 2 * readRegisters.Length), _buffer, out telegramLength, out dataPos, true, ref telegramContext);
                    _buffer[dataPos] = (byte)(2 * readRegisters.Length);
                    for (int i = 0; i < readCount; i++)
                    {
                        ModbusUtils.InsertUShort(_buffer, dataPos + 1 + 2 * i, readRegisters[i]);
                    }
                    intf.SendTelegram(_buffer, telegramLength);
                }
            }
        }
        /// <summary>
        /// Is called when the device identification of this devuice is requested.
        /// </summary>
        /// <param name="intf">Interface from wich the requst was received</param>
        /// <param name="isBroadcast">true if request is a broadcast</param>
        /// <param name="telegramContext">Conext of the telegram</param>
        /// <param name="fc">Function code</param>
        /// <param name="dataPos">Posittion (offset) of the data in the buffer</param>
        /// <param name="dataLength">Length of the data in the buffer</param>
        protected virtual void ReadDeviceIdentification(IModbusInterface intf, bool isBroadcast, object telegramContext, ModbusFunctionCode fc, short dataPos, short dataLength)
        {
            if (isBroadcast)
            {
                return;
            }
            if (dataLength < 3)
            {
                SendErrorResult(intf, false, _deviceAddress, telegramContext, fc, ModbusErrorCode.IllegalDataValue);
            }
            else
            {
                byte deviceIdCode = _buffer[dataPos + 1];
                if (deviceIdCode < 1 || deviceIdCode > 4)
                {
                    SendErrorResult(intf, false, _deviceAddress, telegramContext, fc, ModbusErrorCode.IllegalDataValue);
                    return;
                }
                byte objectId = _buffer[dataPos + 2];
                byte lastObjectId;
                switch (deviceIdCode)
                {
                case 0x00:
                    lastObjectId = 0x02; // basic
                    break;

                case 0x01:
                    lastObjectId = 0x7f; // regular
                    break;

                case 0x02:
                    lastObjectId = 0xff; // extended
                    break;

                default:
                    lastObjectId = objectId; // specific
                    break;
                }

                byte[] values      = new byte[intf.MaxTelegramLength - 6];
                byte   objectCount = 0;
                short  valuePos    = 0;
                bool   moreFolows  = false;
                byte   nextId      = 0;
                for (short id = objectId; id <= lastObjectId; ++id)
                {
                    var value = OnGetDeviceIdentification((ModbusObjectId)id);
                    if (value == null)
                    {
                        // no more values
                        break;
                    }
                    if (values.Length - (valuePos + 2) >= value.Length)
                    {
                        ++objectCount;
                        values[valuePos++] = (byte)id;
                        values[valuePos++] = (byte)value.Length;
                        for (int c = 0; c < value.Length; c++)
                        {
                            values[valuePos++] = (byte)value[c];
                        }
                    }
                    else
                    {
                        // more to come
                        moreFolows = true;
                        nextId     = (byte)(id + 1);
                        break;
                    }
                }

                short telegramLength;
                intf.CreateTelegram(_deviceAddress, (byte)fc, (short)(6 + valuePos), _buffer, out telegramLength, out dataPos, true, ref telegramContext);
                _buffer[dataPos]     = 0x0e;
                _buffer[dataPos + 1] = deviceIdCode;
                _buffer[dataPos + 2] = (byte)((byte)GetConformityLevel() & 0x80);
                _buffer[dataPos + 3] = (byte)(moreFolows ? 0xff : 0x00);
                _buffer[dataPos + 4] = nextId;
                _buffer[dataPos + 5] = objectCount;
                Array.Copy(values, 0, _buffer, dataPos + 6, valuePos);
                intf.SendTelegram(_buffer, telegramLength);
            }
        }
 /// <summary>
 /// Is called when ever a modbus message was received, no matter if it was for this device or not.
 /// </summary>
 /// <param name="modbusInterface">Interface by which the message was received</param>
 /// <param name="deviceAddress">Address to which device the message was sent</param>
 /// <param name="functionCode">Function code</param>
 protected virtual void OnMessageReeived(IModbusInterface modbusInterface, byte deviceAddress, ModbusFunctionCode functionCode)
 {
 }
        /// <summary>
        /// Sends a error result message.
        /// </summary>
        /// <param name="intf">Interface to send message to.</param>
        /// <param name="isBroadcast">true if the message is a broadcast. For broadcast messages no reponse is sent.</param>
        /// <param name="deviceAddress">Device address for response</param>
        /// <param name="telegramContext">Interface specific telegram context.</param>
        /// <param name="fc">Function code. The msg is automatically set.</param>
        /// <param name="modbusErrorCode">Modbus error code to send.</param>
        protected virtual void SendErrorResult(IModbusInterface intf, bool isBroadcast, byte deviceAddress, object telegramContext, ModbusFunctionCode fc, ModbusErrorCode modbusErrorCode)
        {
            if (isBroadcast)
            {
                return;
            }
            short telegramLength;
            short dataPos;

            intf.CreateTelegram(deviceAddress, (byte)((byte)fc | 0x80), 1, _buffer, out telegramLength, out dataPos, true, ref telegramContext);
            _buffer[dataPos] = (byte)modbusErrorCode;
            intf.SendTelegram(_buffer, telegramLength);
        }
        /// <summary>
        /// Handles a received message.
        /// </summary>
        /// <param name="intf">Interface by which the message was received.</param>
        /// <param name="deviceAddress">Address of the target device</param>
        /// <param name="isBroadcast">true if the message is a broadcast. For broadcast messages no response is sent.</param>
        /// <param name="telegramLength">Length of the message in bytes.</param>
        /// <param name="telegramContext">Interface specific message context.</param>
        /// <param name="fc">Function code.</param>
        /// <param name="dataPos">Index of function code specific data.</param>
        /// <param name="dataLength">Length of the function code specific data in bytes.</param>
        protected virtual void OnHandleTelegram(IModbusInterface intf, byte deviceAddress, bool isBroadcast, short telegramLength, object telegramContext, ModbusFunctionCode fc, short dataPos, short dataLength)
        {
            try
            {
                switch (fc)
                {
                case ModbusFunctionCode.ReadCoils:
                    ReadCoils(intf, isBroadcast, telegramContext, fc, dataPos, dataLength);
                    break;

                case ModbusFunctionCode.ReadDiscreteInputs:
                    ReadDiscreteInputs(intf, isBroadcast, telegramContext, fc, dataPos, dataLength);
                    break;

                case ModbusFunctionCode.ReadHoldingRegisters:
                    ReadHoldingRegisters(intf, isBroadcast, telegramContext, fc, dataPos, dataLength);
                    break;

                case ModbusFunctionCode.ReadInputRegisters:
                    ReadInputRegisters(intf, isBroadcast, telegramContext, fc, dataPos, dataLength);
                    break;

                case ModbusFunctionCode.WriteSingleCoil:
                    WriteSingleCoil(intf, isBroadcast, telegramContext, fc, dataPos, dataLength);
                    break;

                case ModbusFunctionCode.WriteSingleRegister:
                    WriteSingleRegister(intf, isBroadcast, telegramContext, fc, dataPos, dataLength);
                    break;

                case ModbusFunctionCode.WriteMultipleCoils:
                    WriteMultipleCoils(intf, isBroadcast, telegramContext, fc, dataPos, dataLength);
                    break;

                case ModbusFunctionCode.WriteMultipleRegisters:
                    WriteMultipleRegisters(intf, isBroadcast, telegramContext, fc, dataPos, dataLength);
                    break;

                case ModbusFunctionCode.ReadWriteMultipleRegisters:
                    ReadWriteMultipleRegisters(intf, isBroadcast, telegramContext, fc, dataPos, dataLength);
                    break;

                case ModbusFunctionCode.ReadDeviceIdentification:
                case ModbusFunctionCode.ReadDeviceIdentification2:
                    ReadDeviceIdentification(intf, isBroadcast, telegramContext, fc, dataPos, dataLength);
                    break;

                default:
                    if (!OnCustomTelegram(intf, isBroadcast, _buffer, telegramLength, telegramContext, fc, dataPos, dataLength))
                    {
                        SendErrorResult(intf, isBroadcast, _deviceAddress, telegramContext, fc, ModbusErrorCode.IllegalFunction);
                    }
                    break;
                }
            }
            catch
            {
                SendErrorResult(intf, isBroadcast, _deviceAddress, telegramContext, fc, ModbusErrorCode.ServerDeviceFailure);
            }
        }
Exemple #35
0
 private void ReadDiscreteInputs(IModbusInterface intf, bool isBroadcast, object telegramContext, ModbusFunctionCode fc, short dataPos, short dataLength)
 {
    if (dataLength < 4)
    {
       SendErrorResult(intf, isBroadcast, _deviceAddress, telegramContext, fc, ModbusErrorCode.IllegalDataValue);
    }
    else
    {
       ushort startAddress = ModbusUtils.ExtractUShort(_buffer, dataPos);
       ushort inputCount = ModbusUtils.ExtractUShort(_buffer, dataPos + 2);
       byte[] inputs;
       if (inputCount % 8 == 0)
       {
          inputs = new byte[inputCount / 8];
       }
       else
       {
          inputs = new byte[inputCount / 8 + 1];
          inputs[inputs.Length - 1] = 0;
       }
       var err = OnReadDiscreteInputs(isBroadcast, startAddress, inputCount, inputs);
       if (isBroadcast)
       {
          return;
       }
       if (err != ModbusErrorCode.NoError)
       {
          SendErrorResult(intf, false, _deviceAddress, telegramContext, fc, err);
       }
       else
       {
          short telegramLength;
          intf.CreateTelegram(_deviceAddress, (byte)fc, (short)(1 + inputs.Length), _buffer, out telegramLength, out dataPos, true, ref telegramContext);
          _buffer[dataPos] = (byte)(inputs.Length);
          Array.Copy(inputs, 0, _buffer, dataPos + 1, inputs.Length);
          intf.SendTelegram(_buffer, telegramLength);
       }
    }
 }
 private void ReadDiscreteInputs(IModbusInterface intf, bool isBroadcast, object telegramContext, ModbusFunctionCode fc, short dataPos, short dataLength)
 {
     if (dataLength < 4)
     {
         SendErrorResult(intf, isBroadcast, _deviceAddress, telegramContext, fc, ModbusErrorCode.IllegalDataValue);
     }
     else
     {
         ushort startAddress = ModbusUtils.ExtractUShort(_buffer, dataPos);
         ushort inputCount   = ModbusUtils.ExtractUShort(_buffer, dataPos + 2);
         byte[] inputs;
         if (inputCount % 8 == 0)
         {
             inputs = new byte[inputCount / 8];
         }
         else
         {
             inputs = new byte[inputCount / 8 + 1];
             inputs[inputs.Length - 1] = 0;
         }
         var err = OnReadDiscreteInputs(isBroadcast, startAddress, inputCount, inputs);
         if (isBroadcast)
         {
             return;
         }
         if (err != ModbusErrorCode.NoError)
         {
             SendErrorResult(intf, false, _deviceAddress, telegramContext, fc, err);
         }
         else
         {
             short telegramLength;
             intf.CreateTelegram(_deviceAddress, (byte)fc, (short)(1 + inputs.Length), _buffer, out telegramLength, out dataPos, true, ref telegramContext);
             _buffer[dataPos] = (byte)(inputs.Length);
             Array.Copy(inputs, 0, _buffer, dataPos + 1, inputs.Length);
             intf.SendTelegram(_buffer, telegramLength);
         }
     }
 }
Exemple #37
0
      /// <summary>
      /// Handles a received message.
      /// </summary>
      /// <param name="intf">Interface by which the message was received.</param>
      /// <param name="deviceAddress">Address of the target device</param>
      /// <param name="isBroadcast">true if the message is a broadcast. For broadcast messages no response is sent.</param>
      /// <param name="telegramLength">Length of the message in bytes.</param>
      /// <param name="telegramContext">Interface specific message context.</param>
      /// <param name="fc">Function code.</param>
      /// <param name="dataPos">Index of function code specific data.</param>
      /// <param name="dataLength">Length of the function code specific data in bytes.</param>
      protected virtual void OnHandleTelegram(IModbusInterface intf, byte deviceAddress, bool isBroadcast, short telegramLength, object telegramContext, ModbusFunctionCode fc, short dataPos, short dataLength)
      {
         try
         {
            switch (fc)
            {
               case ModbusFunctionCode.ReadCoils:
                  ReadCoils(intf, isBroadcast, telegramContext, fc, dataPos, dataLength);
                  break;

               case ModbusFunctionCode.ReadDiscreteInputs:
                  ReadDiscreteInputs(intf, isBroadcast, telegramContext, fc, dataPos, dataLength);
                  break;

               case ModbusFunctionCode.ReadHoldingRegisters:
                  ReadHoldingRegisters(intf, isBroadcast, telegramContext, fc, dataPos, dataLength);
                  break;

               case ModbusFunctionCode.ReadInputRegisters:
                  ReadInputRegisters(intf, isBroadcast, telegramContext, fc, dataPos, dataLength);
                  break;

               case ModbusFunctionCode.WriteSingleCoil:
                  WriteSingleCoil(intf, isBroadcast, telegramContext, fc, dataPos, dataLength);
                  break;

               case ModbusFunctionCode.WriteSingleRegister:
                  WriteSingleRegister(intf, isBroadcast, telegramContext, fc, dataPos, dataLength);
                  break;

               case ModbusFunctionCode.WriteMultipleCoils:
                  WriteMultipleCoils(intf, isBroadcast, telegramContext, fc, dataPos, dataLength);
                  break;

               case ModbusFunctionCode.WriteMultipleRegisters:
                  WriteMultipleRegisters(intf, isBroadcast, telegramContext, fc, dataPos, dataLength);
                  break;

               case ModbusFunctionCode.ReadWriteMultipleRegisters:
                  ReadWriteMultipleRegisters(intf, isBroadcast, telegramContext, fc, dataPos, dataLength);
                  break;

               case ModbusFunctionCode.ReadDeviceIdentification:
               case ModbusFunctionCode.ReadDeviceIdentification2:
                  ReadDeviceIdentification(intf, isBroadcast, telegramContext, fc, dataPos, dataLength);
                  break;

               default:
                  if (!OnCustomTelegram(intf, isBroadcast, _buffer, telegramLength, telegramContext, fc, dataPos, dataLength))
                  {
                     SendErrorResult(intf, isBroadcast, _deviceAddress, telegramContext, fc, ModbusErrorCode.IllegalFunction);
                  }
                  break;
            }
         }
         catch
         {
            SendErrorResult(intf, isBroadcast, _deviceAddress, telegramContext, fc, ModbusErrorCode.ServerDeviceFailure);
         }
      }
        private void WriteSingleRegister(IModbusInterface intf, bool isBroadcast, object telegramContext, ModbusFunctionCode fc, short dataPos, short dataLength)
        {
            if (dataLength < 4)
            {
                SendErrorResult(intf, isBroadcast, _deviceAddress, telegramContext, fc, ModbusErrorCode.IllegalDataValue);
            }
            else
            {
                ushort address = ModbusUtils.ExtractUShort(_buffer, dataPos);
                ushort value   = ModbusUtils.ExtractUShort(_buffer, dataPos + 2);

                var err = OnWriteSingleRegister(isBroadcast, address, value);
                if (isBroadcast)
                {
                    return;
                }
                if (err != ModbusErrorCode.NoError)
                {
                    SendErrorResult(intf, false, _deviceAddress, telegramContext, fc, err);
                }
                else
                {
                    short telegramLength;
                    intf.CreateTelegram(_deviceAddress, (byte)fc, 4, _buffer, out telegramLength, out dataPos, true, ref telegramContext);
                    ModbusUtils.InsertUShort(_buffer, dataPos, address);
                    ModbusUtils.InsertUShort(_buffer, dataPos + 2, value);
                    intf.SendTelegram(_buffer, telegramLength);
                }
            }
        }
Exemple #39
0
 private void WriteExceptionResponse(ModbusFunctionCode functionCode, ModbusExceptionCode exceptionCode)
 {
     this.WriteExceptionResponse((byte)functionCode, exceptionCode);
 }
Exemple #40
0
 /// <summary>
 /// OnCustomTelegram is called for any function code which is not explicitly handeled by a On"FunctionCode" methos.
 /// </summary>
 /// <param name="intf">Interface which sent the request.</param>
 /// <param name="isBroadcast">true if the request is a broadcast</param>
 /// <param name="buffer">Buffer containing the message.</param>
 /// <param name="telegramLength">Total length of message in bytes.</param>
 /// <param name="telegramContext">Interface specific message context.</param>
 /// <param name="fc">Function code.</param>
 /// <param name="dataPos">Index of the function code specific data.</param>
 /// <param name="dataLength">Length of the function code specific data in bytes.</param>
 /// <returns></returns>
 /// <remarks>
 /// Look at http://www.modbus.org/docs/Modbus_Application_Protocol_V1_1b3.pdf for more details about function codes.
 /// </remarks>
 protected virtual bool OnCustomTelegram(IModbusInterface intf, bool isBroadcast, byte[] buffer, short telegramLength, object telegramContext, ModbusFunctionCode fc, short dataPos, short dataLength)
 {
    return false;
 }
Exemple #41
0
        private protected override Span <byte> TransceiveFrame(byte unitIdentifier, ModbusFunctionCode functionCode, Action <ExtendedBinaryWriter> extendFrame)
        {
            int    messageLength;
            int    frameLength;
            byte   rawFunctionCode;
            ushort crc;
            int    mbapHeaderLength = 4;

            // build request
            if (!(0 <= unitIdentifier && unitIdentifier <= 247))
            {
                throw new ModbusException(ErrorMessage.ModbusClient_InvalidUnitIdentifier);
            }

            // special case: broadcast (only for write commands)
            if (unitIdentifier == 0)
            {
                switch (functionCode)
                {
                case ModbusFunctionCode.WriteMultipleRegisters:
                case ModbusFunctionCode.WriteSingleCoil:
                case ModbusFunctionCode.WriteSingleRegister:
                case ModbusFunctionCode.WriteMultipleCoils:
                case ModbusFunctionCode.WriteFileRecord:
                case ModbusFunctionCode.MaskWriteRegister:
                    break;

                default:
                    throw new ModbusException(ErrorMessage.Modbus_InvalidUseOfBroadcast);
                }
            }

            _frameBuffer.Writer.Seek(0, SeekOrigin.Begin);
            _frameBuffer.Writer.Write(unitIdentifier);                                      // 00     Unit Identifier
            extendFrame(_frameBuffer.Writer);
            frameLength = (int)_frameBuffer.Writer.BaseStream.Position;

            // add CRC
            crc = ModbusUtils.CalculateCRC(_frameBuffer.Buffer.AsMemory().Slice(0, frameLength));
            _frameBuffer.Writer.Write(crc);
            frameLength = (int)_frameBuffer.Writer.BaseStream.Position;

            // build message
            _messageBuffer.Writer.Seek(0, SeekOrigin.Begin);
            if (!SwapBytes)
            {
                _messageBuffer.Writer.WriteReverse(ProtocolIdentifier);  //2b
                _messageBuffer.Writer.WriteReverse((ushort)frameLength); //2b
            }
            else
            {
                _messageBuffer.Writer.Write(ProtocolIdentifier);              //2b
                _messageBuffer.Writer.Write((ushort)frameLength);             //2b
            }
            _messageBuffer.Writer.Write(_frameBuffer.Buffer, 0, frameLength); //framelength
            messageLength = frameLength + mbapHeaderLength;

            // send request
            _serialPort.Write(_messageBuffer.Buffer, 0, messageLength);

            // special case: broadcast (only for write commands)
            if (unitIdentifier == 0)
            {
                return(_messageBuffer.Buffer.AsSpan(0, 0));
            }

            // wait for and process response
            messageLength = 0;
            _messageBuffer.Reader.BaseStream.Seek(0, SeekOrigin.Begin);

            while (true)
            {
                messageLength += _serialPort.Read(_messageBuffer.Buffer, messageLength, _messageBuffer.Buffer.Length - messageLength);

                if (ModbusUtils.DetectFrame(unitIdentifier, _messageBuffer.Buffer.AsMemory().Slice(mbapHeaderLength, messageLength - mbapHeaderLength)))
                {
                    break;
                }
                else
                {
                    // reset length because one or more chunks of data were received and written to
                    // the buffer, but no valid Modbus frame could be detected and now the buffer is full
                    if (messageLength == _messageBuffer.Buffer.Length)
                    {
                        messageLength = 0;
                    }
                }
            }

            //write message content to framebuffer
            _frameBuffer.Writer.BaseStream.Seek(0, SeekOrigin.Begin);
            _frameBuffer.Writer.Write(_messageBuffer.Buffer.AsSpan().Slice(mbapHeaderLength).ToArray(), 0, messageLength - mbapHeaderLength);
            frameLength = messageLength - mbapHeaderLength;

            unitIdentifier  = _frameBuffer.Reader.ReadByte();
            rawFunctionCode = _frameBuffer.Reader.ReadByte();

            if (rawFunctionCode == (byte)ModbusFunctionCode.Error + (byte)functionCode)
            {
                this.ProcessError(functionCode, (ModbusExceptionCode)_frameBuffer.Buffer[2]);
            }

            else if (rawFunctionCode != (byte)functionCode)
            {
                throw new ModbusException(ErrorMessage.ModbusClient_InvalidResponseFunctionCode);
            }

            return(_frameBuffer.Buffer.AsSpan(1, frameLength - 3));
        }
Exemple #42
0
        private protected override Span <byte> TransceiveFrame(byte unitIdentifier, ModbusFunctionCode functionCode, Action <ExtendedBinaryWriter> extendFrame)
        {
            int frameLength;
            int partialLength;

            ushort transactionIdentifier;
            ushort protocolIdentifier;
            ushort bytesFollowing;

            byte rawFunctionCode;

            bool isParsed;

            ModbusFrameBuffer    frameBuffer;
            ExtendedBinaryWriter writer;
            ExtendedBinaryReader reader;

            bytesFollowing = 0;
            frameBuffer    = _frameBuffer;
            writer         = _frameBuffer.Writer;
            reader         = _frameBuffer.Reader;

            // build request
            writer.Seek(7, SeekOrigin.Begin);
            extendFrame(writer);
            frameLength = (int)writer.BaseStream.Position;

            writer.Seek(0, SeekOrigin.Begin);

            if (BitConverter.IsLittleEndian)
            {
                writer.WriteReverse(this.GetTransactionIdentifier());          // 00-01  Transaction Identifier
                writer.WriteReverse((ushort)0);                                // 02-03  Protocol Identifier
                writer.WriteReverse((ushort)(frameLength - 6));                // 04-05  Length
            }
            else
            {
                writer.Write(this.GetTransactionIdentifier());                 // 00-01  Transaction Identifier
                writer.Write((ushort)0);                                       // 02-03  Protocol Identifier
                writer.Write((ushort)(frameLength - 6));                       // 04-05  Length
            }

            writer.Write(unitIdentifier);                                      // 06     Unit Identifier

            // send request
            _networkStream.Write(frameBuffer.Buffer, 0, frameLength);

            // wait for and process response
            frameLength = 0;
            isParsed    = false;
            reader.BaseStream.Seek(0, SeekOrigin.Begin);

            while (true)
            {
                partialLength = _networkStream.Read(frameBuffer.Buffer, frameLength, frameBuffer.Buffer.Length - frameLength);

                /* From MSDN (https://docs.microsoft.com/en-us/dotnet/api/system.io.stream.read):
                 * Implementations of this method read a maximum of count bytes from the current stream and store
                 * them in buffer beginning at offset. The current position within the stream is advanced by the
                 * number of bytes read; however, if an exception occurs, the current position within the stream
                 * remains unchanged. Implementations return the number of bytes read. The implementation will block
                 * until at least one byte of data can be read, in the event that no data is available. Read returns
                 * 0 only when there is no more data in the stream and no more is expected (such as a closed socket or end of file).
                 * An implementation is free to return fewer bytes than requested even if the end of the stream has not been reached.
                 */
                if (partialLength == 0)
                {
                    throw new InvalidOperationException(ErrorMessage.ModbusClient_TcpConnectionClosedUnexpectedly);
                }

                frameLength += partialLength;

                if (frameLength >= 7)
                {
                    if (!isParsed) // read MBAP header only once
                    {
                        // read MBAP header
                        transactionIdentifier = reader.ReadUInt16Reverse();              // 00-01  Transaction Identifier
                        protocolIdentifier    = reader.ReadUInt16Reverse();              // 02-03  Protocol Identifier
                        bytesFollowing        = reader.ReadUInt16Reverse();              // 04-05  Length
                        unitIdentifier        = reader.ReadByte();                       // 06     Unit Identifier

                        if (protocolIdentifier != 0)
                        {
                            throw new ModbusException(ErrorMessage.ModbusClient_InvalidProtocolIdentifier);
                        }

                        isParsed = true;
                    }

                    // full frame received
                    if (frameLength - 6 >= bytesFollowing)
                    {
                        break;
                    }
                }
            }

            rawFunctionCode = reader.ReadByte();

            if (rawFunctionCode == (byte)ModbusFunctionCode.Error + (byte)functionCode)
            {
                this.ProcessError(functionCode, (ModbusExceptionCode)frameBuffer.Buffer[8]);
            }
            else if (rawFunctionCode != (byte)functionCode)
            {
                throw new ModbusException(ErrorMessage.ModbusClient_InvalidResponseFunctionCode);
            }

            return(frameBuffer.Buffer.AsSpan(7, frameLength - 7));
        }