Hello,
I got a small program:
use std::{net::SocketAddr, vec, fs::File};
use futures::{future,};
use serde::{Serialize, Deserialize};
use serde_json::json;
use tokio::net::TcpListener;
use tokio_modbus::{
prelude::{*},
server::tcp::{accept_tcp_connection, Server}
};
#[derive(Serialize, Deserialize, Debug)]
struct Config{
rtu_stub: String,
ipv4: String,
port: u16,
default_slave: u8,
}
impl Config{
fn new(rtu_stub:String, ipv4:String, port:u16, default_slave:u8) -> Config{
Config{rtu_stub, ipv4, port, default_slave}
}
fn from_file(path:String) -> Config{
let file = File::open(path).expect("can't read config file");
serde_json::from_reader(file).unwrap_or_default()
}
}
impl Default for Config{
fn default() -> Self {
Self {
rtu_stub: "/usr/bin/modbus/rtu_stub".to_string(),
ipv4: "0.0.0.0".to_string(),
port: 5502,
default_slave: 1
}
}
}
struct ModbusService {}
impl tokio_modbus::server::Service for ModbusService {
type Request = SlaveRequest;
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!("{:?}", &req);
let slave = req.slave;
let req = req.request;
let config = Config::from_file("config.json".to_string());
match req {
Request::ReadHoldingRegisters(addr, cnt) => {
let json = json!({
"slave": slave,
"name": "read_holding_registers",
"args": [addr, cnt]
});
let output = std::process::Command::new("./rtu_stub")
.arg(format!("{}", json.to_string()))
.output()
.expect("can't start rtu_stub");
if output.stderr.len() > 0 {
let err = std::str::from_utf8(&output.stderr).unwrap();
return future::ready(Err(std::io::Error::new(
std::io::ErrorKind::Other,
format!("error on rtu_stub: {}", err.trim())
)));
}
let value = serde_json::from_str(std::str::from_utf8(&output.stdout).unwrap()).unwrap();
future::ready(Ok(Response::ReadHoldingRegisters(value)))
},
_ => {
println!("SERVER: Exception::IllegalFunction - Unimplemented function code in request: {req:?}");
future::ready(Err(std::io::Error::new(
std::io::ErrorKind::AddrNotAvailable,
"Unimplemented function code in request".to_string(),
)))
}
}
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = Config::from_file("config.cfg".to_string());
let socket_addr: SocketAddr = "0.0.0.0:5502".parse().unwrap();
println!("Starting up server on {socket_addr}");
let listener = TcpListener::bind(socket_addr).await.unwrap();
let server = Server::new(listener);
let on_connected = |stream, socket_addr| async move {
let new_service = |_socket_addr| Ok(Some(ModbusService{}));
accept_tcp_connection(stream, socket_addr, new_service)
};
let on_process_error = |err| {
eprintln!("{err}");
};
let rt = tokio::runtime::Runtime::new().unwrap();
rt.spawn( async move {
server.serve(&on_connected, on_process_error).await.unwrap();
});
loop {}
}
I like to get rid of the second call of Config::from_file() in line 58.
I want to send it with ModbusService but it must be a Fn instead of FnOne (106 on_connect and 107 new_service).
How to do it right?