news 2026/7/17 14:52:08

Qt 通过 NMC-G2 读取 UPS 状态

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Qt 通过 NMC-G2 读取 UPS 状态

一、 前期准备

1. 硬件链路

  1. NMC-G2 网卡插入山特 UPS 智能卡槽,网线一头插网卡网口,一头插电脑 / 交换机;
  2. UPS 开机市电正常供电,网卡指示灯绿灯常亮。

2. 获取网卡 IP

  1. 打开官网下载的NMC-utility-tool-1.0.4_Windows.zip工具;(下载过程查看NMC-G2网络管理卡 资料官网下载流程-CSDN博客)
  2. 工具自动扫描局域网,列表里会显示 NMC-G2 的 IP(例:192.168.0.50),记下来。
  3. 设置电脑和网卡处于同一局域网网段。

3. 网卡后台开启 Modbus TCP 服务

  1. 浏览器输入网卡 IP,登录 Web 管理页(默认账号 admin/admin);
  2. 左侧菜单:设置 → 通信协议
  3. 勾选开启Modbus TCP,确认端口 502、从站 ID=1,保存;
  4. 固件低于 2.4.0-r0:上传红色 tar 固件包( (SANTAK_NMCG2_updatePackage-2.4.0-r0.tar下载过程查看NMC-G2网络管理卡 资料官网下载流程-CSDN博客))升级,否则寄存器数据不全。

二、Qt 工程基础配置

1. .pro 文件添加 Modbus 模块

QT += core gui widgets modbus network CONFIG += c++11

2. 核心原理回顾(对应你的协议截图)

NMC-G2 把 UPS 所有数据存在保持寄存器(功能码 03 读取)

  1. 0x0000~0x004F:文本信息(型号、序列号,1 个寄存器存 2 个 ASCII 字符)
  2. 0x0060~0x0065:告警故障位掩码
  3. 0x0070:UPS 运行模式(0 市电 / 1 电池 / 2 旁路 / 3 故障)
  4. 0x0080 起:电压、负载、电池容量(数值 ÷10 等于真实值)

3. 通信逻辑说明

  1. Qt 程序 = Modbus 客户端(主站),NMC-G2=Modbus 服务端(从站 ID=1,端口 502);
  2. 所有 UPS 状态存储在保持寄存器,使用功能码 03 批量读取;
  3. 数据分两类解析规则:
    • 文本(UPS 型号):1 个寄存器 = 2 个 ASCII 字符,高字节在前,读到 0 终止;
    • 数值(电压 / 负载):寄存器数值 ÷10 得到真实值;
    • 运行模式:寄存器 0x0070,0 = 市电、1 = 电池、2 = 旁路、3 = 故障;
    • 告警:寄存器 0x0060,二进制位标记各类故障。

三、完整可运行 Qt 代码(直接复制使用)

头文件 mainwindow.h

#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QModbusTcpClient> #include <QTimer> class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = nullptr); ~MainWindow(); private: QModbusTcpClient* m_modbus; QTimer* m_pollTimer; // 解析寄存器ASCII字符串(读取UPS型号专用) QString parseStr(const QVector<quint16>& regs, int startAddr, int wordLen); // 定时读取UPS全部寄存器 void readUpsAllData(); // Modbus数据接收回调,解析打印所有状态 void onDataRecv(QModbusReply* reply); }; #endif

实现文件 mainwindow.cpp

