You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MODBUS TCP/IP与Node.js:PLC重启后无法读取浮点值的解决方案需求

解决MODBUS PLC断开重连后无法读取浮点值的问题

你的问题核心是MODBUS客户端缺少连接失效后的自动重连逻辑:服务器启动时PLC在线,成功建立连接;但PLC断开后,原有连接失效且未触发重新连接,导致后续读取请求全部失败。

下面是完整的可运行代码,基于modbus-serial和express实现自动重连+实时读取功能:

后端(Node.js)实现

首先安装依赖:

npm install modbus-serial express cors

创建server.js:

const ModbusRTU = require("modbus-serial");
const express = require("express");
const cors = require("cors");

const app = express();
app.use(cors());
app.use(express.json());

// PLC配置信息,根据实际情况修改
const PLC_SETTINGS = {
    host: "192.168.1.100", // 替换为你的PLC IP
    port: 502, // MODBUS TCP默认端口
    slaveId: 1, // PLC从站ID
    pollInterval: 2000, // 数据轮询间隔(毫秒)
    floatRegisterAddr: 0, // 浮点值对应的起始寄存器地址
    registerCount: 2 // 浮点值占用2个16位寄存器
};

const client = new ModbusRTU();
let isPLCConnected = false;
let latestFloatValue = null;

// 连接PLC的封装函数
async function connectToPLC() {
    try {
        // 先关闭已有连接(如果存在)
        if (client.isOpen) await client.close();
        
        await client.connectTCP(PLC_SETTINGS.host, { port: PLC_SETTINGS.port });
        client.setID(PLC_SETTINGS.slaveId);
        isPLCConnected = true;
        console.log("PLC连接成功");
    } catch (error) {
        isPLCConnected = false;
        console.error(`PLC连接失败: ${error.message}`);
        // 1秒后自动重试连接
        setTimeout(connectToPLC, 1000);
    }
}

// 轮询读取PLC浮点值
async function pollPLCData() {
    if (!isPLCConnected) {
        await connectToPLC();
        setTimeout(pollPLCData, 1000);
        return;
    }

    try {
        // 读取保持寄存器
        const response = await client.readHoldingRegisters(PLC_SETTINGS.floatRegisterAddr, PLC_SETTINGS.registerCount);
        // 转换为32位浮点值(大端模式,若PLC是小端则交换两个寄存器的位置)
        latestFloatValue = ModbusRTU.utils.toFloat(response.data[0], response.data[1]);
        console.log(`读取到浮点值: ${latestFloatValue}`);
    } catch (error) {
        isPLCConnected = false;
        console.error(`读取PLC数据失败: ${error.message}`);
    } finally {
        // 无论成功失败,按设定间隔继续轮询
        setTimeout(pollPLCData, PLC_SETTINGS.pollInterval);
    }
}

// 提供给前端的API接口
app.get("/api/plc-data", (req, res) => {
    res.json({
        connected: isPLCConnected,
        floatValue: latestFloatValue,
        timestamp: new Date().toISOString()
    });
});

// 启动服务器并初始化轮询
const SERVER_PORT = 3000;
app.listen(SERVER_PORT, () => {
    console.log(`服务器运行在 http://localhost:${SERVER_PORT}`);
    pollPLCData();
});

前端(HTML/CSS/JS)实现

创建index.html:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>PLC浮点值监控</title>
    <style>
        * {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }
        body {
            font-family: system-ui, -apple-system, sans-serif;
            max-width: 500px;
            margin: 3rem auto;
            padding: 0 1rem;
        }
        .status-card {
            padding: 1.2rem;
            border-radius: 8px;
            margin-bottom: 1.5rem;
            text-align: center;
        }
        .connected {
            background-color: #ecfdf5;
            color: #065f46;
            border: 1px solid #a7f3d0;
        }
        .disconnected {
            background-color: #fef2f2;
            color: #991b1b;
            border: 1px solid #fecaca;
        }
        .value-display {
            font-size: 2.5rem;
            font-weight: 600;
            text-align: center;
            color: #1f2937;
        }
    </style>
</head>
<body>
    <div class="status-card" id="connectionStatus">
        正在连接PLC...
    </div>
    <div class="value-display" id="floatValue">--</div>

    <script>
        const STATUS_ELEMENT = document.getElementById("connectionStatus");
        const VALUE_ELEMENT = document.getElementById("floatValue");
        const FETCH_INTERVAL = 2000; // 和后端轮询间隔保持一致

        async function updatePLCData() {
            try {
                const response = await fetch("http://localhost:3000/api/plc-data");
                const data = await response.json();

                // 更新连接状态显示
                if (data.connected) {
                    STATUS_ELEMENT.className = "status-card connected";
                    STATUS_ELEMENT.textContent = "PLC已连接";
                    VALUE_ELEMENT.textContent = data.floatValue ? data.floatValue.toFixed(2) : "--";
                } else {
                    STATUS_ELEMENT.className = "status-card disconnected";
                    STATUS_ELEMENT.textContent = "PLC未连接";
                    VALUE_ELEMENT.textContent = "--";
                }
            } catch (error) {
                STATUS_ELEMENT.className = "status-card disconnected";
                STATUS_ELEMENT.textContent = "服务器连接异常";
                VALUE_ELEMENT.textContent = "--";
            } finally {
                setTimeout(updatePLCData, FETCH_INTERVAL);
            }
        }

        // 启动前端数据更新
        updatePLCData();
    </script>
</body>
</html>

关键说明

  • 自动重连逻辑:connectToPLC函数在连接失败后会自动重试,pollPLCData每次轮询前都会检查连接状态,确保断开后能重新建立连接。
  • 浮点值转换:根据PLC的字节序调整toFloat的参数顺序,大部分PLC默认大端模式,若读取值异常,可改为ModbusRTU.utils.toFloat(response.data[1], response.data[0])。
  • 状态同步:前后端都维护连接状态,前端实时显示连接情况和最新数据,提升用户体验。

内容的提问来源于stack exchange,提问作者Suriya S

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 01:34:50