Skip to content

Commit

Permalink
Rename types and functions
Browse files Browse the repository at this point in the history
  • Loading branch information
uklotzde committed Oct 13, 2024
1 parent 9d9d1fc commit e0beeb9
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 65 deletions.
17 changes: 8 additions & 9 deletions examples/rtu-client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,28 +21,27 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let builder = tokio_serial::new(SERIAL_PATH, BAUD_RATE);
let transport = SerialStream::open(&builder).unwrap();

let mut connection = rtu::ClientConnection::new(transport);
let mut client = rtu::Client::new(transport);

println!("Reading sensor values (request/response");
println!("Reading sensor values (request/response using the low-level API");
let request = Request::ReadHoldingRegisters(SENSOR_ADDRESS, SENSOR_QUANTITY);
let request_context = connection.send_request(request, SERVER).await?;
let Response::ReadHoldingRegisters(values) =
connection.recv_response(request_context).await??
let request_context = client.send_request(request, SERVER).await?;
let Response::ReadHoldingRegisters(values) = client.recv_response(request_context).await??
else {
// The response variant will always match its corresponding request variant if successful.
unreachable!();
};
println!("Sensor responded with: {values:?}");

println!("Reading sensor values (call");
let mut context = rtu::client_context(connection, SERVER);
let values = context
println!("Reading sensor values (call) using the high-level API");
let mut client_context = client::Context::from(rtu::ClientContext::new(client, SERVER).boxed());
let values = client_context
.read_holding_registers(SENSOR_ADDRESS, SENSOR_QUANTITY)
.await??;
println!("Sensor responded with: {values:?}");

println!("Disconnecting");
context.disconnect().await?;
client_context.disconnect().await?;

Ok(())
}
16 changes: 4 additions & 12 deletions src/client/rtu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
use tokio::io::{AsyncRead, AsyncWrite};

use crate::prelude::rtu::ClientConnection;
use crate::service::rtu::{Client, ClientContext};

use super::*;

Expand All @@ -23,17 +23,9 @@ pub fn attach_slave<T>(transport: T, slave: Slave) -> Context
where
T: AsyncRead + AsyncWrite + Debug + Unpin + Send + 'static,
{
let connection = ClientConnection::new(transport);
client_context(connection, slave)
}

/// Creates a client/server connection.
pub fn client_context<T>(connection: ClientConnection<T>, server: Slave) -> Context
where
T: AsyncRead + AsyncWrite + Debug + Unpin + Send + 'static,
{
let client = crate::service::rtu::Client::new(connection, server);
let client = Client::new(transport);
let context = ClientContext::new(client, slave);
Context {
client: Box::new(client),
client: Box::new(context),
}
}
2 changes: 1 addition & 1 deletion src/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ pub use crate::client;
pub mod rtu {
pub use crate::client::rtu::*;
pub use crate::frame::rtu::RequestContext;
pub use crate::service::rtu::ClientConnection;
pub use crate::service::rtu::{Client, ClientContext};
}

#[allow(missing_docs)]
Expand Down
119 changes: 76 additions & 43 deletions src/service/rtu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ use crate::{

use super::disconnect;

/// Modbus RTU client
/// _Modbus_ RTU client.
#[derive(Debug)]
pub struct ClientConnection<T> {
pub struct Client<T> {
framed: Framed<T, codec::rtu::ClientCodec>,
}

impl<T> ClientConnection<T>
impl<T> Client<T>
where
T: AsyncRead + AsyncWrite + Unpin,
{
Expand Down Expand Up @@ -72,68 +72,93 @@ where

res_adu.try_into_response(request_context)
}
}

fn request_adu<'a, R>(req: R, server: Slave) -> RequestAdu<'a>
where
R: Into<RequestPdu<'a>>,
{
let hdr = Header {
slave_id: server.into(),
};
let pdu = req.into();
RequestAdu { hdr, pdu }
pub async fn call<'a>(&mut self, request: Request<'a>, server: Slave) -> Result<Response> {
let request_context = self.send_request(request, server).await?;
self.recv_response(request_context).await
}
}

