use crate::dp::Peripheral;
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[repr(u8)]
pub enum OperatingState {
Stop,
Clear,
Operate,
}
impl OperatingState {
#[inline(always)]
pub fn is_stop(self) -> bool {
self == OperatingState::Stop
}
#[inline(always)]
pub fn is_clear(self) -> bool {
self == OperatingState::Clear
}
#[inline(always)]
pub fn is_operate(self) -> bool {
self == OperatingState::Operate
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct DpEvents {
pub cycle_completed: bool,
pub peripheral: Option<(crate::dp::PeripheralHandle, crate::dp::PeripheralEvent)>,
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[repr(u8)]
enum CycleState {
DataExchange(u8),
CycleCompleted,
}
pub struct DpMaster<'a> {
peripherals: crate::dp::PeripheralSet<'a>,
state: DpMasterState,
}
pub struct DpMasterState {
pub(crate) operating_state: OperatingState,
last_global_control: Option<crate::time::Instant>,
cycle_state: CycleState,
last_events: DpEvents,
#[cfg(feature = "debug-measure-dp-cycle")]
last_cycle: Option<crate::time::Instant>,
}
impl<'a> DpMaster<'a> {
pub fn new<S>(storage: S) -> Self
where
S: Into<managed::ManagedSlice<'a, crate::dp::PeripheralStorage<'a>>>,
{
let storage = storage.into();
if storage.len() > 124 {
log::warn!("DP master was provided with storage for more than 124 peripherals, this is wasted memory!");
}
Self {
peripherals: crate::dp::PeripheralSet::new(storage),
state: DpMasterState {
operating_state: OperatingState::Stop,
last_global_control: None,
cycle_state: CycleState::DataExchange(0),
last_events: Default::default(),
#[cfg(feature = "debug-measure-dp-cycle")]
last_cycle: None,
},
}
}
pub fn add(&mut self, peripheral: Peripheral<'a>) -> crate::dp::PeripheralHandle {
self.peripherals.add(peripheral)
}
pub fn get_mut(&mut self, handle: crate::dp::PeripheralHandle) -> &mut Peripheral<'a> {
self.peripherals.get_mut(handle)
}
pub fn iter_mut(
&mut self,
) -> impl Iterator<Item = (crate::dp::PeripheralHandle, &mut Peripheral<'a>)> {
self.peripherals.iter_mut()
}
pub fn iter(&self) -> impl Iterator<Item = (crate::dp::PeripheralHandle, &Peripheral<'a>)> {
self.peripherals.iter()
}
pub fn take_last_events(&mut self) -> DpEvents {
core::mem::take(&mut self.state.last_events)
}
#[inline(always)]
pub fn operating_state(&self) -> OperatingState {
self.state.operating_state
}
#[inline]
pub fn enter_state(&mut self, state: OperatingState) {
log::info!("DP master entering state \"{:?}\"", state);
self.state.operating_state = state;
self.state.last_global_control = None;
if state != OperatingState::Operate {
todo!("OperatingState {:?} is not yet supported properly!", state);
}
}
#[inline]
pub fn enter_stop(&mut self) {
self.enter_state(OperatingState::Stop)
}
#[inline]
pub fn enter_clear(&mut self) {
self.enter_state(OperatingState::Clear)
}
#[inline]
pub fn enter_operate(&mut self) {
self.enter_state(OperatingState::Operate)
}
fn increment_cycle_state(&mut self, index: u8, now: crate::time::Instant) -> bool {
if let Some(next) = self.peripherals.get_next_index(index) {
self.state.cycle_state = CycleState::DataExchange(next);
false
} else {
#[cfg(feature = "debug-measure-dp-cycle")]
{
if let Some(last_cycle) = self.state.last_cycle {
log::debug!("DP Cycle Time: {} us", (now - last_cycle).total_micros());
}
self.state.last_cycle = Some(now);
}
self.state.cycle_state = CycleState::CycleCompleted;
true
}
}
}
impl<'a> crate::fdl::FdlApplication for DpMaster<'a> {
fn transmit_telegram(
&mut self,
now: crate::time::Instant,
fdl: &crate::fdl::FdlActiveStation,
mut tx: crate::fdl::TelegramTx,
high_prio_only: bool,
) -> Option<crate::fdl::TelegramTxResponse> {
if self.state.operating_state.is_stop() {
self.state.last_events = DpEvents::default();
return None;
}
if !high_prio_only
&& self
.state
.last_global_control
.map(|t| now - t >= fdl.parameters().slot_time() * 50)
.unwrap_or(true)
{
self.state.last_global_control = Some(now);
log::trace!(
"DP master sending global control for state {:?}",
self.state.operating_state
);
self.state.last_events = DpEvents::default();
return Some(tx.send_data_telegram(
crate::fdl::DataTelegramHeader {
da: 0x7f,
sa: fdl.parameters().address,
dsap: crate::consts::SAP_SLAVE_GLOBAL_CONTROL,
ssap: crate::consts::SAP_MASTER_MS0,
fc: crate::fdl::FunctionCode::Request {
fcb: crate::fdl::FrameCountBit::Inactive,
req: crate::fdl::RequestType::SdnLow,
},
},
2,
|buf| {
buf[0] = match self.state.operating_state {
OperatingState::Clear => 0x02,
OperatingState::Operate => 0x00,
OperatingState::Stop => unreachable!(),
};
buf[1] = 0x00;
},
));
}
let mut peripheral_event = None;
loop {
let index = match self.state.cycle_state {
CycleState::DataExchange(i) => i,
CycleState::CycleCompleted => {
self.state.cycle_state = CycleState::DataExchange(0);
self.state.last_events = DpEvents {
peripheral: peripheral_event,
..Default::default()
};
return None;
}
};
if let Some((handle, peripheral)) = self.peripherals.get_at_index_mut(index) {
let res = peripheral.transmit_telegram(now, &self.state, fdl, tx, high_prio_only);
match res {
Ok(tx_res) => {
self.state.last_events = DpEvents {
peripheral: peripheral_event,
..Default::default()
};
return Some(tx_res);
}
Err((tx_returned, event)) => {
tx = tx_returned;
if let Some(event) = event {
assert!(peripheral_event.is_none());
peripheral_event = Some((handle, event));
}
if self.increment_cycle_state(index, now) {
self.state.cycle_state = CycleState::DataExchange(0);
self.state.last_events = DpEvents {
cycle_completed: true,
peripheral: peripheral_event,
};
return None;
}
}
}
}
}
}
fn receive_reply(
&mut self,
now: crate::time::Instant,
fdl: &crate::fdl::FdlActiveStation,
addr: u8,
telegram: crate::fdl::Telegram,
) {
let index = match self.state.cycle_state {
CycleState::DataExchange(i) => i,
CycleState::CycleCompleted => {
unreachable!("impossible to get a reply when the cycle was completed!");
}
};
match self.peripherals.get_at_index_mut(index) {
Some((handle, peripheral)) if addr == peripheral.address() => {
let event = peripheral.receive_reply(now, &self.state, fdl, telegram);
let cycle_completed = self.increment_cycle_state(index, now);
self.state.last_events = DpEvents {
cycle_completed,
peripheral: event.map(|ev| (handle, ev)),
};
}
_ => {
unreachable!(
"Received reply for unknown/unexpected peripheral #{addr}: {telegram:?}"
);
}
}
}
fn handle_timeout(
&mut self,
now: crate::time::Instant,
fdl: &crate::fdl::FdlActiveStation,
addr: u8,
) {
}
}