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); } } }
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); } } }
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); } } }
/// <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); } }
/// <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 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 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); } } }
/// <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> /// Sends a Read Coils (0x01) telegram to a device and waits for the response /// </summary> /// <param name="deviceAddress">Address of the modbus device.</param> /// <param name="startAddress">Start address: 0x0000 .. 0xFFFF</param> /// <param name="coilCount">>Number of coils to read: 1 .. 2000</param> /// <param name="timeout">Timeout for response in Milli seconds.</param> /// <returns>Returns a byte array which contains the coils. The coils are written as single bits into the array starting with coil 1 at the lsb.</returns> /// <remarks> /// Look at http://www.modbus.org/docs/Modbus_Application_Protocol_V1_1b3.pdf for more details. /// </remarks> public virtual byte[] ReadCoils(byte deviceAddress, ushort startAddress, ushort coilCount, int timeout = 2000) { short telegramLength; short dataPos; object telegramContext = null; _Interface.CreateTelegram(deviceAddress, (byte)ModbusFunctionCode.ReadCoils, 4, _Buffer, out telegramLength, out dataPos, false, ref telegramContext); ModbusUtils.InsertUShort(_Buffer, dataPos, startAddress); ModbusUtils.InsertUShort(_Buffer, dataPos + 2, coilCount); short desiredDataLength = (short)(1 + coilCount / 8); if (coilCount % 8 != 0) { ++desiredDataLength; } var dataLength = SendReceive(deviceAddress, ModbusFunctionCode.ReadCoils, timeout, telegramLength, desiredDataLength, telegramContext, ref dataPos); if (deviceAddress == ModbusConst.BroadcastAddress) { return(null); } var coils = new byte[dataLength - 1]; Array.Copy(_Buffer, dataPos + 1, coils, 0, dataLength - 1); // Send data to whom ever interested string displayString = "<-- " + HexEncoding.ToString(_Buffer, 0, dataLength + 4) + "\r\n"; if (NewSerialDataRecieved != null) { NewSerialDataRecieved(this, new ReceivedDataEventArgs(displayString)); } //********************************* return(coils); }
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> /// Sends a Read Coils (0x01) telegram to a device and waits for the response /// </summary> /// <param name="deviceAddress">Address of the modbus device.</param> /// <param name="startAddress">Start address: 0x0000 .. 0xFFFF</param> /// <param name="coilCount">>Number of coils to read: 1 .. 2000</param> /// <param name="timeout">Timeout for response in Milli seconds.</param> /// <returns>Returns a byte array which contains the coils. The coils are written as single bits into the array starting with coil 1 at the lsb.</returns> /// <remarks> /// Look at http://www.modbus.org/docs/Modbus_Application_Protocol_V1_1b3.pdf for more details. /// </remarks> public virtual byte[] ReadCoils(byte deviceAddress, ushort startAddress, ushort coilCount, int timeout = 2000) { short telegramLength; short dataPos; object telegramContext = null; _Interface.CreateTelegram(deviceAddress, (byte)ModbusFunctionCode.ReadCoils, 4, _Buffer, out telegramLength, out dataPos, false, ref telegramContext); ModbusUtils.InsertUShort(_Buffer, dataPos, startAddress); ModbusUtils.InsertUShort(_Buffer, dataPos + 2, coilCount); short desiredDataLength = (short)(1 + coilCount / 8); if (coilCount % 8 != 0) { ++desiredDataLength; } var dataLength = SendReceive(deviceAddress, ModbusFunctionCode.ReadCoils, timeout, telegramLength, desiredDataLength, telegramContext, ref dataPos); if (deviceAddress == ModbusConst.BroadcastAddress) { return(null); } var coils = new byte[dataLength - 1]; Array.Copy(_Buffer, dataPos + 1, coils, 0, dataLength - 1); return(coils); }