#include "mainwindow.h" #include <QModbusDataUnit> #include <QModbusReply> #include <QDebug> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), m_modbus(new QModbusTcpClient(this)), m_pollTimer(new QTimer(this)) { // ========== 这里改成你工具扫描到的NMC-G2网卡IP ========== QString cardIp = "192.168.0.50"; m_modbus->setConnectionParameter(QModbusDevice::NetworkAddressParameter, cardIp); m_modbus->setConnectionParameter(QModbusDevice::NetworkPortParameter, 502); // 绑定信号 connect(m_pollTimer, &QTimer::timeout, this, &MainWindow::readUpsAllData); connect(m_modbus, &QModbusTcpClient::errorOccurred, this, [=](){ qDebug() << "Modbus通信错误:" << m_modbus->errorString(); }); // 连接网卡 if(m_modbus->connectDevice()) { qDebug() << "成功连接NMC-G2网卡,开始轮询UPS状态"; m_pollTimer->start(2000); // 2秒读取一次实时状态 } else { qDebug() << "网卡连接失败,请检查IP和网线"; } } MainWindow::~MainWindow() { m_pollTimer->stop(); if(m_modbus->isConnected()) m_modbus->disconnectDevice(); } // 按照协议文档规则,解析寄存器里的型号、序列号字符串 QString MainWindow::parseStr(const QVector<quint16>& regs, int startAddr, int wordLen) { QString result; for(int i=0; i<wordLen; i++) { int regIndex = startAddr + i; if(regIndex >= regs.size()) break; quint16 word = regs[regIndex]; // 拆分高低字节,文档规定高字节在前 char high = (word >> 8) & 0xFF; char low = word & 0xFF; if(high == 0) break; result.append(QChar(high)); if(low == 0) break; result.append(QChar(low)); } return result; } // 发起读取请求:一次性读取0 ~ 0x90所有寄存器,覆盖全部UPS状态 void MainWindow::readUpsAllData() { // 读取保持寄存器:起始地址0,读取144个Word QModbusDataUnit readCmd(QModbusDataUnit::HoldingRegisters, 0, 0x90); QModbusReply* reply = m_modbus->sendReadRequest(readCmd, 1); // 从站ID固定1 if(!reply) return; if(!reply->isFinished()) connect(reply, &QModbusReply::finished, this, &MainWindow::onDataRecv); else reply->deleteLater(); } // 收到网卡返回数据,解析UPS各类状态 void MainWindow::onDataRecv(QModbusReply *reply) { if(reply->error() != QModbusDevice::NoError) { qDebug() << "读取寄存器失败:" << reply->errorString(); reply->deleteLater(); return; } QVector<quint16> regList = reply->result().values(); // 1. 读取UPS型号(0x0010起始,32个Word) QString upsModel = parseStr(regList, 0x0010, 32); qDebug() << "【UPS型号】" << upsModel; // 2. 读取运行模式 地址0x0070 quint16 modeCode = regList[0x0070]; QString runMode; switch(modeCode) { case 0: runMode = "市电正常供电"; break; case 1: runMode = "电池放电(已停电)"; break; case 2: runMode = "旁路模式"; break; case 3: runMode = "UPS故障"; break; default: runMode = "未知状态"; } qDebug() << "【运行模式】" << runMode; // 3. 输入市电电压 地址0x0082,数值/10 float inputVol = regList[0x0082] / 10.0f; qDebug() << "【输入电压】" << inputVol << " V"; // 4. 输出负载百分比 地址0x0088,数值/10 float loadRate = regList[0x0088] / 10.0f; qDebug() << "【负载占比】" << loadRate << " %"; // 5. 电池剩余容量 地址0x0090 quint16 batCap = regList[0x0090]; qDebug() << "【电池容量】" << batCap << " %"; // 6. 告警掩码 地址0x0060 quint16 alarm = regList[0x0060]; if(alarm != 0) qDebug() << "【存在告警】告警掩码二进制:" << QString::number(alarm,2); else qDebug() << "【告警状态】无故障"; qDebug() << "---------------------------"; reply->deleteLater(); }

1. 什么是「功能码 03」

Modbus TCP 规定:客户端(你的 Qt 程序)发给网卡(NMC-G2)的指令里,用数字区分你想做什么操作,这个数字就叫功能码

  • 03= 读保持寄存器(你现在读 UPS 电压、型号、负载全部用这个)
  • 其他功能码你暂时用不上:01 读开关、06 写参数、16 批量写参数

