use std::time::Duration;
use bacnet_objects::database::ObjectDatabase;
use bacnet_transport::mstp::{MstpConfig, MstpExecutionMode, MstpTransport, SerialPort};
use bacnet_types::error::Error;
use crate::session::{EndpointSession, SessionConfig, SessionRole};
pub struct MstpEndpointBuilder<S: SerialPort> {
serial: Option<S>,
this_station: u8,
max_master: u8,
max_info_frames: u8,
baud_rate: u32,
extended_frames: bool,
usage_timeout: Duration,
reply_timeout: Duration,
transmit_latency_pad: Duration,
execution_mode: MstpExecutionMode,
role: SessionRole,
session: SessionConfig,
database: Option<ObjectDatabase>,
identity: Option<crate::identity::DeviceIdentity>,
}
impl<S: SerialPort> MstpEndpointBuilder<S> {
pub fn new(serial: S, this_station: u8) -> Self {
let link = MstpConfig::default();
Self {
serial: Some(serial),
this_station,
max_master: 127,
max_info_frames: 1,
baud_rate: 9600,
extended_frames: false,
usage_timeout: link.usage_timeout,
reply_timeout: link.reply_timeout,
transmit_latency_pad: link.transmit_latency_pad,
execution_mode: MstpExecutionMode::default(),
role: SessionRole::Both,
session: SessionConfig::default(),
database: None,
identity: None,
}
}
pub fn max_master(mut self, max_master: u8) -> Self {
self.max_master = max_master;
self
}
pub fn max_info_frames(mut self, max_info_frames: u8) -> Self {
self.max_info_frames = max_info_frames;
self
}
pub fn baud_rate(mut self, baud_rate: u32) -> Self {
self.baud_rate = baud_rate;
self
}
pub fn extended_frames(mut self, extended_frames: bool) -> Self {
self.extended_frames = extended_frames;
self
}
pub fn usage_timeout(mut self, usage_timeout: Duration) -> Self {
self.usage_timeout = usage_timeout;
self
}
pub fn reply_timeout(mut self, reply_timeout: Duration) -> Self {
self.reply_timeout = reply_timeout;
self
}
pub fn transmit_latency_pad(mut self, pad: Duration) -> Self {
self.transmit_latency_pad = pad;
self
}
pub fn execution_mode(mut self, mode: MstpExecutionMode) -> Self {
self.execution_mode = mode;
self
}
pub fn role(mut self, role: SessionRole) -> Self {
self.role = role;
self
}
pub fn queue_capacity(mut self, capacity: usize) -> Self {
self.session.queue_capacity = capacity;
self
}
pub fn read_work_limit(mut self, limit: usize) -> Self {
self.session.read_work_limit = limit;
self
}
pub fn client_timers(mut self, timeout_ms: u64, retries: u8) -> Self {
self.session.apdu_timeout_ms = timeout_ms;
self.session.apdu_retries = retries;
self
}
pub fn min_request_interval_ms(mut self, ms: u64) -> Self {
self.session.min_request_interval_ms = ms;
self
}
pub fn database(mut self, db: ObjectDatabase) -> Self {
self.database = Some(db);
self
}
pub fn device(mut self, identity: crate::identity::DeviceIdentity) -> Self {
self.identity = Some(identity);
self
}
pub fn validate_only(&self) -> Result<(), Error> {
self.config().validate()?;
match &self.serial {
Some(serial) => serial.echo().validate(),
None => Ok(()),
}
}
fn config(&self) -> MstpConfig {
MstpConfig {
this_station: self.this_station,
max_master: self.max_master,
max_info_frames: self.max_info_frames,
baud_rate: self.baud_rate,
extended_frames: self.extended_frames,
usage_timeout: self.usage_timeout,
reply_timeout: self.reply_timeout,
transmit_latency_pad: self.transmit_latency_pad,
}
}
pub fn build_transport(mut self) -> Result<MstpTransport<S>, Error> {
self.validate_only()?;
let config = self.config();
let serial = self
.serial
.take()
.ok_or_else(|| Error::Encoding("MS/TP endpoint serial owner is missing".into()))?;
Ok(MstpTransport::new(serial, config).with_execution_mode(self.execution_mode))
}
pub fn build_session(mut self) -> Result<EndpointSession<MstpTransport<S>>, Error> {
self.validate_only()?;
let role = self.role;
let session = self.session.clone();
let database = self.database.take();
let identity = self.identity.take();
let transport = self.build_transport()?;
let diagnostics = transport.diagnostics();
let mut endpoint = EndpointSession::new(transport, role, session)?;
endpoint.mstp_diagnostics = Some(diagnostics);
if let Some(db) = database {
endpoint = endpoint.with_database(db);
}
if let Some(id) = identity {
endpoint = endpoint.device(id);
}
Ok(endpoint)
}
}