pub struct BACnetClient<T: TransportPort = AnyTransport> { /* private fields */ }Expand description
BACnet client with low-level and high-level request APIs.
T is the transport the client runs over. It defaults to
AnyTransport, the type the builders’ build_any() methods return,
so BACnetClient alone names a client over any built-in transport;
BACnetClient<BipTransport> and the like name one over a concrete
transport.
Implementations§
Source§impl<T: TransportPort + 'static> BACnetClient<T>
impl<T: TransportPort + 'static> BACnetClient<T>
Sourcepub async fn confirmed_audit_notification(
&self,
destination_mac: &[u8],
request: &AuditNotificationRequest,
) -> Result<(), Error>
pub async fn confirmed_audit_notification( &self, destination_mac: &[u8], request: &AuditNotificationRequest, ) -> Result<(), Error>
Send a typed ConfirmedAuditNotification to a directly addressed MAC.
The request uses the existing qualified Clause 21 Audit wire model.
Sourcepub async fn unconfirmed_audit_notification(
&self,
destination_mac: &[u8],
request: &AuditNotificationRequest,
) -> Result<(), Error>
pub async fn unconfirmed_audit_notification( &self, destination_mac: &[u8], request: &AuditNotificationRequest, ) -> Result<(), Error>
Send a typed UnconfirmedAuditNotification to a directly addressed MAC.
The request uses the existing qualified Clause 21 Audit wire model and completes after transport send without waiting for a response.
Sourcepub async fn audit_log_query(
&self,
destination_mac: &[u8],
request: &AuditLogQueryRequest,
) -> Result<AuditLogQueryAck, Error>
pub async fn audit_log_query( &self, destination_mac: &[u8], request: &AuditLogQueryRequest, ) -> Result<AuditLogQueryAck, Error>
Query an Audit Log at a directly addressed MAC and decode its typed ACK.
Request and response use the existing qualified Clause 21 Audit wire model; this helper does not reinterpret query fields or execution.
Source§impl BACnetClient<Bip6Transport>
impl BACnetClient<Bip6Transport>
Sourcepub fn bip6_builder() -> Bip6ClientBuilder
Available on crate feature ipv6 only.
pub fn bip6_builder() -> Bip6ClientBuilder
ipv6 only.Create a BIP6-specific builder for BACnet/IPv6 transport.
Source§impl<T: TransportPort + 'static> BACnetClient<T>
impl<T: TransportPort + 'static> BACnetClient<T>
Sourcepub async fn subscribe_cov(
&self,
destination_mac: &[u8],
subscriber_process_identifier: u32,
monitored_object_identifier: ObjectIdentifier,
confirmed: bool,
lifetime: Option<u32>,
) -> Result<(), Error>
pub async fn subscribe_cov( &self, destination_mac: &[u8], subscriber_process_identifier: u32, monitored_object_identifier: ObjectIdentifier, confirmed: bool, lifetime: Option<u32>, ) -> Result<(), Error>
Subscribe to COV notifications for an object at a directly reachable MAC address.
Sourcepub async fn subscribe_cov_to_device(
&self,
device_instance: u32,
subscriber_process_identifier: u32,
monitored_object_identifier: ObjectIdentifier,
confirmed: bool,
lifetime: Option<u32>,
) -> Result<(), Error>
pub async fn subscribe_cov_to_device( &self, device_instance: u32, subscriber_process_identifier: u32, monitored_object_identifier: ObjectIdentifier, confirmed: bool, lifetime: Option<u32>, ) -> Result<(), Error>
Subscribe to COV notifications for a discovered device, auto-routing if needed.
Sourcepub async fn unsubscribe_cov(
&self,
destination_mac: &[u8],
subscriber_process_identifier: u32,
monitored_object_identifier: ObjectIdentifier,
) -> Result<(), Error>
pub async fn unsubscribe_cov( &self, destination_mac: &[u8], subscriber_process_identifier: u32, monitored_object_identifier: ObjectIdentifier, ) -> Result<(), Error>
Cancel a COV subscription on a remote device.
Sourcepub async fn unsubscribe_cov_to_device(
&self,
device_instance: u32,
subscriber_process_identifier: u32,
monitored_object_identifier: ObjectIdentifier,
) -> Result<(), Error>
pub async fn unsubscribe_cov_to_device( &self, device_instance: u32, subscriber_process_identifier: u32, monitored_object_identifier: ObjectIdentifier, ) -> Result<(), Error>
Cancel a COV subscription for a discovered device, auto-routing if needed.
Sourcepub async fn subscribe_cov_property(
&self,
destination_mac: &[u8],
subscription: CovPropertySubscription,
) -> Result<(), Error>
pub async fn subscribe_cov_property( &self, destination_mac: &[u8], subscription: CovPropertySubscription, ) -> Result<(), Error>
Subscribe to COV notifications for a single property at a directly reachable MAC address.
Lifetime must be positive; use unsubscribe_cov_property for cancellation.
Sourcepub async fn subscribe_cov_property_to_device(
&self,
device_instance: u32,
subscription: CovPropertySubscription,
) -> Result<(), Error>
pub async fn subscribe_cov_property_to_device( &self, device_instance: u32, subscription: CovPropertySubscription, ) -> Result<(), Error>
Subscribe to COV notifications for a single property on a discovered device, auto-routing if needed. Lifetime must be positive; cancellation has a separate method.
Sourcepub async fn unsubscribe_cov_property(
&self,
destination_mac: &[u8],
subscriber_process_identifier: u32,
monitored_object_identifier: ObjectIdentifier,
monitored_property_identifier: PropertyIdentifier,
monitored_property_array_index: Option<u32>,
) -> Result<(), Error>
pub async fn unsubscribe_cov_property( &self, destination_mac: &[u8], subscriber_process_identifier: u32, monitored_object_identifier: ObjectIdentifier, monitored_property_identifier: PropertyIdentifier, monitored_property_array_index: Option<u32>, ) -> Result<(), Error>
Cancel a single-property COV subscription at a directly reachable MAC address.
Sourcepub async fn unsubscribe_cov_property_to_device(
&self,
device_instance: u32,
subscriber_process_identifier: u32,
monitored_object_identifier: ObjectIdentifier,
monitored_property_identifier: PropertyIdentifier,
monitored_property_array_index: Option<u32>,
) -> Result<(), Error>
pub async fn unsubscribe_cov_property_to_device( &self, device_instance: u32, subscriber_process_identifier: u32, monitored_object_identifier: ObjectIdentifier, monitored_property_identifier: PropertyIdentifier, monitored_property_array_index: Option<u32>, ) -> Result<(), Error>
Cancel a single-property COV subscription for a discovered device, auto-routing if needed.
Sourcepub async fn unsubscribe_cov_target(
&self,
target: &CovTarget,
subscriber_process_identifier: u32,
monitored_object_identifier: ObjectIdentifier,
) -> Result<(), Error>
pub async fn unsubscribe_cov_target( &self, target: &CovTarget, subscriber_process_identifier: u32, monitored_object_identifier: ObjectIdentifier, ) -> Result<(), Error>
Cancel a COV subscription at target, the device a
manage_cov_subscription went to.
The cancel goes the way the subscription did, with no device-table
lookup, so it reaches the same device even if the table has since
dropped or moved its entry.
Sourcepub async fn unsubscribe_cov_property_target(
&self,
target: &CovTarget,
subscriber_process_identifier: u32,
monitored_object_identifier: ObjectIdentifier,
monitored_property_identifier: PropertyIdentifier,
monitored_property_array_index: Option<u32>,
) -> Result<(), Error>
pub async fn unsubscribe_cov_property_target( &self, target: &CovTarget, subscriber_process_identifier: u32, monitored_object_identifier: ObjectIdentifier, monitored_property_identifier: PropertyIdentifier, monitored_property_array_index: Option<u32>, ) -> Result<(), Error>
Cancel a single-property COV subscription at target:
unsubscribe_cov_target for one
property.
Sourcepub fn cov_notifications(&self) -> Receiver<ReceivedCOVNotification>
pub fn cov_notifications(&self) -> Receiver<ReceivedCOVNotification>
Get a receiver for incoming COV notifications. Each call returns a new independent receiver.
Source§impl<T: TransportPort + 'static> BACnetClient<T>
impl<T: TransportPort + 'static> BACnetClient<T>
Sourcepub async fn manage_cov_subscription(
self: Arc<Self>,
target: CovTarget,
subscriber_process_identifier: u32,
monitored_object_identifier: ObjectIdentifier,
confirmed: bool,
lifetime: u32,
options: ManagedCOVSubscriptionOptions,
) -> Result<ManagedCOVSubscription, Error>
pub async fn manage_cov_subscription( self: Arc<Self>, target: CovTarget, subscriber_process_identifier: u32, monitored_object_identifier: ObjectIdentifier, confirmed: bool, lifetime: u32, options: ManagedCOVSubscriptionOptions, ) -> Result<ManagedCOVSubscription, Error>
Start a managed finite SubscribeCOV renewal task for the device at
target, on this network or behind a router.
The helper sends the initial SubscribeCOV request, then renews the same
subscription before expiry. It also listens to Self::cov_notifications
and moves the renewal earlier when matching notifications report a lower
time_remaining than the requested lifetime-based schedule. A
notification matches when it comes from target’s address (see
CovTarget) and names this subscription’s process and object.
The helper only renews; after ManagedCOVSubscription::stop, cancel
with Self::unsubscribe_cov_target.
This method takes Arc<Self> so the renewal task can keep the client alive.
Call it as Arc::clone(&client).manage_cov_subscription(...).
Sourcepub async fn manage_cov_property_subscription(
self: Arc<Self>,
target: CovTarget,
subscription: CovPropertySubscription,
options: ManagedCOVSubscriptionOptions,
) -> Result<ManagedCOVSubscription, Error>
pub async fn manage_cov_property_subscription( self: Arc<Self>, target: CovTarget, subscription: CovPropertySubscription, options: ManagedCOVSubscriptionOptions, ) -> Result<ManagedCOVSubscription, Error>
Start a managed finite SubscribeCOVProperty renewal task for the
device at target: Self::manage_cov_subscription for one property.
Every renewal repeats subscription as given, its lifetime included.
The helper only renews; after ManagedCOVSubscription::stop, cancel
with Self::unsubscribe_cov_property_target.
Source§impl<T: TransportPort + 'static> BACnetClient<T>
impl<T: TransportPort + 'static> BACnetClient<T>
Sourcepub fn device_events(&self) -> Receiver<DeviceEvent>
pub fn device_events(&self) -> Receiver<DeviceEvent>
Get a receiver for device discovery events. Each call returns a new independent receiver.
Events are notification-only; discovered_devices() remains the
authoritative snapshot of the current discovery table.
Sourcepub fn device_collision_events(&self) -> Receiver<DeviceCollisionEvent>
pub fn device_collision_events(&self) -> Receiver<DeviceCollisionEvent>
Get a receiver for Device-instance collision notifications. Each call returns a new independent receiver.
Delivery is notification-only and best-effort: receivers can lag and
missed notifications do not affect collision handling. The retained
snapshot identifies the discovery-table row that remains authoritative;
the conflicting incoming snapshot is not installed in the table.
Source§impl<T: TransportPort + 'static> BACnetClient<T>
impl<T: TransportPort + 'static> BACnetClient<T>
Sourcepub async fn device_communication_control(
&self,
destination_mac: &[u8],
enable_disable: EnableDisable,
time_duration: Option<u16>,
password: Option<String>,
) -> Result<(), Error>
pub async fn device_communication_control( &self, destination_mac: &[u8], enable_disable: EnableDisable, time_duration: Option<u16>, password: Option<String>, ) -> Result<(), Error>
Send DeviceCommunicationControl to a remote device.
Sourcepub async fn reinitialize_device(
&self,
destination_mac: &[u8],
reinitialized_state: ReinitializedState,
password: Option<String>,
) -> Result<(), Error>
pub async fn reinitialize_device( &self, destination_mac: &[u8], reinitialized_state: ReinitializedState, password: Option<String>, ) -> Result<(), Error>
Send ReinitializeDevice to a remote device.
Sourcepub async fn time_synchronization(
&self,
destination_mac: &[u8],
date: Date,
time: Time,
) -> Result<(), Error>
pub async fn time_synchronization( &self, destination_mac: &[u8], date: Date, time: Time, ) -> Result<(), Error>
Send a TimeSynchronization request (unconfirmed, no response expected).
Sourcepub async fn utc_time_synchronization(
&self,
destination_mac: &[u8],
date: Date,
time: Time,
) -> Result<(), Error>
pub async fn utc_time_synchronization( &self, destination_mac: &[u8], date: Date, time: Time, ) -> Result<(), Error>
Send a UTCTimeSynchronization request (unconfirmed, no response expected).
Sourcepub async fn device_communication_control_from_device(
&self,
device_instance: u32,
enable_disable: EnableDisable,
time_duration: Option<u16>,
password: Option<String>,
) -> Result<(), Error>
pub async fn device_communication_control_from_device( &self, device_instance: u32, enable_disable: EnableDisable, time_duration: Option<u16>, password: Option<String>, ) -> Result<(), Error>
Send DeviceCommunicationControl to a discovered device, routing when needed.
Preserves the request fields and confirmation semantics of Self::device_communication_control.
A missing device fails before sending.
Sourcepub async fn reinitialize_device_from_device(
&self,
device_instance: u32,
reinitialized_state: ReinitializedState,
password: Option<String>,
) -> Result<(), Error>
pub async fn reinitialize_device_from_device( &self, device_instance: u32, reinitialized_state: ReinitializedState, password: Option<String>, ) -> Result<(), Error>
Send ReinitializeDevice to a discovered device, routing when needed.
Preserves the request fields and confirmation semantics of Self::reinitialize_device.
A missing device fails before sending.
Sourcepub async fn time_synchronization_from_device(
&self,
device_instance: u32,
date: Date,
time: Time,
) -> Result<(), Error>
pub async fn time_synchronization_from_device( &self, device_instance: u32, date: Date, time: Time, ) -> Result<(), Error>
Send local time synchronization to a discovered device, routing when needed.
This unconfirmed service sends once without waiting for a response. Success means local send completion, not acceptance by the remote clock.
Sourcepub async fn utc_time_synchronization_from_device(
&self,
device_instance: u32,
date: Date,
time: Time,
) -> Result<(), Error>
pub async fn utc_time_synchronization_from_device( &self, device_instance: u32, date: Date, time: Time, ) -> Result<(), Error>
Send UTC time synchronization to a discovered device, routing when needed.
This unconfirmed service sends once without waiting for a response. Success means local send completion, not acceptance by the remote clock.
Source§impl<T: TransportPort + 'static> BACnetClient<T>
impl<T: TransportPort + 'static> BACnetClient<T>
Sourcepub async fn who_is(
&self,
range: Option<DeviceInstanceRange>,
) -> Result<(), Error>
pub async fn who_is( &self, range: Option<DeviceInstanceRange>, ) -> Result<(), Error>
Send a WhoIs broadcast to discover devices.
range limits the devices that answer to those whose instance lies
in it; None asks every device. A DeviceInstanceRange holds both
limits, low no greater than high, so the request can’t carry one
alone (Clause 16.10, #1483).
Sourcepub async fn who_is_directed(
&self,
destination_mac: &[u8],
range: Option<DeviceInstanceRange>,
) -> Result<(), Error>
pub async fn who_is_directed( &self, destination_mac: &[u8], range: Option<DeviceInstanceRange>, ) -> Result<(), Error>
Send a directed (unicast) WhoIs to a specific device, with the same
range as Self::who_is.
Sourcepub async fn who_is_network(
&self,
dest_network: u16,
range: Option<DeviceInstanceRange>,
) -> Result<(), Error>
pub async fn who_is_network( &self, dest_network: u16, range: Option<DeviceInstanceRange>, ) -> Result<(), Error>
Send a WhoIs broadcast to a specific remote network, with the same
range as Self::who_is.
Sourcepub async fn who_has(
&self,
object: WhoHasObject,
range: Option<DeviceInstanceRange>,
) -> Result<(), Error>
pub async fn who_has( &self, object: WhoHasObject, range: Option<DeviceInstanceRange>, ) -> Result<(), Error>
Send a WhoHas as a global broadcast, as Self::who_is does, so that
devices behind routers hear it too, to find an object by identifier
or name, asking the devices in range, or every device for None
(Clause 16.9, #1483).
Sourcepub async fn discovered_devices(&self) -> Vec<DiscoveredDevice>
pub async fn discovered_devices(&self) -> Vec<DiscoveredDevice>
Get a snapshot of all discovered devices.
Sourcepub async fn get_device(&self, instance: u32) -> Option<DiscoveredDevice>
pub async fn get_device(&self, instance: u32) -> Option<DiscoveredDevice>
Look up a discovered device by instance number.
Sourcepub async fn clear_devices(&self)
pub async fn clear_devices(&self)
Clear the discovered devices table.
Sourcepub async fn add_device(&self, instance: u32, mac: &[u8]) -> Result<(), Error>
pub async fn add_device(&self, instance: u32, mac: &[u8]) -> Result<(), Error>
Manually register a local device in the device table.
Useful for adding known devices without requiring WhoIs/IAm exchange. The metadata defaults below (max_apdu_length 1476, segmentation NONE, vendor_id 0) are local/manual registration defaults, not values learned from an I-Am; requests to this device are bounded by them until a real I-Am refreshes the row. The row is unambiguously local: routed lookups by SNET/SADR will not match it.
Sourcepub async fn add_routed_device(
&self,
config: RoutedDeviceConfig,
) -> Result<(), Error>
pub async fn add_routed_device( &self, config: RoutedDeviceConfig, ) -> Result<(), Error>
Manually register a routed peer whose address and capabilities are already known, without fabricating an I-Am exchange.
router_mac is only the immediate transport next hop (Clause 6.2.2);
the peer’s identity is the (remote_network, remote_mac) SNET/SADR
pair, which is what BACnetClient::confirmed_request_routed and the
device-table routed lookup match against. The supplied Max APDU
Length Accepted, segmentation capability, and optional Max Segments
Accepted are treated as advertised peer limits for request sizing.
Vendor ID is recorded as 0 when unknown; this does not affect routing or request-size decisions.
Malformed routing metadata is rejected deliberately: an empty
router_mac or remote_mac cannot identify a next hop or a peer, so
such a registration returns Error::Encoding and the table is left
unchanged (an empty SADR could never satisfy a routed lookup). A
remote_network outside 1..=65534 or a remote_mac longer than
MAX_MAC_LEN is refused the same way, since
BACnetClient::confirmed_request_routed refuses that destination.
Source§impl<T: TransportPort + 'static> BACnetClient<T>
impl<T: TransportPort + 'static> BACnetClient<T>
Sourcepub fn event_notifications(&self) -> Receiver<ReceivedEventNotification>
pub fn event_notifications(&self) -> Receiver<ReceivedEventNotification>
Subscribe to incoming event notifications with a new independent receiver.
Only subsequent notifications are delivered. Slow receivers observe broadcast lag; no receivers or lag never prevent a valid confirmed request from being acknowledged. The wire acknowledgment confirms receipt, not operator acknowledgment or durable application processing. Stopping the client ends dispatch; dropping it closes the channel after queued messages.
Source§impl<T: TransportPort + 'static> BACnetClient<T>
impl<T: TransportPort + 'static> BACnetClient<T>
Sourcepub async fn acknowledge_alarm_request(
&self,
destination_mac: &[u8],
request: &AcknowledgeAlarmRequest,
) -> Result<(), Error>
pub async fn acknowledge_alarm_request( &self, destination_mac: &[u8], request: &AcknowledgeAlarmRequest, ) -> Result<(), Error>
Send a caller-supplied AcknowledgeAlarm request without fabricating fields.
Sourcepub async fn acknowledge_alarm_request_from_device(
&self,
device_instance: u32,
request: &AcknowledgeAlarmRequest,
) -> Result<(), Error>
pub async fn acknowledge_alarm_request_from_device( &self, device_instance: u32, request: &AcknowledgeAlarmRequest, ) -> Result<(), Error>
Send exact caller-supplied alarm acknowledgment fields to a discovered device.
Routes when needed and preserves both timestamps, confirmation and errors
from Self::acknowledge_alarm_request. A missing device fails before sending.
Sourcepub async fn acknowledge_alarm(
&self,
destination_mac: &[u8],
acknowledging_process_identifier: u32,
event_object_identifier: ObjectIdentifier,
event_state_acknowledged: EventState,
acknowledgment_source: &str,
) -> Result<(), Error>
👎Deprecated: use acknowledge_alarm_request with caller-supplied correlation timestamps
pub async fn acknowledge_alarm( &self, destination_mac: &[u8], acknowledging_process_identifier: u32, event_object_identifier: ObjectIdentifier, event_state_acknowledged: EventState, acknowledgment_source: &str, ) -> Result<(), Error>
use acknowledge_alarm_request with caller-supplied correlation timestamps
Acknowledge an alarm on a remote device.
This compatibility helper fabricates sequence-zero timestamps, which
are not a valid general correlation mechanism. Use
Self::acknowledge_alarm_request with the original notification’s
exact timestamp and a caller-selected acknowledgment time.
Sourcepub async fn atomic_read_file(
&self,
destination_mac: &[u8],
file_identifier: ObjectIdentifier,
access: FileAccessMethod,
) -> Result<Bytes, Error>
pub async fn atomic_read_file( &self, destination_mac: &[u8], file_identifier: ObjectIdentifier, access: FileAccessMethod, ) -> Result<Bytes, Error>
Read file data from a remote device (stream or record access).
Sourcepub async fn atomic_read_file_decoded(
&self,
destination_mac: &[u8],
file_identifier: ObjectIdentifier,
access: FileAccessMethod,
) -> Result<AtomicReadFileAck, Error>
pub async fn atomic_read_file_decoded( &self, destination_mac: &[u8], file_identifier: ObjectIdentifier, access: FileAccessMethod, ) -> Result<AtomicReadFileAck, Error>
Read and strictly decode one AtomicReadFile ACK window.
This additive typed boundary validates that the ACK uses the requested
stream/record access arm and does not return more octets or records than
requested. Use Self::atomic_read_file when the raw encoded service
payload is required for compatibility.
Sourcepub async fn atomic_read_file_from_device(
&self,
device_instance: u32,
file_identifier: ObjectIdentifier,
access: FileAccessMethod,
) -> Result<Bytes, Error>
pub async fn atomic_read_file_from_device( &self, device_instance: u32, file_identifier: ObjectIdentifier, access: FileAccessMethod, ) -> Result<Bytes, Error>
Read raw file acknowledgement bytes from a discovered device.
Routes stream and record access when needed. The device address is
resolved once for this request; a missing device fails before sending.
Use Self::atomic_read_file_decoded_from_device for a checked ACK.
Sourcepub async fn atomic_read_file_decoded_from_device(
&self,
device_instance: u32,
file_identifier: ObjectIdentifier,
access: FileAccessMethod,
) -> Result<AtomicReadFileAck, Error>
pub async fn atomic_read_file_decoded_from_device( &self, device_instance: u32, file_identifier: ObjectIdentifier, access: FileAccessMethod, ) -> Result<AtomicReadFileAck, Error>
Read one checked file window from a discovered device, routing if needed.
Preserves the access-arm and window checks of
Self::atomic_read_file_decoded. Successive calls resolve separately;
a sequence of windows is not an atomic snapshot of the remote file.
Sourcepub async fn atomic_write_file(
&self,
destination_mac: &[u8],
file_identifier: ObjectIdentifier,
access: FileWriteAccessMethod,
) -> Result<Bytes, Error>
pub async fn atomic_write_file( &self, destination_mac: &[u8], file_identifier: ObjectIdentifier, access: FileWriteAccessMethod, ) -> Result<Bytes, Error>
Write file data to a remote device (stream or record access).
Sourcepub async fn atomic_write_file_from_device(
&self,
device_instance: u32,
file_identifier: ObjectIdentifier,
access: FileWriteAccessMethod,
) -> Result<Bytes, Error>
pub async fn atomic_write_file_from_device( &self, device_instance: u32, file_identifier: ObjectIdentifier, access: FileWriteAccessMethod, ) -> Result<Bytes, Error>
Write stream or record data to a discovered device, routing if needed.
Returns the raw acknowledgement, as in Self::atomic_write_file.
A missing device fails before sending.
Sourcepub async fn add_list_element(
&self,
destination_mac: &[u8],
object_identifier: ObjectIdentifier,
property_identifier: PropertyIdentifier,
property_array_index: Option<u32>,
list_of_elements: Vec<u8>,
) -> Result<(), Error>
pub async fn add_list_element( &self, destination_mac: &[u8], object_identifier: ObjectIdentifier, property_identifier: PropertyIdentifier, property_array_index: Option<u32>, list_of_elements: Vec<u8>, ) -> Result<(), Error>
Add elements to a list property on a remote device.
Rejects index zero, empty elements and malformed tag framing before transaction admission or traffic. Element datatype remains target-owned.
Sourcepub async fn remove_list_element(
&self,
destination_mac: &[u8],
object_identifier: ObjectIdentifier,
property_identifier: PropertyIdentifier,
property_array_index: Option<u32>,
list_of_elements: Vec<u8>,
) -> Result<(), Error>
pub async fn remove_list_element( &self, destination_mac: &[u8], object_identifier: ObjectIdentifier, property_identifier: PropertyIdentifier, property_array_index: Option<u32>, list_of_elements: Vec<u8>, ) -> Result<(), Error>
Remove elements from a list property on a remote device.
Rejects index zero, empty elements and malformed tag framing before transaction admission or traffic. Element datatype remains target-owned.
Source§impl<T: TransportPort + 'static> BACnetClient<T>
impl<T: TransportPort + 'static> BACnetClient<T>
Sourcepub fn i_have_events(&self) -> Receiver<ReceivedIHave>
pub fn i_have_events(&self) -> Receiver<ReceivedIHave>
Get a receiver for every I-Have this client receives, an answer to
its own Who-Has or not (a device may send one at any time). Each call
returns a new independent receiver. A receiver that falls
I_HAVE_CHANNEL_CAPACITY behind loses the oldest.
Sourcepub async fn who_has_replies(
&self,
object: WhoHasObject,
range: Option<DeviceInstanceRange>,
wait: Duration,
) -> Result<Vec<ReceivedIHave>, Error>
pub async fn who_has_replies( &self, object: WhoHasObject, range: Option<DeviceInstanceRange>, wait: Duration, ) -> Result<Vec<ReceivedIHave>, Error>
Send a Who-Has as Self::who_has does, then collect the I-Haves
that answer it (ReceivedIHave::answers) for wait, one per
device and object, in the order they arrived. No answer is an empty
list, not an error.
Source§impl<T: TransportPort + 'static> BACnetClient<T>
impl<T: TransportPort + 'static> BACnetClient<T>
Sourcepub async fn start(config: ClientConfig, transport: T) -> Result<Self, Error>
pub async fn start(config: ClientConfig, transport: T) -> Result<Self, Error>
Start the client: bind transport, start network layer, spawn dispatch.
Sourcepub async fn start_with_options(
config: ClientConfig,
transport: T,
options: ClientOptions,
) -> Result<Self, Error>
pub async fn start_with_options( config: ClientConfig, transport: T, options: ClientOptions, ) -> Result<Self, Error>
Start the client with additional startup options.
Sourcepub fn group_source_request_drops(&self) -> u64
pub fn group_source_request_drops(&self) -> u64
Confirmed requests ignored since start because the link-layer address
they came from is a group destination of the transport
(TransportPort::is_group_destination), such as a B/IP broadcast or
multicast address (#1504). The answer, such as the acknowledgment of
a confirmed COV notification, would go back to that address and reach
every node in the group, so the request is neither handled nor
answered. No built-in transport hands one up; a custom one can.
Sourcepub fn max_apdu_length(&self) -> u16
pub fn max_apdu_length(&self) -> u16
Current BACnet max-APDU bucket advertised by this client.
Returns 0 if the live transport budget is below the smallest BACnet
max-APDU bucket and a confirmed request would currently fail before
encoding.
Sourcepub fn transport_max_apdu_length(&self) -> u16
pub fn transport_max_apdu_length(&self) -> u16
Current APDU payload budget reported by the underlying transport.
For BACnet/SC this reflects the negotiated hub envelope after connect and may be lower than the encoded BACnet max-APDU bucket advertised by the client.
Source§impl<T: TransportPort + 'static> BACnetClient<T>
impl<T: TransportPort + 'static> BACnetClient<T>
Sourcepub fn network_reports(&self) -> Receiver<ReceivedNetworkReport>
pub fn network_reports(&self) -> Receiver<ReceivedNetworkReport>
Get a receiver for the network reports this client receives: the
routers that announce themselves, Network-Number-Is broadcasts and
the rejects of messages it sent. Each call returns a new independent
receiver. A receiver that falls NETWORK_REPORT_CHANNEL_CAPACITY
behind loses the oldest.
Sourcepub async fn who_is_router_to_network(
&self,
network: Option<u16>,
destination_mac: Option<&[u8]>,
) -> Result<(), Error>
pub async fn who_is_router_to_network( &self, network: Option<u16>, destination_mac: Option<&[u8]>, ) -> Result<(), Error>
Send a Who-Is-Router-To-Network (Clause 6.4.1), asking which router
reaches network, or, for None, every router for all the networks
it reaches. It is broadcast on the local network when
destination_mac is None, else sent to that router alone. Answers
come as NetworkReport::IAmRouterToNetwork through
Self::network_reports. network 0 and 0xFFFF are refused.
Sourcepub async fn what_is_network_number(
&self,
destination_mac: Option<&[u8]>,
) -> Result<(), Error>
pub async fn what_is_network_number( &self, destination_mac: Option<&[u8]>, ) -> Result<(), Error>
Send a What-Is-Network-Number (Clause 6.4.19), broadcast when
destination_mac is None. Any node holding this network’s number,
a router in particular, broadcasts a Network-Number-Is in reply,
which comes as NetworkReport::NetworkNumberIs through
Self::network_reports. Ask once and keep the answer: the
standard doesn’t want the question repeated.
Sourcepub async fn discover_routers(
&self,
network: Option<u16>,
wait: Duration,
) -> Result<Vec<DiscoveredRouter>, Error>
pub async fn discover_routers( &self, network: Option<u16>, wait: Duration, ) -> Result<Vec<DiscoveredRouter>, Error>
Broadcast a Who-Is-Router-To-Network for network (or every
network), then collect the I-Am-Router-To-Network messages heard for
wait: one entry per router MAC, in the order first heard. For a
network, only the routers that list it are kept. No answer is an
empty list, not an error.
Source§impl<T: TransportPort + 'static> BACnetClient<T>
impl<T: TransportPort + 'static> BACnetClient<T>
Sourcepub async fn delete_object(
&self,
destination_mac: &[u8],
object_identifier: ObjectIdentifier,
) -> Result<(), Error>
pub async fn delete_object( &self, destination_mac: &[u8], object_identifier: ObjectIdentifier, ) -> Result<(), Error>
Delete an object on a remote device (DeleteObject, Clause 15.4).
Sourcepub async fn create_object(
&self,
destination_mac: &[u8],
object_specifier: ObjectSpecifier,
initial_values: Vec<BACnetPropertyValue>,
) -> Result<ObjectIdentifier, Error>
pub async fn create_object( &self, destination_mac: &[u8], object_specifier: ObjectSpecifier, initial_values: Vec<BACnetPropertyValue>, ) -> Result<ObjectIdentifier, Error>
Create an object on a remote device (CreateObject, Clause 15.3).
Returns the identifier the device’s CreateObject-ACK names, which tells a create by type the instance the device chose. An ACK that doesn’t decode is an error, though the device may have created the object.
Sourcepub async fn delete_object_from_device(
&self,
device_instance: u32,
object_identifier: ObjectIdentifier,
) -> Result<(), Error>
pub async fn delete_object_from_device( &self, device_instance: u32, object_identifier: ObjectIdentifier, ) -> Result<(), Error>
Delete an object on a discovered device, routing when needed.
Uses the same confirmation and error semantics as Self::delete_object.
A missing device fails before sending.
Sourcepub async fn create_object_from_device(
&self,
device_instance: u32,
object_specifier: ObjectSpecifier,
initial_values: Vec<BACnetPropertyValue>,
) -> Result<ObjectIdentifier, Error>
pub async fn create_object_from_device( &self, device_instance: u32, object_specifier: ObjectSpecifier, initial_values: Vec<BACnetPropertyValue>, ) -> Result<ObjectIdentifier, Error>
Create an object on a discovered device, routing when needed.
Retains the initial values and decoded acknowledgement of Self::create_object.
A missing device fails before sending.
Source§impl<T: TransportPort + 'static> BACnetClient<T>
impl<T: TransportPort + 'static> BACnetClient<T>
Sourcepub async fn read_property(
&self,
destination_mac: &[u8],
object_identifier: ObjectIdentifier,
property_identifier: PropertyIdentifier,
property_array_index: Option<u32>,
) -> Result<ReadPropertyACK, Error>
pub async fn read_property( &self, destination_mac: &[u8], object_identifier: ObjectIdentifier, property_identifier: PropertyIdentifier, property_array_index: Option<u32>, ) -> Result<ReadPropertyACK, Error>
Read a property from a remote device.
The ACK must match the requested object, property and array index. Device/Network Port wildcard requests accept only a same-type concrete peer-reported object; malformed or mismatched ACKs return a decoding error.
Sourcepub async fn read_property_from_device(
&self,
device_instance: u32,
object_identifier: ObjectIdentifier,
property_identifier: PropertyIdentifier,
property_array_index: Option<u32>,
) -> Result<ReadPropertyACK, Error>
pub async fn read_property_from_device( &self, device_instance: u32, object_identifier: ObjectIdentifier, property_identifier: PropertyIdentifier, property_array_index: Option<u32>, ) -> Result<ReadPropertyACK, Error>
Read a property from a discovered device, auto-routing if needed.
Sourcepub async fn read_property_routed(
&self,
router_mac: &[u8],
dest_network: u16,
dest_mac: &[u8],
object_identifier: ObjectIdentifier,
property_identifier: PropertyIdentifier,
property_array_index: Option<u32>,
) -> Result<ReadPropertyACK, Error>
pub async fn read_property_routed( &self, router_mac: &[u8], dest_network: u16, dest_mac: &[u8], object_identifier: ObjectIdentifier, property_identifier: PropertyIdentifier, property_array_index: Option<u32>, ) -> Result<ReadPropertyACK, Error>
Read a property from a device on a remote BACnet network via a router.
Sourcepub async fn read_property_multiple(
&self,
destination_mac: &[u8],
specs: Vec<ReadAccessSpecification>,
) -> Result<ReadPropertyMultipleACK, Error>
pub async fn read_property_multiple( &self, destination_mac: &[u8], specs: Vec<ReadAccessSpecification>, ) -> Result<ReadPropertyMultipleACK, Error>
Read multiple properties from one or more objects on a remote device.
Sourcepub async fn read_property_multiple_from_device(
&self,
device_instance: u32,
specs: Vec<ReadAccessSpecification>,
) -> Result<ReadPropertyMultipleACK, Error>
pub async fn read_property_multiple_from_device( &self, device_instance: u32, specs: Vec<ReadAccessSpecification>, ) -> Result<ReadPropertyMultipleACK, Error>
Read multiple properties from a discovered device, auto-routing if needed.
Sourcepub async fn read_property_from_devices(
&self,
requests: Vec<DeviceReadRequest>,
max_concurrent: Option<NonZeroUsize>,
) -> Vec<DeviceReadResult>
pub async fn read_property_from_devices( &self, requests: Vec<DeviceReadRequest>, max_concurrent: Option<NonZeroUsize>, ) -> Vec<DeviceReadResult>
Read one property from multiple discovered devices concurrently.
All reads are dispatched concurrently (up to max_concurrent,
default 32). The nonzero limit prevents a stalled batch. Results are
returned in completion order; request_index identifies the original input
occurrence, including duplicates. Dropping the future cancels pending requests
and returns no partial vector.
Sourcepub async fn read_property_multiple_from_devices(
&self,
requests: Vec<DeviceRpmRequest>,
max_concurrent: Option<NonZeroUsize>,
) -> Vec<DeviceRpmResult>
pub async fn read_property_multiple_from_devices( &self, requests: Vec<DeviceRpmRequest>, max_concurrent: Option<NonZeroUsize>, ) -> Vec<DeviceRpmResult>
Read multiple properties from multiple devices concurrently (RPM batch).
Sends an RPM to each device concurrently. This is the most efficient
way to poll many properties across many devices — RPM batches within
a single device, and this method batches across devices. A nonzero limit
bounds concurrency (None uses 32). Results use completion order and carry the
original input occurrence in request_index. Dropping the future cancels
pending requests and returns no partial vector.
Sourcepub async fn write_property(
&self,
destination_mac: &[u8],
object_identifier: ObjectIdentifier,
property_identifier: PropertyIdentifier,
property_array_index: Option<u32>,
property_value: Vec<u8>,
priority: Option<u8>,
) -> Result<(), Error>
pub async fn write_property( &self, destination_mac: &[u8], object_identifier: ObjectIdentifier, property_identifier: PropertyIdentifier, property_array_index: Option<u32>, property_value: Vec<u8>, priority: Option<u8>, ) -> Result<(), Error>
Write a property on a remote device.
Sourcepub async fn write_property_multiple(
&self,
destination_mac: &[u8],
specs: Vec<WriteAccessSpecification>,
) -> Result<(), Error>
pub async fn write_property_multiple( &self, destination_mac: &[u8], specs: Vec<WriteAccessSpecification>, ) -> Result<(), Error>
Write multiple properties on one or more objects on a remote device.
Sourcepub async fn write_property_to_device(
&self,
device_instance: u32,
object_identifier: ObjectIdentifier,
property_identifier: PropertyIdentifier,
property_array_index: Option<u32>,
property_value: Vec<u8>,
priority: Option<u8>,
) -> Result<(), Error>
pub async fn write_property_to_device( &self, device_instance: u32, object_identifier: ObjectIdentifier, property_identifier: PropertyIdentifier, property_array_index: Option<u32>, property_value: Vec<u8>, priority: Option<u8>, ) -> Result<(), Error>
Write a property on a discovered device, auto-routing if needed.
Sourcepub async fn write_property_multiple_to_device(
&self,
device_instance: u32,
specs: Vec<WriteAccessSpecification>,
) -> Result<(), Error>
pub async fn write_property_multiple_to_device( &self, device_instance: u32, specs: Vec<WriteAccessSpecification>, ) -> Result<(), Error>
Write multiple properties on a discovered device, auto-routing if needed.
Sourcepub async fn write_property_to_devices(
&self,
requests: Vec<DeviceWriteRequest>,
max_concurrent: Option<NonZeroUsize>,
) -> Vec<DeviceWriteResult>
pub async fn write_property_to_devices( &self, requests: Vec<DeviceWriteRequest>, max_concurrent: Option<NonZeroUsize>, ) -> Vec<DeviceWriteResult>
Write one property on multiple discovered devices concurrently.
All writes are dispatched concurrently (up to max_concurrent,
default 32). The nonzero limit prevents a stalled batch. Results are
returned in completion order; request_index identifies the original input
occurrence, including duplicates. Dropping the future cancels pending requests
and returns no partial vector.
Source§impl<T: TransportPort + 'static> BACnetClient<T>
impl<T: TransportPort + 'static> BACnetClient<T>
Sourcepub async fn read_range(
&self,
destination_mac: &[u8],
object_identifier: ObjectIdentifier,
property_identifier: PropertyIdentifier,
property_array_index: Option<u32>,
range: Option<RangeSpec>,
) -> Result<ReadRangeAck, Error>
pub async fn read_range( &self, destination_mac: &[u8], object_identifier: ObjectIdentifier, property_identifier: PropertyIdentifier, property_array_index: Option<u32>, range: Option<RangeSpec>, ) -> Result<ReadRangeAck, Error>
Read a range of items from a list or log-buffer property.
The acknowledgement must keep every rule
ReadRangeAck::violations checks against the request; one that
breaks a rule fails with Error::ReadRangeViolation. Use
read_range_with and
ReadRangeValidation::Lenient to keep such an answer.
Sourcepub async fn read_range_with(
&self,
destination_mac: &[u8],
request: &ReadRangeRequest,
validation: ReadRangeValidation,
) -> Result<ReadRangeReply, Error>
pub async fn read_range_with( &self, destination_mac: &[u8], request: &ReadRangeRequest, validation: ReadRangeValidation, ) -> Result<ReadRangeReply, Error>
Read a range of items, choosing how to treat an acknowledgement that
breaks a rule: Strict refuses it,
Lenient returns it with the list of
rules it broke, so a device that numbers the record after its
sequence wrap 0 doesn’t cost the page.
An acknowledgement that doesn’t decode fails under either mode.
Sourcepub async fn read_range_from_device(
&self,
device_instance: u32,
object_identifier: ObjectIdentifier,
property_identifier: PropertyIdentifier,
property_array_index: Option<u32>,
range: Option<RangeSpec>,
) -> Result<ReadRangeAck, Error>
pub async fn read_range_from_device( &self, device_instance: u32, object_identifier: ObjectIdentifier, property_identifier: PropertyIdentifier, property_array_index: Option<u32>, range: Option<RangeSpec>, ) -> Result<ReadRangeAck, Error>
Read a range from a discovered device, routing when needed.
The acknowledgement is checked strictly, as in Self::read_range.
A missing device fails before sending.
Sourcepub async fn read_range_from_device_with(
&self,
device_instance: u32,
request: &ReadRangeRequest,
validation: ReadRangeValidation,
) -> Result<ReadRangeReply, Error>
pub async fn read_range_from_device_with( &self, device_instance: u32, request: &ReadRangeRequest, validation: ReadRangeValidation, ) -> Result<ReadRangeReply, Error>
Read a range from a discovered device with strict or lenient validation.
This resolves the device once and preserves the acknowledgement checks
of Self::read_range_with, including decode failures in either mode.
Sourcepub async fn read_log_page_from_device(
&self,
device_instance: u32,
log: ObjectIdentifier,
cursor: LogCursor,
page_size: u16,
) -> Result<LogPage, Error>
pub async fn read_log_page_from_device( &self, device_instance: u32, log: ObjectIdentifier, cursor: LogCursor, page_size: u16, ) -> Result<LogPage, Error>
Read one log page from a discovered device, routing every subrequest.
The router, remote network and station are resolved once for this page;
metadata property reads and range requests all use that address snapshot.
A later page resolves the device again. Cursor, page-size and reply rules
are those of Self::read_log_page; a missing device fails before sending.
Source§impl<T: TransportPort + 'static> BACnetClient<T>
impl<T: TransportPort + 'static> BACnetClient<T>
Sourcepub async fn confirmed_request(
&self,
destination_mac: &[u8],
service_choice: ConfirmedServiceChoice,
service_data: &[u8],
) -> Result<Bytes, Error>
pub async fn confirmed_request( &self, destination_mac: &[u8], service_choice: ConfirmedServiceChoice, service_data: &[u8], ) -> Result<Bytes, Error>
Send a confirmed request and wait for the response.
Returns the service response data (empty for SimpleAck). Automatically uses segmented transfer when the payload exceeds the remote device’s max APDU length.
A confirmed request goes to one device, so a destination_mac that
reaches a group of nodes (TransportPort::is_group_destination:
the link’s broadcast MAC, or on B/IP the limited broadcast, the
configured broadcast IP or a multicast address at any port), which
would make it a local broadcast (Clause 6.3), fails with
Error::Encoding before any transaction state is taken (#1479). So
does a routed target that names this client’s own network with such a
MAC, since it goes local.
Sourcepub async fn confirmed_request_routed(
&self,
router_mac: &[u8],
dest_network: u16,
dest_mac: &[u8],
service_choice: ConfirmedServiceChoice,
service_data: &[u8],
) -> Result<Bytes, Error>
pub async fn confirmed_request_routed( &self, router_mac: &[u8], dest_network: u16, dest_mac: &[u8], service_choice: ConfirmedServiceChoice, service_data: &[u8], ) -> Result<Bytes, Error>
Send a confirmed request routed through a BACnet router.
The NPDU is sent as a unicast to router_mac with DNET/DADR set so
the router forwards it to dest_network/dest_mac.
A confirmed request goes to one device, so dest_network must be in
1..=65534 and dest_mac must hold 1 to
MAX_MAC_LEN octets. DNET 0 names no network, DNET
65535 and an empty DADR address a broadcast, and any of them fails
with Error::Encoding before path or transaction state is taken.
Every routed confirmed entry point, such as the _to_device and
_from_device methods, applies the same rule.
Once the client has learned its own network’s number, a
dest_network naming it is this network. After those checks the
request goes to dest_mac as a local request, with no DNET and not
through router_mac (#1358). An answer from dest_mac with no SNET
completes it, and so does one a router relays back with dest_network
as its SNET and dest_mac as its SADR (#1465), since both name the
same station.
Sourcepub async fn unconfirmed_request(
&self,
destination_mac: &[u8],
service_choice: UnconfirmedServiceChoice,
service_data: &[u8],
) -> Result<(), Error>
pub async fn unconfirmed_request( &self, destination_mac: &[u8], service_choice: UnconfirmedServiceChoice, service_data: &[u8], ) -> Result<(), Error>
Send an unconfirmed request (fire-and-forget) to a specific destination.
Sourcepub async fn broadcast_unconfirmed(
&self,
service_choice: UnconfirmedServiceChoice,
service_data: &[u8],
) -> Result<(), Error>
pub async fn broadcast_unconfirmed( &self, service_choice: UnconfirmedServiceChoice, service_data: &[u8], ) -> Result<(), Error>
Broadcast an unconfirmed request on the local network.
Sourcepub async fn broadcast_global_unconfirmed(
&self,
service_choice: UnconfirmedServiceChoice,
service_data: &[u8],
) -> Result<(), Error>
pub async fn broadcast_global_unconfirmed( &self, service_choice: UnconfirmedServiceChoice, service_data: &[u8], ) -> Result<(), Error>
Broadcast an unconfirmed request globally (DNET=0xFFFF).
Sourcepub async fn broadcast_network_unconfirmed(
&self,
service_choice: UnconfirmedServiceChoice,
service_data: &[u8],
dest_network: u16,
) -> Result<(), Error>
pub async fn broadcast_network_unconfirmed( &self, service_choice: UnconfirmedServiceChoice, service_data: &[u8], dest_network: u16, ) -> Result<(), Error>
Broadcast an unconfirmed request to a specific remote network.
Once the client has learned its own network’s number, a
dest_network naming it is this network, and the request goes as a
local broadcast with no DNET (#1358).
Source§impl<T: TransportPort + 'static> BACnetClient<T>
impl<T: TransportPort + 'static> BACnetClient<T>
Sourcepub async fn configure_routed_path_max_npdu(
&self,
router_mac: &[u8],
dnet: u16,
max_npdu: u16,
) -> Result<(), Error>
pub async fn configure_routed_path_max_npdu( &self, router_mac: &[u8], dnet: u16, max_npdu: u16, ) -> Result<(), Error>
Configure the maximum NPDU for one immediate-router/DNET path.
The update waits for any request already active on the same path. It
replaces the previous configured value and deliberately clears learned
reason-4 evidence for that path. A router MAC outside 1 to
MAX_MAC_LEN octets or a DNET outside 1..=65534 is
refused with Error::Encoding.
Sourcepub async fn clear_routed_path_limit(
&self,
router_mac: &[u8],
dnet: u16,
) -> Result<(), Error>
pub async fn clear_routed_path_limit( &self, router_mac: &[u8], dnet: u16, ) -> Result<(), Error>
Restore one immediate-router/DNET path to the conservative default.
The update waits for an active request and clears both configured and
learned reason-4 evidence. Later requests therefore use the 228-octet
conservative routed NPDU envelope until new evidence is supplied. The
router MAC and DNET are checked as in
configure_routed_path_max_npdu.
Source§impl<T: TransportPort + 'static> BACnetClient<T>
impl<T: TransportPort + 'static> BACnetClient<T>
Sourcepub async fn get_event_information(
&self,
destination_mac: &[u8],
last_received_object_identifier: Option<ObjectIdentifier>,
) -> Result<GetEventInformationAck, Error>
pub async fn get_event_information( &self, destination_mac: &[u8], last_received_object_identifier: Option<ObjectIdentifier>, ) -> Result<GetEventInformationAck, Error>
GetEventInformation to a device on this network, its answer decoded:
the objects after last_received_object_identifier (from the first
when None) with an active event or an unacknowledged transition.
While the answer’s more_events is set, ask again from its last
object.
Sourcepub async fn get_event_information_from_device(
&self,
device_instance: u32,
last_received_object_identifier: Option<ObjectIdentifier>,
) -> Result<GetEventInformationAck, Error>
pub async fn get_event_information_from_device( &self, device_instance: u32, last_received_object_identifier: Option<ObjectIdentifier>, ) -> Result<GetEventInformationAck, Error>
Self::get_event_information to a discovered device, routed when
it is behind a router. A device not in the table fails before
sending.
Sourcepub async fn get_alarm_summary(
&self,
destination_mac: &[u8],
) -> Result<GetAlarmSummaryAck, Error>
pub async fn get_alarm_summary( &self, destination_mac: &[u8], ) -> Result<GetAlarmSummaryAck, Error>
GetAlarmSummary to a device on this network, its answer decoded.
Sourcepub async fn get_alarm_summary_from_device(
&self,
device_instance: u32,
) -> Result<GetAlarmSummaryAck, Error>
pub async fn get_alarm_summary_from_device( &self, device_instance: u32, ) -> Result<GetAlarmSummaryAck, Error>
Self::get_alarm_summary to a discovered device, routed when it
is behind a router. A device not in the table fails before sending.
Sourcepub async fn get_enrollment_summary(
&self,
destination_mac: &[u8],
request: &GetEnrollmentSummaryRequest,
) -> Result<GetEnrollmentSummaryAck, Error>
pub async fn get_enrollment_summary( &self, destination_mac: &[u8], request: &GetEnrollmentSummaryRequest, ) -> Result<GetEnrollmentSummaryAck, Error>
GetEnrollmentSummary to a device on this network, its answer decoded. A filter out of its range fails before sending.
Sourcepub async fn get_enrollment_summary_from_device(
&self,
device_instance: u32,
request: &GetEnrollmentSummaryRequest,
) -> Result<GetEnrollmentSummaryAck, Error>
pub async fn get_enrollment_summary_from_device( &self, device_instance: u32, request: &GetEnrollmentSummaryRequest, ) -> Result<GetEnrollmentSummaryAck, Error>
Self::get_enrollment_summary to a discovered device, routed when
it is behind a router. A device not in the table fails before
sending.
Source§impl<T: TransportPort + 'static> BACnetClient<T>
impl<T: TransportPort + 'static> BACnetClient<T>
Sourcepub fn transport(&self) -> &T
pub fn transport(&self) -> &T
The transport this client was built with.
The client keeps its transport inside the network layer that its
dispatch task shares, and lends it out only as a shared borrow.
Methods that take &mut self, such as start, stop and the
configuration setters, stay out of reach; stop stops
the transport in place.
§Supported through this borrow
Reading transport state and diagnostics while the client runs:
- BACnet/SC: the
connection_state_changes()watch, theconnection_events()broadcast (reconnects, failover, primary restore, disconnect causes), theconnection_status()snapshot (active hub, hub VMAC, heartbeat age and round-trip time),hub_peer_info()andnpdu_drop_counts(). - B/IP:
management_counters(),fdt_counters(),fanout_counters(),receive_counters(),send_counters(), andbbmd_state()for a brief read of the BBMD tables. - MS/TP: the
diagnostics()handle. - B/IP, B/IPv6, BACnet/SC and MS/TP:
frame_tap(), to subscribe to every link-layer frame the transport sends and receives (#957). - Any transport:
local_mac()and the APDU size limits.
The watch receiver, the diagnostics handle and the BBMD state Arc are
owned and outlive the borrow, so a long-lived UI takes them once and
moves them into its own tasks, as MS/TP callers already do with
diagnostics() before handing a transport over. The counters are cheap
snapshots: poll them through the borrow as often as the UI redraws.
§Not supported while the client runs
These go around the client, which does not guard against them:
- Sending through the transport (
send_unicast,send_broadcastand their variants). The client’s transaction state machine never sees the frame, so a hand-built confirmed request can reuse an invoke ID that is already in flight. Send through the client’s request methods, and use its BBMD helpers for BBMD management. - Holding the
bbmd_state()lock across an await or a client call. It is a synchronous mutex the receive loop and a broadcast from the client take too, so holding it stalls the transport or deadlocks. Lock, copy what you need and release.
The live MS/TP master node and the BACnet/SC connection, whose locks the token loop and the hub link take, are not public at all.
§Why a borrow
This is one accessor for every transport, rather than a bundle of
handles returned by each builder. Each transport already offers its
diagnostics on &self, so nothing new is needed per transport, and
clients from generic_builder get the same
access. Sharing the transport through an Arc was ruled out too:
stop needs sole ownership of the network layer to stop
the transport, which an outstanding clone would block.
use std::net::Ipv4Addr;
use bacnet_client::client::BACnetClient;
let runtime = tokio::runtime::Builder::new_current_thread().enable_all().build()?;
runtime.block_on(Box::pin(async {
let mut client = BACnetClient::bip_builder()
.interface(Ipv4Addr::LOCALHOST)
.port(0)
.build()
.await?;
// Counter snapshots: poll them whenever the UI redraws.
let counters = client.transport().management_counters();
assert_eq!(counters.read_bdt_responses, 0);
assert_eq!(client.transport().fanout_counters().packets_forwarded, 0);
// This client is not a BBMD, so it has no BBMD state or FDT.
assert!(client.transport().bbmd_state().is_none());
assert!(client.transport().fdt_counters().is_none());
client.stop().await?;
Ok::<_, bacnet_types::error::Error>(())
}))?;Source§impl<T: TransportPort + AsBip + 'static> BACnetClient<T>
BBMD management helpers (Annex J), for a client whose transport is B/IP.
impl<T: TransportPort + AsBip + 'static> BACnetClient<T>
BBMD management helpers (Annex J), for a client whose transport is B/IP.
They work on a client over BipTransport, and on one over
AnyTransport when its variant is Bip. Each request fails with a
BvlcExchangeError: a NAK, no reply, or a local failure. On any other
variant each returns
Error::UnsupportedTransport, as BvlcExchangeError::Transport,
before sending anything.
use bacnet_client::client::BACnetClient;
use bacnet_transport::any::AnyTransport;
use bacnet_transport::bip::BvlcExchangeError;
use bacnet_transport::loopback::LoopbackTransport;
use bacnet_types::data_link::DataLink;
use bacnet_types::error::Error;
let runtime = tokio::runtime::Builder::new_current_thread().enable_all().build()?;
runtime.block_on(Box::pin(async {
let (ours, _peer) = LoopbackTransport::pair(vec![1], vec![2]);
let mut client = BACnetClient::generic_builder()
.transport(AnyTransport::Loopback(ours))
.build()
.await?;
let bbmd = [127, 0, 0, 1, 0xBA, 0xC0];
assert!(matches!(
client.read_bdt(&bbmd).await,
Err(BvlcExchangeError::Transport(Error::UnsupportedTransport {
required: DataLink::Bip,
actual: DataLink::Loopback,
}))
));
client.stop().await?;
Ok::<_, Error>(())
}))?;Sourcepub fn foreign_registration(&self) -> Result<ForeignRegistrationSnapshot, Error>
pub fn foreign_registration(&self) -> Result<ForeignRegistrationSnapshot, Error>
How this client’s foreign-device registration is going (#938): whether
it holds a live lease, the time left on it, and the last attempt’s
result and outcome, a BBMD’s refusal included. Outside
BipMode::Foreign only manual registrations show. It reads
BipTransport::bvlc_client_snapshot and sends nothing.
Sourcepub async fn read_bdt(
&self,
target: &[u8],
) -> Result<Vec<BdtEntry>, BvlcExchangeError>
pub async fn read_bdt( &self, target: &[u8], ) -> Result<Vec<BdtEntry>, BvlcExchangeError>
Read the Broadcast Distribution Table from a BBMD.
Sourcepub async fn write_bdt(
&self,
target: &[u8],
entries: &[BdtEntry],
) -> Result<(), BvlcExchangeError>
pub async fn write_bdt( &self, target: &[u8], entries: &[BdtEntry], ) -> Result<(), BvlcExchangeError>
Write the Broadcast Distribution Table to a BBMD.
A BBMD that follows the 2020 standard answers with the not-supported
result, BvlcExchangeError::Nak, and keeps its table.
Sourcepub async fn read_fdt(
&self,
target: &[u8],
) -> Result<Vec<FdtEntryWire>, BvlcExchangeError>
pub async fn read_fdt( &self, target: &[u8], ) -> Result<Vec<FdtEntryWire>, BvlcExchangeError>
Read the Foreign Device Table from a BBMD.
Sourcepub async fn delete_fdt_entry(
&self,
target: &[u8],
ip: [u8; 4],
port: u16,
) -> Result<(), BvlcExchangeError>
pub async fn delete_fdt_entry( &self, target: &[u8], ip: [u8; 4], port: u16, ) -> Result<(), BvlcExchangeError>
Delete a Foreign Device Table entry on a BBMD.
Sourcepub async fn register_foreign_device_bvlc(
&self,
target: &[u8],
ttl: u16,
) -> Result<(), BvlcExchangeError>
pub async fn register_foreign_device_bvlc( &self, target: &[u8], ttl: u16, ) -> Result<(), BvlcExchangeError>
Register as a foreign device with a BBMD; Ok when it accepts,
BvlcExchangeError::Nak when it refuses.
This sends one registration. It does not make the transport a foreign device for broadcasts or renew the registration.
Source§impl<T: TransportPort + 'static> BACnetClient<T>
impl<T: TransportPort + 'static> BACnetClient<T>
Sourcepub async fn write_group(
&self,
destination: &WriteGroupDestination,
request: &WriteGroupRequest,
) -> Result<(), Error>
pub async fn write_group( &self, destination: &WriteGroupDestination, request: &WriteGroupRequest, ) -> Result<(), Error>
Send a WriteGroup request to destination.
The service is unconfirmed: this returns once the request is sent, and
no device answers it, so delivery isn’t confirmed either. Fails before
anything is sent when request doesn’t encode (see
WriteGroupRequest::encode) or a remote network number is 0 or
65535; use WriteGroupDestination::GlobalBroadcast for every
network.
Source§impl BACnetClient<BipTransport>
impl BACnetClient<BipTransport>
Sourcepub fn bip_builder() -> BipClientBuilder
pub fn bip_builder() -> BipClientBuilder
Create a BIP-specific builder with interface/port/broadcast fields.
Source§impl BACnetClient<ScTransport<TlsWebSocket>>
impl BACnetClient<ScTransport<TlsWebSocket>>
Sourcepub fn sc_builder() -> ScClientBuilder
Available on crate feature sc-tls only.
pub fn sc_builder() -> ScClientBuilder
sc-tls only.Create an SC-specific builder that connects to a BACnet/SC hub.
Source§impl<T: TransportPort + 'static> BACnetClient<T>
impl<T: TransportPort + 'static> BACnetClient<T>
Sourcepub fn generic_builder() -> ClientBuilder<T>
pub fn generic_builder() -> ClientBuilder<T>
Create a generic builder that accepts a pre-built transport, such as
an MS/TP port. A serial port opened with TokioSerialPort::open (the
serial feature) takes the place of the in-memory line here; give
the MstpConfig the port’s
baud rate, which its timers follow.
use bacnet_client::client::BACnetClient;
use bacnet_transport::mstp::{LoopbackSerial, MstpConfig, MstpTransport};
let (serial, _line) = LoopbackSerial::pair();
let config = MstpConfig {
this_station: 1,
max_master: 127,
baud_rate: 76_800,
..MstpConfig::default()
};
let mut client = BACnetClient::generic_builder()
.transport(MstpTransport::new(serial, config))
.build()
.await?;
client.stop().await?;Source§impl<T: TransportPort + 'static> BACnetClient<T>
impl<T: TransportPort + 'static> BACnetClient<T>
Sourcepub async fn read_log_page(
&self,
mac: &[u8],
log: ObjectIdentifier,
cursor: LogCursor,
page_size: u16,
) -> Result<LogPage, Error>
pub async fn read_log_page( &self, mac: &[u8], log: ObjectIdentifier, cursor: LogCursor, page_size: u16, ) -> Result<LogPage, Error>
Read one page of log’s Log_Buffer from cursor, asking for up to
page_size records (1..=32767); see read_log_page.
let mut cursor = LogCursor::Oldest;
loop {
let page = client.read_log_page(mac, log, cursor, 100).await?;
// ... store page.records ...
cursor = page.next;
if page.done {
return Ok(cursor); // persist as the checkpoint
}
}Trait Implementations§
Source§impl<T: TransportPort> Drop for BACnetClient<T>
impl<T: TransportPort> Drop for BACnetClient<T>
Source§impl<T: TransportPort + 'static> LogRequester for BACnetClient<T>
impl<T: TransportPort + 'static> LogRequester for BACnetClient<T>
Source§async fn read_unsigned(
&self,
mac: &[u8],
log: ObjectIdentifier,
property: PropertyIdentifier,
) -> Result<u64, Error>
async fn read_unsigned( &self, mac: &[u8], log: ObjectIdentifier, property: PropertyIdentifier, ) -> Result<u64, Error>
property of log at mac as an Unsigned.Source§async fn read_range_lenient(
&self,
mac: &[u8],
request: &ReadRangeRequest,
) -> Result<ReadRangeReply, Error>
async fn read_range_lenient( &self, mac: &[u8], request: &ReadRangeRequest, ) -> Result<ReadRangeReply, Error>
mac and check its answer leniently.