# BACnet HTTP 接口说明 ## 目标 本服务通过 HTTP 接口连接 BACnet/IP 设备。点位读取和点位搜索使用 `BAC0` 实现,BBMD Who-Is 设备发现使用 BACnet/IP BVLC 报文实现。 当前仅支持 `BACnet/IP`。 ## 接口列表 - `POST /api/dc-gateway/bacnet/read_points`:按点位对象读取,固定返回 `present-value`。 - `POST /api/dc-gateway/bacnet/search_points`:读取设备 `object-list`,搜索常见 BACnet 点位对象并返回基础信息和值。 - `POST /api/dc-gateway/bacnet/bbmd/whois`:按环境变量配置注册 BBMD Foreign Device,通过 BBMD 分发 `Who-Is` 并返回 `I-Am` 设备列表。 - `GET /api/dc-gateway/health`:健康检查。 ## 通用返回约定 接口业务结果统一使用 HTTP `200` 返回,是否成功由 JSON 中的 `code` 判断。 成功: ```json { "code": 0, "msg": "success", "data": {} } ``` 失败: ```json { "code": 1, "msg": "BACnet communication error", "data": { "points": [] } } ``` 字段说明: | 字段 | 类型 | 说明 | |---|---|---| | `code` | integer | `0` 表示成功,`1` 表示失败。 | | `msg` | string | 成功时为 `success`,失败时为错误信息。 | | `data` | object | 接口数据。 | `data` 固定为对象。点位接口会在 `data.device` 返回设备信息,点位列表返回在 `data.points`。BBMD Who-Is 接口会在 `data.bbmd` 返回 BBMD 请求信息,设备列表返回在 `data.devices`。 ## 设备参数校验 以下字段用于 `/bacnet/read_points` 和 `/bacnet/search_points` 请求体顶层。 | 字段 | 是否必传 | 默认值 | 校验 | 说明 | |---|---:|---|---|---| | `ip` | 是 | 无 | 必须是合法 IP 地址 | BACnet/IP 设备地址。 | | `bacnet_device_id` | 是 | 无 | `0..4194303` | BACnet 设备对象实例号。 | | `port` | 否 | `47808` | `1..65535` | BACnet/IP UDP 端口。 | 响应中的设备信息固定包含: ```json { "device_type": "BACnet/IP", "ip": "192.168.75.240", "port": 47808, "bacnet_device_id": 12345 } ``` ## 对象类型 请求中的 `object_type` 支持常见写法,例如 `AnalogInput`、`analogInput`、`analog-input`。响应统一返回 `AnalogInput` 这类格式。 常见点位对象类型: | 响应值 | BACnet 含义 | |---|---| | `AnalogInput` | 模拟输入 | | `AnalogOutput` | 模拟输出 | | `AnalogValue` | 模拟值 | | `BinaryInput` | 二进制输入 | | `BinaryOutput` | 二进制输出 | | `BinaryValue` | 二进制值 | | `MultiStateInput` | 多状态输入 | | `MultiStateOutput` | 多状态输出 | | `MultiStateValue` | 多状态值 | `/bacnet/search_points` 只返回上表中的常见点位对象类型。`/bacnet/read_points` 可按对象类型读取 `present-value`,但目标对象必须支持该属性。 ## 接口一:读取点位 ### URL ```http POST /api/dc-gateway/bacnet/read_points ``` ### 请求体 `read_points` 固定读取每个对象的 `present-value`。 ```json { "ip": "192.168.75.240", "bacnet_device_id": 12345, "port": 47808, "points": [ { "object_type": "AnalogInput", "object_id": 1 } ] } ``` 字段说明: | 字段 | 类型 | 必填 | 校验 | 说明 | |---|---|---:|---|---| | `points` | object[] | 是 | 至少 1 个点位 | BACnet 点位对象列表。 | | `points[].object_type` | string | 是 | 合法 BACnet 对象类型写法 | BACnet 对象类型。 | | `points[].object_id` | integer | 是 | `0..4194303` | BACnet 对象实例号。 | ### 成功返回 ```json { "code": 0, "msg": "success", "data": { "device": { "device_type": "BACnet/IP", "ip": "192.168.75.240", "port": 47808, "bacnet_device_id": 12345 }, "points": [ { "object_type": "AnalogInput", "object_id": 1, "present_value": 12.3 } ] } } ``` ### 失败返回 设备通信失败: ```json { "code": 1, "msg": "No response from BACnet device", "data": { "device": { "device_type": "BACnet/IP", "ip": "192.168.75.240", "port": 47808, "bacnet_device_id": 12345 }, "points": [] } } ``` 请求字段校验失败: ```json { "code": 1, "msg": "points: List should have at least 1 item after validation, not 0", "data": { "points": [] } } ``` ## 接口二:搜索点位 ### URL ```http POST /api/dc-gateway/bacnet/search_points ``` ### 请求体 搜索点位只需要设备信息。服务会读取设备对象的 `object-list`,并过滤常见点位对象类型。 ```json { "ip": "192.168.75.240", "bacnet_device_id": 12345, "port": 47808 } ``` ### 成功返回 ```json { "code": 0, "msg": "success", "data": { "device": { "device_type": "BACnet/IP", "ip": "192.168.75.240", "port": 47808, "bacnet_device_id": 12345 }, "points": [ { "name": "Zone Temperature", "description": "Room temperature", "object_type": "AnalogInput", "object_id": 1, "present_value": 24.5 } ] } } ``` 搜索每个点位时读取以下属性: | 返回字段 | BACnet 属性 | |---|---| | `name` | `object-name` | | `description` | `description` | | `present_value` | `present-value` | 单个点位的 `description` 或 `present_value` 读取失败时,该字段返回 `null`,不会中断整个搜索。设备连接或 `object-list` 读取失败时,接口返回 `code=1`。 ### 失败返回 ```json { "code": 1, "msg": "No response from BACnet device", "data": { "device": { "device_type": "BACnet/IP", "ip": "192.168.75.240", "port": 47808, "bacnet_device_id": 12345 }, "points": [] } } ``` ## 接口三:通过 BBMD 查询 BACnet 设备 ### URL ```http POST /api/dc-gateway/bacnet/bbmd/whois ``` 服务会先向 BBMD 发送 `Register-Foreign-Device`,注册成功后发送 `Distribute-Broadcast-To-Network` 包装的 `Who-Is`,并在配置的超时时间内收集 `I-Am` 响应。请求结束前会用 TTL=0 注销 Foreign Device。 ### 请求体 不需要请求体,直接调用即可: ```bash curl -X POST http://127.0.0.1:8000/api/dc-gateway/bacnet/bbmd/whois ``` 配置环境变量: | 环境变量 | 是否必填 | 默认值 | 说明 | |---|---:|---|---| | `BACNET_BBMD_IP` | 是 | 无 | BBMD 地址。 | | `BACNET_BBMD_PORT` | 否 | `47808` | BBMD UDP 端口。 | | `BACNET_BBMD_TTL` | 否 | `60` | Foreign Device 注册 TTL,单位秒。 | | `BACNET_BBMD_WHOIS_TIMEOUT` | 否 | `5` | 收集 `I-Am` 的等待时间,单位秒。 | | `BACNET_BBMD_LOW_LIMIT` | 否 | `0` | Who-Is 设备号下限。 | | `BACNET_BBMD_HIGH_LIMIT` | 否 | `4194303` | Who-Is 设备号上限。 | | `BACNET_BBMD_LOCAL_DEVICE_ID` | 否 | 无 | 若收到同设备号的 `I-Am`,会作为本机设备忽略。 | | `BACNET_LOCAL_IP` | 否 | 自动按到 BBMD 的路由推断 | 本机 BACnet 源地址。 | | `BACNET_LOCAL_PORT` | 否 | 自动选择空闲端口 | 本机 UDP 绑定端口。 | ### 成功返回 ```json { "code": 0, "msg": "success", "data": { "bbmd": { "bbmd_ip": "192.168.1.1", "bbmd_port": 47808, "ttl": 60, "timeout": 5, "low_limit": 0, "high_limit": 4194303 }, "devices": [ { "bacnet_device_id": 12345, "ip": "192.168.10.20", "port": 47808, "max_apdu": 1476, "segmentation": "noSegmentation", "vendor_id": 842 } ] } } ``` ### 失败返回 ```json { "code": 1, "msg": "BBMD foreign device registration timeout", "data": { "bbmd": { "bbmd_ip": "192.168.1.1", "bbmd_port": 47808, "ttl": 60, "timeout": 5, "low_limit": 0, "high_limit": 4194303 }, "devices": [] } } ``` ## 本地 BACnet 客户端绑定 网关会为每次 BACnet 请求启动本地 BACnet 客户端,并自动推断到目标设备的本机地址。可通过环境变量覆盖: | 字段 | 默认值 | 说明 | |---|---|---| | `BACNET_LOCAL_IP` | 自动按到目标设备的路由推断 | 本地 BACnet 客户端绑定 IP。 | | `BACNET_LOCAL_MASK` | `24` | 本地 BACnet 客户端网络掩码位数。 | | `BACNET_LOCAL_PORT` | 自动选择空闲 UDP 端口 | 本地 BACnet 客户端绑定 UDP 端口。 | ## 测试默认设备 BACnet 集成测试默认设备参数: | 字段 | 默认值 | |---|---| | `BACNET_DEVICE_IP` | `192.168.75.240` | | `BACNET_DEVICE_ID` | `12345` | | `BACNET_PORT` | `47808` |