use crate::app::Shutdown;
use crate::link::error::LinkError;
use crate::util::phys::PhysLayer;
use std::time::Duration;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub(crate) enum Enabled {
Yes,
No,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub(crate) enum StopReason {
Disable,
Shutdown,
}
impl std::fmt::Display for StopReason {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
StopReason::Disable => write!(f, "disabled"),
StopReason::Shutdown => write!(f, "shutdown"),
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub(crate) enum RunError {
Stop(StopReason),
Link(LinkError),
}
impl std::fmt::Display for RunError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
RunError::Stop(err) => write!(f, "{err}"),
RunError::Link(err) => write!(f, "{err}"),
}
}
}
impl From<Shutdown> for StopReason {
fn from(_: Shutdown) -> Self {
StopReason::Shutdown
}
}
impl From<StopReason> for RunError {
fn from(x: StopReason) -> Self {
RunError::Stop(x)
}
}
impl From<LinkError> for RunError {
fn from(err: LinkError) -> Self {
RunError::Link(err)
}
}
impl From<Shutdown> for RunError {
fn from(_: Shutdown) -> Self {
RunError::Stop(StopReason::Shutdown)
}
}
enum SessionType {
Master(Box<crate::master::task::MasterTask>),
Outstation(Box<crate::outstation::task::OutstationTask>),
}
pub(crate) struct Session {
inner: SessionType,
}
impl Session {
pub(crate) fn master(task: crate::master::task::MasterTask) -> Self {
Self {
inner: SessionType::Master(Box::new(task)),
}
}
pub(crate) fn outstation(task: crate::outstation::task::OutstationTask) -> Self {
Self {
inner: SessionType::Outstation(Box::new(task)),
}
}
pub(crate) fn enabled(&self) -> Enabled {
match &self.inner {
SessionType::Master(x) => x.enabled(),
SessionType::Outstation(x) => x.enabled(),
}
}
pub(crate) async fn process_next_message(&mut self) -> Result<(), StopReason> {
match &mut self.inner {
SessionType::Master(x) => x.process_next_message().await,
SessionType::Outstation(x) => x.process_next_message().await,
}
}
pub(crate) async fn run(&mut self, io: &mut PhysLayer) -> RunError {
match &mut self.inner {
SessionType::Master(x) => x.run(io).await,
SessionType::Outstation(x) => x.run(io).await,
}
}
pub(crate) async fn wait_for_enabled(&mut self) -> Result<(), Shutdown> {
loop {
if self.enabled() == Enabled::Yes {
return Ok(());
}
if let Err(StopReason::Shutdown) = self.process_next_message().await {
return Err(Shutdown);
}
}
}
pub(crate) async fn wait_for_retry(&mut self, duration: Duration) -> Result<(), StopReason> {
use std::ops::Add;
let deadline = tokio::time::Instant::now().add(duration);
loop {
tokio::select! {
result = self.process_next_message() => {
result?;
if self.enabled() == Enabled::No {
return Err(StopReason::Disable)
}
}
_ = tokio::time::sleep_until(deadline) => {
return Ok(());
}
}
}
}
}