bacnet-network 0.15.0

BACnet network layer: routing, router tables, priority channels
Documentation
use super::NetworkLayer;
use bacnet_encoding::npdu::{Npdu, encode_npdu};
use bacnet_npdu::NetworkPriority;
use bacnet_transport::sc::{LoopbackWebSocket, ScTransport, WebSocketPort};
use bacnet_transport::sc_frame::{
    ScFunction, ScMessage, ScOption, Vmac, decode_sc_message, encode_sc_message,
};
use bytes::{Bytes, BytesMut};
use tokio::time::{Duration, timeout};

async fn sc_hub_accept(ws_hub: &LoopbackWebSocket, hub_vmac: Vmac) {
    let data = ws_hub.recv().await.unwrap();
    let req = decode_sc_message(&data).unwrap();
    assert_eq!(req.function, ScFunction::ConnectRequest);

    let mut accept_payload = Vec::with_capacity(26);
    accept_payload.extend_from_slice(&hub_vmac);
    accept_payload.extend_from_slice(&[0x33; 16]);
    accept_payload.extend_from_slice(&1476u16.to_be_bytes());
    accept_payload.extend_from_slice(&1476u16.to_be_bytes());

    let accept = ScMessage {
        function: ScFunction::ConnectAccept,
        message_id: req.message_id,
        originating_vmac: None,
        destination_vmac: None,
        dest_options: Vec::new(),
        data_options: Vec::new(),
        payload: Bytes::from(accept_payload),
    };
    let mut buf = BytesMut::new();
    encode_sc_message(&mut buf, &accept);
    ws_hub.send(&buf).await.unwrap();
}

#[tokio::test]
async fn sc_data_options_reach_received_apdu_data_attributes() {
    let (ws_client, ws_hub) = LoopbackWebSocket::pair();
    let hub_vmac = [0x10; 6];
    let mut net =
        NetworkLayer::new(ScTransport::new(ws_client, [0x01; 6]).with_device_uuid([1; 16]));

    let hub_accept_task = tokio::spawn(async move {
        sc_hub_accept(&ws_hub, hub_vmac).await;
        ws_hub
    });

    let mut rx = net.start().await.unwrap();
    let ws_hub = hub_accept_task.await.unwrap();

    let apdu = Bytes::from_static(&[0x10, 0x08]);
    let npdu = Npdu {
        expecting_reply: false,
        priority: NetworkPriority::Normal,
        destination: None,
        source: None,
        payload: apdu.clone(),
        ..Npdu::default()
    };
    let mut npdu_buf = BytesMut::new();
    encode_npdu(&mut npdu_buf, &npdu).unwrap();

    let msg = ScMessage {
        function: ScFunction::EncapsulatedNpdu,
        message_id: 0x2345,
        originating_vmac: Some(hub_vmac),
        destination_vmac: None,
        dest_options: Vec::new(),
        data_options: vec![
            ScOption {
                option_type: 1,
                must_understand: true,
                data: Vec::new(),
            },
            ScOption {
                option_type: 31,
                must_understand: false,
                data: vec![0x12, 0x34, 0x56],
            },
        ],
        payload: npdu_buf.freeze(),
    };
    let mut sc_buf = BytesMut::new();
    encode_sc_message(&mut sc_buf, &msg);
    ws_hub.send(&sc_buf).await.unwrap();

    let received = timeout(Duration::from_secs(1), rx.recv())
        .await
        .expect("timed out waiting for APDU")
        .expect("APDU channel closed");

    assert_eq!(received.apdu, apdu);
    assert_eq!(received.source_mac.as_slice(), hub_vmac);
    assert!(received.source_network.is_none());
    assert_eq!(received.data_attributes.len(), 2);
    assert_eq!(received.data_attributes[0].option_type, 1);
    assert!(received.data_attributes[0].must_understand);
    assert!(received.data_attributes[0].data.is_empty());
    assert_eq!(received.data_attributes[1].option_type, 31);
    assert!(!received.data_attributes[1].must_understand);
    assert_eq!(received.data_attributes[1].data, vec![0x12, 0x34, 0x56]);

    net.stop().await.unwrap();
}