#[cfg(feature = "std")]
use std::{
collections::VecDeque,
sync::{Arc, Mutex},
};
#[cfg(not(feature = "std"))]
use alloc::{collections::VecDeque, string::String, vec::Vec};
use crate::datalink::{DataLink, DataLinkAddress, DataLinkError, DataLinkType, Result};
use crate::util::crc16_mstp;
pub const MSTP_PREAMBLE_55: u8 = 0x55;
pub const MSTP_PREAMBLE_FF: u8 = 0xFF;
pub const MSTP_MAX_DATA_LENGTH: usize = 501;
pub const MSTP_HEADER_SIZE: usize = 8;
pub const MSTP_MAX_FRAME_SIZE: usize = MSTP_HEADER_SIZE + MSTP_MAX_DATA_LENGTH + 2;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum MstpFrameType {
Token = 0,
PollForMaster = 1,
ReplyToPollForMaster = 2,
TestRequest = 3,
TestResponse = 4,
BacnetDataExpectingReply = 5,
BacnetDataNotExpectingReply = 6,
ReplyPostponed = 7,
}
impl MstpFrameType {
pub fn from_u8(value: u8) -> Option<Self> {
match value {
0 => Some(Self::Token),
1 => Some(Self::PollForMaster),
2 => Some(Self::ReplyToPollForMaster),
3 => Some(Self::TestRequest),
4 => Some(Self::TestResponse),
5 => Some(Self::BacnetDataExpectingReply),
6 => Some(Self::BacnetDataNotExpectingReply),
7 => Some(Self::ReplyPostponed),
_ => None,
}
}
}
#[derive(Debug, Clone)]
pub struct MstpFrame {
pub frame_type: MstpFrameType,
pub destination: u8,
pub source: u8,
pub data_length: u16,
pub header_crc: u8,
pub data: Vec<u8>,
pub data_crc: Option<u16>,
}
impl MstpFrame {
pub fn new(
frame_type: MstpFrameType,
destination: u8,
source: u8,
data: Vec<u8>,
) -> Result<Self> {
if data.len() > MSTP_MAX_DATA_LENGTH {
return Err(DataLinkError::InvalidFrame);
}
let data_length = data.len() as u16;
let header_bytes = [
frame_type as u8,
destination,
source,
(data_length >> 8) as u8,
(data_length & 0xFF) as u8,
];
let header_crc = calculate_header_crc(&header_bytes);
let data_crc = if !data.is_empty() {
Some(crc16_mstp(&data))
} else {
None
};
Ok(Self {
frame_type,
destination,
source,
data_length,
header_crc,
data,
data_crc,
})
}
pub fn token(destination: u8, source: u8) -> Result<Self> {
Self::new(MstpFrameType::Token, destination, source, Vec::new())
}
pub fn bacnet_data(
destination: u8,
source: u8,
data: Vec<u8>,
expecting_reply: bool,
) -> Result<Self> {
let frame_type = if expecting_reply {
MstpFrameType::BacnetDataExpectingReply
} else {
MstpFrameType::BacnetDataNotExpectingReply
};
Self::new(frame_type, destination, source, data)
}
pub fn encode(&self) -> Vec<u8> {
let mut frame = Vec::with_capacity(MSTP_HEADER_SIZE + self.data.len() + 2);
frame.push(MSTP_PREAMBLE_55);
frame.push(MSTP_PREAMBLE_FF);
frame.push(self.frame_type as u8);
frame.push(self.destination);
frame.push(self.source);
frame.push((self.data_length >> 8) as u8);
frame.push((self.data_length & 0xFF) as u8);
frame.push(self.header_crc);
if !self.data.is_empty() {
frame.extend_from_slice(&self.data);
if let Some(crc) = self.data_crc {
frame.push((crc & 0xFF) as u8);
frame.push((crc >> 8) as u8);
}
}
frame
}
pub fn decode(data: &[u8]) -> Result<Self> {
if data.len() < MSTP_HEADER_SIZE {
return Err(DataLinkError::InvalidFrame);
}
if data[0] != MSTP_PREAMBLE_55 || data[1] != MSTP_PREAMBLE_FF {
return Err(DataLinkError::InvalidFrame);
}
let frame_type = MstpFrameType::from_u8(data[2]).ok_or(DataLinkError::InvalidFrame)?;
let destination = data[3];
let source = data[4];
let data_length = ((data[5] as u16) << 8) | (data[6] as u16);
let header_crc = data[7];
let header_bytes = [data[2], data[3], data[4], data[5], data[6]];
let calculated_crc = calculate_header_crc(&header_bytes);
if calculated_crc != header_crc {
return Err(DataLinkError::CrcError);
}
let expected_size =
MSTP_HEADER_SIZE + data_length as usize + if data_length > 0 { 2 } else { 0 };
if data.len() != expected_size {
return Err(DataLinkError::InvalidFrame);
}
let (frame_data, data_crc) = if data_length > 0 {
let data_start = MSTP_HEADER_SIZE;
let data_end = data_start + data_length as usize;
let frame_data = data[data_start..data_end].to_vec();
let crc_low = data[data_end];
let crc_high = data[data_end + 1];
let data_crc = ((crc_high as u16) << 8) | (crc_low as u16);
let calculated_crc = crc16_mstp(&frame_data);
if calculated_crc != data_crc {
return Err(DataLinkError::CrcError);
}
(frame_data, Some(data_crc))
} else {
(Vec::new(), None)
};
Ok(Self {
frame_type,
destination,
source,
data_length,
header_crc,
data: frame_data,
data_crc,
})
}
pub fn is_token(&self) -> bool {
self.frame_type == MstpFrameType::Token
}
pub fn is_data(&self) -> bool {
matches!(
self.frame_type,
MstpFrameType::BacnetDataExpectingReply | MstpFrameType::BacnetDataNotExpectingReply
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MstpState {
Initialize,
Idle,
UseToken,
WaitForReply,
PassToken,
NoToken,
PollForMaster,
AnswerDataRequest,
DoneWithToken,
}
#[derive(Debug, Clone)]
pub struct MstpConfig {
pub station_address: u8,
pub max_master: u8,
pub max_info_frames: u8,
pub token_timeout: u64,
pub reply_timeout: u64,
pub usage_timeout: u64,
}
impl Default for MstpConfig {
fn default() -> Self {
Self {
station_address: 1,
max_master: 127,
max_info_frames: 1,
token_timeout: 500,
reply_timeout: 255,
usage_timeout: 50,
}
}
}
#[cfg(feature = "std")]
type ReceiveQueue = Arc<Mutex<VecDeque<(Vec<u8>, DataLinkAddress)>>>;
#[cfg(feature = "std")]
pub struct MstpDataLink {
config: MstpConfig,
_state: Arc<Mutex<MstpState>>,
_token_holder: Arc<Mutex<Option<u8>>>,
_next_station: Arc<Mutex<u8>>,
send_queue: Arc<Mutex<VecDeque<(MstpFrame, DataLinkAddress)>>>,
receive_queue: ReceiveQueue,
_port_name: String,
_running: Arc<Mutex<bool>>,
}
#[cfg(feature = "std")]
impl MstpDataLink {
pub fn new(port_name: &str, config: MstpConfig) -> Result<Self> {
let state = Arc::new(Mutex::new(MstpState::Initialize));
let token_holder = Arc::new(Mutex::new(None));
let next_station = Arc::new(Mutex::new(
(config.station_address + 1) % (config.max_master + 1),
));
let send_queue = Arc::new(Mutex::new(VecDeque::new()));
let receive_queue = Arc::new(Mutex::new(VecDeque::new()));
let running = Arc::new(Mutex::new(true));
Ok(Self {
config,
_state: state,
_token_holder: token_holder,
_next_station: next_station,
send_queue,
receive_queue,
_port_name: port_name.to_string(),
_running: running,
})
}
fn _send_mstp_frame(&self, frame: &MstpFrame) -> Result<()> {
let encoded = frame.encode();
println!(
"MS/TP: Sending {} frame from {} to {}, {} bytes",
match frame.frame_type {
MstpFrameType::Token => "Token",
MstpFrameType::BacnetDataExpectingReply => "Data (expecting reply)",
MstpFrameType::BacnetDataNotExpectingReply => "Data (no reply)",
_ => "Other",
},
frame.source,
frame.destination,
encoded.len()
);
Ok(())
}
fn _handle_token(&mut self) -> Result<()> {
let mut send_queue = self.send_queue.lock().unwrap();
let mut frames_sent = 0;
while frames_sent < self.config.max_info_frames && !send_queue.is_empty() {
if let Some((frame, _)) = send_queue.pop_front() {
self._send_mstp_frame(&frame)?;
frames_sent += 1;
}
}
let next = *self._next_station.lock().unwrap();
let token_frame = MstpFrame::token(next, self.config.station_address)?;
self._send_mstp_frame(&token_frame)?;
let mut next_station = self._next_station.lock().unwrap();
*next_station = (*next_station + 1) % (self.config.max_master + 1);
Ok(())
}
#[cfg(test)]
pub fn simulate_receive(&self, frame: MstpFrame) {
if frame.is_data() && !frame.data.is_empty() {
let mut receive_queue = self.receive_queue.lock().unwrap();
receive_queue.push_back((frame.data.clone(), DataLinkAddress::MsTP(frame.source)));
}
if frame.is_token() && frame.destination == self.config.station_address {
let mut token_holder = self._token_holder.lock().unwrap();
*token_holder = Some(self.config.station_address);
let mut state = self._state.lock().unwrap();
*state = MstpState::UseToken;
}
}
}
#[cfg(feature = "std")]
impl DataLink for MstpDataLink {
fn send_frame(&mut self, frame: &[u8], dest: &DataLinkAddress) -> Result<()> {
let dest_addr = match dest {
DataLinkAddress::MsTP(addr) => *addr,
DataLinkAddress::Broadcast => 255,
_ => {
return Err(DataLinkError::AddressError(
"Invalid address type for MS/TP".into(),
))
}
};
let mstp_frame = MstpFrame::bacnet_data(
dest_addr,
self.config.station_address,
frame.to_vec(),
false, )?;
let mut send_queue = self.send_queue.lock().unwrap();
send_queue.push_back((mstp_frame, DataLinkAddress::MsTP(dest_addr)));
Ok(())
}
fn receive_frame(&mut self) -> Result<(Vec<u8>, DataLinkAddress)> {
let mut receive_queue = self.receive_queue.lock().unwrap();
if let Some((data, source)) = receive_queue.pop_front() {
Ok((data, source))
} else {
Err(DataLinkError::InvalidFrame)
}
}
fn link_type(&self) -> DataLinkType {
DataLinkType::MsTP
}
fn local_address(&self) -> DataLinkAddress {
DataLinkAddress::MsTP(self.config.station_address)
}
}
fn calculate_header_crc(header: &[u8; 5]) -> u8 {
let mut crc = 0xFFu8;
for &byte in header {
crc ^= byte;
for _ in 0..8 {
if crc & 0x01 != 0 {
crc = (crc >> 1) ^ 0x55;
} else {
crc >>= 1;
}
}
}
!crc
}
pub fn validate_mstp_address(address: u8) -> Result<()> {
match address {
0..=127 => Ok(()), 128..=254 => Ok(()), 255 => Ok(()), }
}
pub fn is_master_node(address: u8) -> bool {
address <= 127
}
pub fn is_slave_node(address: u8) -> bool {
(128..=254).contains(&address)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_mstp_frame_encode_decode() {
let token_frame = MstpFrame::token(5, 3).unwrap();
let encoded = token_frame.encode();
let decoded = MstpFrame::decode(&encoded).unwrap();
assert_eq!(decoded.frame_type, MstpFrameType::Token);
assert_eq!(decoded.destination, 5);
assert_eq!(decoded.source, 3);
assert_eq!(decoded.data_length, 0);
assert!(decoded.data.is_empty());
assert!(decoded.data_crc.is_none());
let data = vec![0x01, 0x02, 0x03, 0x04];
let data_frame = MstpFrame::bacnet_data(10, 20, data.clone(), true).unwrap();
let encoded = data_frame.encode();
let decoded = MstpFrame::decode(&encoded).unwrap();
assert_eq!(decoded.frame_type, MstpFrameType::BacnetDataExpectingReply);
assert_eq!(decoded.destination, 10);
assert_eq!(decoded.source, 20);
assert_eq!(decoded.data_length, 4);
assert_eq!(decoded.data, data);
assert!(decoded.data_crc.is_some());
}
#[test]
fn test_header_crc() {
let header = [0x00, 0x05, 0x03, 0x00, 0x00]; let crc = calculate_header_crc(&header);
let frame = MstpFrame::token(5, 3).unwrap();
assert_eq!(frame.header_crc, crc);
}
#[test]
fn test_frame_validation() {
let mut bad_frame = vec![0x00, 0xFF]; bad_frame.extend_from_slice(&[0x00, 0x05, 0x03, 0x00, 0x00, 0x00]);
assert!(MstpFrame::decode(&bad_frame).is_err());
let mut bad_frame = vec![0x55, 0xFF, 0xFF]; bad_frame.extend_from_slice(&[0x05, 0x03, 0x00, 0x00, 0x00]);
assert!(MstpFrame::decode(&bad_frame).is_err());
let bad_frame = vec![0x55, 0xFF, 0x00];
assert!(MstpFrame::decode(&bad_frame).is_err());
}
#[test]
fn test_address_validation() {
assert!(validate_mstp_address(0).is_ok()); assert!(validate_mstp_address(127).is_ok()); assert!(validate_mstp_address(128).is_ok()); assert!(validate_mstp_address(254).is_ok()); assert!(validate_mstp_address(255).is_ok());
assert!(is_master_node(0));
assert!(is_master_node(127));
assert!(!is_master_node(128));
assert!(!is_slave_node(127));
assert!(is_slave_node(128));
assert!(is_slave_node(254));
assert!(!is_slave_node(255));
}
#[test]
fn test_max_data_length() {
let data = vec![0u8; MSTP_MAX_DATA_LENGTH + 1];
let result = MstpFrame::bacnet_data(10, 20, data, false);
assert!(result.is_err());
let data = vec![0u8; MSTP_MAX_DATA_LENGTH];
let result = MstpFrame::bacnet_data(10, 20, data, false);
assert!(result.is_ok());
}
#[cfg(feature = "std")]
#[test]
fn test_mstp_datalink() {
let config = MstpConfig {
station_address: 5,
..Default::default()
};
let mut datalink = MstpDataLink::new("COM1", config).unwrap();
assert_eq!(datalink.link_type(), DataLinkType::MsTP);
assert_eq!(datalink.local_address(), DataLinkAddress::MsTP(5));
let npdu = vec![0x01, 0x02, 0x03, 0x04];
let result = datalink.send_frame(&npdu, &DataLinkAddress::MsTP(10));
assert!(result.is_ok());
let result = datalink.send_frame(&npdu, &DataLinkAddress::Broadcast);
assert!(result.is_ok());
}
}