{"id":17156341,"url":"https://github.com/jtroo/bacnet_parse","last_synced_at":"2025-04-13T13:20:36.281Z","repository":{"id":62438492,"uuid":"223705366","full_name":"jtroo/bacnet_parse","owner":"jtroo","description":"#![no_std] BACnet protocol parsing","archived":false,"fork":false,"pushed_at":"2020-07-11T08:55:45.000Z","size":73,"stargazers_count":3,"open_issues_count":0,"forks_count":2,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-27T04:22:18.690Z","etag":null,"topics":["bacnet","no-std","parser","protocol"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mpl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jtroo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-11-24T07:01:05.000Z","updated_at":"2024-05-24T05:07:26.000Z","dependencies_parsed_at":"2022-11-01T22:01:28.000Z","dependency_job_id":null,"html_url":"https://github.com/jtroo/bacnet_parse","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jtroo%2Fbacnet_parse","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jtroo%2Fbacnet_parse/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jtroo%2Fbacnet_parse/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jtroo%2Fbacnet_parse/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jtroo","download_url":"https://codeload.github.com/jtroo/bacnet_parse/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248718016,"owners_count":21150488,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bacnet","no-std","parser","protocol"],"created_at":"2024-10-14T22:06:11.968Z","updated_at":"2025-04-13T13:20:36.259Z","avatar_url":"https://github.com/jtroo.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# bacnet_parse\n\n[![LICENSE](https://img.shields.io/badge/license-MPL_2.0-blue.svg)](LICENSE)\n[![Crates.io Version](https://img.shields.io/crates/v/bacnet_parse.svg)](https://crates.io/crates/bacnet_parse)\n\n## bacnet_parse is a #![no_std] library to parse BACnet bytes into read-only data structures\n\nCurrently handles:\n* MS/TP\n* BVLL (basic - just enough to get NPDU)\n* NPDU\n\nTargeting support for:\n* NSDU ([NLM/RPDU](http://www.bacnetwiki.com/wiki/index.php?title=Network_Layer_Message_Type), APDU)\n\nTo assist parsing BACnet IP or BACnet Ethernet, two recommended libraries are:\n* [pnet](https://crates.io/crates/pnet)\n* [etherparse](https://crates.io/crates/etherparse)\n\n### How to use this library\n\nFor BACnet ethernet and BACnet IP, first identify your BACnet application layer bytes then call\nto `parse_bvlc(bytes)` and go from there.\n\nFor MSTP, call either `parse_mstp(bytes)` or `parse_mstp_skip_crc_compute(bytes)`.\n\nNot yet implemented below:\n\nIn order to parse the RPDU or APDU, first check which one you have with `npdu.is_apdu()` then\ncall either `parse_apdu(npdu.payload())` or `parse_rpdu(npdu.payload())`.\n\n### Examples\n\nBVLC example\n\n```rust\nlet bytes: \u0026[u8] = \u0026[\n    0x81, 0x0a, 0x00, 0x1b, // BVLC\n    0x01, 0x20, 0x00, 0x0d, 0x01, 0x3d, 0xff, // NPDU\n    0x30, 0xc9, 0x0c, 0x0c, 0x02, 0x00, 0x00, 0x6f, 0x19, 0x4c, 0x29, 0x00, 0x3e, 0x21,\n    0x21, 0x3f, // APDU\n];\n\nlet bvlc = parse_bvlc(\u0026bytes)?;\n\nassert_eq!(bvlc.bvlc_function(), BVLCFunction::UnicastNPDU);\n\nlet npdu = bvlc.npdu().as_ref().expect(\"npdu\");\n\nassert_eq!(npdu.ncpi_control(), 0x20);\nassert_eq!(npdu.is_apdu(), true);\nassert_eq!(npdu.is_src_spec_present(), false);\nassert_eq!(npdu.is_dst_spec_present(), true);\nassert_eq!(npdu.is_expecting_reply(), false);\nassert_eq!(npdu.src().is_none(), true);\n\nlet dst_hopcount = npdu.dst_hopcount().as_ref().expect(\"dst_hopcount\");\n\nassert_eq!(dst_hopcount.hopcount(), 255);\n\nlet dst = dst_hopcount.dst();\n\nassert_eq!(dst.net(), 13);\nassert_eq!(dst.addr().len(), 1);\nassert_eq!(dst.addr()[0], 61);\n```\n\nMSTP example\n```rust\nlet bytes: \u0026[u8] = \u0026[\n    0x55, 0xff, 0x05, 0x0c, 0x7f, 0x00, 0x1f, 0x35, 0x01, 0x0c, 0x00, 0x01, 0x06, 0xc0,\n    0xa8, 0x01, 0x12, 0xba, 0xc0, 0x02, 0x01, 0x6a, 0x0f, 0x0c, 0x00, 0x80, 0x00, 0x0a,\n    0x19, 0x55, 0x3e, 0x44, 0x41, 0xe8, 0x00, 0x01, 0x3f, 0x49, 0x09, 0xc9, 0x6f,\n];\n\nlet frame = parse_mstp(bytes)?;\n\nlet (actual, expected) = frame.crcs().header();\nassert_eq!(actual, expected);\nassert_eq!(actual, 0x35);\n\nlet (actual, expected) = frame.crcs().data();\nassert_eq!(actual, expected);\nassert_eq!(actual, 0x6fc9);\n\nassert_eq!(frame.frame_type(), MSTPFrameType::BACnetDataExpectingReply(5));\nlet npdu = frame.npdu().as_ref().expect(\"npdu\");\n\nlet src = npdu.src().as_ref().expect(\"src\");\nassert_eq!(src.net(), 1);\nassert_eq!(src.addr().len(), 6);\nlet addr_cmp: \u0026[u8] = \u0026[0xc0, 0xa8, 0x01, 0x12, 0xba, 0xc0];\nassert_eq!(src.addr(), addr_cmp);\nassert_eq!(npdu.dst_hopcount().is_none(), true);\n\nlet bytes: \u0026[u8] = \u0026[\n    0x55, 0xff, 0x05, 0x0c, 0x7f, 0x00, 0x1f, 0x34, 0x01, 0x0c, 0x00, 0x01, 0x06, 0xc0,\n    0xa8, 0x01, 0x12, 0xba, 0xc0, 0x02, 0x01, 0x6a, 0x0f, 0x0c, 0x00, 0x80, 0x00, 0x0a,\n    0x19, 0x55, 0x3e, 0x44, 0x41, 0xe8, 0x00, 0x01, 0x3f, 0x49, 0x09, 0xc9, 0x6e,\n];\n\nlet frame = parse_mstp(bytes)?;\n\nlet (actual, expected) = frame.crcs().header();\nassert_ne!(actual, expected);\nassert_eq!(actual, 0x34);\n\nlet (actual, expected) = frame.crcs().data();\nassert_ne!(actual, expected);\nassert_eq!(actual, 0x6ec9);\n```\n\n### Why not use [nom](https://crates.io/crates/nom)?\n\nnom is a great library, but I don't think it's well suited to application layer data with weird\nformats like BACnet. For example, the weirdness of the NPDU layout where the hop count value's\nexistence is tied to but may or may not be contiguous with the destination port/address.\n\nAvoiding the use of nom may also lower the barrier to entry for contribution so that a\npotential contributor does not also need to learn the nom library.\n\nThese are opinions, so if you disagree and would like to use nom for parsing, feel free to make\na pull request that includes nom.\n\nLicense: MPL-2.0\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjtroo%2Fbacnet_parse","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjtroo%2Fbacnet_parse","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjtroo%2Fbacnet_parse/lists"}