手把手教你用开源 HMI 工具 FUXA 对接 MySQL、PostgreSQL、SQL Server,实现报警记录、生产报表、工单同步等高级功能!
在工业自动化项目中,你是否遇到过这些场景?
- 想把 PLC 的报警信息自动存到数据库,方便后期分析?
- 需要从 MES 系统读取当日工单,并在 HMI 上显示?
- 希望操作员点击"保存"按钮,就把产品批次写入 SQL Server?
- 但又不想花大价钱买商业 SCADA 的历史数据库模块?
好消息是------开源 HMI 工具 FUXA 完全可以做到!
通过 ODBC 接口 ,FUXA 能轻松连接 MySQL、PostgreSQL、SQL Server 等主流数据库,实现数据双向交互。今天,我们就来从零开始,一步步配置并实战应用。
🔧 一、为什么选 FUXA + ODBC?
FUXA 是一个基于 Web 的开源 SCADA/HMI 框架,支持 Modbus、S7、OPC UA 等工业协议。而它的 ODBC 功能,让你能:
- 将实时数据持久化存储
- 查询历史记录生成报表
- 与企业 ERP/MES 系统集成
- 构建完整的"边缘采集 + 中心存储"架构
最关键的是:完全免费、开源、可私有部署!
🐳 二、最快上手方式:用 Docker 一键启动
如果你只是想快速测试,强烈推荐使用官方 Docker 镜像------已内置所有常用 ODBC 驱动!
docker run -d -p 1881:1881 --name fuxa frangoteam/fuxa:latest
访问 http://你的IP:1881,即可打开 FUXA 编辑器。
✅ 无需安装驱动,开箱即用!
💡 提示:生产环境建议挂载配置卷,避免重启丢失工程。
⚙️ 三、本地部署?手动安装 ODBC 驱动也不难!
如果你在 Ubuntu/Debian 上用 NPM 安装 FUXA,则需手动配置驱动。
第一步:安装基础依赖
sudo apt update
sudo apt install -y unixodbc unixodbc-dev
第二步:下载 FUXA 官方驱动包
前往 GitHub 仓库的 odbc 目录,下载:
install_odbc_drivers.shodbcinst.ini
第三步:运行安装脚本
chmod +x install_odbc_drivers.sh
sudo ./install_odbc_drivers.sh
sudo cp odbcinst.ini /etc/odbcinst.ini
完成后,执行 odbcinst -q -d,应能看到:
[MySQL]
[PostgreSQL]
[ODBC Driver 17 for SQL Server]
✅ 驱动就绪!
🌐 四、两种连接方式:DSN vs 连接字符串
方式 1:配置 DSN(适合固定部署)
编辑 /etc/odbc.ini:
[my-mysql-db]
Driver = MySQL
Server = 192.168.1.50
Database = factory_db
User = scada
Password = your_password
Port = 3306
测试连接:
isql my-mysql-db
方式 2:直接用连接字符串(推荐!灵活迁移)
在 FUXA 设备配置中填写:
DRIVER=PostgreSQL Unicode;SERVER=10.0.0.10;PORT=5432;DATABASE=plant;UID=scada;PWD=pass;
✅ 优点:不依赖服务器配置,工程可移植性强。
🖥️ 五、在 FUXA 中添加 ODBC 设备
- 打开 FUXA 编辑器 → Devices → + Add Device
- 类型选择 ODBC
- Name 填
myDB(后续脚本中引用) - Connection 填 DSN 名或完整连接串
- 保存!
💻 六、实战代码:读写数据库就这么简单!
场景 1:每秒读取最新报警
// Server Script
setInterval(async () => {
const db = await $getDevice('myDB', true);
const alarms = await db.pool.query(
'SELECT * FROM alarms WHERE resolved = false ORDER BY time DESC'
);
$setTag($getTagId('activeAlarms'), JSON.stringify(alarms));
}, 1000);
场景 2:点击按钮保存产品信息
// 边沿检测,防止重复触发
let lastClick = false;
setInterval(async () => {
const clicked = $getTag($getTagId('btnSave'));
const name = $getTag($getTagId('productName'));
if (clicked && !lastClick) {
const db = await $getDevice('myDB', true);
await db.pool.query(
'INSERT INTO products (name, created_at) VALUES (?, NOW())',
[name]
);
}
lastClick = clicked;
}, 100);
场景 3:前端表格展示历史数据
- Server 脚本定时拉取数据并存入 Tag
- Client 脚本解析 JSON 并绑定到 Table 控件
- 用户看到动态刷新的生产报表!
步骤 1:Server 脚本定时拉取数据
位置:View 编辑器 → 右侧 "Script" 标签 → 切换到 Server 选项卡
// === Server Script: 定时从数据库拉取数据并更新 Tag ===
// 每 2000 毫秒(2秒)执行一次
setInterval(async () => {
try {
// 1. 获取已配置的 ODBC 设备(名称需与 Devices 中一致)
const db = await $getDevice('myProductionDB', true); // ← 替换为你自己的设备名!
// 2. 执行 SQL 查询(以 PostgreSQL 为例)
// 注意:PostgreSQL 表名区分大小写,建议加双引号
const result = await db.pool.query(`
SELECT
"id",
"product_name" AS name,
"output_qty" AS qty,
"shift",
TO_CHAR("log_time", 'YYYY-MM-DD HH24:MI:SS') AS time
FROM "production_log"
ORDER BY "log_time" DESC
LIMIT 50
`);
// 3. 将结果转为 JSON 字符串,存入 Tag
const jsonData = JSON.stringify(result);
$setTag($getTagId('productionTableData'), jsonData);
// 可选:打印日志(在 FUXA 后台 Console 查看)
console.log(`[DB] Fetched ${result.length} records`);
} catch (error) {
console.error('[DB ERROR]', error.message || error);
// 即使出错,也可清空表格或显示错误提示
$setTag($getTagId('productionTableData'), '[]');
}
}, 2000);
🔧 你需要修改的地方:
'myProductionDB'→ 替换为你在 Devices 中创建的 ODBC 设备名称- SQL 语句 → 根据你的实际表结构调整字段名和表名
步骤 2:Client 脚本绑定到 Table 控件
位置:View 编辑器 → "Script" 标签 → 切换到 Client 选项卡
// === Client Script: 监听 Tag 变化,更新前端表格 ===
// 监听 productionTableData 的变化
$watch($getTagId('productionTableData'), (newValue) => {
if (!newValue || newValue === '[]') {
// 数据为空时清空表格
const emptyTable = { columns: [], rows: [] };
$invokeObject('productionTable', 'setTableAndData', emptyTable);
return;
}
try {
// 解析 JSON 数据
const rows = JSON.parse(newValue);
// 定义表格列(必须与 SQL 查询的 AS 别名一致)
const columns = [
{ id: 'id', label: 'ID', width: 60 },
{ id: 'name', label: '产品名称', width: 120 },
{ id: 'qty', label: '产量', width: 80 },
{ id: 'shift', label: '班次', width: 80 },
{ id: 'time', label: '时间', width: 160 }
];
// 构造表格数据对象
const tableData = {
columns: columns,
rows: rows
};
// 调用 Table 控件的方法更新内容
$invokeObject('productionTable', 'setTableAndData', tableData);
} catch (e) {
console.error('Failed to parse table data:', e);
// 可选:显示错误提示
alert('表格数据解析失败,请检查数据库返回格式');
}
});
✅ 关键点:
- 使用
$watch监听 Tag 变化,自动响应更新 columns.id必须与 SQL 中的字段别名(AS)完全一致$invokeObject('productionTable', ...)中的 ID 必须与 Table 控件 ID 一致
⚠️ 七、避坑指南:这些细节你必须知道
|-----------------|-----------------------------|
| 问题 | 解决方案 |
| MySQL 连不上 | 加 SSLMODE=DISABLED (测试环境) |
| PostgreSQL 表名报错 | 用双引号:"Customer" |
| 脚本改了没生效 | 重启 FUXA 服务 |
| 密码明文不安全 | 用环境变量或加密 Tag |
| 高频写入卡顿 | 降低轮询频率,启用连接池 |
🚀 八、不止于数据库:FUXA 的更多可能
- 结合 Node-RED 做数据清洗
- 用 WebSocket 推送实时变化
- 部署在 树莓派 上做边缘 HMI
- 对接 阿里云/华为云 IoT 平台
FUXA 不只是一个 HMI,更是你构建 低成本工业智能系统 的核心引擎!
📣 结语
开源的力量,正在改变工业软件的格局。
FUXA + ODBC,让你用 零成本 实现过去万元级 SCADA 才有的数据库功能。
🔗 项目地址:https://github.com/frangoteam/FUXA
📘 官方 Wiki:https://github.com/frangoteam/FUXA/wiki/HowTo-ODBC
如果你觉得有用,欢迎点赞、转发,或在评论区留下你的应用场景!