Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion integration_tests/tests/nmt_test.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use zencan_client::nmt_master::NmtMaster;
use zencan_common::{messages::NmtState, NodeId};
use zencan_common::{nmt::NmtState, NodeId};
use zencan_node::{Callbacks, Node};

use integration_tests::prelude::*;
Expand Down
6 changes: 3 additions & 3 deletions zencan-build/src/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ fn pdo_init_tokens(cfg: Option<&PdoDefaultConfig>) -> TokenStream {
let mappings: Vec<u32> = mappings.iter().map(|m| m.to_object_value()).collect();

quote! {
Pdo::new_with_defaults(&OD_TABLE, &PdoDefaults::new(
Pdo::new_with_defaults(&OD_TABLE, &NODE_STATE, &PdoDefaults::new(
#cob_id,
#extended,
#add_node_id,
Expand All @@ -32,7 +32,7 @@ fn pdo_init_tokens(cfg: Option<&PdoDefaultConfig>) -> TokenStream {
))
}
} else {
quote! { Pdo::new_with_defaults(&OD_TABLE, &PdoDefaults::DEFAULT) }
quote! { Pdo::new_with_defaults(&OD_TABLE, &NODE_STATE, &PdoDefaults::DEFAULT) }
}
}

Expand Down Expand Up @@ -829,7 +829,7 @@ pub fn device_config_to_tokens(dev: &DeviceConfig) -> Result<TokenStream, Compil
#[allow(unused_imports)]
use zencan_node::NodeMbox;
#[allow(unused_imports)]
use zencan_node::{NodeState, NodeStateAccess};
use zencan_node::NodeState;
#[allow(unused_imports)]
use zencan_node::priority_queue::PriorityQueue;
#object_defs
Expand Down
3 changes: 2 additions & 1 deletion zencan-client/src/bus_manager/bus_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ use zencan_common::constants::object_ids::{
RPDO_COMM_BASE, RPDO_MAP_BASE, TPDO_COMM_BASE, TPDO_MAP_BASE,
};
use zencan_common::lss::{LssIdentity, LssState};
use zencan_common::messages::{NmtCommand, NmtCommandSpecifier, NmtState, ZencanMessage};
use zencan_common::messages::{NmtCommand, NmtCommandSpecifier, ZencanMessage};
use zencan_common::nmt::NmtState;
use zencan_common::node_id::ConfiguredNodeId;
use zencan_common::sdo::AbortCode;
use zencan_common::{
Expand Down
3 changes: 2 additions & 1 deletion zencan-client/src/nmt_master.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
use std::time::Instant;

use zencan_common::{
messages::{CanMessage, NmtCommand, NmtCommandSpecifier, NmtState, ZencanMessage},
messages::{CanMessage, NmtCommand, NmtCommandSpecifier, ZencanMessage},
nmt::NmtState,
traits::{AsyncCanReceiver, AsyncCanSender},
};

Expand Down
1 change: 1 addition & 0 deletions zencan-common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ pub mod constants;
pub mod device_config;
pub mod lss;
pub mod messages;
pub mod nmt;
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
pub mod node_configuration;
Expand Down
51 changes: 1 addition & 50 deletions zencan-common/src/messages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use snafu::Snafu;

use crate::{
lss::{LssRequest, LssResponse},
nmt::{InvalidNmtStateError, NmtState},
sdo::{SdoRequest, SdoResponse},
};

Expand Down Expand Up @@ -258,56 +259,6 @@ impl From<NmtCommand> for CanMessage {
}
}

/// Possible NMT states for a node
#[derive(Copy, Clone, Debug, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
pub enum NmtState {
/// Bootup
///
/// A node never remains in this state, as all nodes should transition automatically into PreOperational
Bootup = 0,
/// Node has been stopped
Stopped = 4,
/// Normal operational state
Operational = 5,
/// Node is awaiting command to enter operation
PreOperational = 127,
}

impl core::fmt::Display for NmtState {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
NmtState::Bootup => write!(f, "Bootup"),
NmtState::Stopped => write!(f, "Stopped"),
NmtState::Operational => write!(f, "Operational"),
NmtState::PreOperational => write!(f, "PreOperational"),
}
}
}

#[derive(Clone, Copy, Debug)]
/// An error for [`NmtState::try_from()`]
pub struct InvalidNmtStateError(u8);

impl TryFrom<u8> for NmtState {
type Error = InvalidNmtStateError;

/// Attempt to convert a u8 to an NmtState enum
///
/// Fails with BadNmtStateError if value is not a valid state
fn try_from(value: u8) -> Result<Self, Self::Error> {
use NmtState::*;
match value {
x if x == Bootup as u8 => Ok(Bootup),
x if x == Stopped as u8 => Ok(Stopped),
x if x == Operational as u8 => Ok(Operational),
x if x == PreOperational as u8 => Ok(PreOperational),
_ => Err(InvalidNmtStateError(value)),
}
}
}

