干了十来年自动化,车间里最常见的一个场景就是:地上摆着几台不同品牌的PLC,西门子的、台达的、汇川的,各自跑逻辑,但MES要看产量,能耗平台要看电表数据,管理看板要显示温度曲线,每接一个系统就得用不同的协议折腾半天。后来我做了一个“PLC转Web API服务器框架”,把所有PLC统一封装成HTTP接口,上层系统只认JSON,不关心底层是什么协议。这套思路解决的不只是通信问题,更是把PLC从封闭的工业现场带进了物联网的体系里。这篇文章就围绕这个框架,从架构设计、点位表、协议适配、轮询策略到坑点排查,完整拆一遍,适合正在做产线信息化、物联网网关、毕业设计或者单纯想把手里的PLC“接上云”的朋友。
1. 这个东西到底解决了什么问题
1.1 从一堆PLC到一套API:核心思路
先聊一个最常见的痛点。假设你管着三条产线,西门子S7-200SMART控制一条线,台达PLC控制另外一台设备,还有几台老设备只有串口。上层系统要数据,传统做法是给MES写一套S7驱动,给能耗平台写一套Modbus采集,给看板再写一套串口解析,代码重复且不说,一旦现场PLC的程序变了、寄存器地址换了,所有挨个改,运维起来极其痛苦。
PLC转Web API框架的核心思路,就是做一层“隔离”。PLC还是那个PLC,协议还是那个协议,但框加在中间把一切都消化掉,对外只暴露RESTful风格的接口,比如GET /api/devices/line1/points/temperature。调用方不用关心你用的是Modbus TCP还是串口,不用关心寄存器地址是多少,甚至不用关心你底层换了一台PLC。设备变了,框架侧改配置就行,上层系统的代码一行不用动。
这层隔离看起来简单,真正做扎实了,价值非常大。我见过很多项目死在“每个系统各搞一套采集”上,现场连的设备越多,冲突越严重,最后谁的数据都不准。有了统一API层,数据流向是清晰的:PLC → 采集与缓存 → API → 业务系统,整个体系一下子变整洁了。
1.2 为什么组态软件和OPC不够用
说到PLC对接,老工程师第一反应大概率是组态软件或者OPC。组态软件(比如WinCC、组态王)确实能采集PLC数据,还能画漂亮的画面,但它有两个致命问题:一是贵,按点数授权,产线一多成本高了去了;二是封闭,你很难把它的数据接口嵌进自己写的Web系统里,就算能,操作也极其别扭。
OPC(OLE for Process Control)比组态软件开放一些,尤其是OPC UA,跨平台、带安全认证、数据模型也很完善。但OPC Server本身要装Windows服务、要配置证书、要处理复杂的安全模型,对大多数中小产线来说是门技术门槛。更关键的是,OPC解决的是“数据能不能拿到”的问题,没解决“接口长什么样”的问题——系统还是得写代码去连OPC,而且很多轻量级物联网平台根本不想引一套OPC客户端依赖进来。
对比一下几种方案:
| 方案 | 优点 | 痛点 |
|---|---|---|
| 组态软件 | 画面直观,功能全 | 成本高,封闭,Web集成难 |
| OPC UA | 标准,安全,模型完善 | 部署重,学习曲线陡 |
| 自研PLC转Web API | 轻量,灵活,跨平台 | 需要自己维护协议适配 |
| 现成网关产品 | 开箱即用 | 定制能力弱,按价格收费 |
我当时选择自研这个框架,不是因为它简单,而是因为它是性价比最高、可定制性最强的。按自己的产线情况来控制协议栈、点位表、缓存策略,想怎么扩展怎么扩展。
1.3 物联网三层架构中的定位
做物联网的人都知道三层架构:感知层、网络层、应用层。总有人拿这个框架理论套项目,但在落地的时候经常发现感知层和应用层之间缺一座桥。PLC属于感知层,云端平台属于应用层,网络层不只是一根网线或者4G路由器,它真正缺的是一个“翻译官”。
这个翻译官,就是本文说的PLC转Web API服务器框架。它承担的是边缘网关的职责:向下,把PLC的寄存器数据读回来;向上,把标准HTTP接口或者MQTT主题提供给应用层。很多人做的“物联网毕业设计”,往往卡在“传感器数据怎么传上云”,如果传感器是PLC,那这套框架就是整个方案的中枢。
不管你是做食用菌栽培车间环境监控,还是做十字路口红绿灯PLC程序的数据采集,底层逻辑都是一样的:PLC负责控制逻辑,框架负责把设备数据“翻译”成业务系统听得懂的语言。有了这层翻译,三层架构才真正在现实项目中落地。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 框架分层与核心设计
2.1 分层架构:协议驱动、点位映射、API暴露
我前后迭代了三版,最后稳定下来的分层模型是四层,每一层只干一件事。
第一层,设备连接层。负责管理到PLC的物理连接,是Modbus TCP还是S7协议还是串口RTU,都由这一层屏蔽。它要维护连接状态、超时重连、心跳检测,保证底层通道是通的。很多新人写代码最容易在这一层翻车,一个connect写到主流程里,PLC一重启程序就崩了。
第二层,协议适配层。这一层把不同PLC的“方言”翻译成统一的数据结构。比如西门子S7-200SMART用PPI或者Modbus TCP,三菱FX用MC协议,台达和汇川用Modbus。协议适配层输出的东西应该统一成“寄存器号 + 值 + 类型 + 时间戳”,而不是让上层去分辨“这个地址是FC3还是FC4”。
第三层,点位映射层。这是框架的大脑,后面单独说。它把“PLC寄存器地址”映射成业务上的“温度”、“转速”、“产量”等点位。
第四层,API服务层。对外暴露HTTP接口,提供查询、写入、订阅三类能力。订阅能力最实用:点位变化后主动推给MQTT或者WebSocket,比让业务系统反复轮询高效得多。
这四层之间的依赖是单向的:API只认识点位表,点位表的数值由协议适配层填充,协议适配层只和设备连接层打交道。任何人接手这套代码,只需要看点位表和API文档就能干活,不需要把PLC协议啃一遍。
2.2 点位表:整个框架的灵魂
如果把框架比作一个翻译公司,点位表就是公司的词典。一行点位,定义了“业务名称”和“PLC地址”的对应关系。
我见过不少代码把点位信息写死在业务逻辑里,比如readRegister(0x100)直接写在查询温度的API里。刚开始跑没问题,后来换设备、加量程,就得改代码重新部署,极其痛苦。点位表把这部分彻底解耦了。
一张标准的点位表至少包含这些字段:
| 字段 | 说明 | 示例 |
|---|---|---|
| point_name | 业务点位名,API对外暴露的名称 | temp_zone1 |
| device_name | 设备标识,对应哪台PLC | drying_room_plc |
| register_type | 寄存器类型 | HOLDING_REGISTER / INPUT_REGISTER / COIL |
| register_addr | 寄存器地址 | 40001(4开头是保持寄存器) |
| data_type | 数据类型 | FLOAT / INT / BOOL / WORD |
| byte_order | 字节序 | BIG_ENDIAN / LITTLE_ENDIAN |
| scale | 缩放系数 | 0.1,表示原值除以10 |
| offset | 偏移量 | 0 |
| rw_flag | 读写权限 | RW / RO / WO |
| poll_cycle | 轮询周期(ms) | 1000 |
实战里scale和offset最容易忽略。举个例子,PLC里存储的温度是3270,实际上代表32.70摄氏度,缩放系数就是0.01。如果不做换算,业务系统拿到3270就会一脸懵。这个换算逻辑应该放在框架里做,而不是让每个上层系统自己算。
点位表的存储方式,简单项目用SQLite就够了,或者干脆用JSON文件。别用MySQL,多一层网络依赖不说,启动时还得先连数据库,根本没有必要。我甚至见过用Excel当点位表的,框架启动时读一次,也完全可行。
2.3 数据类型与字节序:最容易被坑的地方
写PLC转Web API前,很多人以为难的是协议,实际跑起来才发现难的是数据解析。PLC内部的数据类型和JSON完全不搭边,你不细细处理,处处是坑。
PLC里常用的数据类型对应关系大约是:
| PLC类型 | 位数 | JSON类型 | 备注 |
|---|---|---|---|
| BOOL | 1位 | boolean | 离散量,读线圈 |
| BYTE / USINT | 8位 | number | 0-255 |
| INT / SINT | 16位 | number | -32768到32767 |
| DINT / UDINT | 32位 | number | 注意高精度,JS里要小心 |
| REAL | 32位 | number | IEEE754浮点 |
| STRING | 变长 | string | 西门子用S7 String,解析要特殊处理 |
类型不匹配的后果是灾难性的。把DINT当INT读,数值直接截断;把REAL当成两个INT读,能读出天文数字。这也是新手最容易懵的地方:“明明都是读4个字,为什么两台PLC读出来的数完全不一样?”
字节序问题更要命。Modbus协议标准规定寄存器用大端序,但很多PLC在存储32位数据时用低位在前的小端序,还有的PLC把字序和字节序拆开存。实际项目里,西门子和大部分国产PLC用大端序,三菱部分型号和某些老设备用“字交换”,读出来的浮点数往往要做字节交换才能正常显示。
我踩过最狠的一次坑,是台达PLC读浮点温度,读出来非常大,怎么调都不对。最后发现台达的REAL在Modbus里是按“低字在前、高字在后”存储的,和西门子的顺序刚好相反。解决办法就是在配置表里加一列byte_order,读取后做一次字节交换。这个字段谁都不许省,写进规范里。
另一个容易被忽略的点是模拟量标定。PLC读到的模拟量是原始值,比如西门子SMART系列是0~27648对应0~10V,三菱和台达一般是0~4095。如果传感器是4~20mA的,量程还得换成工程量。比如温度传感器是-20~80摄氏度,对应4~20mA,原始值和实际温度就是线性关系。这些换算写在点位表里,框架统一处理,业务系统拿到的永远是“有意义”的数字。
顺带说一句,如果上层系统做温度控制,发现“PLC温度PID波动温差大”,别急着去调参数,先确认网关侧拿到的数据是不是连续的、时间戳是不是准确的。很多波动其实是采集缓存和显示延迟制造的假象,底层PID本身没有问题。
3. 最小可用框架落地实操
3.1 技术选型与跑通环境
做这类框架,技术栈的选择其实很自由。C#用ASP.NET Core加NModbus,Python用pymodbus加FastAPI,Java用Spring加jamod,都能做。但我个人最推荐Node.js方案,原因有两点:一是Node的异步事件循环天然适合同时轮询多台PLC,IO密集型的活干起来很顺手;二是实现轻量,一个树莓派甚至工控软路由就能跑,部署特别方便。
我这里给出一套可以直接抄作业的组合:Node.js + Express + modbus-serial + better-sqlite3。都是成熟库,文档全,社区活跃。
跑通环境只需要三步:
- 安装Node.js(建议LTS版本,比如18或20)。
- 建个目录,运行
npm init -y,然后安装express modbus-serial better-sqlite3。 - 准备一台能连通的PLC,或者先用Modbus模拟软件(比如Modbus Slave)代替。
模拟软件太重要了,强烈建议没有实体PLC的时候先用它练手。我经常给朋友说,先把网关对着模拟器调通,再上现场,能省下一半的调试时间。
3.2 从读取PLC寄存器到暴露HTTP接口
下面用一个最典型的例子走一遍:用Modbus TCP读取PLC的保持寄存器,把数据暴露成HTTP API。以台达或者汇川的PLC为例,假设温度存在保持寄存器地址0(Modbus地址40001),是一个32位浮点数。
先看和设备连接相关的代码:
javascript复制const ModbusRTU = require("modbus-serial");
class PlcConnection {
constructor(ip, port = 502, timeout = 3000) {
this.ip = ip;
this.port = port;
this.timeout = timeout;
this.client = new ModbusRTU();
this.isConnected = false;
}
async connect() {
try {
await this.client.connectTCP(this.ip, { port: this.port, timeout: this.timeout });
this.isConnected = true;
console.log(`[PLC] connected to ${this.ip}:${this.port}`);
} catch (err) {
this.isConnected = false;
console.error(`[PLC] connect failed: ${err.message}`);
throw err;
}
}
async readFloat32(addr) {
if (!this.isConnected) {
await this.connect();
}
// 读取2个寄存器,得到32位数据
const res = await this.client.readHoldingRegisters(addr, 2);
return res.data[0];
}
}
注意,readFloat32在这里读出来的其实还是两个16位寄存器。真正的浮点解析由点位表的byte_order逻辑处理,我没有写进这段基础代码里,这是个很重要的设计决策:连接层只负责搬运,不负责解释。
接着是点位表驱动的轮询器和API。点位表用SQLite存储,框架启动时加载到内存,然后定期轮询。下面是一个极简的轮询调度器:
javascript复制const Express = require("express");
const Database = require("better-sqlite3");
const db = new Database("points.db");
// 模拟点位表
db.exec(`
CREATE TABLE IF NOT EXISTS points (
id INTEGER PRIMARY KEY AUTOINCREMENT,
point_name TEXT UNIQUE,
device_name TEXT,
register_addr INTEGER,
data_type TEXT,
byte_order TEXT,
scale REAL,
offset REAL,
rw_flag TEXT
);
`);
const pointCache = new Map(); // 内存缓存,API直接读这里
function fetchAllPoints() {
const points = db.prepare("SELECT * FROM points").all();
points.forEach((p) => {
const raw = plc.readFloat32(p.register_addr);
const value = applyScale(raw, p.scale, p.offset);
pointCache.set(p.point_name, {
value,
ts: Date.now(),
device: p.device_name,
});
});
}
// 每隔1秒轮询一次
setInterval(fetchAllPoints, 1000);
然后定义一个简单的API接口,让业务系统查询:
javascript复制const app = new Express();
app.get("/api/points/:name", (req, res) => {
const name = req.params.name;
const data = pointCache.get(name);
if (!data) {
return res.status(404).json({ error: "point not found" });
}
res.json(data);
});
app.listen(8080, () => {
console.log("PLC Web API server running on :8080");
});
这个版本虽然粗糙,但确实是能跑的最小闭环:定时读取PLC → 换算 → 缓存 → 通过HTTP输出。后面所有的可靠性、安全性、性能优化,都是在这个闭环上做的升级。
3.3 轮询策略与性能取舍
第一个要确定的参数就是轮询周期。很多新手会把轮询时间设置得特别短,觉得越快越好,比如50毫秒扫一遍全部点位。这么做往往得不偿失,因为PLC的通信响应能力是有限的,你发请求的速度超过了它处理的速度,就会积压。积压多了,PLC的通信模块直接“卡死”,严重的时候连编程软件下载程序都受影响。
轮询周期的核心原则是:够用就行。如果温度变化是慢变量,1秒一次甚至5秒一次就够了;如果是伺服位置、计数器之类的快变量,再适当缩短。为了适配不同点位,点位表里专门做了poll_cycle字段,不同变量用不同周期。快速变化的数据读得密一点,缓慢变化的读得疏一点,不要一刀切。
另一个关键的性能优化是“块读取”。PLC的寄存器地址是连续的,几个相邻的点位完全可以一次读回来,而不是一个点位发一次请求。比如温度、湿度、压力在寄存器0、2、4三处,都是浮点数,那就是连续6个寄存器,一次读出来再拆,比发三次请求效率高太多。
javascript复制// 批量读取,减少交互次数
const result = await plc.client.readHoldingRegisters(0, 6);
const temp = decodeFloat(result.data[0], result.data[1]);
const humi = decodeFloat(result.data[2], result.data[3]);
const press = decodeFloat(result.data[4], result.data[5]);
实测下来,同样20个点位,如果逐个读需要约400毫秒,用块读取几十毫秒就搞定了。这个差距在PLC数量多的时候非常可观。
再就是读写分离。我的框架里,写操作不占用轮询队列,而是走独立的“即时执行”通道。为什么?因为写操作是需要立即响应的,比如启动设备、改写设定值,如果它排在轮询队列后面,可能等上好几秒才被执行,操作人员早就骂人了。写操作单独处理,还能做权限控制,防止误写。
3.4 写操作与可靠性设计
API不光是读数据,还得能下发指令。写保持寄存器最常用的功能码是FC6写单个寄存器,写多个寄存器用FC16。点位表里的rw_flag字段就是为这个准备的:RO的变量,请求写入时直接拒绝,返回403;RW的变量才允许业务系统改。
写入逻辑要处理一个很现实的问题:PLC侧数据类型的格式化。你在API里收到一个JSON数字,比如60.0,要写回给温度设定值,但PLC的设定值可能存储的是600,对应60.0摄氏度。这个换算就依赖点位表里的scale和offset,反过来执行一次即可。
javascript复制app.post("/api/points/:name", async (req, res) => {
const point = getPoint(req.params.name);
if (!point || point.rw_flag !== "RW") {
return res.status(403).json({ error: "read-only point" });
}
// 从业务值换算回原始值
const businessValue = req.body.value;
const rawValue = Math.round((businessValue - point.offset) / point.scale);
try {
await plc.writeSingleRegister(point.register_addr, rawValue);
res.json({ ok: true, rawValue });
} catch (err) {
res.status(500).json({ error: err.message });
}
});
可靠性这块,断线自动重连是必须做的。Modbus TCP的连接其实很脆弱,PLC重启、网线松动、交换机掉电都会导致连接断开。框架要具备自我修复能力:检测到异常时自动重连,重连失败按指数退避的方式增加间隔,比如1秒、2秒、4秒、8秒,最多拉长到60秒,避免疯狂重连把网络刷爆。
javascript复制async function ensureConnected() {
if (plc.isConnected) return;
let retryMs = 1000;
while (!plc.isConnected) {
try {
await plc.connect();
} catch (err) {
console.error(`[PLC] retry in ${retryMs}ms`);
await sleep(retryMs);
retryMs = Math.min(retryMs * 2, 60000);
}
}
}
数据缓存也要设计超时。假如PLC断线了,API还返回旧数据,业务系统会被误导,以为设备还在正常运转。我的做法是每个缓存数据都带时间戳,且记录“最近一次成功刷新时间”。超过一定时间(比如5个轮询周期)没刷新,接口返回时加一个stale: true的标记,让上层系统知道这是过期数据。
4. 常见问题与排查技巧
4.1 连接类问题速查
这类问题最折磨人,因为涉及链路的所有环节。
现象:API一直超时,框架日志不断报连接失败。
可能原因和对策:
| 现象 | 可能原因 | 排查思路和解决 |
|---|---|---|
| 永远连不上 | PLC网口没启用或IP配错 | 先ping PLC_IP,不通就查物理链路 |
| 偶尔连上又断开 | PLC侧设置了通信超时 | 把PLC的通信看门狗时间调大,框架侧同步加大超时 |
| 连接1分钟就断 | 交换机端口或网线问题 | 换一条网线,把自动协商关闭,固定100M全双工 |
| 连上了但读不到数据 | 寄存器地址越界 | 对照PLC程序设计文档,检查Modbus地址范围 |
| 连接被拒绝 | 端口被占用或防火墙拦截 | telnet PLC_IP 502 测试端口通不通 |
还有一个容易被忽略的点:协议参数对不上。比如有朋友问“inproshow怎么设置plc端口号”,这本质上就是设备侧通信参数和采集侧要一致。很多国产PLC的编程口默认波特率是9600,但网关侧配置成了19200,结果就是连不上。这类问题不要瞎猜,先把PLC侧的实际参数截图,再和框架配置逐项比对。
4.2 数据类问题速查
数据不对,比连不上更折磨人,因为它是“部分成功”的状态,系统跑着但数据是错的。
| 现象 | 可能原因 | 排查思路 |
|---|---|---|
| 读到的数值变成两倍大 | 缩放系数没配置 | 检查scale,比如0.1变成1了 |
| 读出的浮点是天文数字 | 字节序反了 | 调换word_order,或者做高低字交换 |
| 读到的整数是负数 | 数据类型不匹配 | 无符号当有符号读了,检查data_type |
| 位状态反了 | 线圈地址偏移 | 确认是从0还是从1开始计数 |
| 数值长期不动 | 轮询线程卡死或PLC程序变量没更新 | 看日志里最近刷新时间戳 |
字节序问题解决得多了,我总结出一个习惯:凡是解析浮点数的代码,必须把字节序的校验写进单元测试里,用固定的16进制样本跑一遍,确认字节序正确再上现场。别指望现场调试时能看出来,因为实际数据千变万化,你很难判断“看起来合理”的数值真的是对的。
4.3 现场排障的三步法与工具
排障顺序永远是:先物理层,再协议层,再应用层。
第一步,确认物理链路。用笔记本的调试工具直接ping PLC的IP,不通就查网线、交换机、IP配置。这一步能过滤掉70%的问题。
第二步,用测试工具验证协议。Modbus Poll、Modbus Slave这两个工具是神器。Modbus Poll模拟上位机,直接往PLC发Modbus报文;Modbus Slave模拟PLC,方便测框架的读取逻辑。上现场前,我习惯先用Modbus Slave把点位表里所有地址、类型都测一遍,确认数据能正常读出。
第三步,开启框架的调试日志,确认协议层数据解析正确。日志里一定要记录每次轮询的耗时、错误码、最后一次成功刷新时间。没有日志的框架就是盲人摸象,问题一出现毫无抓手。
提到“建立连接需要目标PLC的AMS NetId和端口号”这个问题,这也是典型的协议参数排查。在工业以太网协议里,很多都需要目标设备的网络标识参数,比如西门子S7协议会用到机架号和槽号,倍福、力士乐等基于AMS的协议会要求AMS NetId。这类参数看起来玄学,实际就是对号入座,从编程软件里抄出来填进配置就行。如果填错,表现就是“能ping通但连接不上”,走一遍三步法就能定位到协议层参数问题。
5. 框架的扩展方向
5.1 从本地API到云端平台
本地框架跑通后,下一步自然是对接云平台。最常见的是两种方式。
第一种,平台主动拉取。云平台通过HTTP接口,每隔几秒调用一次框架的/api/points拉数据。这种方式实现简单,但云的拉取频率受限于框架的缓存周期,实时性一般,而且云平台要管理大量设备,轮询起来压力也不小。
第二种,框架主动推送。框架把点位数据封装成MQTT消息,推送到云平台的Topic。这种方式实时性好、协议轻量,是工业物联网的标准做法。框架侧可以用mqtt这个库,发布消息的代码也就几行:
javascript复制const mqtt = require("mqtt");
const mqttClient = mqtt.connect("mqtt://broker.example.com:1883");
setInterval(() => {
const payload = JSON.stringify(Object.fromEntries(pointCache));
mqttClient.publish("factory/line1/telemetry", payload);
}, 5000);
MQTT非常适合大批量设备上报,云平台侧只要订阅Topic就能拿到全厂数据。而且MQTT天然支持遗嘱消息(Last Will),设备掉线时云端能第一时间感知,这在设备状态监测场景里是刚需。很多做“物联网网络岗位细分”的团队,其实核心工作就是在折腾网关、MQTT桥接和数据清洗。
5.2 多品牌PLC与异构设备接入
一个产线里同时有西门子、台达、汇川、三菱,甚至AB的PLC,太正常了。框架要真正可用,就得把这些异构设备的协议都适配进来。好消息是,这类工作本质上是“协议驱动插拔化”:每种品牌写一个独立的驱动模块,实现相同的接口,比如connect()、read(point)、write(point),然后在点位表里加一个driver_type字段,指定用哪个驱动即可。
例如,西门子S7-200SMART,可以用snap7库走S7协议直接读写V区;汇川和台达用Modbus TCP;老的三菱FX系列走串口MC协议。不同驱动之间的代码互不影响,加一台新设备只加一个驱动,不动其他代码,这就是插件化架构的威力。
我认识一个做“PLC管理六轴机械臂伺服”的同行,他们的网关也是这个思路:机械臂的每个轴位置、速度映射成点位,上层系统通过API读实时坐标,通过写操作下发目标位置。四层架构完全没变,只是底层协议从Modbus变成了以太网与伺服驱动器通信的专用协议。这说明框架的抽象成果是可以复用的。
5.3 框架化的核心建议
最后给几条过来人的建议,算不上真理,但都是我踩坑总结出来的。
第一,配置一定不要写死在代码里。点位表、设备表、驱动选择、轮询周期,全部做成配置文件或者数据库配置。换设备、改量程、加点位,运营人员自己改配置就行,不用等开发改代码重新部署。
第二,第一时间处理缓存有效性。任何API返回的数据都必须带着时间戳状态。宁可让业务系统知道数据旧了,也不要让假数据误导决策。
第三,日志系统搭建好。这条最简单也最容易被忽略。框架的每一条通信错误、每一次轮询耗时、每一次断线重连,都应该有迹可循。后期出了任何问题,日志都是定位问题的第一手证据。
第四,别追求大而全。市面上的开源方案不少,比如Node-RED、ThingsBoard Gateway、Fuxa,选型时先想清楚自己的场景:现场设备数量、数据实时性要求、是否需要画面展示、团队维护能力。如果只是简单采集,直接用现成产品;如果你有定制化需求,自研框架是更合适的路。
我这套框架从第一版到稳定运行,前后迭代了快两年。最大的体会就是,好的框架不是“写完就完了”,而是“改起来不痛”。点位表设计好、协议层做薄、API层稳定,后期加设备、接平台都是一件非常轻松的事。如果你也在做PLC上云或者产线信息化的方案,可以从这篇文章里的最小闭环先搭起来,然后按你的实际场景一点点加功能。这是个越用越顺手的路子。
