Skip to main content

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.

note

In Rust, the WriteHandler does not exist, it is merged with the RequestHandler trait.

danger

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

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
}
}