mod bits;
pub mod memory;
use std::future::Future;
use rusty_modbus_types::{
DiagnosticSubFunction, ExceptionCode, MAX_FIFO_VALUES, MAX_PDU_SIZE, MAX_READ_COILS,
MAX_READ_DISCRETE_INPUTS, MAX_READ_REGISTERS, MAX_WRITE_COILS, MAX_WRITE_REGISTERS,
};
pub(crate) const MAX_FILE_RECORD_REGISTERS: usize = 122;
pub(crate) const MAX_COMM_EVENT_LOG_EVENTS: usize = 64;
pub(crate) const MAX_DIAGNOSTIC_RESPONSE_DATA_LEN: usize = MAX_PDU_SIZE - 3;
pub(crate) const MAX_SERVER_ID_BYTES: usize = MAX_PDU_SIZE - 2;
#[derive(Debug, Clone, Copy, Default)]
pub struct CommEventLogMeta {
pub status: u16,
pub event_count: u16,
pub message_count: u16,
}
#[derive(Debug, Clone, Default)]
pub struct CommEventLog {
pub status: u16,
pub event_count: u16,
pub message_count: u16,
pub events: Vec<u8>,
}
pub(crate) fn unpack_packed_coils(
quantity: u16,
packed_values: &[u8],
out: &mut [bool],
) -> Result<usize, ExceptionCode> {
let quantity = validate_packed_coils(quantity, packed_values)?;
if out.len() < quantity {
return Err(ExceptionCode::IllegalDataValue);
}
for (byte_index, &byte) in packed_values.iter().enumerate() {
let start = byte_index * 8;
let bit_count = (quantity - start).min(8);
for bit in 0..bit_count {
out[start + bit] = (byte >> bit) & 1 == 1;
}
}
Ok(quantity)
}
pub(crate) fn unpack_register_values_be(
quantity: u16,
value_bytes: &[u8],
out: &mut [u16],
) -> Result<usize, ExceptionCode> {
let quantity = validate_register_values_be(quantity, value_bytes)?;
if out.len() < quantity {
return Err(ExceptionCode::IllegalDataValue);
}
for (slot, chunk) in out
.iter_mut()
.zip(value_bytes.chunks_exact(2))
.take(quantity)
{
*slot = u16::from_be_bytes([chunk[0], chunk[1]]);
}
Ok(quantity)
}
pub(crate) fn validate_packed_coils(
quantity: u16,
packed_values: &[u8],
) -> Result<usize, ExceptionCode> {
if quantity == 0 || quantity > MAX_WRITE_COILS {
return Err(ExceptionCode::IllegalDataValue);
}
let expected = usize::from(quantity).div_ceil(8);
if packed_values.len() != expected {
return Err(ExceptionCode::IllegalDataValue);
}
Ok(usize::from(quantity))
}
pub(crate) fn validate_register_values_be(
quantity: u16,
value_bytes: &[u8],
) -> Result<usize, ExceptionCode> {
if quantity == 0 || quantity > MAX_WRITE_REGISTERS {
return Err(ExceptionCode::IllegalDataValue);
}
let expected = usize::from(quantity) * 2;
if value_bytes.len() != expected {
return Err(ExceptionCode::IllegalDataValue);
}
Ok(usize::from(quantity))
}
pub(crate) fn pack_coils(bits: &[bool], out: &mut [u8]) -> Result<(), ExceptionCode> {
let byte_count = bits.len().div_ceil(8);
if out.len() < byte_count {
return Err(ExceptionCode::IllegalDataValue);
}
for (byte_index, out_byte) in out[..byte_count].iter_mut().enumerate() {
let start = byte_index * 8;
let end = (start + 8).min(bits.len());
let mut byte = 0u8;
for (bit, &value) in bits[start..end].iter().enumerate() {
byte |= u8::from(value) << bit;
}
*out_byte = byte;
}
Ok(())
}
pub(crate) fn pack_registers_be(registers: &[u16], out: &mut [u8]) -> Result<(), ExceptionCode> {
let byte_count = registers.len() * 2;
if out.len() < byte_count {
return Err(ExceptionCode::IllegalDataValue);
}
for (chunk, &value) in out[..byte_count].chunks_exact_mut(2).zip(registers) {
chunk.copy_from_slice(&value.to_be_bytes());
}
Ok(())
}
pub trait DataStore: Send + Sync {
fn read_coils(
&self,
address: u16,
quantity: u16,
buf: &mut [bool],
) -> 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 {
async move {
let mut values = [false; MAX_READ_COILS as usize];
let count = self.read_coils(address, quantity, &mut values).await?;
if count > values.len() || count > usize::from(quantity) {
return Err(ExceptionCode::ServerDeviceFailure);
}
pack_coils(&values[..count], out)?;
Ok(count)
}
}
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 write_coils_packed(
&self,
address: u16,
quantity: u16,
packed_values: &[u8],
) -> impl Future<Output = Result<(), ExceptionCode>> + Send {
async move {
let mut values = [false; MAX_WRITE_COILS as usize];
let quantity = unpack_packed_coils(quantity, packed_values, &mut values)?;
self.write_coils(address, &values[..quantity]).await
}
}
fn read_discrete_inputs(
&self,
address: u16,
quantity: u16,
buf: &mut [bool],
) -> 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 {
async move {
let mut values = [false; MAX_READ_DISCRETE_INPUTS as usize];
let count = self
.read_discrete_inputs(address, quantity, &mut values)
.await?;
if count > values.len() || count > usize::from(quantity) {
return Err(ExceptionCode::ServerDeviceFailure);
}
pack_coils(&values[..count], out)?;
Ok(count)
}
}
fn read_holding_registers(
&self,
address: u16,
quantity: u16,
buf: &mut [u16],
) -> 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 {
async move {
let mut values = [0u16; MAX_READ_REGISTERS as usize];
let count = self
.read_holding_registers(address, quantity, &mut values)
.await?;
if count > values.len() || count > usize::from(quantity) {
return Err(ExceptionCode::ServerDeviceFailure);
}
pack_registers_be(&values[..count], out)?;
Ok(count)
}
}
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 write_registers_be(
&self,
address: u16,
quantity: u16,
value_bytes: &[u8],
) -> impl Future<Output = Result<(), ExceptionCode>> + Send {
async move {
let mut values = [0u16; MAX_WRITE_REGISTERS as usize];
let quantity = unpack_register_values_be(quantity, value_bytes, &mut values)?;
self.write_registers(address, &values[..quantity]).await
}
}
fn read_input_registers(
&self,
address: u16,
quantity: u16,
buf: &mut [u16],
) -> 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 {
async move {
let mut values = [0u16; MAX_READ_REGISTERS as usize];
let count = self
.read_input_registers(address, quantity, &mut values)
.await?;
if count > values.len() || count > usize::from(quantity) {
return Err(ExceptionCode::ServerDeviceFailure);
}
pack_registers_be(&values[..count], out)?;
Ok(count)
}
}
fn read_file_record(
&self,
file_number: u16,
record_number: u16,
record_length: u16,
buf: &mut [u16],
) -> impl Future<Output = Result<usize, ExceptionCode>> + Send {
async move {
let _ = (file_number, record_number, record_length, buf);
Err(ExceptionCode::IllegalFunction)
}
}
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 {
async move {
let mut values = [0u16; MAX_FILE_RECORD_REGISTERS];
let count = self
.read_file_record(file_number, record_number, record_length, &mut values)
.await?;
if count > values.len() || count > usize::from(record_length) {
return Err(ExceptionCode::ServerDeviceFailure);
}
pack_registers_be(&values[..count], out)?;
Ok(count)
}
}
fn write_file_record(
&self,
file_number: u16,
record_number: u16,
values: &[u16],
) -> impl Future<Output = Result<(), ExceptionCode>> + Send {
async move {
let _ = (file_number, record_number, values);
Err(ExceptionCode::IllegalFunction)
}
}
fn write_file_record_be(
&self,
file_number: u16,
record_number: u16,
record_length: u16,
value_bytes: &[u8],
) -> impl Future<Output = Result<(), ExceptionCode>> + Send {
async move {
let expected = usize::from(record_length) * 2;
if value_bytes.len() != expected {
return Err(ExceptionCode::IllegalDataValue);
}
let values: Vec<u16> = value_bytes
.chunks_exact(2)
.map(|chunk| u16::from_be_bytes([chunk[0], chunk[1]]))
.collect();
self.write_file_record(file_number, record_number, &values)
.await
}
}
fn read_fifo_queue(
&self,
address: u16,
) -> impl Future<Output = Result<Vec<u16>, ExceptionCode>> + Send {
async move {
let _ = address;
Err(ExceptionCode::IllegalDataAddress)
}
}
fn read_fifo_queue_be(
&self,
address: u16,
out: &mut [u8],
) -> impl Future<Output = Result<usize, ExceptionCode>> + Send {
async move {
let values = self.read_fifo_queue(address).await?;
if values.len() > usize::from(MAX_FIFO_VALUES) {
return Err(ExceptionCode::IllegalDataValue);
}
pack_registers_be(&values, out)?;
Ok(values.len())
}
}
fn read_exception_status(&self) -> impl Future<Output = Result<u8, ExceptionCode>> + Send {
async { Err(ExceptionCode::IllegalFunction) }
}
fn get_comm_event_counter(
&self,
) -> impl Future<Output = Result<(u16, u16), ExceptionCode>> + Send {
async { Err(ExceptionCode::IllegalFunction) }
}
fn get_comm_event_log(
&self,
) -> impl Future<Output = Result<CommEventLog, ExceptionCode>> + Send {
async { Err(ExceptionCode::IllegalFunction) }
}
fn append_comm_event_log(
&self,
out: &mut Vec<u8>,
) -> impl Future<Output = Result<CommEventLogMeta, ExceptionCode>> + Send {
async move {
let log = self.get_comm_event_log().await?;
if log.events.len() > MAX_COMM_EVENT_LOG_EVENTS {
return Err(ExceptionCode::ServerDeviceFailure);
}
out.extend_from_slice(&log.events);
Ok(CommEventLogMeta {
status: log.status,
event_count: log.event_count,
message_count: log.message_count,
})
}
}
fn report_server_id(&self) -> impl Future<Output = Result<Vec<u8>, ExceptionCode>> + Send {
async { Err(ExceptionCode::IllegalFunction) }
}
fn append_server_id(
&self,
out: &mut Vec<u8>,
) -> impl Future<Output = Result<usize, ExceptionCode>> + Send {
async move {
let data = self.report_server_id().await?;
if data.len() > MAX_SERVER_ID_BYTES {
return Err(ExceptionCode::ServerDeviceFailure);
}
out.extend_from_slice(&data);
Ok(data.len())
}
}
fn diagnostic(
&self,
sub_function: DiagnosticSubFunction,
data: &[u8],
) -> impl Future<Output = Result<Option<Vec<u8>>, ExceptionCode>> + Send {
async move {
match sub_function {
DiagnosticSubFunction::ReturnQueryData => Ok(Some(data.to_vec())),
_ => Err(ExceptionCode::IllegalFunction),
}
}
}
fn append_diagnostic_response(
&self,
sub_function: DiagnosticSubFunction,
data: &[u8],
out: &mut Vec<u8>,
) -> impl Future<Output = Result<Option<usize>, ExceptionCode>> + Send {
async move {
let Some(data) = self.diagnostic(sub_function, data).await? else {
return Ok(None);
};
if data.len() > MAX_DIAGNOSTIC_RESPONSE_DATA_LEN {
return Err(ExceptionCode::ServerDeviceFailure);
}
out.extend_from_slice(&data);
Ok(Some(data.len()))
}
}
}