예제 #1
0
        private void ReadInputRegisters(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   registerCount = ModbusUtils.ExtractUShort(_buffer, dataPos + 2);
                ushort[] registers     = new ushort[registerCount];

                var err = OnReadInputRegisters(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, (short)(1 + 2 * registers.Length), _buffer, out telegramLength, out dataPos, true, ref telegramContext);
                    _buffer[dataPos] = (byte)(2 * registers.Length);
                    for (int i = 0; i < registerCount; i++)
                    {
                        ModbusUtils.InsertUShort(_buffer, dataPos + 1 + 2 * i, registers[i]);
                    }
                    intf.SendTelegram(_buffer, telegramLength);
                }
            }
        }
예제 #2
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);
                }
            }
        }
예제 #3
0
 /// <summary>
 /// Creates a modbus master using the given interface
 /// </summary>
 /// <param name="intf">Interface to use for the master</param>
 /// <param name="syncObject">Object to use for multi threaded communication locking.</param>
 public ModbusMaster(IModbusInterface intf, object syncObject = null)
 {
    _Interface = intf;
    _SyncObject = syncObject ?? new object();
    _Buffer = new byte[_Interface.MaxTelegramLength];
    _Interface.PrepareWrite();
 }
예제 #4
0
 /// <summary>
 /// Creates a modbus master using the given interface
 /// </summary>
 /// <param name="intf">Interface to use for the master</param>
 /// <param name="syncObject">Object to use for multi threaded communication locking.</param>
 public RoSchmiModbusMaster(IModbusInterface intf, object syncObject = null)
 {
     _Interface  = intf;
     _SyncObject = syncObject ?? new object();
     _Buffer     = new byte[_Interface.MaxTelegramLength];
     _Interface.PrepareWrite();
 }
예제 #5
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);
                }
            }
        }
예제 #6
0
 /// <summary>
 /// Creates a new ModebusDevice with one initial interface.
 /// </summary>
 /// <param name="intf">Initial interface.</param>
 /// <param name="deviceAddress">Device address. Must be between 1 and 247 for RTU and should be 248 for TCP.</param>
 /// <param name="syncObject">Optional object for communication interface synchronization.</param>
 /// <remarks>
 /// More interfaces can be add or removed by AddInterface and RemoveInterface methods.
 /// </remarks>
 protected ModbusDevice(IModbusInterface intf, byte deviceAddress, object syncObject = null) :
     this(deviceAddress, syncObject)
 {
     intf.PrepareRead();
     _interfaces.Add(intf);
     _buffer = new byte[intf.MaxTelegramLength];
 }
예제 #7
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);
            }
        }
예제 #8
0
 public GatewayMaster(IModbusInterface masterInterface, byte targetAddress, int timeout)
 {
     MasterInterface = masterInterface;
     TargetAddress   = targetAddress;
     Timeout         = timeout;
     Buffer          = new byte[masterInterface.MaxTelegramLength];
 }
예제 #9
0
 /// <summary>
 /// Creates a new ModebusDevice with one initial interface.
 /// </summary>
 /// <param name="intf">Initial interface.</param>
 /// <param name="deviceAddress">Device address. Must be between 1 and 247 for RTU and should be 248 for TCP.</param>
 /// <param name="syncObject">Optional object for communication interface synchronization.</param>
 /// <remarks>
 /// More interfaces can be add or removed by AddInterface and RemoveInterface methods.
 /// </remarks>
 protected ModbusDevice(IModbusInterface intf, byte deviceAddress, object syncObject = null) :
    this(deviceAddress, syncObject)
 {
    intf.PrepareRead();
    _interfaces.Add(intf);
    _buffer = new byte[intf.MaxTelegramLength];
 }
예제 #10
0
 /// <summary>
 /// Creates a modbus master using the given interface
 /// </summary>
 /// <param name="intf">Interface to use for the master</param>
 /// <param name="syncObject">Object to use for multi threaded communication locking.</param>
 public ModbusMaster(IModbusInterface intf, object syncObject = null)
 {
     this.@interface = intf;
     this.syncObject = syncObject ?? new object();
     this.buffer     = new byte[[email protected]];
     [email protected]();
 }
예제 #11
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);
            }
        }
