Download as pdf or txt
Download as pdf or txt
You are on page 1of 5

main.

rs Strona 1

use chrono::Local;
use futures::future;
use lazy_static::lazy_static;
use rand::prelude::*;
use std::net::SocketAddr;
use std::sync::RwLock;
use tokio::{signal, time};

use tokio_modbus::prelude::*;
use tokio_modbus::server::{self, Service};

const TIMESTAMP_FORMAT: &str = "%Y-%m-%dT%H:%M:%S.%9f%:z";


struct MbMemory {
holding_registers: RwLock<Vec<u16>>,
input_registers: RwLock<Vec<u16>>,
coils: RwLock<Vec<bool>>,
discrete_inputs: RwLock<Vec<bool>>,
}
struct MbServer;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
lazy_static! {
static ref MEMORY: MbMemory = MbMemory {
holding_registers: RwLock::new(vec![0u16; u16::MAX.into()]),
input_registers: RwLock::new(vec![0u16; u16::MAX.into()]),
coils: RwLock::new(vec![false; u16::MAX.into()]),
discrete_inputs: RwLock::new(vec![false; u16::MAX.into()]),
};
}

async fn memory_context() {
tokio::join!(async {
let mut interval = time::interval(time::Duration::from_millis(8000));
loop {
interval.tick().await;
println!(
"{:<35} [MEM] Generating random data...",
Local::now().format(TIMESTAMP_FORMAT)
);
for v in MEMORY.holding_registers.write().unwrap().iter_mut() {
*v = random::<u16>();
}
for v in MEMORY.input_registers.write().unwrap().iter_mut() {
*v = random::<u16>();
}
for v in MEMORY.coils.write().unwrap().iter_mut() {
*v = random::<bool>();
}
for v in MEMORY.discrete_inputs.write().unwrap().iter_mut() {
*v = random::<bool>();
}
}
});
}