/// Modbus RTU client
/// _Modbus_ RTU client with (server) context and connection state.
///
/// Client that invokes methods (request/response) on a single or many (broadcast) server(s).
///
/// The server can be switched between method calls.
#[derive(Debug)]
pub(crate) struct Client<T> {
connection: Option<ClientConnection<T>>,
slave_id: SlaveId,
pub struct ClientContext<T> {
client: Option<Client<T>>,
server: Slave,
}

impl<T> Client<T>
where
T: AsyncRead + AsyncWrite + Unpin,
{
pub(crate) fn new(connection: ClientConnection<T>, slave: Slave) -> Self {
let slave_id = slave.into();
impl<T> ClientContext<T> {
pub fn new(client: Client<T>, server: Slave) -> Self {
Self {
connection: Some(connection),
slave_id,
client: Some(client),
server,
}
}

async fn disconnect(&mut self) -> io::Result<()> {
let Some(connection) = self.connection.take() else {
#[must_use]
pub const fn is_connected(&self) -> bool {
self.client.is_some()
}

#[must_use]
pub const fn server(&self) -> Slave {
self.server
}

pub fn set_server(&mut self, server: Slave) {
self.server = server;
}
}

impl<T> ClientContext<T>
where
T: AsyncWrite + Unpin,
{
pub async fn disconnect(&mut self) -> io::Result<()> {
let Some(client) = self.client.take() else {
// Already disconnected.
return Ok(());
};
disconnect(connection.framed).await
disconnect(client.framed).await
}
}

async fn call(&mut self, request: Request<'_>) -> Result<Response> {
impl<T> ClientContext<T>
where
T: AsyncRead + AsyncWrite + Unpin,
{
pub async fn call(&mut self, request: Request<'_>) -> Result<Response> {
log::debug!("Call {:?}", request);

let Some(connection) = &mut self.connection else {
let Some(client) = &mut self.client else {
return Err(io::Error::new(io::ErrorKind::NotConnected, "disconnected").into());
};

let request_context = connection
.send_request(request, Slave(self.slave_id))
.await?;
connection.recv_response(request_context).await
client.call(request, self.server).await
}
}

impl<T> ClientContext<T>
where
T: AsyncRead + AsyncWrite + Unpin + fmt::Debug + Send + 'static,
{
#[must_use]
pub fn boxed(self) -> Box<dyn crate::client::Client> {
Box::new(self)
}
}

impl<T> SlaveContext for Client<T> {
impl<T> SlaveContext for ClientContext<T> {
fn set_slave(&mut self, slave: Slave) {
self.slave_id = slave.into();
self.set_server(slave);
}
}

#[async_trait::async_trait]
impl<T> crate::client::Client for Client<T>
impl<T> crate::client::Client for ClientContext<T>
where
T: fmt::Debug + AsyncRead + AsyncWrite + Send + Unpin,
{
Expand All @@ -146,6 +171,17 @@ where
}
}

fn request_adu<'a, R>(req: R, server: Slave) -> RequestAdu<'a>
where
R: Into<RequestPdu<'a>>,
{
let hdr = Header {
slave_id: server.into(),
};
let pdu = req.into();
RequestAdu { hdr, pdu }
}

#[cfg(test)]
mod tests {
use core::{
Expand Down Expand Up @@ -190,12 +226,9 @@ mod tests {
#[tokio::test]
async fn handle_broken_pipe() {
let transport = MockTransport;
let connection = ClientConnection::new(transport);
let mut client =
crate::service::rtu::Client::new(connection, crate::service::rtu::Slave::broadcast());
let res = client
.call(crate::service::rtu::Request::ReadCoils(0x00, 5))
.await;
let client = Client::new(transport);
let mut context = ClientContext::new(client, Slave::broadcast());
let res = context.call(Request::ReadCoils(0x00, 5)).await;
assert!(res.is_err());
let err = res.err().unwrap();
assert!(
Expand Down

0 comments on commit e0beeb9

Please sign in to comment.