Skip to content

Commit

Permalink
Broadcast support
Browse files Browse the repository at this point in the history
  • Loading branch information
emgre committed Jan 6, 2022
1 parent 10e1eb6 commit 8687905
Show file tree
Hide file tree
Showing 12 changed files with 278 additions and 94 deletions.
4 changes: 2 additions & 2 deletions rodbus/examples/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ impl RequestHandler for SimpleHandler {
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// initialize logging
tracing_subscriber::fmt()
.with_max_level(tracing::Level::INFO)
.with_max_level(tracing::Level::DEBUG)
.with_target(false)
.init();

Expand Down Expand Up @@ -159,7 +159,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
DecodeLevel::new(
PduDecodeLevel::DataValues,
AduDecodeLevel::Payload,
PhysDecodeLevel::Nothing,
PhysDecodeLevel::Data,
),
)?;

Expand Down
6 changes: 3 additions & 3 deletions rodbus/src/client/task.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ pub(crate) enum SessionError {
IoError,
// unrecoverable framing issue,
BadFrame,
// the mpsc is closed (dropped) on the sender side
// the mpsc is closed (dropped) on the sender side
Shutdown,
}

Expand Down Expand Up @@ -101,7 +101,7 @@ where
tx_id: TxId,
) -> Result<(), RequestError> {
let bytes = self.formatter.format(
FrameHeader::new_with_tx_id(request.id, tx_id),
FrameHeader::new_tcp_header(request.id, tx_id),
request.details.function(),
&request.details,
self.decode,
Expand Down Expand Up @@ -253,7 +253,7 @@ mod tests {
T: Serialize + Loggable + Sized,
{
let mut fmt = MbapFormatter::new(AduDecodeLevel::Nothing);
let header = FrameHeader::new_with_tx_id(UnitId::new(1), TxId::new(0));
let header = FrameHeader::new_tcp_header(UnitId::new(1), TxId::new(0));
let bytes = fmt
.format(header, function, payload, PduDecodeLevel::Nothing)
.unwrap();
Expand Down
101 changes: 84 additions & 17 deletions rodbus/src/common/frame.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,23 +52,54 @@ impl std::fmt::Display for TxId {
}
}

#[derive(Debug, Copy, Clone, PartialEq)]
pub(crate) enum FrameDestination {
/// Normal unit ID
UnitId(UnitId),
/// Broadcast ID (only in RTU)
Broadcast,
}

impl FrameDestination {
#[cfg(test)]
pub(crate) fn new_unit_id(value: u8) -> Self {
Self::UnitId(UnitId::new(value))
}

pub(crate) fn value(&self) -> u8 {
match self {
Self::UnitId(unit_id) => unit_id.value,
Self::Broadcast => UnitId::broadcast().value,
}
}
}

impl std::fmt::Display for FrameDestination {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::UnitId(unit_id) => write!(f, "{}", unit_id),
Self::Broadcast => write!(f, "BCAST ({})", UnitId::broadcast()),
}
}
}

#[derive(Copy, Clone)]
pub(crate) struct FrameHeader {
pub(crate) unit_id: UnitId,
pub(crate) destination: FrameDestination,
pub(crate) tx_id: Option<TxId>,
}

impl FrameHeader {
pub(crate) fn new_with_tx_id(unit_id: UnitId, tx_id: TxId) -> Self {
pub(crate) fn new_tcp_header(unit_id: UnitId, tx_id: TxId) -> Self {
FrameHeader {
unit_id,
destination: FrameDestination::UnitId(unit_id),
tx_id: Some(tx_id),
}
}

pub(crate) fn new_without_tx_id(unit_id: UnitId) -> Self {
pub(crate) fn new_rtu_header(destination: FrameDestination) -> Self {
FrameHeader {
unit_id,
destination,
tx_id: None,
}
}
Expand Down Expand Up @@ -190,32 +221,68 @@ pub(crate) trait FrameFormatter {
ex: ExceptionCode,
level: PduDecodeLevel,
) -> Result<&[u8], RequestError> {
if level.enabled() {
tracing::warn!("PDU TX - Modbus exception {:?} ({:#04X})", ex, u8::from(ex));
}

self.error(header, ErrorResponse::new(function, ex))
self.error(header, ErrorResponse::new(function, ex), level)
}

// make a single effort to serialize an exception response
fn error(
&mut self,
header: FrameHeader,
response: ErrorResponse,
level: PduDecodeLevel,
) -> Result<&[u8], RequestError> {
if level.enabled() {
tracing::warn!(
"PDU TX - Modbus exception {:?} ({:#04X})",
response.exception,
response.function
);
}

let len = self.format_impl(header, &response)?;
self.get_full_buffer(len)
}
}

// make a single effort to serialize an exception response
fn unknown_function(
pub(crate) struct NullFrameFormatter;

impl FrameFormatter for NullFrameFormatter {
fn format_impl(
&mut self,
header: FrameHeader,
unknown: u8,
_header: FrameHeader,
_msg: &dyn Serialize,
) -> Result<usize, RequestError> {
Ok(0)
}

fn get_full_buffer_impl(&self, _size: usize) -> Option<&[u8]> {
None
}

fn get_payload_impl(&self, _size: usize) -> Option<&[u8]> {
None
}

fn format<T>(
&mut self,
_header: FrameHeader,
_function: FunctionCode,
_msg: &T,
_level: PduDecodeLevel,
) -> Result<&[u8], RequestError>
where
T: Serialize + Loggable,
{
Ok(&[])
}

fn error(
&mut self,
_header: FrameHeader,
_response: ErrorResponse,
_level: PduDecodeLevel,
) -> Result<&[u8], RequestError> {
let response = ErrorResponse::unknown_function(unknown);
let len = self.format_impl(header, &response)?;
self.get_full_buffer(len)
Ok(&[])
}
}

Expand Down
13 changes: 13 additions & 0 deletions rodbus/src/common/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,17 @@ impl FunctionCode {
_ => None,
}
}

pub(crate) fn supports_broadcast(&self) -> bool {
match self {
Self::ReadCoils => false,
Self::ReadDiscreteInputs => false,
Self::ReadHoldingRegisters => false,
Self::ReadInputRegisters => false,
Self::WriteSingleCoil => true,
Self::WriteSingleRegister => true,
Self::WriteMultipleCoils => true,
Self::WriteMultipleRegisters => true,
}
}
}
5 changes: 5 additions & 0 deletions rodbus/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,8 @@ pub enum InvalidRequest {
CountTooBigForU16(usize),
/// Count too big for specific request
CountTooBigForType(u16, u16),
/// Broadcast is not supported on this type of link (only supported on RTU)
BroadcastNotSupported,
}

impl std::error::Error for InvalidRequest {}
Expand All @@ -294,6 +296,9 @@ impl std::fmt::Display for InvalidRequest {
"the request count of {} exceeds maximum allowed count of {} for this type",
count, max
),
InvalidRequest::BroadcastNotSupported => {
write!(f, "broadcast is not supported on this link",)
}
}
}
}
Expand Down

0 comments on commit 8687905

Please sign in to comment.