use mbus_core::function_codes::public::DiagnosticSubFunction;
use mbus_core::models::diagnostic::{ObjectId, ReadDeviceIdCode};
use mbus_core::transport::UnitIdOrSlaveAddr;
use super::pool::MbusClientId;
#[cfg(feature = "network-tcp")]
use super::pool::with_tcp_client;
#[cfg(any(feature = "serial-rtu", feature = "serial-ascii"))]
use super::pool::with_serial_client_uniform;
use crate::c::error::MbusStatusCode;
macro_rules! tcp_diag_fn {
($name:ident, $method:ident $(, $arg:ident : $ty:ty)*) => {
#[unsafe(no_mangle)]
pub extern "C" fn $name(
id: MbusClientId,
txn_id: u16,
unit_id: u8,
$($arg: $ty,)*
) -> MbusStatusCode {
with_tcp_client(id, |inner| {
let uid = match UnitIdOrSlaveAddr::new(unit_id) { Ok(u) => u, Err(e) => return MbusStatusCode::from(e) };
match inner.$method(txn_id, uid $(, $arg)*) {
Ok(()) => MbusStatusCode::MbusOk,
Err(e) => MbusStatusCode::from(e),
}
}).unwrap_or_else(|e| e)
}
};
}
macro_rules! serial_diag_fn {
($name:ident, $method:ident $(, $arg:ident : $ty:ty)*) => {
#[unsafe(no_mangle)]
pub extern "C" fn $name(
id: MbusClientId,
txn_id: u16,
unit_id: u8,
$($arg: $ty,)*
) -> MbusStatusCode {
with_serial_client_uniform!(id, |inner| {
let uid = match UnitIdOrSlaveAddr::new(unit_id) { Ok(u) => u, Err(e) => return MbusStatusCode::from(e) };
match inner.$method(txn_id, uid $(, $arg)*) {
Ok(()) => MbusStatusCode::MbusOk,
Err(e) => MbusStatusCode::from(e),
}
}).unwrap_or_else(|e| e)
}
};
}
#[cfg(all(feature = "diagnostics", feature = "network-tcp"))]
tcp_diag_fn!(mbus_tcp_read_exception_status, read_exception_status);
#[cfg(all(
feature = "diagnostics",
any(feature = "serial-rtu", feature = "serial-ascii")
))]
serial_diag_fn!(mbus_serial_read_exception_status, read_exception_status);
#[cfg(all(feature = "diagnostics", feature = "network-tcp"))]
tcp_diag_fn!(mbus_tcp_get_comm_event_counter, get_comm_event_counter);
#[cfg(all(
feature = "diagnostics",
any(feature = "serial-rtu", feature = "serial-ascii")
))]
serial_diag_fn!(mbus_serial_get_comm_event_counter, get_comm_event_counter);
#[cfg(all(feature = "diagnostics", feature = "network-tcp"))]
tcp_diag_fn!(mbus_tcp_get_comm_event_log, get_comm_event_log);
#[cfg(all(
feature = "diagnostics",
any(feature = "serial-rtu", feature = "serial-ascii")
))]
serial_diag_fn!(mbus_serial_get_comm_event_log, get_comm_event_log);
#[cfg(all(feature = "diagnostics", feature = "network-tcp"))]
tcp_diag_fn!(mbus_tcp_report_server_id, report_server_id);
#[cfg(all(
feature = "diagnostics",
any(feature = "serial-rtu", feature = "serial-ascii")
))]
serial_diag_fn!(mbus_serial_report_server_id, report_server_id);
#[cfg(all(feature = "diagnostics", feature = "network-tcp"))]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mbus_tcp_diagnostics(
id: MbusClientId,
txn_id: u16,
unit_id: u8,
sub_fn: u16,
data: *const u16,
data_len: u16,
) -> MbusStatusCode {
with_tcp_client(id, |inner| {
diagnostics_impl(inner, txn_id, unit_id, sub_fn, data, data_len)
})
.unwrap_or_else(|e| e)
}
#[cfg(all(
feature = "diagnostics",
any(feature = "serial-rtu", feature = "serial-ascii")
))]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mbus_serial_diagnostics(
id: MbusClientId,
txn_id: u16,
unit_id: u8,
sub_fn: u16,
data: *const u16,
data_len: u16,
) -> MbusStatusCode {
with_serial_client_uniform!(id, |inner| {
diagnostics_impl(inner, txn_id, unit_id, sub_fn, data, data_len)
})
.unwrap_or_else(|e| e)
}
#[cfg(feature = "diagnostics")]
fn diagnostics_impl<T, A, const N: usize>(
inner: &mut mbus_client::services::ClientServices<T, A, N>,
txn_id: u16,
unit_id: u8,
sub_fn: u16,
data: *const u16,
data_len: u16,
) -> MbusStatusCode
where
T: mbus_core::transport::Transport,
A: mbus_client::services::ClientCommon + mbus_client::app::DiagnosticsResponse,
{
let uid = match UnitIdOrSlaveAddr::new(unit_id) {
Ok(u) => u,
Err(e) => return MbusStatusCode::from(e),
};
let sub_function = match DiagnosticSubFunction::try_from(sub_fn) {
Ok(f) => f,
Err(e) => return MbusStatusCode::from(e),
};
let slice = if data_len > 0 && !data.is_null() {
unsafe { core::slice::from_raw_parts(data, data_len as usize) }
} else {
&[]
};
match inner.diagnostics(txn_id, uid, sub_function, slice) {
Ok(()) => MbusStatusCode::MbusOk,
Err(e) => MbusStatusCode::from(e),
}
}
#[cfg(all(feature = "diagnostics", feature = "network-tcp"))]
#[unsafe(no_mangle)]
pub extern "C" fn mbus_tcp_read_device_identification(
id: MbusClientId,
txn_id: u16,
unit_id: u8,
dev_id_code: u8,
object_id: u8,
) -> MbusStatusCode {
with_tcp_client(id, |inner| {
read_device_id_impl(inner, txn_id, unit_id, dev_id_code, object_id)
})
.unwrap_or_else(|e| e)
}
#[cfg(all(
feature = "diagnostics",
any(feature = "serial-rtu", feature = "serial-ascii")
))]
#[unsafe(no_mangle)]
pub extern "C" fn mbus_serial_read_device_identification(
id: MbusClientId,
txn_id: u16,
unit_id: u8,
dev_id_code: u8,
object_id: u8,
) -> MbusStatusCode {
with_serial_client_uniform!(id, |inner| {
read_device_id_impl(inner, txn_id, unit_id, dev_id_code, object_id)
})
.unwrap_or_else(|e| e)
}
#[cfg(feature = "diagnostics")]
fn read_device_id_impl<T, A, const N: usize>(
inner: &mut mbus_client::services::ClientServices<T, A, N>,
txn_id: u16,
unit_id: u8,
dev_id_code: u8,
object_id: u8,
) -> MbusStatusCode
where
T: mbus_core::transport::Transport,
A: mbus_client::services::ClientCommon + mbus_client::app::DiagnosticsResponse,
{
let uid = match UnitIdOrSlaveAddr::new(unit_id) {
Ok(u) => u,
Err(e) => return MbusStatusCode::from(e),
};
let code = match ReadDeviceIdCode::try_from(dev_id_code) {
Ok(c) => c,
Err(e) => return MbusStatusCode::from(e),
};
let oid = ObjectId::from(object_id);
match inner.read_device_identification(txn_id, uid, code, oid) {
Ok(()) => MbusStatusCode::MbusOk,
Err(e) => MbusStatusCode::from(e),
}
}