简单理解:03= 固定暗号,告诉网卡:我要读取 UPS 存在寄存器里的数据

2. 什么是「批量读取」

网卡里存放 UPS 数据的寄存器有上百个(型号、电压、负载、告警分占不同地址)。 有两种读取方式:

  1. 单次只读 1 个寄存器:读电压发一次请求、读负载再发一次请求,来回频繁通信,卡顿、效率低;
  2. 批量读取:一次网络请求,告诉网卡「从地址 0 开始,连续读 144 个寄存器」,网卡一次性把所有数据打包返回给 Qt。 只收发 1 次报文,就能拿到全部 UPS 状态,这就是批量读取。

main.cpp

#include <QApplication> #include "mainwindow.h" int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow w; w.show(); return a.exec(); }

四、运行后输出示例

【UPS型号】SANTAK C10K 【运行模式】市电正常供电 【输入电压】225.4 V 【负载占比】32.0 % 【电池容量】98 % 【告警状态】无故障 --------------------------- 【UPS型号】SANTAK C10K 【运行模式】电池放电(已停电) 【输入电压】0.0 V 【负载占比】32.0 % 【电池容量】96 % 【存在告警】告警掩码二进制:1 ---------------------------

五,编译问题

报错含义

Qt 找不到modbus模块,工程无法识别 Modbus 相关类(QModbusTcpClient等),编译直接告警失败。

1. 进入目录,运行MaintenanceTool.exe

打开文件夹:C:\Qt\Qt5.12.12目录里直接存在MaintenanceTool.exe,双击即可启动。

2,选择「添加或移除组件」,进入对应版本Qt 5.12.12

3,展开MSVC 2017 64-bit,勾选Qt Modbus模块

4,点击下一步完成安装,重启 Qt Creator

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/17 14:52:04

3分钟掌握Forza Mods AIO:极限竞速地平线游戏修改的终极指南

3分钟掌握Forza Mods AIO&#xff1a;极限竞速地平线游戏修改的终极指南 【免费下载链接】Forza-Mods-AIO Free and open-source FH4 & FH5 mod tool 项目地址: https://gitcode.com/gh_mirrors/fo/Forza-Mods-AIO 想要在《极限竞速&#xff1a;地平线》系列游戏中获…

作者头像 李华
网站建设 2026/7/17 14:50:20

cann/asc-devkit TQue分配Tensor函数

AllocTensor 【免费下载链接】asc-devkit 本项目是CANN 推出的昇腾AI处理器专用的算子程序开发语言&#xff0c;原生支持C和C标准规范&#xff0c;主要由类库和语言扩展层构成&#xff0c;提供多层级API&#xff0c;满足多维场景算子开发诉求。 项目地址: https://gitcode.co…

作者头像 李华
网站建设 2026/7/17 14:49:09

Akamai Cloud Manager完全指南:从入门到精通Linode服务管理

Akamai Cloud Manager完全指南&#xff1a;从入门到精通Linode服务管理 【免费下载链接】manager The Akamai Cloud Manager is an open-source, single-page application serving as the official UI for interacting with the Linode API. It is trusted by hundreds of thou…

作者头像 李华
网站建设 2026/7/17 14:49:08

终极指南:如何用Linkinator快速检测和修复网站死链

终极指南&#xff1a;如何用Linkinator快速检测和修复网站死链 【免费下载链接】linkinator Broken link checker that crawls websites and validates links. Find broken links, dead links, and invalid URLs in websites, documentation, and local files. Perfect for SEO…

作者头像 李华
网站建设 2026/7/17 14:48:44

CANN/Ascend C SIMT-API FP8转换函数

__hif822float2 【免费下载链接】asc-devkit 本项目是CANN 推出的昇腾AI处理器专用的算子程序开发语言&#xff0c;原生支持C和C标准规范&#xff0c;主要由类库和语言扩展层构成&#xff0c;提供多层级API&#xff0c;满足多维场景算子开发诉求。 项目地址: https://gitcode…

作者头像 李华