impl Service for MbServer {


type Request = Request;
type Response = Response;
type Error = std::io::Error;
type Future = future::Ready<Result<Self::Response, Self::Error>>;

fn call(&self, req: Self::Request) -> Self::Future {


println!(
"{:<35} [SRV] {:0>4x?}",
Local::now().format(TIMESTAMP_FORMAT),
req
);
match req {
main.rs Strona 2

Request::ReadHoldingRegisters(addr, cnt) => {


let registers = MEMORY.holding_registers.read().unwrap()
[(addr as usize)..((addr + cnt) as usize)]
.to_vec();
future::ready(Ok(Response::ReadHoldingRegisters(registers)))
}
Request::ReadInputRegisters(addr, cnt) => {
let registers = MEMORY.input_registers.read().unwrap()
[(addr as usize)..((addr + cnt) as usize)]
.to_vec();
future::ready(Ok(Response::ReadInputRegisters(registers)))
}
Request::ReadCoils(addr, cnt) => {
let coils = MEMORY.coils.read().unwrap()
[(addr as usize)..((addr + cnt) as usize)]
.to_vec();
future::ready(Ok(Response::ReadCoils(coils)))
}
Request::ReadDiscreteInputs(addr, cnt) => {
let inputs = MEMORY.discrete_inputs.read().unwrap()
[(addr as usize)..((addr + cnt) as usize)]
.to_vec();
future::ready(Ok(Response::ReadDiscreteInputs(inputs)))
}
Request::WriteSingleRegister(addr, word) => {
MEMORY.holding_registers.write().unwrap()[addr as usize] = word;
future::ready(Ok(Response::WriteSingleRegister(addr, word)))
}
Request::WriteMultipleRegisters(addr, words) => {
MEMORY.holding_registers.write().unwrap()
[(addr as usize)..(addr as usize + words.len())]
.copy_from_slice(&words);
future::ready(Ok(Response::WriteMultipleRegisters(
addr,
words.len() as u16,
)))
}
Request::WriteSingleCoil(addr, coil) => {
MEMORY.coils.write().unwrap()[addr as usize] = coil;
future::ready(Ok(Response::WriteSingleCoil(addr, coil)))
}
Request::WriteMultipleCoils(addr, coils) => {
MEMORY.coils.write().unwrap()[(addr as usize)..(addr as usize +
coils.len())]
.copy_from_slice(&coils);
future::ready(Ok(Response::WriteMultipleCoils(addr, coils.len()
as u16)))
}
Request::ReadWriteMultipleRegisters(read_addr, cnt, write_addr, word
s) => {
MEMORY.holding_registers.write().unwrap()
[(write_addr as usize)..(write_addr as usize + words.len())]
.copy_from_slice(&words);
let registers = MEMORY.holding_registers.read().unwrap()
[(read_addr as usize)..((read_addr + cnt) as usize)]
.to_vec();
future::ready(Ok(Response::ReadWriteMultipleRegisters(registers)
))
}
Request::Custom(func, _data) => {
let exception_code_illegal_function = vec![0x01u8];
future::ready(Ok(Response::Custom(
func + 0x80u8,
exception_code_illegal_function,
)))
}
_ => unimplemented!(),
}
}
main.rs Strona 3

async fn server_context(socket_addr: SocketAddr) {


println!(
"{:<35} [SRV] Starting up server...",
Local::now().format(TIMESTAMP_FORMAT)
);
let server = server::tcp::Server::new(socket_addr);
server.serve(|| Ok(MbServer)).await.unwrap();
}

async fn client_context(socket_addr: SocketAddr) {


tokio::join!(async {
// Give the server some time for starting up
tokio::time::sleep(time::Duration::from_secs(1)).await;
let mut interval = time::interval(time::Duration::from_millis(1000));

println!(
"{:<35} [CLI] Connecting to server...",
Local::now().format(TIMESTAMP_FORMAT)
);
let mut ctx = tcp::connect(socket_addr).await.unwrap();

loop {
interval.tick().await;
let timestamp = Local::now().format(TIMESTAMP_FORMAT);

//
// read_holding_registers
//
let addr = 0x0008u16;
let cnt = 7u16;
let response = ctx.read_holding_registers(addr, cnt).await.unwrap();
println!(
"{:<35} [CLI] ReadHoldingRegisters({:0>4x}, {:0>4x}) {:0>4x?}",
timestamp, addr, cnt, response
);
MEMORY.holding_registers.write().unwrap()
[(addr as usize)..(addr as usize + response.len())]
.copy_from_slice(&response);

//
// read_input_registers
//
let addr = 0x0002u16;
let cnt = 7u16;
let response = ctx.read_input_registers(addr, cnt).await.unwrap();
println!(
"{:<35} [CLI] ReadInputRegisters({:0>4x}, {:0>4x}) {:0>4x?}",
timestamp, addr, cnt, response
);
MEMORY.input_registers.write().unwrap()
[(addr as usize)..(addr as usize + response.len())]
.copy_from_slice(&response);

//
// read_coils
//
let addr = 0x0004u16;
let cnt = 7u16;
let response = ctx.read_coils(addr, cnt).await.unwrap();
println!(
"{:<35} [CLI] ReadCoils({:0>4x}, {:0>4x}) {:?}",
timestamp, addr, cnt, response
);
MEMORY.coils.write().unwrap()[(addr as usize)..(addr as usize + resp
onse.len())]
.copy_from_slice(&response);
main.rs Strona 4

//
// read_discrete_inputs
//
let addr = 0x0008u16;
let cnt = 7u16;
let response = ctx.read_discrete_inputs(addr, cnt).await.unwrap();
println!(
"{:<35} [CLI] ReadDiscreteInputs({:0>4x}, {:0>4x}) {:?}",
timestamp, addr, cnt, response
);
MEMORY.discrete_inputs.write().unwrap()
[(addr as usize)..(addr as usize + response.len())]
.copy_from_slice(&response);

//
// write_single_register
//
let addr = 0x0008u16;
let word = MEMORY.holding_registers.read().unwrap()[addr as usize];
let _ = ctx.write_single_register(addr, word).await.unwrap();
println!(
"{:<35} [CLI] WriteSingleRegister({:0>4x}, {:0>4x})",
timestamp, addr, word
);

//
// write_multiple_registers
//
let addr = 0x0008u16;
let cnt = 7u16;
let words = MEMORY.holding_registers.read().unwrap()
[(addr as usize)..(addr + cnt) as usize]
.to_vec();
let _ = ctx.write_multiple_registers(addr, &words).await.unwrap();
println!(
"{:<35} [CLI] WriteMultipleRegisters({:0>4x}, {:0>4x}) {:0>4x?}"
,
timestamp, addr, cnt, words
);

//
// write_single_coil
//
let addr = 0x0008u16;
let coil = MEMORY.coils.read().unwrap()[addr as usize];
let _ = ctx.write_single_coil(addr, coil).await.unwrap();
println!(
"{:<35} [CLI] WriteSingleCoil({:0>4x}, {:?})",
timestamp, addr, coil
);

//
// write_multiple_coils
//
let addr = 0x001fu16;
let cnt = 7u16;
let coils =
MEMORY.coils.read().unwrap()[(addr as usize)..(addr + cnt) as us
ize].to_vec();
let _ = ctx.write_multiple_coils(addr, &coils).await.unwrap();
println!(
"{:<35} [CLI] WriteMultipleCoils({:0>4x}, {:0>4x}) {:?}",
timestamp, addr, cnt, coils
);

//
// read_write_multiple_registers
//
let read_addr = 0x0008u16;
main.rs Strona 5

let read_cnt = 7u16;


let write_addr = 0x0008u16;
let words = MEMORY.holding_registers.read().unwrap()
[(addr as usize)..(addr + cnt) as usize]
.to_vec();
let response = ctx
.read_write_multiple_registers(read_addr, read_cnt, write_addr,
&words)
.await
.unwrap();
println!(
"{:<35} [CLI] ReadWriteMultipleRegisters({:0>4x}, {:0>4x}, {:0>4
x}, {:0>4x?}) {:0>4x?}",
timestamp, read_addr, read_cnt, write_addr, words, response
);
}
});
}

let socket_addr = "127.0.0.1:5502".parse().unwrap();

tokio::select! {
_ = memory_context() => unreachable!(),
_ = server_context(socket_addr) => unreachable!(),
_ = client_context(socket_addr) => unreachable!(),
//_ = client_context(socket_addr) => unreachable!(),
//_ = client_context(socket_addr) => unreachable!(),
//_ = client_context(socket_addr) => unreachable!(),
_ = signal::ctrl_c() => println!("Exiting"),
}

Ok(())
}

You might also like