use std::sync::{Arc, RwLock};
use async_trait::async_trait;
use crate::frame::{Exception, Request, Response};
use crate::server::Service;
const DEFAULT_REG_COUNT: usize = 1024;
pub struct SlaveStore {
coils: RwLock<Vec<u8>>,
discrete_inputs: RwLock<Vec<u8>>,
holding_registers: RwLock<Vec<u16>>,
input_registers: RwLock<Vec<u16>>,
}
impl Default for SlaveStore {
fn default() -> Self {
Self {
coils: RwLock::new(vec![0u8; DEFAULT_REG_COUNT / 8]),
discrete_inputs: RwLock::new(vec![0u8; DEFAULT_REG_COUNT / 8]),
holding_registers: RwLock::new(vec![0u16; DEFAULT_REG_COUNT]),
input_registers: RwLock::new(vec![0u16; DEFAULT_REG_COUNT]),
}
}
}
impl SlaveStore {
pub fn new() -> Self {
Self::default()
}
pub fn with_holding_registers(regs: &[(u16, u16)]) -> Self {
let store = Self::new();
let max_addr = regs.iter().map(|&(a, _)| a as usize).max().unwrap_or(0);
{
let mut hr = store
.holding_registers
.write()
.unwrap_or_else(|e| e.into_inner());
if hr.len() <= max_addr {
hr.resize(max_addr + 1, 0);
}
for &(addr, val) in regs {
hr[addr as usize] = val;
}
}
store
}
fn read_bit(lock: &RwLock<Vec<u8>>, addr: u16) -> bool {
let bytes = lock.read().unwrap_or_else(|e| e.into_inner());
let byte_idx = addr as usize / 8;
let bit_idx = addr as usize % 8;
bytes
.get(byte_idx)
.map(|b| (b >> bit_idx) & 1 != 0)
.unwrap_or(false)
}
fn write_bit(lock: &RwLock<Vec<u8>>, addr: u16, value: bool) {
let mut bytes = lock.write().unwrap_or_else(|e| e.into_inner());
let byte_idx = addr as usize / 8;
if byte_idx >= bytes.len() {
bytes.resize(byte_idx + 1, 0);
}
if value {
bytes[byte_idx] |= 1 << (addr as usize % 8);
} else {
bytes[byte_idx] &= !(1 << (addr as usize % 8));
}
}
fn read_bits(lock: &RwLock<Vec<u8>>, addr: u16, count: u16) -> Vec<bool> {
let bytes = lock.read().unwrap_or_else(|e| e.into_inner());
let mut result = Vec::with_capacity(count as usize);
for i in 0..count {
let a = addr as usize + i as usize;
let byte_idx = a / 8;
let bit_idx = a % 8;
result.push(
bytes
.get(byte_idx)
.map(|b| (b >> bit_idx) & 1 != 0)
.unwrap_or(false),
);
}
result
}
fn read_registers(lock: &RwLock<Vec<u16>>, addr: u16, count: u16) -> Vec<u16> {
let regs = lock.read().unwrap_or_else(|e| e.into_inner());
let start = addr as usize;
if start >= regs.len() {
return vec![0u16; count as usize];
}
let end = (start + count as usize).min(regs.len());
let mut result = vec![0u16; count as usize];
result[..end - start].copy_from_slice(®s[start..end]);
result
}
fn write_register(lock: &RwLock<Vec<u16>>, addr: u16, value: u16) {
let mut regs = lock.write().unwrap_or_else(|e| e.into_inner());
if addr as usize >= regs.len() {
regs.resize(addr as usize + 1, 0);
}
regs[addr as usize] = value;
}
pub fn read_coil(&self, addr: u16) -> bool {
Self::read_bit(&self.coils, addr)
}
pub fn write_coil(&self, addr: u16, value: bool) {
Self::write_bit(&self.coils, addr, value);
}
pub fn read_coils_range(&self, addr: u16, count: u16) -> Vec<bool> {
Self::read_bits(&self.coils, addr, count)
}
pub fn read_discrete_input(&self, addr: u16) -> bool {
Self::read_bit(&self.discrete_inputs, addr)
}
pub fn write_discrete_input(&self, addr: u16, value: bool) {
Self::write_bit(&self.discrete_inputs, addr, value);
}
pub fn read_discrete_inputs_range(&self, addr: u16, count: u16) -> Vec<bool> {
Self::read_bits(&self.discrete_inputs, addr, count)
}
pub fn read_holding_register(&self, addr: u16) -> u16 {
let hr = self
.holding_registers
.read()
.unwrap_or_else(|e| e.into_inner());
hr.get(addr as usize).copied().unwrap_or(0)
}
pub fn read_holding_range(&self, addr: u16, count: u16) -> Vec<u16> {
Self::read_registers(&self.holding_registers, addr, count)
}
pub fn write_holding_register(&self, addr: u16, value: u16) {
Self::write_register(&self.holding_registers, addr, value);
}
pub fn read_input_register(&self, addr: u16) -> u16 {
let ir = self
.input_registers
.read()
.unwrap_or_else(|e| e.into_inner());
ir.get(addr as usize).copied().unwrap_or(0)
}
pub fn read_input_range(&self, addr: u16, count: u16) -> Vec<u16> {
Self::read_registers(&self.input_registers, addr, count)
}
pub fn write_input_register(&self, addr: u16, value: u16) {
Self::write_register(&self.input_registers, addr, value);
}
}
#[async_trait]
impl Service for Arc<SlaveStore> {
async fn call(&self, request: Request<'_>) -> Result<Response, Exception> {
let store = self.as_ref();
Ok(match request {
Request::ReadCoils(addr, cnt) => Response::ReadCoils(store.read_coils_range(addr, cnt)),
Request::ReadDiscreteInputs(addr, cnt) => {
Response::ReadDiscreteInputs(store.read_discrete_inputs_range(addr, cnt))
}
Request::ReadHoldingRegisters(addr, cnt) => {
Response::ReadHoldingRegisters(store.read_holding_range(addr, cnt))
}
Request::ReadInputRegisters(addr, cnt) => {
Response::ReadInputRegisters(store.read_input_range(addr, cnt))
}
Request::WriteSingleCoil(addr, value) => {
store.write_coil(addr, value);
Response::WriteSingleCoil(addr, value)
}
Request::WriteSingleRegister(addr, value) => {
store.write_holding_register(addr, value);
Response::WriteSingleRegister(addr, value)
}
Request::WriteMultipleCoils(addr, values) => {
let mut coils = store.coils.write().unwrap_or_else(|e| e.into_inner());
for (i, &v) in values.iter().enumerate() {
let a = addr as usize + i;
let byte_idx = a / 8;
let bit_idx = a % 8;
if byte_idx >= coils.len() {
coils.resize(byte_idx + 1, 0);
}
if v {
coils[byte_idx] |= 1 << bit_idx;
} else {
coils[byte_idx] &= !(1 << bit_idx);
}
}
drop(coils);
Response::WriteMultipleCoils(addr, values.len() as u16)
}
Request::WriteMultipleRegisters(addr, values) => {
let mut hr = store
.holding_registers
.write()
.unwrap_or_else(|e| e.into_inner());
for (i, &v) in values.iter().enumerate() {
let a = addr as usize + i;
if a >= hr.len() {
hr.resize(a + 1, 0);
}
hr[a] = v;
}
drop(hr);
Response::WriteMultipleRegisters(addr, values.len() as u16)
}
Request::MaskWriteRegister(addr, and_mask, or_mask) => {
let mut hr = store
.holding_registers
.write()
.unwrap_or_else(|e| e.into_inner());
let a = addr as usize;
if a >= hr.len() {
hr.resize(a + 1, 0);
}
let current = hr[a];
hr[a] = (current & and_mask) | (or_mask & !and_mask);
Response::MaskWriteRegister(addr, and_mask, or_mask)
}
Request::ReadWriteMultipleRegisters(read_addr, read_qty, write_addr, values) => {
let regs = store.read_holding_range(read_addr, read_qty);
let mut hr = store
.holding_registers
.write()
.unwrap_or_else(|e| e.into_inner());
for (i, &v) in values.iter().enumerate() {
let a = write_addr as usize + i;
if a >= hr.len() {
hr.resize(a + 1, 0);
}
hr[a] = v;
}
drop(hr);
Response::ReadWriteMultipleRegisters(regs)
}
Request::Diagnostic(sf, data) => {
match sf {
0x0000 => Response::Diagnostic(sf, data),
0x000A => Response::Diagnostic(sf, 0x0000),
0x000B..=0x000E => Response::Diagnostic(sf, 0x0000),
_ => return Err(Exception::IllegalDataValue),
}
}
_ => return Err(Exception::IllegalFunction),
})
}
}