pub trait DataStore: Send + Sync {
Show 28 methods
// Required methods
fn read_coils(
&self,
address: u16,
quantity: u16,
buf: &mut [bool],
) -> impl Future<Output = Result<usize, ExceptionCode>> + Send;
fn write_coil(
&self,
address: u16,
value: bool,
) -> impl Future<Output = Result<(), ExceptionCode>> + Send;
fn write_coils(
&self,
address: u16,
values: &[bool],
) -> impl Future<Output = Result<(), ExceptionCode>> + Send;
fn read_discrete_inputs(
&self,
address: u16,
quantity: u16,
buf: &mut [bool],
) -> impl Future<Output = Result<usize, ExceptionCode>> + Send;
fn read_holding_registers(
&self,
address: u16,
quantity: u16,
buf: &mut [u16],
) -> impl Future<Output = Result<usize, ExceptionCode>> + Send;
fn write_register(
&self,
address: u16,
value: u16,
) -> impl Future<Output = Result<(), ExceptionCode>> + Send;
fn write_registers(
&self,
address: u16,
values: &[u16],
) -> impl Future<Output = Result<(), ExceptionCode>> + Send;
fn read_input_registers(
&self,
address: u16,
quantity: u16,
buf: &mut [u16],
) -> impl Future<Output = Result<usize, ExceptionCode>> + Send;
// Provided methods
fn read_coils_packed(
&self,
address: u16,
quantity: u16,
out: &mut [u8],
) -> impl Future<Output = Result<usize, ExceptionCode>> + Send { ... }
fn write_coils_packed(
&self,
address: u16,
quantity: u16,
packed_values: &[u8],
) -> impl Future<Output = Result<(), ExceptionCode>> + Send { ... }
fn read_discrete_inputs_packed(
&self,
address: u16,
quantity: u16,
out: &mut [u8],
) -> impl Future<Output = Result<usize, ExceptionCode>> + Send { ... }
fn read_holding_registers_be(
&self,
address: u16,
quantity: u16,
out: &mut [u8],
) -> impl Future<Output = Result<usize, ExceptionCode>> + Send { ... }
fn write_registers_be(
&self,
address: u16,
quantity: u16,
value_bytes: &[u8],
) -> impl Future<Output = Result<(), ExceptionCode>> + Send { ... }
fn read_input_registers_be(
&self,
address: u16,
quantity: u16,
out: &mut [u8],
) -> impl Future<Output = Result<usize, ExceptionCode>> + Send { ... }
fn read_file_record(
&self,
file_number: u16,
record_number: u16,
record_length: u16,
buf: &mut [u16],
) -> impl Future<Output = Result<usize, ExceptionCode>> + Send { ... }
fn read_file_record_be(
&self,
file_number: u16,
record_number: u16,
record_length: u16,
out: &mut [u8],
) -> impl Future<Output = Result<usize, ExceptionCode>> + Send { ... }
fn write_file_record(
&self,
file_number: u16,
record_number: u16,
values: &[u16],
) -> impl Future<Output = Result<(), ExceptionCode>> + Send { ... }
fn write_file_record_be(
&self,
file_number: u16,
record_number: u16,
record_length: u16,
value_bytes: &[u8],
) -> impl Future<Output = Result<(), ExceptionCode>> + Send { ... }
fn read_fifo_queue(
&self,
address: u16,
) -> impl Future<Output = Result<Vec<u16>, ExceptionCode>> + Send { ... }
fn read_fifo_queue_be(
&self,
address: u16,
out: &mut [u8],
) -> impl Future<Output = Result<usize, ExceptionCode>> + Send { ... }
fn read_exception_status(
&self,
) -> impl Future<Output = Result<u8, ExceptionCode>> + Send { ... }
fn get_comm_event_counter(
&self,
) -> impl Future<Output = Result<(u16, u16), ExceptionCode>> + Send { ... }
fn get_comm_event_log(
&self,
) -> impl Future<Output = Result<CommEventLog, ExceptionCode>> + Send { ... }
fn append_comm_event_log(
&self,
out: &mut Vec<u8>,
) -> impl Future<Output = Result<CommEventLogMeta, ExceptionCode>> + Send { ... }
fn report_server_id(
&self,
) -> impl Future<Output = Result<Vec<u8>, ExceptionCode>> + Send { ... }
fn append_server_id(
&self,
out: &mut Vec<u8>,
) -> impl Future<Output = Result<usize, ExceptionCode>> + Send { ... }
fn diagnostic(
&self,
sub_function: DiagnosticSubFunction,
data: &[u8],
) -> impl Future<Output = Result<Option<Vec<u8>>, ExceptionCode>> + Send { ... }
fn append_diagnostic_response(
&self,
sub_function: DiagnosticSubFunction,
data: &[u8],
out: &mut Vec<u8>,
) -> impl Future<Output = Result<Option<usize>, ExceptionCode>> + Send { ... }
}Expand description
Async trait abstracting the four Modbus data tables (Spec V1.1b3 §4.3).
All methods are async to support database-backed and remote-proxied stores.
Return types use impl Future<...> + Send to ensure compatibility with
tokio::spawn in the server runtime.
Read methods take &mut [T] buffers to avoid heap allocation per request.
The eight methods covering the four core data tables (coils, discrete inputs, holding/input registers) are required. The remaining methods — file records, FIFO queues, and the serial-line diagnostics family — are optional: each has a default body returning the spec-correct exception for an unsupported capability, so existing implementations keep compiling and only override the capabilities they actually serve.
Required Methods§
Sourcefn read_coils(
&self,
address: u16,
quantity: u16,
buf: &mut [bool],
) -> impl Future<Output = Result<usize, ExceptionCode>> + Send
fn read_coils( &self, address: u16, quantity: u16, buf: &mut [bool], ) -> impl Future<Output = Result<usize, ExceptionCode>> + Send
Read coil statuses into buf. Returns number of coils written.
§Errors
Returns IllegalDataAddress if address + quantity exceeds the address space.
Sourcefn write_coil(
&self,
address: u16,
value: bool,
) -> impl Future<Output = Result<(), ExceptionCode>> + Send
fn write_coil( &self, address: u16, value: bool, ) -> impl Future<Output = Result<(), ExceptionCode>> + Send
Write a single coil.
Sourcefn write_coils(
&self,
address: u16,
values: &[bool],
) -> impl Future<Output = Result<(), ExceptionCode>> + Send
fn write_coils( &self, address: u16, values: &[bool], ) -> impl Future<Output = Result<(), ExceptionCode>> + Send
Write multiple coils.
Sourcefn read_discrete_inputs(
&self,
address: u16,
quantity: u16,
buf: &mut [bool],
) -> impl Future<Output = Result<usize, ExceptionCode>> + Send
fn read_discrete_inputs( &self, address: u16, quantity: u16, buf: &mut [bool], ) -> impl Future<Output = Result<usize, ExceptionCode>> + Send
Read discrete input statuses into buf.
Sourcefn read_holding_registers(
&self,
address: u16,
quantity: u16,
buf: &mut [u16],
) -> impl Future<Output = Result<usize, ExceptionCode>> + Send
fn read_holding_registers( &self, address: u16, quantity: u16, buf: &mut [u16], ) -> impl Future<Output = Result<usize, ExceptionCode>> + Send
Read holding registers into buf. Returns number of registers written.
Sourcefn write_register(
&self,
address: u16,
value: u16,
) -> impl Future<Output = Result<(), ExceptionCode>> + Send
fn write_register( &self, address: u16, value: u16, ) -> impl Future<Output = Result<(), ExceptionCode>> + Send
Write a single holding register.
Sourcefn write_registers(
&self,
address: u16,
values: &[u16],
) -> impl Future<Output = Result<(), ExceptionCode>> + Send
fn write_registers( &self, address: u16, values: &[u16], ) -> impl Future<Output = Result<(), ExceptionCode>> + Send
Write multiple holding registers.
Sourcefn read_input_registers(
&self,
address: u16,
quantity: u16,
buf: &mut [u16],
) -> impl Future<Output = Result<usize, ExceptionCode>> + Send
fn read_input_registers( &self, address: u16, quantity: u16, buf: &mut [u16], ) -> impl Future<Output = Result<usize, ExceptionCode>> + Send
Read input registers into buf.
Provided Methods§
Sourcefn read_coils_packed(
&self,
address: u16,
quantity: u16,
out: &mut [u8],
) -> impl Future<Output = Result<usize, ExceptionCode>> + Send
fn read_coils_packed( &self, address: u16, quantity: u16, out: &mut [u8], ) -> impl Future<Output = Result<usize, ExceptionCode>> + Send
Read coil statuses directly into the Modbus packed-bit wire format.
The default implementation delegates to Self::read_coils through a
bounded scratch buffer. Stores with direct table access can override this
method to avoid the intermediate bool slice and response repacking.
Sourcefn write_coils_packed(
&self,
address: u16,
quantity: u16,
packed_values: &[u8],
) -> impl Future<Output = Result<(), ExceptionCode>> + Send
fn write_coils_packed( &self, address: u16, quantity: u16, packed_values: &[u8], ) -> impl Future<Output = Result<(), ExceptionCode>> + Send
Write multiple coils from the Modbus packed-bit wire representation.
The default implementation unpacks into a bounded stack buffer and then
delegates to Self::write_coils. Stores with direct table access can
override this method to avoid the intermediate bool slice entirely.
Sourcefn read_discrete_inputs_packed(
&self,
address: u16,
quantity: u16,
out: &mut [u8],
) -> impl Future<Output = Result<usize, ExceptionCode>> + Send
fn read_discrete_inputs_packed( &self, address: u16, quantity: u16, out: &mut [u8], ) -> impl Future<Output = Result<usize, ExceptionCode>> + Send
Read discrete input statuses directly into the Modbus packed-bit wire format.
The default implementation delegates to Self::read_discrete_inputs
through a bounded scratch buffer. Stores with direct table access can
override this method to avoid the intermediate bool slice.
Sourcefn read_holding_registers_be(
&self,
address: u16,
quantity: u16,
out: &mut [u8],
) -> impl Future<Output = Result<usize, ExceptionCode>> + Send
fn read_holding_registers_be( &self, address: u16, quantity: u16, out: &mut [u8], ) -> impl Future<Output = Result<usize, ExceptionCode>> + Send
Read holding registers directly into big-endian Modbus wire bytes.
The default implementation delegates to Self::read_holding_registers
through a bounded scratch buffer. Stores with direct table access can
override this method to avoid the intermediate register slice and
response encoding pass.
Sourcefn write_registers_be(
&self,
address: u16,
quantity: u16,
value_bytes: &[u8],
) -> impl Future<Output = Result<(), ExceptionCode>> + Send
fn write_registers_be( &self, address: u16, quantity: u16, value_bytes: &[u8], ) -> impl Future<Output = Result<(), ExceptionCode>> + Send
Write multiple holding registers from big-endian Modbus wire bytes.
The default implementation unpacks into a bounded stack buffer and then
delegates to Self::write_registers. Stores with direct table access
can override this method to avoid the intermediate register slice.
Sourcefn read_input_registers_be(
&self,
address: u16,
quantity: u16,
out: &mut [u8],
) -> impl Future<Output = Result<usize, ExceptionCode>> + Send
fn read_input_registers_be( &self, address: u16, quantity: u16, out: &mut [u8], ) -> impl Future<Output = Result<usize, ExceptionCode>> + Send
Read input registers directly into big-endian Modbus wire bytes.
The default implementation delegates to Self::read_input_registers
through a bounded scratch buffer. Stores with direct table access can
override this method to avoid the intermediate register slice.
Sourcefn read_file_record(
&self,
file_number: u16,
record_number: u16,
record_length: u16,
buf: &mut [u16],
) -> impl Future<Output = Result<usize, ExceptionCode>> + Send
fn read_file_record( &self, file_number: u16, record_number: u16, record_length: u16, buf: &mut [u16], ) -> impl Future<Output = Result<usize, ExceptionCode>> + Send
Read one file sub-record (record_length registers from record_number
in file file_number) into buf; returns the number of registers
written (Spec V1.1b3 §6.14).
The default returns ExceptionCode::IllegalFunction — a store that
does not maintain file records reports 0x01 for FC 0x14.
Sourcefn read_file_record_be(
&self,
file_number: u16,
record_number: u16,
record_length: u16,
out: &mut [u8],
) -> impl Future<Output = Result<usize, ExceptionCode>> + Send
fn read_file_record_be( &self, file_number: u16, record_number: u16, record_length: u16, out: &mut [u8], ) -> impl Future<Output = Result<usize, ExceptionCode>> + Send
Read one file sub-record directly into big-endian Modbus wire bytes.
The default implementation delegates to Self::read_file_record with
a bounded scratch buffer and then packs that scratch into out. Stores
with direct file access can override this method to avoid the
intermediate register slice.
Sourcefn write_file_record(
&self,
file_number: u16,
record_number: u16,
values: &[u16],
) -> impl Future<Output = Result<(), ExceptionCode>> + Send
fn write_file_record( &self, file_number: u16, record_number: u16, values: &[u16], ) -> impl Future<Output = Result<(), ExceptionCode>> + Send
Write values to record_number in file file_number (Spec V1.1b3 §6.15).
The default returns ExceptionCode::IllegalFunction.
Sourcefn write_file_record_be(
&self,
file_number: u16,
record_number: u16,
record_length: u16,
value_bytes: &[u8],
) -> impl Future<Output = Result<(), ExceptionCode>> + Send
fn write_file_record_be( &self, file_number: u16, record_number: u16, record_length: u16, value_bytes: &[u8], ) -> impl Future<Output = Result<(), ExceptionCode>> + Send
Write one file sub-record from big-endian Modbus wire bytes.
The default implementation unpacks the wire bytes and delegates to
Self::write_file_record. Stores with direct file access can override
this method to avoid the intermediate register vector.
Sourcefn read_fifo_queue(
&self,
address: u16,
) -> impl Future<Output = Result<Vec<u16>, ExceptionCode>> + Send
fn read_fifo_queue( &self, address: u16, ) -> impl Future<Output = Result<Vec<u16>, ExceptionCode>> + Send
Return a non-destructive snapshot of the FIFO queue whose pointer is at
address (at most 31 values; Spec V1.1b3 §6.18 — reading the queue MUST
NOT drain it).
The default returns ExceptionCode::IllegalDataAddress — there is no
FIFO at the requested address (Figure 28).
Sourcefn read_fifo_queue_be(
&self,
address: u16,
out: &mut [u8],
) -> impl Future<Output = Result<usize, ExceptionCode>> + Send
fn read_fifo_queue_be( &self, address: u16, out: &mut [u8], ) -> impl Future<Output = Result<usize, ExceptionCode>> + Send
Read a FIFO queue snapshot directly into big-endian Modbus wire bytes.
The default implementation delegates to Self::read_fifo_queue and
packs the returned values. Stores with direct queue access can override
this method to avoid cloning the queue and allocating an intermediate
byte buffer.
Sourcefn read_exception_status(
&self,
) -> impl Future<Output = Result<u8, ExceptionCode>> + Send
fn read_exception_status( &self, ) -> impl Future<Output = Result<u8, ExceptionCode>> + Send
Read the eight device-specific exception-status coils as one byte
(FC 0x07, §6.7). The default returns ExceptionCode::IllegalFunction.
Sourcefn get_comm_event_counter(
&self,
) -> impl Future<Output = Result<(u16, u16), ExceptionCode>> + Send
fn get_comm_event_counter( &self, ) -> impl Future<Output = Result<(u16, u16), ExceptionCode>> + Send
Get the comm event counter as (status, event_count) (FC 0x0B, §6.9).
The default returns ExceptionCode::IllegalFunction.
Sourcefn get_comm_event_log(
&self,
) -> impl Future<Output = Result<CommEventLog, ExceptionCode>> + Send
fn get_comm_event_log( &self, ) -> impl Future<Output = Result<CommEventLog, ExceptionCode>> + Send
Get the communications event log (FC 0x0C, §6.10).
The default returns ExceptionCode::IllegalFunction.
Sourcefn append_comm_event_log(
&self,
out: &mut Vec<u8>,
) -> impl Future<Output = Result<CommEventLogMeta, ExceptionCode>> + Send
fn append_comm_event_log( &self, out: &mut Vec<u8>, ) -> impl Future<Output = Result<CommEventLogMeta, ExceptionCode>> + Send
Append FC 0x0C communication event bytes to out.
The default delegates to Self::get_comm_event_log. Direct-access
stores can override this method to avoid materializing the event list in
an intermediate Vec before response construction.
Sourcefn report_server_id(
&self,
) -> impl Future<Output = Result<Vec<u8>, ExceptionCode>> + Send
fn report_server_id( &self, ) -> impl Future<Output = Result<Vec<u8>, ExceptionCode>> + Send
Report a device-specific server-identification blob (FC 0x11, §6.13).
The returned bytes become the response’s data field (the handler
prepends the byte count). The default returns
ExceptionCode::IllegalFunction.
Sourcefn append_server_id(
&self,
out: &mut Vec<u8>,
) -> impl Future<Output = Result<usize, ExceptionCode>> + Send
fn append_server_id( &self, out: &mut Vec<u8>, ) -> impl Future<Output = Result<usize, ExceptionCode>> + Send
Append the FC 0x11 server-identification data bytes to out.
The default delegates to Self::report_server_id. Direct-access
stores can override this method to avoid cloning the identification blob
before the handler copies it into the final response PDU.
Sourcefn diagnostic(
&self,
sub_function: DiagnosticSubFunction,
data: &[u8],
) -> impl Future<Output = Result<Option<Vec<u8>>, ExceptionCode>> + Send
fn diagnostic( &self, sub_function: DiagnosticSubFunction, data: &[u8], ) -> impl Future<Output = Result<Option<Vec<u8>>, ExceptionCode>> + Send
Execute a Diagnostics sub-function (FC 0x08, §6.8).
Ok(Some(data)) echoes data back in the response; Ok(None)
suppresses the response entirely (per the spec, e.g. Force Listen Only
Mode); Err returns an exception.
The default loops back Return Query Data (0x0000) and reports every other
sub-function as ExceptionCode::IllegalFunction (Figure 18: an
unsupported sub-function is an illegal function, not an illegal data
value).
Sourcefn append_diagnostic_response(
&self,
sub_function: DiagnosticSubFunction,
data: &[u8],
out: &mut Vec<u8>,
) -> impl Future<Output = Result<Option<usize>, ExceptionCode>> + Send
fn append_diagnostic_response( &self, sub_function: DiagnosticSubFunction, data: &[u8], out: &mut Vec<u8>, ) -> impl Future<Output = Result<Option<usize>, ExceptionCode>> + Send
Append Diagnostics response data bytes to out.
The default delegates to Self::diagnostic. Stores that can produce a
response from borrowed request bytes can override this method to avoid an
intermediate owned Vec before the handler builds the final response PDU.
Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".