3 个不稳定版本

0.2.0 2019 年 11 月 30 日
0.1.1 2019 年 11 月 25 日
0.1.0 2019 年 11 月 24 日

#9#apdu

MPL-2.0 许可证

78KB
2K SLoC

bacnet_parse

LICENSE Crates.io Version

bacnet_parse 是一个 #![no_std] 库,用于将 BACnet 字节解析到只读数据结构中

目前处理

  • MS/TP
  • BVLL(基本 - 只需足够的 NPDU)
  • NPDU

目标支持

  • NSDU(NLM/RPDU,APDU)

为了帮助解析 BACnet IP 或 BACnet 以太网,推荐使用以下两个库:

如何使用此库

对于 BACnet 以太网和 BACnet IP,首先确定您的 BACnet 应用层字节,然后调用 parse_bvlc(bytes) 并继续操作。

对于 MSTP,调用 parse_mstp(bytes)parse_mstp_skip_crc_compute(bytes)

以下尚未实现

为了解析 RPDU 或 APDU,首先使用 npdu.is_apdu() 检查您拥有哪一个,然后调用 parse_apdu(npdu.payload())parse_rpdu(npdu.payload())

示例

BVLC 示例

let bytes: &[u8] = &[
    0x81, 0x0a, 0x00, 0x1b, // BVLC
    0x01, 0x20, 0x00, 0x0d, 0x01, 0x3d, 0xff, // NPDU
    0x30, 0xc9, 0x0c, 0x0c, 0x02, 0x00, 0x00, 0x6f, 0x19, 0x4c, 0x29, 0x00, 0x3e, 0x21,
    0x21, 0x3f, // APDU
];

let bvlc = parse_bvlc(&bytes)?;

assert_eq!(bvlc.bvlc_function(), BVLCFunction::UnicastNPDU);

let npdu = bvlc.npdu().as_ref().expect("npdu");

assert_eq!(npdu.ncpi_control(), 0x20);
assert_eq!(npdu.is_apdu(), true);
assert_eq!(npdu.is_src_spec_present(), false);
assert_eq!(npdu.is_dst_spec_present(), true);
assert_eq!(npdu.is_expecting_reply(), false);
assert_eq!(npdu.src().is_none(), true);

let dst_hopcount = npdu.dst_hopcount().as_ref().expect("dst_hopcount");

assert_eq!(dst_hopcount.hopcount(), 255);

let dst = dst_hopcount.dst();

assert_eq!(dst.net(), 13);
assert_eq!(dst.addr().len(), 1);
assert_eq!(dst.addr()[0], 61);

MSTP 示例

let bytes: &[u8] = &[
    0x55, 0xff, 0x05, 0x0c, 0x7f, 0x00, 0x1f, 0x35, 0x01, 0x0c, 0x00, 0x01, 0x06, 0xc0,
    0xa8, 0x01, 0x12, 0xba, 0xc0, 0x02, 0x01, 0x6a, 0x0f, 0x0c, 0x00, 0x80, 0x00, 0x0a,
    0x19, 0x55, 0x3e, 0x44, 0x41, 0xe8, 0x00, 0x01, 0x3f, 0x49, 0x09, 0xc9, 0x6f,
];

let frame = parse_mstp(bytes)?;

let (actual, expected) = frame.crcs().header();
assert_eq!(actual, expected);
assert_eq!(actual, 0x35);

let (actual, expected) = frame.crcs().data();
assert_eq!(actual, expected);
assert_eq!(actual, 0x6fc9);

assert_eq!(frame.frame_type(), MSTPFrameType::BACnetDataExpectingReply);
let npdu = frame.npdu().as_ref().expect("npdu");

let src = npdu.src().as_ref().expect("src");
assert_eq!(src.net(), 1);
assert_eq!(src.addr().len(), 6);
let addr_cmp: &[u8] = &[0xc0, 0xa8, 0x01, 0x12, 0xba, 0xc0];
assert_eq!(src.addr(), addr_cmp);
assert_eq!(npdu.dst_hopcount().is_none(), true);

let bytes: &[u8] = &[
    0x55, 0xff, 0x05, 0x0c, 0x7f, 0x00, 0x1f, 0x34, 0x01, 0x0c, 0x00, 0x01, 0x06, 0xc0,
    0xa8, 0x01, 0x12, 0xba, 0xc0, 0x02, 0x01, 0x6a, 0x0f, 0x0c, 0x00, 0x80, 0x00, 0x0a,
    0x19, 0x55, 0x3e, 0x44, 0x41, 0xe8, 0x00, 0x01, 0x3f, 0x49, 0x09, 0xc9, 0x6e,
];

let frame = parse_mstp(bytes)?;

let (actual, expected) = frame.crcs().header();
assert_ne!(actual, expected);
assert_eq!(actual, 0x34);

let (actual, expected) = frame.crcs().data();
assert_ne!(actual, expected);
assert_eq!(actual, 0x6ec9);

为什么不使用 nom

nom 是一个很棒的库,但我认为它不适合像 BACnet 这样具有奇怪格式的应用层数据。例如,NPDU 布局中的奇怪之处,其中跳数值的存在与目的端口/地址的连续性有关,但可能不是连续的。

避免使用 nom 也可能降低入门门槛,以便潜在的贡献者不需要学习 nom 库。

这些是意见,如果您不同意并希望使用nom进行解析,请随意提交包含nom的pull request。

许可证:MPL-2.0

依赖项