
一、先说说现场的五个真实困扰做过设备数据接入的人都遇到过这些设备协议五花八门。每来一种设备就要写一段解析代码改一次编译一次现场还不能停。一台设备要两种数据。平时 1Hz 采 20 个测点一旦触发事件采样率要拉高、测点数可能变成 8 个甚至另一组。传统做法是开两条连接、跑两个程序设备和网关两侧都要改。帧长靠猜。协议里没有标准头帧长字段到底是整帧长度还是payload 长度只能在运行期试探试错了就错位、丢帧。攒批带来的延迟。1Hz 的状态量本来一个点就要立刻进库却被凑成 4000 行才写监控画面迟迟不出数。换算把原始值弄丢了。一乘 factor存进库的就不是设备真实上报的码值事后想做标定、追溯、二次换算都没有依据。etherAdapter 就是为这五件事写的一个进程、一套配置、一条 TCP 连接把设备数据原样汇入 EtherDB。二、一条连接两种采样靠 frameType 区分同一台设备用一条 TCP 连接上报两类数据状态帧status周期采样一般 1Hz测点固定事件帧event突发采样频率更高、测点数可能更少或不同。两者在6 字节自定义头的第一个字节里用帧类型区分[frameType 1B][flag 1B][frameLen 2B][sequenceId 2B][payload ...][flag 1B?]frameType—— 这台设备上的哪套测点。对适配器来说它就是另一台逻辑设备有自己的字段表、自己的解析规则、自己的 EtherDB 表。flag—— 事件流的定界字节。它只对事件帧有意义在同一条 TCP 数据流里适配器靠它认出接下来是另一套解析规则。状态帧不带定界符这个字节被忽略。frameLen—— 本帧 payload 字节数。一次可以携带多条记录frameLen / 单条记录长度多个采样点共用一个头省带宽。sequenceId—— 预留字段当前不校验、不解析将来扩展用。配置上同一(ip, 端口)写多行设备表即可device_idip源端口data_proto_idframe_typedev_T100_001127.0.0.1100021000770xC状态dev_T100_001_E127.0.0.1100021000780xE事件第一行是状态设备其余是事件设备。设备侧只需要在头里改一个字节数据就分别落进dev_T100_001和dev_T100_001_E两张表。这一步的价值设备固件不用为加一路事件改造连接管理网关侧也不用新开监听端口。三、状态数据不攒批来了就提交大多数状态量是 1Hz根本不值得等。etherAdapter 的策略是状态帧每收到一个数据块立即刷写不攒批、不等定时器 —— 画面出数最快事件帧按批写入—— 突发事件频率高、点数多批量写入才是吞吐最优。两者在同一条管道里共存配置里只需要把设备行写对。四、只存原始值factor 先放一边时序库的价值在于如实地留下现场所以适配器不做物理量换算data_proto_table.factor当前不参与计算启动时若发现配置了非 1 的系数只打一条告警提醒整型字段就按原始码值存有符号/无符号类型自动选合适的列宽FLOAT32/FLOAT64按原值存想换算查询时再乘或者交给上层 —— 原始值永远还在。五、从设备到 EtherDB 的路径设备 ──TCP── muduo 网络层 ──无锁队列(moodycamel)── 单线程解析 │ RowBatch ──┬──────────────┘ ▼ 写入队列 ── 列绑定预编译 INSERT ── EtherDB └─ 发布队列 ── ZMQ PUB预留几个刻意的设计解析单线程所有协议解析集中在一个线程里配置驱动、没有锁竞争也避开了多线程解析的时序问题网络线程只负责把字节搬进队列。列绑定批量写入按设备预编译INSERT每列一块连续内存一次绑定多行 → 一次执行。慢速 HTTP 数据走普通 SQL不占预绑定资源。配置驱动三张 SQLite 表设备 / 数据头 / 字段决定一切加设备就是加一行改协议就是改一行。EtherDB 侧的性能已经有公开实测见EtherDB/docs/EtherDB-performance.md同机批量写入约214 万行/秒、8 进程聚合约618 万行/秒、COUNT(*)0.34 ms、千万行分页0.82 ms服务端单文件1.07 MB。适配器只负责把数据正确地喂进去不改变这条写入路径。六、本机跑通的一条完整链路用仓库自带的 fixtures从设备模拟到落库实测csv2sqlite.exe config\etherAdapter.db tests\fixtures --force etherdb_dserver.exe -p 7040 etherAdapter.exe :: 状态 事件在一条 TCP 连接上交替上报 device_sim.exe -n 60 -i 5 -t C,E -l 73,62 -f 0,01 -e 0,03 -v 100结果query_check.exe直接查库表内容说明dev_T100_001item1 100,102,104…状态帧单字段来了就提交dev_T100_001_Eitem1 101,103,105…含 item2事件帧两个测点另一张表dev_MB_401reg12000k, reg22100kMODBUS 3s 轮询原始寄存器值dev_HTTP_001temp12.5, humidity60HTTP POST JSON直拼 SQL运行统计里可以看到分流是干净的frames 60 rows 60 (event 30) written 61 drop 0 err 0。同一份 fixtures 还覆盖了多记录共用一个头device_sim -m 3、MODBUS 四种响应头9/7/2/0、HTTP 缺字段写 NULL、以及sequenceId随便填也不影响解析。七、适合谁用现场设备不能改或改不动但要把数据接进时序库一台设备既要周期状态、又要突发事件还不想开两条连接需要在边缘侧轻量部署适配器与 EtherDB 都是单文件、无外部依赖数据要留原始值标定/换算放在后面做。八、小结etherAdapter 做的是最后一公里里最脏的那段活把设备怎么说的话原样、低延迟地翻译成 EtherDB 的列。一条 TCP两种采样 —— 靠frameType状态数据来了就提交事件数据按批落表配置即接入三张表、一个进程。设备不用动数据库不用改中间只多了一个进程。数据库地址https://gitee.com/kinyi/EtherDB/blob/main/docs/EtherDB-performance.md项目地址https://gitee.com/kinyi/etherdb-adapter