예제 #12
0
 /// <summary>
 /// Adds an additional master interface with an custom device id mapping
 /// </summary>
 /// <param name="masterInterface">master interface to add</param>
 /// <param name="timeout">Timeout for master interface</param>
 /// <param name="addressMap">A hashtable where the key is the incoming device id that is mapped to it's value as device id on the master interface.
 /// Both, key and value of the hashtable must be of type byte.</param>
 public void AddMaster(IModbusInterface masterInterface, int timeout, Hashtable addressMap)
 {
     masterInterface.PrepareWrite();
     foreach (byte sourceAddress in addressMap.Keys)
     {
         _masterMap[sourceAddress] = new GatewayMaster(masterInterface, (byte)addressMap[sourceAddress], timeout);
     }
 }
예제 #13
0
 /// <summary>
 /// Adds an additional master interface with an custom device id mapping
 /// </summary>
 /// <param name="masterInterface">master interface to add</param>
 /// <param name="timeout">Timeout for master interface</param>
 /// <param name="addressMap">A hashtable where the key is the incoming device id that is mapped to it's value as device id on the master interface.
 /// Both, key and value of the hashtable must be of type byte.</param>
 public void AddMaster(IModbusInterface masterInterface, int timeout, Hashtable addressMap)
 {
    masterInterface.PrepareWrite();
    foreach (byte sourceAddress in addressMap.Keys)
    {
       _masterMap[sourceAddress] = new GatewayMaster(masterInterface, (byte)addressMap[sourceAddress], timeout);
    }
 }
예제 #14
0
        private short SendReceive(IModbusInterface masterInterface, byte[] buffer, byte deviceAddress, ModbusFunctionCode fc, int timeout, short telegramLength, short desiredDataLength,
                                  object telegramContext, ref short dataPos)
        {
            lock (masterInterface)
            {
                masterInterface.SendTelegram(buffer, telegramLength);

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

                try
                {
                    masterInterface.PrepareRead();

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

                    while (timeout > 0)
                    {
                        var ts = DateTime.Now.Ticks;
                        if (!masterInterface.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 (masterInterface.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
                {
                    masterInterface.PrepareWrite();
                }
            }
        }
예제 #15
0
 /// <summary>
 /// Adds an additional master interface with a 1:1 device id mapping
 /// </summary>
 /// <param name="masterInterface">Master interface to add</param>
 /// <param name="timeout">Timeout for master interface</param>
 public void AddMaster(IModbusInterface masterInterface, int timeout)
 {
    var addressMap = new Hashtable();
    for (byte n = 1; n < 248; ++n)
    {
       addressMap.Add(n, n);
    }
    AddMaster(masterInterface, timeout, addressMap);
 }
예제 #16
0
 /// <summary>
 /// Removes an interface.
 /// </summary>
 /// <param name="intf">Interface to remove.</param>
 public void RemoveInterface(IModbusInterface intf)
 {
     lock (this.syncObject) {
         this.interfaces.Remove(intf);
         if (this.interfaces.Count == 0)
         {
             this.Buffer1 = null;
         }
     }
 }
예제 #17
0
        /// <summary>
        /// Adds an additional master interface with a 1:1 device id mapping
        /// </summary>
        /// <param name="masterInterface">Master interface to add</param>
        /// <param name="timeout">Timeout for master interface</param>
        public void AddMaster(IModbusInterface masterInterface, int timeout)
        {
            var addressMap = new Hashtable();

            for (byte n = 1; n < 248; ++n)
            {
                addressMap.Add(n, n);
            }
            AddMaster(masterInterface, timeout, addressMap);
        }
예제 #18
0
 /// <summary>
 /// Removes an interface.
 /// </summary>
 /// <param name="intf">Interface to remove.</param>
 public void RemoveInterface(IModbusInterface intf)
 {
    lock (_syncObject)
    {
       _interfaces.Remove(intf);
       if (_interfaces.Count == 0)
       {
          _buffer = null;
       }
    }
 }
예제 #19
0
 /// <summary>
 /// Adds an additional interface to be polled for incoming messages.
 /// </summary>
 /// <param name="intf">Interface to add.</param>
 public void AddInterface(IModbusInterface intf)
 {
     lock (this.syncObject) {
         if (this.Buffer1 == null || intf.MaxTelegramLength > this.Buffer1.Length)
         {
             this.Buffer1 = new byte[intf.MaxTelegramLength];
         }
         intf.PrepareRead();
         this.interfaces.Add(intf);
     }
 }
예제 #20
0
 /// <summary>
 /// Removes an interface.
 /// </summary>
 /// <param name="intf">Interface to remove.</param>
 public void RemoveInterface(IModbusInterface intf)
 {
     lock (_syncObject)
     {
         _interfaces.Remove(intf);
         if (_interfaces.Count == 0)
         {
             _buffer = null;
         }
     }
 }
예제 #21
0
 /// <summary>
 /// Adds an additional interface to be polled for incoming messages.
 /// </summary>
 /// <param name="intf">Interface to add.</param>
 public void AddInterface(IModbusInterface intf)
 {
     lock (_syncObject)
     {
         if (_buffer == null || intf.MaxTelegramLength > _buffer.Length)
         {
             _buffer = new byte[intf.MaxTelegramLength];
         }
         intf.PrepareRead();
         _interfaces.Add(intf);
     }
 }
예제 #22
0
 /// <summary>
 /// Adds an additional interface to be polled for incoming messages.
 /// </summary>
 /// <param name="intf">Interface to add.</param>
 public void AddInterface(IModbusInterface intf)
 {
    lock (_syncObject)
    {
       if (_buffer == null || intf.MaxTelegramLength > _buffer.Length)
       {
          _buffer = new byte[intf.MaxTelegramLength];
       }
       intf.PrepareRead();
       _interfaces.Add(intf);
    }
 }
예제 #23
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);
        }
예제 #24
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);
         }
     }
 }