/// A Heartbeat message
#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
Expand Down
51 changes: 51 additions & 0 deletions zencan-common/src/nmt.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//! Definitions for the NMT protocol

/// Possible NMT states for a node
#[derive(Copy, Clone, Debug, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
pub enum NmtState {
/// Bootup
///
/// A node never remains in this state, as all nodes should transition automatically into PreOperational
Bootup = 0,
/// Node has been stopped
Stopped = 4,
/// Normal operational state
Operational = 5,
/// Node is awaiting command to enter operation
PreOperational = 127,
}

impl core::fmt::Display for NmtState {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
NmtState::Bootup => write!(f, "Bootup"),
NmtState::Stopped => write!(f, "Stopped"),
NmtState::Operational => write!(f, "Operational"),
NmtState::PreOperational => write!(f, "PreOperational"),
}
}
}

#[derive(Clone, Copy, Debug)]
/// An error for [`NmtState::try_from()`]
pub struct InvalidNmtStateError(pub u8);

impl TryFrom<u8> for NmtState {
type Error = InvalidNmtStateError;

/// Attempt to convert a u8 to an NmtState enum
///
/// Fails with BadNmtStateError if value is not a valid state
fn try_from(value: u8) -> Result<Self, Self::Error> {
use NmtState::*;
match value {
x if x == Bootup as u8 => Ok(Bootup),
x if x == Stopped as u8 => Ok(Stopped),
x if x == Operational as u8 => Ok(Operational),
x if x == PreOperational as u8 => Ok(PreOperational),
_ => Err(InvalidNmtStateError(value)),
}
}
}
2 changes: 1 addition & 1 deletion zencan-node/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ pub use bootloader::{BootloaderInfo, BootloaderSection, BootloaderSectionCallbac
pub use common::open_socketcan;
pub use node::{Callbacks, Node};
pub use node_mbox::NodeMbox;
pub use node_state::{NodeState, NodeStateAccess};
pub use node_state::NodeState;
pub use persist::{restore_stored_comm_objects, restore_stored_objects};
pub use sdo_server::SDO_BUFFER_SIZE;

Expand Down
59 changes: 29 additions & 30 deletions zencan-node/src/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,19 @@ use core::{convert::Infallible, sync::atomic::Ordering};
use zencan_common::{
constants::object_ids,
lss::LssIdentity,
messages::{
CanId, CanMessage, Heartbeat, NmtCommandSpecifier, NmtState, ZencanMessage, LSS_RESP_ID,
},
messages::{CanId, CanMessage, Heartbeat, NmtCommandSpecifier, ZencanMessage, LSS_RESP_ID},
nmt::NmtState,
NodeId,
};

use crate::sdo_server::SdoServer;
use crate::{
lss_slave::{LssConfig, LssSlave},
node_mbox::NodeMbox,
node_state::NmtStateAccess as _,
object_dict::{find_object, ODEntry},
NodeState,
};
use crate::{node_state::NodeStateAccess, sdo_server::SdoServer};

use defmt_or_log::{debug, info};

Expand Down Expand Up @@ -124,13 +125,12 @@ fn read_autostart(od: &[ODEntry]) -> Option<bool> {
#[allow(missing_debug_implementations)]
pub struct Node<'a> {
node_id: NodeId,
nmt_state: NmtState,
sdo_server: SdoServer<'a>,
lss_slave: LssSlave,
message_count: u32,
od: &'a [ODEntry<'a>],
mbox: &'a NodeMbox,
state: &'a dyn NodeStateAccess,
od: &'static [ODEntry<'static>],
mbox: &'static NodeMbox,
state: &'static NodeState<'static>,
reassigned_node_id: Option<NodeId>,
next_heartbeat_time_us: u64,
heartbeat_period_ms: u16,
Expand All @@ -152,9 +152,9 @@ impl<'a> Node<'a> {
pub fn new(
node_id: NodeId,
callbacks: Callbacks<'a>,
mbox: &'a NodeMbox,
state: &'a dyn NodeStateAccess,
od: &'a [ODEntry<'a>],
mbox: &'static NodeMbox,
state: &'static NodeState<'static>,
od: &'static [ODEntry<'static>],
) -> Self {
let message_count = 0;
let sdo_server = SdoServer::new();
Expand All @@ -163,7 +163,6 @@ impl<'a> Node<'a> {
node_id,
store_supported: false,
});
let nmt_state = NmtState::Bootup;
let reassigned_node_id = None;

