
关键词:信创适配、智慧档案馆、环境监控平台、恒温恒湿设备、国产操作系统、国产数据库、Modbus TCP、BACnet/IP、南向对接、北向集成、协议栈替换、等保合规 标签:#物联网 #Modbus #TCP/IP #POE供电 #InfluxDB #以太网温湿度传感器 #网口温湿度变送器 #机房监控 #边缘计算 #信创 #国产数据库 #等保合规
信创不是把 x86 换成 ARM、把 CentOS 换成统信就完事。档案馆环境监控系统的适配,本质是在国产软硬件栈上,重建一条从感知到平台到处置的可用链路,并保证交付后运维动作不变形。
先分清适配边界,避免把精力花错地方:
核心目标:原系统功能等价迁移 + 南向设备对接不回退 + 运维工具链可用。
┌──────────────────────────────────────────────────────────────────┐
│ 北向集成层(信创平台内) │
│ Web控制台 / Grafana替代(国产BI或自研前端) / 告警通知 / 工单 │
│ REST API + WebSocket + MQ │
└──────────────────────┬───────────────────────────────────────────┘
│ 内部数据总线(Kafka/NATS/Redis Stream,信创验证)
┌──────────────────────▼───────────────────────────────────────────┐
│ 平台服务层(国产OS+国产库) │
│ 采集网关管理 · 设备模型 · 点表映射 · 联动规则引擎 · 审计日志 │
└──────────────────────┬───────────────────────────────────────────┘
│ 北向协议代理 / EdgeAgent
┌──────────────────────▼───────────────────────────────────────────┐
│ 边缘层(信创边缘节点或工控机) │
│ 协议转换 · 缓存 · 本地自治 · 断网续传 │
└──────────────────────┬───────────────────────────────────────────┘
│ Modbus TCP / BACnet/IP / MQTT
┌──────────────────────▼───────────────────────────────────────────┐
│ 设备层 │
│ 网口温湿度变送器 · DDC/PLC · 恒温恒湿机组控制器 · 水阀/风阀执行器 │
└──────────────────────────────────────────────────────────────────┘适配时坚持三条原则:
pymodbus 3.x:纯 Python + asyncio,跨平台 OK,注意异常码、事务ID处理。 bacpypes3(BACnet/IP):纯 Python,aarch64 无问题;性能敏感时评估多设备轮询开销。 influxdb-client:HTTP 写,无 C 依赖;若替换国产时序库,改写入适配层。 redis / pika / confluent-kafka:Kafka 客户端纯 Python 可选 kafka-python;librdkafka 绑定需编译。 原栈 InfluxDB+TSDB,信创场景常见两种路径:
适配层建议抽象存储接口,避免业务代码散落多库方言:
# store_backend.py - 存储适配层参考
from abc import ABC, abstractmethod
class MetricSink(ABC):
@abstractmethod
async def write_point(self, measurement, tags, fields, ts):
...
class InfluxSink(MetricSink):
def __init__(self, client): self.c = client
async def write_point(self, m, tags, fields, ts):
from influxdb_client import Point
p = Point(m).time(ts)
for k,v in tags.items(): p = p.tag(k,v)
for k,v in fields.items(): p = p.field(k,v)
self.c.write_api().write(record=p)
class TDEngineSink(MetricSink):
def __init__(self, conn): self.conn = conn # taosrest 或 taospy
async def write_point(self, m, tags, fields, ts):
cols = ",".join(list(tags.keys())+list(fields.keys()))
vals = ",".join([f"'{v}'" if isinstance(v,str) else str(v)
for v in list(tags.values())+list(fields.values())])
sql = f"INSERT INTO {m} ({cols}) VALUES ({vals})"
await self.conn.execute(sql)北向查询接口同样抽象,前端/报表不改 SQL 细节。
交付前拿到的对接物料,信创环境下不变:
device → subsystem=hvac → points[],绑定固件版本、点表版本。 """
edge_agent.py - 信创边缘侧采集与北向上行(精简参考)
- 读配置,连南向设备,归一化后写本地缓冲+北向MQTT
- 接收北向下发,写回设备;失败退避,状态回报
"""
import asyncio, json, logging, signal
from typing import Dict, Any
from pymodbus.client import AsyncModbusTcpClient
logging.basicConfig(level=logging.INFO)
class EdgeAgent:
def __init__(self, cfg: Dict[str, Any]):
self.cfg = cfg
self.clients: Dict[str, AsyncModbusTcpClient] = {}
self.local_buf = [] # 断网续传缓冲
self.uplink_ok = False
self._stop = False
async def modbus_read(self, dev):
host = dev["host"]; unit = dev["unit"]
c = self.clients.get(host)
if c is None or not c.connected:
c = AsyncModbusTcpClient(host, port=dev.get("port",502))
await c.connect()
self.clients[host] = c
rr = await c.read_holding_registers(0, 4, slave=unit)
if rr.isError():
return None
regs = rr.registers
# 示例:ret_temp, ret_hum, setpoint_temp, setpoint_hum
return {
"ret_temp": regs[0]/10.0, "ret_hum": regs[1]/10.0,
"setpoint_temp": regs[2]/10.0, "setpoint_hum": regs[3]/10.0,
}
async def modbus_write_setpoint(self, dev, sp_t, sp_h):
c = self.clients.get(dev["host"])
if c is None or not c.connected:
return False
# 写保持寄存器 2,3(0-based offset),缩放回原值
await c.write_registers(2, [int(sp_t*10), int(sp_h*10)], slave=dev["unit"])
# 回读确认
rr = await c.read_holding_registers(2, 2, slave=dev["unit"])
if rr.isError():
return False
got_t, got_h = rr.registers[0]/10.0, rr.registers[1]/10.0
return abs(got_t-sp_t) < 0.2 and abs(got_h-sp_h) < 0.5
async def poll_loop(self):
while not self._stop:
for dev in self.cfg["devices"]:
try:
state = await self.modbus_read(dev)
if state:
msg = {"device": dev["id"], "ts": asyncio.get_event_loop().time(), "state": state}
# 北向上行(MQTT示意,实际接信创消息总线)
await self.publish(msg)
self.local_buf.append(msg)
except Exception as e:
logging.warning("poll %s: %s", dev["id"], e)
await asyncio.sleep(self.cfg.get("poll", 5.0))
async def publish(self, msg):
# 占位:NATS/Kafka/MQTT over TLS/国密网关
self.uplink_ok = True
async def control_handler(self, cmd):
dev = next(d for d in self.cfg["devices"] if d["id"]==cmd["device"])
ok = await self.modbus_write_setpoint(dev, cmd["sp_t"], cmd["sp_h"])
return {"ack": ok, "device": cmd["device"]}
def stop(self):
self._stop = True
async def main():
cfg = json.load(open("/etc/edge_agent/config.json"))
agent = EdgeAgent(cfg)
loop = asyncio.get_event_loop()
for s in (signal.SIGTERM, signal.SIGINT):
loop.add_signal_handler(s, agent.stop)
await agent.poll_loop()
if __name__ == "__main__":
asyncio.run(main())落地时注意:生产环境把配置加载、指标暴露(Prometheus exporter)、健康探针、优雅退出、日志轮转补上;国密/TLS 在消息总线侧终结,别在采集进程内硬绑。
# Modbus TCP 抓包
tcpdump -i any -nn 'host 10.20.1.11 and port 502' -w /tmp/mb.pcap
# BACnet/IP
tcpdump -i any -nn 'port 47808' -w /tmp/bac.pcap
# 国产OS上确认监听与连通
ss -tlnp | grep 502
nc -zv 10.20.1.11 502Wireshark 过滤 modbus、bacnet;验证事务ID匹配、异常响应码、广播分发。BACnet 跨网段配 BBMD 或静态绑定,确认 I-Am/Read-Property/Write-Property。
信创适配不是协议重做,是运行栈迁移前提下的对接保全:南向机组接口契约不变,边缘代理与平台服务在国产 OS+国产库上重建;重点投入在依赖构建、存储抽象、北向集成、合规加固和回滚预案。交付完成度看三件事——采样等价、控制可回读、故障下边缘自治。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。