예제 #25
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);
                }
            }
        }
예제 #26
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);
       }
    }
 }
예제 #27
0
 public ModbusSlave(IModbusInterface intf, byte ModbusID, object syncObject = null)
     : base(intf, ModbusID, syncObject)
 {
     this.onCoilsChanged  = this.OnCoilsChanged;
     this.onInputsChanged = this.OnInputsChanged;
 }
예제 #28
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);
            }
         }
      }
예제 #29
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);
            }
         }
      }
예제 #30
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)
 { }
예제 #31
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);
            }
         }
      }
예제 #32
0
 /// <summary>
 /// Creates a new Modbus Gateway with a single master interface and an 1:1 device id mapping
 /// </summary>
 /// <param name="deviceAddress">Address of this device</param>
 /// <param name="masterInterface">Master interface to add</param>
 /// <param name="timeout">Timeout for master interface</param>
 public ModbusGateway(byte deviceAddress, IModbusInterface masterInterface, int timeout) :
    this(deviceAddress)
 {
    AddMaster(masterInterface, timeout);
 }
예제 #33
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);
 }
 public MyModbusDevice(IModbusInterface intf, byte deviceAddress, object syncObject = null)
     : base(intf, deviceAddress, syncObject)
 {
 }
예제 #35
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);
 }
예제 #36
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;
 }
예제 #37
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);
            }
        }
예제 #38
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);
         }
      }
예제 #39
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)
 {
 }
예제 #40
0
      private short SendReceive(IModbusInterface masterInterface, byte[] buffer, byte deviceAddress, ModbusFunctionCode fc, int timeout, short telegramLength, short desiredDataLength,
                                          object telegramContext, ref short dataPos)
      {
         lock (masterInterface)
         {
            masterInterface.SendTelegram(buffer, telegramLength);

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

            try
            {
               masterInterface.PrepareRead();

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

               while (timeout > 0)
               {
                  var ts = DateTime.Now.Ticks;
                  if (!masterInterface.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 (masterInterface.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
            {
               masterInterface.PrepareWrite();
            }
         }
      }
예제 #41
0
 /// <summary>
 /// Creates a new Modbus Gateway with a single master interface and an 1:1 device id mapping
 /// </summary>
 /// <param name="deviceAddress">Address of this device</param>
 /// <param name="masterInterface">Master interface to add</param>
 /// <param name="timeout">Timeout for master interface</param>
 public ModbusGateway(byte deviceAddress, IModbusInterface masterInterface, int timeout) :
     this(deviceAddress)
 {
     AddMaster(masterInterface, timeout);
 }
예제 #42
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);
         }
      }
예제 #43
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);
            }
         }
      }
예제 #44
0
 public GatewayMaster(IModbusInterface masterInterface, byte targetAddress, int timeout)
 {
    MasterInterface = masterInterface;
    TargetAddress = targetAddress;
    Timeout = timeout;
    Buffer = new byte[masterInterface.MaxTelegramLength];
 }
예제 #45
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);
         }
      }