// Storage command is supported if the application provides a callback
Expand All @@ -183,7 +182,6 @@ impl<'a> Node<'a> {
let mut node = Self {
node_id,
callbacks,
nmt_state,
sdo_server,
lss_slave,
message_count,
Expand Down Expand Up @@ -235,10 +233,10 @@ impl<'a> Node<'a> {
let mut update_flag = false;
if let Some(new_node_id) = self.reassigned_node_id.take() {
self.node_id = new_node_id;
self.nmt_state = NmtState::Bootup;
self.state.set_nmt_state(NmtState::Bootup);
}

if self.nmt_state == NmtState::Bootup {
if self.nmt_state() == NmtState::Bootup {
// Set state before calling boot_up, so the heartbeat state is correct
self.enter_preoperational();
self.boot_up();
Expand Down Expand Up @@ -322,7 +320,7 @@ impl<'a> Node<'a> {
}
}

if self.nmt_state == NmtState::Operational {
if self.nmt_state() == NmtState::Operational {
// check if a sync has been received
let sync = self.mbox.read_sync_flag();

Expand All @@ -332,7 +330,7 @@ impl<'a> Node<'a> {
// possible when it has nothing to do, so it can be called frequently with little cost.
let global_trigger = self.state.object_flag_sync().toggle();

for pdo in self.state.get_tpdos() {
for pdo in self.state.tpdos() {
if !(pdo.valid()) {
continue;
}
Expand All @@ -348,11 +346,11 @@ impl<'a> Node<'a> {
}
}

for pdo in self.state.get_tpdos() {
for pdo in self.state.tpdos() {
pdo.clear_events();
}

for rpdo in self.state.get_rpdos() {
for rpdo in self.state.rpdos() {
if !rpdo.valid() {
continue;
}
Expand All @@ -371,7 +369,7 @@ impl<'a> Node<'a> {
}

fn handle_nmt_command(&mut self, cmd: NmtCommandSpecifier) {
let prev_state = self.nmt_state;
let prev_state = self.nmt_state();

match cmd {
NmtCommandSpecifier::Start => self.enter_operational(),
Expand All @@ -383,7 +381,8 @@ impl<'a> Node<'a> {

debug!(
"NMT state changed from {:?} to {:?}",
prev_state, self.nmt_state
prev_state,
self.nmt_state()
);
}

Expand All @@ -394,7 +393,7 @@ impl<'a> Node<'a> {

/// Get the current NMT state of the node
pub fn nmt_state(&self) -> NmtState {
self.nmt_state
self.state.nmt_state()
}

/// Get the number of received messages
Expand All @@ -419,46 +418,46 @@ impl<'a> Node<'a> {
}

fn enter_operational(&mut self) {
self.nmt_state = NmtState::Operational;
self.state.set_nmt_state(NmtState::Operational);
if let Some(cb) = &mut self.callbacks.enter_operational {
(*cb)(self.od);
}
}

fn enter_stopped(&mut self) {
self.nmt_state = NmtState::Stopped;
self.state.set_nmt_state(NmtState::Stopped);
if let Some(cb) = &mut self.callbacks.enter_stopped {
(*cb)(self.od);
}
}

fn enter_preoperational(&mut self) {
self.nmt_state = NmtState::PreOperational;
self.state.set_nmt_state(NmtState::PreOperational);
if let Some(cb) = &mut self.callbacks.enter_preoperational {
(*cb)(self.od);
}
}

fn reset_app(&mut self) {
// TODO: All objects should get reset to their defaults, but that isn't yet supported
for pdo in self.state.get_rpdos().iter().chain(self.state.get_tpdos()) {
for pdo in self.state.rpdos().iter().chain(self.state.tpdos()) {
pdo.init_defaults(self.node_id);
}

if let Some(reset_app_cb) = &mut self.callbacks.reset_app {
(*reset_app_cb)(self.od);
}
self.nmt_state = NmtState::Bootup;
self.state.set_nmt_state(NmtState::Bootup);
}

fn reset_comm(&mut self) {
for pdo in self.state.get_rpdos().iter().chain(self.state.get_tpdos()) {
for pdo in self.state.rpdos().iter().chain(self.state.tpdos()) {
pdo.init_defaults(self.node_id);
}
if let Some(reset_comms_cb) = &mut self.callbacks.reset_comms {
(*reset_comms_cb)(self.od);
}
self.nmt_state = NmtState::Bootup;
self.state.set_nmt_state(NmtState::Bootup);
}

fn boot_up(&mut self) {
Expand All @@ -482,7 +481,7 @@ impl<'a> Node<'a> {
let heartbeat = Heartbeat {
node: node_id.raw(),
toggle: false,
state: self.nmt_state,
state: self.nmt_state(),
};
self.send_message(heartbeat.into());
self.next_heartbeat_time_us += (self.heartbeat_period_ms as u64) * 1000;
Expand Down
Loading