WriteHandler Interface
The WriteHandler
interface allows you to process write requests in your application code. Each callback receives a Database
object on which changes can be applied. Generally, when a value is written, it should be reflected back in
the internal database.
In Rust, the WriteHandler
does not exist, it is merged with the RequestHandler
trait.
The callback functions should never block as they are being made from a Runtime
thread.
Return value
The return value of each callback function determines what is returned by the server. Here are the most common use cases:
- The received index and value are valid, the database is updated and a success value is returned.
- If the requested index doesn't exist, then an
ILLEGAL_DATA_ADDRESS
Modbus exception should be returned. - If the requested value is not allowed for the point, then a
ILLEGAL_DATA_VALUE
Modbus exception should be returned.
The library automatically responds to improperly formatted requests such as ones containing invalid start/count combinations. Such invalid requests are never forwarded to your application code.
To return an exception, the WriteResult
value is used. It has three static methods to help build results. The success_init
methods is used to create a success result. The exception_init
is used to write a standard Modbus exception. The raw_exception_init
is used to create a non-standard Modbus exception.
Example
- Rust
- C
- C++
- Java
- C#
impl RequestHandler for SimpleHandler {
fn read_coil(&self, address: u16) -> Result<bool, ExceptionCode> {
self.coils.get(address as usize).to_result()
}
fn read_discrete_input(&self, address: u16) -> Result<bool, ExceptionCode> {
self.discrete_inputs.get(address as usize).to_result()
}
fn read_holding_register(&self, address: u16) -> Result<u16, ExceptionCode> {
self.holding_registers.get(address as usize).to_result()
}
fn read_input_register(&self, address: u16) -> Result<u16, ExceptionCode> {
self.input_registers.get(address as usize).to_result()
}
fn write_single_coil(&mut self, value: Indexed<bool>) -> Result<(), ExceptionCode> {
tracing::info!(
"write single coil, index: {} value: {}",
value.index,
value.value
);
if let Some(coil) = self.coils.get_mut(value.index as usize) {
*coil = value.value;
Ok(())
} else {
Err(ExceptionCode::IllegalDataAddress)
}
}
fn write_single_register(&mut self, value: Indexed<u16>) -> Result<(), ExceptionCode> {
tracing::info!(
"write single register, index: {} value: {}",
value.index,
value.value
);
if let Some(reg) = self.holding_registers.get_mut(value.index as usize) {
*reg = value.value;
Ok(())
} else {
Err(ExceptionCode::IllegalDataAddress)
}
}
fn write_multiple_coils(&mut self, values: WriteCoils) -> Result<(), ExceptionCode> {
tracing::info!("write multiple coils {:?}", values.range);
let mut result = Ok(());
for value in values.iterator {
if let Some(coil) = self.coils.get_mut(value.index as usize) {
*coil = value.value;
} else {
result = Err(ExceptionCode::IllegalDataAddress)
}
}
result
}
fn write_multiple_registers(&mut self, values: WriteRegisters) -> Result<(), ExceptionCode> {
tracing::info!("write multiple registers {:?}", values.range);
let mut result = Ok(());
for value in values.iterator {
if let Some(reg) = self.holding_registers.get_mut(value.index as usize) {
*reg = value.value;
} else {
result = Err(ExceptionCode::IllegalDataAddress)
}
}
result
}
}
rodbus_write_result_t on_write_single_coil(uint16_t index, bool value, rodbus_database_t *db, void *ctx)
{
if (rodbus_database_update_coil(db, index, value)) {
return rodbus_write_result_success_init();
}
else {
return rodbus_write_result_exception_init(RODBUS_MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS);
}
}
rodbus_write_result_t on_write_single_register(uint16_t index, uint16_t value, rodbus_database_t *db, void *ctx)
{
if (rodbus_database_update_holding_register(db, index, value)) {
return rodbus_write_result_success_init();
}
else {
return rodbus_write_result_exception_init(RODBUS_MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS);
}
}
rodbus_write_result_t on_write_multiple_coils(uint16_t start, rodbus_bit_value_iterator_t *it, rodbus_database_t *db, void *ctx)
{
rodbus_write_result_t result = rodbus_write_result_success_init();
rodbus_bit_value_t *bit = NULL;
while ((bit = rodbus_bit_value_iterator_next(it))) {
if (!rodbus_database_update_coil(db, bit->index, bit->value)) {
result = rodbus_write_result_exception_init(RODBUS_MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS);
}
}
return result;
}
rodbus_write_result_t on_write_multiple_registers(uint16_t start, rodbus_register_value_iterator_t *it, rodbus_database_t *db, void *ctx)
{
rodbus_write_result_t result = rodbus_write_result_success_init();
rodbus_register_value_t *reg = NULL;
while ((reg = rodbus_register_value_iterator_next(it))) {
if (!rodbus_database_update_holding_register(db, reg->index, reg->value)) {
result = rodbus_write_result_exception_init(RODBUS_MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS);
}
}
return result;
}
class WriterHandler : public rodbus::WriteHandler
{
rodbus::WriteResult write_single_coil(uint16_t index, bool value, rodbus::Database& db) override
{
if(db.update_coil(index, value))
{
return rodbus::WriteResult::success_init();
}
else
{
return rodbus::WriteResult::exception_init(rodbus::ModbusException::illegal_data_address);
}
}
rodbus::WriteResult write_single_register(uint16_t index, uint16_t value, rodbus::Database& db) override
{
if(db.update_holding_register(index, value))
{
return rodbus::WriteResult::success_init();
}
else
{
return rodbus::WriteResult::exception_init(rodbus::ModbusException::illegal_data_address);
}
}
rodbus::WriteResult write_multiple_coils(uint16_t index, rodbus::BitValueIterator& it, rodbus::Database& db) override
{
auto result = rodbus::WriteResult::success_init();
while (it.next())
{
const auto bit = it.get();
if (!db.update_coil(bit.index, bit.value))
{
result = rodbus::WriteResult::exception_init(rodbus::ModbusException::illegal_data_address);
}
}
return result;
}
rodbus::WriteResult write_multiple_registers(uint16_t index, rodbus::RegisterValueIterator& it, rodbus::Database& db) override
{
auto result = rodbus::WriteResult::success_init();
while (it.next())
{
const auto register_value = it.get();
if (!db.update_holding_register(register_value.index, register_value.value))
{
result = rodbus::WriteResult::exception_init(rodbus::ModbusException::illegal_data_address);
}
}
return result;
}
};
class ExampleWriteHandler implements WriteHandler {
@Override
public WriteResult writeSingleCoil(UShort index, boolean value, Database database) {
if(database.updateCoil(index, value)) {
return WriteResult.successInit();
} else {
return WriteResult.exceptionInit(ModbusException.ILLEGAL_DATA_ADDRESS);
}
}
@Override
public WriteResult writeSingleRegister(UShort index, UShort value, Database database) {
if(database.updateHoldingRegister(index, value)) {
return WriteResult.successInit();
} else {
return WriteResult.exceptionInit(ModbusException.ILLEGAL_DATA_ADDRESS);
}
}
@Override
public WriteResult writeMultipleCoils(UShort start, List<BitValue> it, Database database) {
WriteResult result = WriteResult.successInit();
for(BitValue bit : it) {
if(!database.updateCoil(bit.index, bit.value)) {
result = WriteResult.exceptionInit(ModbusException.ILLEGAL_DATA_ADDRESS);
}
}
return result;
}
@Override
public WriteResult writeMultipleRegisters(UShort start, List<RegisterValue> it, Database database) {
WriteResult result = WriteResult.successInit();
for(RegisterValue reg : it) {
if(!database.updateHoldingRegister(reg.index, reg.value)) {
result = WriteResult.exceptionInit(ModbusException.ILLEGAL_DATA_ADDRESS);
}
}
return result;
}
}
class WriteHandler : IWriteHandler
{
public WriteResult WriteSingleCoil(ushort index, bool value, Database database)
{
if (database.UpdateCoil(index, value))
{
return WriteResult.SuccessInit();
}
else
{
return WriteResult.ExceptionInit(rodbus.ModbusException.IllegalDataAddress);
}
}
public WriteResult WriteSingleRegister(ushort index, ushort value, Database database)
{
if (database.UpdateHoldingRegister(index, value))
{
return WriteResult.SuccessInit();
}
else
{
return WriteResult.ExceptionInit(rodbus.ModbusException.IllegalDataAddress);
}
}
public WriteResult WriteMultipleCoils(ushort start, ICollection<BitValue> it, Database database)
{
var result = WriteResult.SuccessInit();
foreach (var bit in it)
{
if (!database.UpdateCoil(bit.Index, bit.Value))
{
result = WriteResult.ExceptionInit(rodbus.ModbusException.IllegalDataAddress);
}
}
return result;
}
public WriteResult WriteMultipleRegisters(ushort start, ICollection<RegisterValue> it, Database database)
{
var result = WriteResult.SuccessInit();
foreach (var bit in it)
{
if (!database.UpdateHoldingRegister(bit.Index, bit.Value))
{
result = WriteResult.ExceptionInit(rodbus.ModbusException.IllegalDataAddress);
}
}
return result;
}
}