use crate::app::attr::Attribute;
use crate::app::variations::Group50Var2;
use crate::app::RequestHeader;
use crate::app::Sequence;
use crate::app::{control::*, Timestamp};
use crate::app::{FunctionCode, MaybeAsync};
use crate::outstation::database::DatabaseHandle;
#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
pub struct ApplicationIin {
pub need_time: bool,
pub local_control: bool,
pub device_trouble: bool,
pub config_corrupt: bool,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum RestartDelay {
Seconds(u16),
Milliseconds(u16),
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum RequestError {
ParameterError,
NotSupported,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum ConnectionState {
Connected,
Disconnected,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct ClassCount {
pub num_class_1: usize,
pub num_class_2: usize,
pub num_class_3: usize,
}
impl ClassCount {
pub fn is_empty(&self) -> bool {
self.num_class_1 == 0 && self.num_class_2 == 0 && self.num_class_3 == 0
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct TypeCount {
pub num_binary_input: usize,
pub num_double_bit_binary_input: usize,
pub num_binary_output_status: usize,
pub num_counter: usize,
pub num_frozen_counter: usize,
pub num_analog: usize,
pub num_analog_output_status: usize,
pub num_octet_string: usize,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct BufferState {
pub classes: ClassCount,
pub types: TypeCount,
}
pub trait OutstationApplication: Sync + Send + 'static {
fn get_processing_delay_ms(&self) -> u16 {
0
}
#[allow(unused_variables)]
fn write_absolute_time(&mut self, time: Timestamp) -> Result<(), RequestError> {
Err(RequestError::NotSupported)
}
fn get_application_iin(&self) -> ApplicationIin {
ApplicationIin::default()
}
fn cold_restart(&mut self) -> Option<RestartDelay> {
None
}
fn warm_restart(&mut self) -> Option<RestartDelay> {
None
}
#[allow(unused_variables)]
fn freeze_counter(
&mut self,
indices: FreezeIndices,
freeze_type: FreezeType,
database: &mut DatabaseHandle,
) -> Result<(), RequestError> {
Err(RequestError::NotSupported)
}
fn support_write_analog_dead_bands(&mut self) -> bool {
false
}
fn begin_write_analog_dead_bands(&mut self) {}
#[allow(unused_variables)]
fn write_analog_dead_band(&mut self, index: u16, dead_band: f64) {}
fn end_write_analog_dead_bands(&mut self) -> MaybeAsync<()> {
MaybeAsync::ready(())
}
#[allow(unused_variables)]
fn write_device_attr(&mut self, attr: Attribute) -> MaybeAsync<bool> {
MaybeAsync::ready(true)
}
fn begin_confirm(&mut self) {}
#[allow(unused_variables)]
fn event_cleared(&mut self, id: u64) {}
#[allow(unused_variables)]
fn end_confirm(&mut self, state: BufferState) -> MaybeAsync<()> {
MaybeAsync::ready(())
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum BroadcastAction {
Processed,
IgnoredByConfiguration,
BadObjectHeaders,
UnsupportedFunction(FunctionCode),
}
pub trait OutstationInformation: Sync + Send + 'static {
fn process_request_from_idle(&mut self, _header: RequestHeader) {}
fn broadcast_received(&mut self, _function: FunctionCode, _action: BroadcastAction) {}
fn enter_solicited_confirm_wait(&mut self, _ecsn: Sequence) {}
fn solicited_confirm_timeout(&mut self, _ecsn: Sequence) {}
fn solicited_confirm_received(&mut self, _ecsn: Sequence) {}
fn solicited_confirm_wait_new_request(&mut self) {}
fn wrong_solicited_confirm_seq(&mut self, _ecsn: Sequence, _seq: Sequence) {}
fn unexpected_confirm(&mut self, _unsolicited: bool, _seq: Sequence) {}
fn enter_unsolicited_confirm_wait(&mut self, _ecsn: Sequence) {}
fn unsolicited_confirm_timeout(&mut self, _ecsn: Sequence, _retry: bool) {}
fn unsolicited_confirmed(&mut self, _ecsn: Sequence) {}
fn clear_restart_iin(&mut self) {}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum OperateType {
SelectBeforeOperate,
DirectOperate,
DirectOperateNoAck,
}
pub trait ControlSupport<T> {
fn select(&mut self, control: T, index: u16, database: &mut DatabaseHandle) -> CommandStatus;
fn operate(
&mut self,
control: T,
index: u16,
op_type: OperateType,
database: &mut DatabaseHandle,
) -> CommandStatus;
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum FreezeIndices {
All,
Range(u16, u16),
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum FreezeInterval {
FreezeOnceImmediately,
FreezeOnceAtTime(Timestamp),
PeriodicallyFreeze(Timestamp, u32),
PeriodicallyFreezeRelative(u32),
}
impl FreezeInterval {
pub fn new(timestamp: Timestamp, interval: u32) -> Self {
match (timestamp.raw_value(), interval) {
(0, 0) => Self::FreezeOnceImmediately,
(_, 0) => Self::FreezeOnceAtTime(timestamp),
(0, _) => Self::PeriodicallyFreezeRelative(interval),
(_, _) => Self::PeriodicallyFreeze(timestamp, interval),
}
}
pub fn get_time_and_interval(&self) -> (Timestamp, u32) {
match self {
FreezeInterval::FreezeOnceImmediately => (Timestamp::zero(), 0),
FreezeInterval::FreezeOnceAtTime(t) => (*t, 0),
FreezeInterval::PeriodicallyFreeze(t, i) => (*t, *i),
FreezeInterval::PeriodicallyFreezeRelative(i) => (Timestamp::zero(), *i),
}
}
}
impl From<Group50Var2> for FreezeInterval {
fn from(value: Group50Var2) -> Self {
Self::new(value.time, value.interval)
}
}
impl From<FreezeInterval> for Group50Var2 {
fn from(value: FreezeInterval) -> Self {
let (time, interval) = value.get_time_and_interval();
Self { time, interval }
}
}
#[cfg_attr(not(feature = "ffi"), non_exhaustive)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum FreezeType {
ImmediateFreeze,
FreezeAndClear,
FreezeAtTime(FreezeInterval),
}
pub trait ControlHandler:
ControlSupport<Group12Var1>
+ ControlSupport<Group41Var1>
+ ControlSupport<Group41Var2>
+ ControlSupport<Group41Var3>
+ ControlSupport<Group41Var4>
+ Sync
+ Send
+ 'static
{
fn begin_fragment(&mut self) {}
fn end_fragment(&mut self, _database: &mut DatabaseHandle) -> MaybeAsync<()> {
MaybeAsync::ready(())
}
}
#[derive(Copy, Clone)]
pub struct DefaultControlHandler {
status: CommandStatus,
}
impl DefaultControlHandler {
pub fn create() -> Box<dyn ControlHandler> {
Self::with_status(CommandStatus::NotSupported)
}
pub fn with_status(status: CommandStatus) -> Box<dyn ControlHandler> {
Box::new(DefaultControlHandler { status })
}
}
impl ControlHandler for DefaultControlHandler {}
impl ControlSupport<Group12Var1> for DefaultControlHandler {
fn select(
&mut self,
_control: Group12Var1,
_index: u16,
_database: &mut DatabaseHandle,
) -> CommandStatus {
self.status
}
fn operate(
&mut self,
_control: Group12Var1,
_index: u16,
_op_type: OperateType,
_database: &mut DatabaseHandle,
) -> CommandStatus {
self.status
}
}
impl ControlSupport<Group41Var1> for DefaultControlHandler {
fn select(
&mut self,
_control: Group41Var1,
_index: u16,
_database: &mut DatabaseHandle,
) -> CommandStatus {
self.status
}
fn operate(
&mut self,
_control: Group41Var1,
_index: u16,
_op_type: OperateType,
_database: &mut DatabaseHandle,
) -> CommandStatus {
self.status
}
}
impl ControlSupport<Group41Var2> for DefaultControlHandler {
fn select(
&mut self,
_control: Group41Var2,
_index: u16,
_database: &mut DatabaseHandle,
) -> CommandStatus {
self.status
}
fn operate(
&mut self,
_control: Group41Var2,
_index: u16,
_op_type: OperateType,
_database: &mut DatabaseHandle,
) -> CommandStatus {
self.status
}
}
impl ControlSupport<Group41Var3> for DefaultControlHandler {
fn select(
&mut self,
_control: Group41Var3,
_index: u16,
_database: &mut DatabaseHandle,
) -> CommandStatus {
self.status
}
fn operate(
&mut self,
_control: Group41Var3,
_index: u16,
_op_type: OperateType,
_database: &mut DatabaseHandle,
) -> CommandStatus {
self.status
}
}
impl ControlSupport<Group41Var4> for DefaultControlHandler {
fn select(
&mut self,
_control: Group41Var4,
_index: u16,
_database: &mut DatabaseHandle,
) -> CommandStatus {
self.status
}
fn operate(
&mut self,
_control: Group41Var4,
_index: u16,
_op_type: OperateType,
_database: &mut DatabaseHandle,
) -> CommandStatus {
self.status
}
}