Refactor modbus connection starting
parent
cd3cc6f19c
commit
32cd2b2e78
|
@ -1,88 +1,76 @@
|
||||||
use crate::modbus::{self};
|
use crate::modbus::{self};
|
||||||
|
use crate::Error;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::convert::TryFrom;
|
|
||||||
use tokio::{select, sync::mpsc};
|
use tokio::{select, sync::mpsc};
|
||||||
use tokio_modbus::client::{rtu, tcp, Context as ModbusClient};
|
use tokio_modbus::client::{rtu, tcp, Context as ModbusClient};
|
||||||
use tracing::error;
|
use tracing::{debug, error};
|
||||||
|
|
||||||
use crate::{mqtt, shutdown::Shutdown};
|
use crate::{mqtt, shutdown::Shutdown};
|
||||||
|
|
||||||
// TODO make this into run() and have it spawn the task
|
pub(crate) async fn run(
|
||||||
pub(crate) async fn new(
|
|
||||||
config: Config,
|
config: Config,
|
||||||
mqtt: mqtt::Handle,
|
mqtt: mqtt::Handle,
|
||||||
shutdown: Shutdown,
|
shutdown: Shutdown,
|
||||||
) -> crate::Result<Connection> {
|
) -> crate::Result<Handle> {
|
||||||
let client = match config.settings {
|
let (handle_tx, handle_rx) = tokio::sync::oneshot::channel();
|
||||||
#[cfg(feature = "winet-s")]
|
|
||||||
ModbusProto::SungrowWiNetS { ref host } => {
|
|
||||||
tokio_modbus_winets::connect_slave(host, config.unit).await?
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "tcp")]
|
tokio::spawn(async move {
|
||||||
ModbusProto::Tcp { ref host, port } => {
|
// Can unwrap because if MQTT handler is bad, we have nothing to do here.
|
||||||
let socket_addr = format!("{}:{}", host, port).parse()?;
|
mqtt.publish("state", "connecting").await.unwrap();
|
||||||
tcp::connect_slave(socket_addr, config.unit).await?
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "rtu")]
|
match config.settings.connect(config.unit).await {
|
||||||
ModbusProto::Rtu {
|
Ok(client) => {
|
||||||
ref tty,
|
// Can unwrap because if MQTT handler is bad, we have nothing to do here.
|
||||||
baud_rate,
|
mqtt.publish("state", "connected").await.unwrap();
|
||||||
data_bits,
|
|
||||||
stop_bits,
|
|
||||||
flow_control,
|
|
||||||
parity,
|
|
||||||
} => {
|
|
||||||
let builder = tokio_serial::new(tty, baud_rate)
|
|
||||||
.data_bits(data_bits)
|
|
||||||
.flow_control(flow_control)
|
|
||||||
.parity(parity)
|
|
||||||
.stop_bits(stop_bits);
|
|
||||||
let port = tokio_serial::SerialStream::open(&builder)?;
|
|
||||||
rtu::connect_slave(port, config.unit).await?
|
|
||||||
}
|
|
||||||
|
|
||||||
ModbusProto::Unknown => {
|
|
||||||
error!("Unrecognised protocol");
|
|
||||||
return Err(crate::Error::UnrecognisedModbusProtocol);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
// Create handle and send to caller
|
||||||
let (tx, rx) = mpsc::channel(32);
|
let (tx, rx) = mpsc::channel(32);
|
||||||
|
handle_tx.send(Ok(Handle { tx })).unwrap();
|
||||||
|
|
||||||
Ok(Connection {
|
let conn = Connection {
|
||||||
rx,
|
|
||||||
client,
|
client,
|
||||||
mqtt,
|
mqtt,
|
||||||
shutdown,
|
shutdown,
|
||||||
})
|
rx,
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(error) = conn.run().await {
|
||||||
|
error!(?error, "Modbus connection failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(error) => handle_tx.send(Err(error.into())).unwrap(),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
handle_rx.await.map_err(|_| crate::Error::RecvError)?
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Connection {
|
struct Connection {
|
||||||
client: ModbusClient,
|
client: ModbusClient,
|
||||||
mqtt: mqtt::Handle,
|
mqtt: mqtt::Handle,
|
||||||
shutdown: Shutdown,
|
shutdown: Shutdown,
|
||||||
rx: mpsc::Receiver<Message>,
|
rx: mpsc::Receiver<Message>,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Message {}
|
#[derive(Debug)]
|
||||||
|
pub struct Handle {
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Handler {
|
|
||||||
tx: mpsc::Sender<Message>,
|
tx: mpsc::Sender<Message>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum Message {}
|
||||||
|
|
||||||
impl Connection {
|
impl Connection {
|
||||||
pub async fn run(mut self) -> crate::Result<()> {
|
pub async fn run(mut self) -> crate::Result<()> {
|
||||||
|
loop {
|
||||||
select! {
|
select! {
|
||||||
|
Some(msg) = self.rx.recv() => { debug!(?msg); },
|
||||||
_ = self.shutdown.recv() => {
|
_ = self.shutdown.recv() => {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// pub fn handle(&self) -> Handle {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
|
@ -143,6 +131,48 @@ pub(crate) enum ModbusProto {
|
||||||
Unknown,
|
Unknown,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ModbusProto {
|
||||||
|
// Can we use the "slave context" thing in Modbus to pass the unit later?
|
||||||
|
pub async fn connect(&self, unit: modbus::Unit) -> crate::Result<ModbusClient> {
|
||||||
|
let client = match *self {
|
||||||
|
#[cfg(feature = "winet-s")]
|
||||||
|
ModbusProto::SungrowWiNetS { ref host } => {
|
||||||
|
tokio_modbus_winets::connect_slave(host, unit).await?
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "tcp")]
|
||||||
|
ModbusProto::Tcp { ref host, port } => {
|
||||||
|
let socket_addr = format!("{}:{}", host, port).parse()?;
|
||||||
|
tcp::connect_slave(socket_addr, unit).await?
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "rtu")]
|
||||||
|
ModbusProto::Rtu {
|
||||||
|
ref tty,
|
||||||
|
baud_rate,
|
||||||
|
data_bits,
|
||||||
|
stop_bits,
|
||||||
|
flow_control,
|
||||||
|
parity,
|
||||||
|
} => {
|
||||||
|
let builder = tokio_serial::new(tty, baud_rate)
|
||||||
|
.data_bits(data_bits)
|
||||||
|
.flow_control(flow_control)
|
||||||
|
.parity(parity)
|
||||||
|
.stop_bits(stop_bits);
|
||||||
|
let port = tokio_serial::SerialStream::open(&builder)?;
|
||||||
|
rtu::connect_slave(port, unit).await?
|
||||||
|
}
|
||||||
|
|
||||||
|
ModbusProto::Unknown => {
|
||||||
|
error!("Unrecognised protocol");
|
||||||
|
Err(Error::UnrecognisedModbusProtocol)?
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(client)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn default_modbus_port() -> u16 {
|
pub(crate) fn default_modbus_port() -> u16 {
|
||||||
502
|
502
|
||||||
}
|
}
|
||||||
|
@ -166,11 +196,3 @@ pub(crate) fn default_modbus_flow_control() -> tokio_serial::FlowControl {
|
||||||
pub(crate) fn default_modbus_parity() -> tokio_serial::Parity {
|
pub(crate) fn default_modbus_parity() -> tokio_serial::Parity {
|
||||||
tokio_serial::Parity::None
|
tokio_serial::Parity::None
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<Config> for Connection {
|
|
||||||
type Error = crate::Error;
|
|
||||||
|
|
||||||
fn try_from(_value: Config) -> Result<Self, Self::Error> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -90,32 +90,7 @@ async fn connect(config: Config<'_>, mqtt: mqtt::Handle, shutdown: Shutdown) ->
|
||||||
|
|
||||||
mqtt.publish("state", "connecting").await?;
|
mqtt.publish("state", "connecting").await?;
|
||||||
|
|
||||||
// connection isn't able to be Send, so we have to create connection inside its run task and find out if instantiation failed by connection on a channel.
|
let connection_handler = connection::run(settings, mqtt.clone(), shutdown).await?;
|
||||||
{
|
|
||||||
let mqtt = mqtt.clone();
|
|
||||||
let (tx, rx) = tokio::sync::oneshot::channel::<crate::Result<()>>();
|
|
||||||
tokio::spawn(async move {
|
|
||||||
match connection::new(settings, mqtt.clone(), shutdown).await {
|
|
||||||
Ok(connection) => {
|
|
||||||
if let Err(e) = mqtt.publish("state", "connected").await {
|
|
||||||
tx.send(Err(e))
|
|
||||||
} else {
|
|
||||||
tx.send(Ok(()))
|
|
||||||
}
|
|
||||||
.expect("unexpected closed receiver");
|
|
||||||
|
|
||||||
if let Err(error) = connection.run().await {
|
|
||||||
error!(?error, "Modbus connection quit unexpectedly");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
tx.send(Err(e)).expect("unexpected closed receiver");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
rx.await.map_err(|_| crate::Error::RecvError)??;
|
|
||||||
}
|
|
||||||
|
|
||||||
for reg in input {
|
for reg in input {
|
||||||
let mqtt = mqtt.scoped("input");
|
let mqtt = mqtt.scoped("input");
|
||||||
|
|
|
@ -7,9 +7,6 @@ pub mod connection;
|
||||||
pub mod connector;
|
pub mod connector;
|
||||||
pub mod register;
|
pub mod register;
|
||||||
|
|
||||||
pub use connection::Connection;
|
|
||||||
// pub use register::Register;
|
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
#[serde(rename_all = "lowercase")]
|
#[serde(rename_all = "lowercase")]
|
||||||
pub enum ConnectState {
|
pub enum ConnectState {
|
||||||
|
|
Loading…
Reference in New Issue