
前几篇覆盖了控制闭环、故障排查、老旧馆改造。本文收口到落地前的最后一段工程:传感器怎么组网上线、恒温恒湿设备怎么组态接入。重点不在原理,在交付现场反复踩的那几件事——IP 规划、PoE 预算、Modbus 点表、BACnet 对象映射、DDC 组态、联调验证。目标是让一套系统从出厂状态走到可运行、可纳管、可回滚。
档案馆场景典型拓扑:
核心/汇聚交换机
│ VLAN 10 业务网(平台、Grafana、InfluxDB)
│ VLAN 20 动环/暖通网(传感器、DDC、网关)
│ VLAN 30 设备网(机组控制器、BACnet/IP)
▼
接入交换机(PoE+)——边缘采集网关/容器宿主机
├── 网口温湿度变送器(Modbus TCP,502)
├── DDC/PLC(Modbus TCP / BACnet/IP)
└── IO 扩展模块(Modbus TCP / RTU 转 TCP)区域 | VLAN | 网段 | 端口组 | 描述 |
|---|---|---|---|---|
A 库 | 20 | 10.20.1.0/24 | PoE1-1~12 | 回风2+工作区4+角落2 |
B 库 | 20 | 10.20.2.0/24 | PoE2-1~8 | 同上 |
DDC | 20 | 10.20.0.0/24 | 管理口+控制口 | 双网卡或单臂按厂定 |
网关 | 20/10 | 10.20.0.250 | trunk | 采集+上行 |
zone-loc-type-id,如 A-ret-htu-01、B-ws-htu-03。平台侧 device_id 与此绑定,避免后期排障时对着 IP 发呆。 别按端口数乘单口功耗算,按交换机池算:
交换机 PoE 预算 P_budget(W)
Σ 端口分配 = Σ(device_poe_load × 1.2 余量)
预留 ≥20% headroom 给启动冲击和扩展"""
poll_env.py - 边缘采集服务参考实现(精简版)
- 分片轮询 Modbus TCP 设备,归一化后批量写 InfluxDB,并上行 MQTT
- 生产环境需补:重连、退避、质量位、配置热加载、健康上报
"""
import asyncio, signal
from collections import defaultdict
from pymodbus.client import AsyncModbusTcpClient
from influxdb_client import InfluxDBClient, Point
from influxdb_client.client.write_api import SYNCHRONOUS
DEVICES = {
"A-ret-htu-01": {"host": "10.20.1.11", "unit": 1, "regs": {"t": (0,1), "h": (1,1)}},
"A-ws-htu-02": {"host": "10.20.1.12", "unit": 1, "regs": {"t": (0,1), "h": (1,1)}},
# ...
}
POLL_INT = 5.0 # s
INFLUX = {"url": "http://localhost:8086", "token": "...", "org": "arch", "bucket": "env"}
def scale_temp(raw): # 依厂家表:0.1℃ 定点,有符号
return raw / 10.0
def scale_hum(raw):
return raw / 10.0
class Collector:
def __init__(self):
self.clients = {}
self.influx = InfluxDBClient(url=INFLUX["url"], token=INFLUX["token"], org=INFLUX["org"])
self.write_api = self.influx.write_api(write_options=SYNCHRONOUS)
async def client_for(self, host):
if host not in self.clients:
c = AsyncModbusTcpClient(host)
await c.connect()
self.clients[host] = c
return self.clients[host]
async def poll_dev(self, dev_id, cfg):
try:
c = await self.client_for(cfg["host"])
if not c.connected:
await c.connect()
# 批量读保持寄存器(示例:t,h 连续两个寄存器)
rr = await c.read_holding_registers(0, 2, slave=cfg["unit"])
if rr.isError():
return self.emit(dev_id, None, False)
raw_t, raw_h = rr.registers
t = scale_temp(raw_t); h = scale_hum(raw_h)
self.emit(dev_id, {"temperature": t, "humidity": h}, True)
except Exception as e:
self.emit(dev_id, None, False, err=str(e))
def emit(self, dev_id, val, ok, err=None):
zone = dev_id.split("-")[0]
p = Point("env").tag("device", dev_id).tag("zone", zone)
if ok and val:
p = p.field("temperature", val["temperature"]).field("humidity", val["humidity"]).field("quality",1)
else:
p = p.field("quality",0)
self.write_api.write(bucket=INFLUX["bucket"], org=INFLUX["org"], record=p)
# 上行 MQTT(略)
async def run(self):
while not self._stop:
for dev_id, cfg in DEVICES.items():
await self.poll_dev(dev_id, cfg)
await asyncio.sleep(POLL_INT)
def stop(self):
self._stop = True
if __name__ == "__main__":
col = Collector()
loop = asyncio.get_event_loop()
loop.add_signal_handler(signal.SIGTERM, col.stop)
try:
loop.run_until_complete(col.run())
finally:
loop.run_until_complete(asyncio.sleep(0))注意点:
设备侧 | 对接方式 | 平台侧职责 |
|---|---|---|
精密空调/恒湿机组,带通信卡 | Modbus TCP / BACnet/IP / 厂家 SDK | 读状态、写下发设定值、收告警 |
DDC/PLC 自研控制逻辑 | Modbus TCP 保持寄存器/线圈、BACnet 对象 | 监督控制,不直接驱执行器 |
传统机组无通信 | DO/AI 扩展模块 + DDC | 现场闭环,平台监状态 |
第三方 BA 系统 | 网关桥接 / OPC-UA / BACnet 路由 | 点表映射进统一模型 |
关键:组态不在平台 UI 里画逻辑,而在现场控制器/DDC 工程文件里。平台拿到的是监督接口。
bacwi/YABE 发现设备,记录 device-id、object-list。 # Modbus TCP 交互
tcpdump -i eth0 -nn 'host 10.20.1.11 and port 502' -w /tmp/mb.pcap
# BACnet
tcpdump -i eth0 -nn 'port 47808' -w /tmp/bac.pcap
# 连通性/ACL
nmap -p 502,47808 10.20.x.xWireshark 过滤:modbus、bacnet。重点看:
组网侧:IP/VLAN/PoE/采集分片,上线前单点验证再批量注册;组态侧:边界分清——现场 DDC 跑闭环,平台跑监督,点表版本化,联调逐级放开。交付完成度看三件事:采样可信、组态可回滚、联动可验证。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。