# upper **Repository Path**: wang-qin928/upper ## Basic Information - **Project Name**: upper - **Description**: 上位机 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-08-29 - **Last Updated**: 2026-08-29 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # SC3U Modbus TCP 只读核心模块 本项目为雷赛 `SC3U-60A12D` PLC 提供独立的 C++17 Modbus TCP 读取核心模块。它用于上位机读取 PLC 的本体输入、M 辅助继电器和 D 字寄存器;后续 Qt 客户端只需链接 `sc3u_modbus`,无需依赖 QtNetwork。 > 安全边界:项目没有实现 Modbus 写线圈或写寄存器功能,不会经由网络改变 PLC 状态、输出或运动控制命令。 ## 能力范围 ```text Qt 客户端 / 控制台程序 │ 调用 C++ 接口 ▼ sc3u_modbus(持久 TCP 连接、报文校验、地址解析) │ Modbus TCP,只读功能码 01 / 02 / 03 / 04 ▼ SC3U-60A12D(Modbus TCP 从站) │ ├─ X:现场传感器与开关量输入 ├─ M:PLC 内部布尔状态 └─ D:PLC 内部 16 位数值 ``` 核心模块只保留以下读取功能: | 方法 | Modbus 功能码 | 读取对象 | | --- | --- | --- | | `readCoils` / `readMbits` | `0x01` | M、Y、B、S 等线圈;本项目优先封装 M | | `readDiscreteInputs` / `readXbits` | `0x02` | X 本体输入 | | `readHoldingRegisters` / `readDwords` | `0x03` | D、R 字寄存器;本项目优先封装 D | | `readInputRegisters` | `0x04` | 输入寄存器 | ## 地址映射 LeadStudio V3.1 默认采用“通讯地址兼容模式一”。此模式下软元件编号并不总是等于 Modbus 地址,模块通过 `AddressResolver` 统一换算,禁止调用方自行猜测偏移。 | PLC 变量 | 截图中的地址 | 功能码 | 模式一 Modbus 地址 | 说明 | | --- | --- | --- | --- | --- | | `RC_FeedRightRev` | `M721` | `0x01` | `0x0AD1` | 布尔状态 | | `RC_FeedRightValue` | `D610` | `0x03` | `0x1262` | `INT`,有符号 16 位 | | `RC_FeedLeftValue` | `D611` | `0x03` | `0x1263` | `INT`,有符号 16 位 | | 本体输入 | `X0` 至 `X43` | `0x02` | `0x0400` 起连续 36 位 | 传感器/开关量输入 | 模式一存在不连续区间:`M1535` 到 `M1536`、`D4095` 到 `D4096` 之间不能用一条 Modbus 请求连续读取。核心模块会主动拒绝跨越这些边界的高层 `readMbits`、`readDwords` 请求,调用方应拆分为两次读取。 若 PLC 工程切换到“模式二”,调用时必须显式传入 `sc3u::AddressMode::ModeTwo`。例如: ```cpp const auto flags = client.readMbits(721, 1, sc3u::AddressMode::ModeTwo); ``` ## 构建 ### MinGW ```powershell cmake -S . -B build -G Ninja -DCMAKE_CXX_COMPILER=g++ -DCMAKE_BUILD_TYPE=Debug cmake --build build --parallel 2 ctest --test-dir build --output-on-failure ``` ### MSVC 在“x64 Native Tools Command Prompt for VS”中执行: ```powershell cmake -S . -B build-msvc -G Ninja -DCMAKE_CXX_COMPILER=cl -DCMAKE_BUILD_TYPE=Debug cmake --build build-msvc --parallel 2 ctest --test-dir build-msvc --output-on-failure ``` ## 运行最小示例 先确认 PLC IP,再运行: ```powershell .\build-msvc\sc3u_read_demo.exe 192.168.1.3 ``` 示例会建立一条 TCP 连接,并只读输出: - `X0` 至 `X43` 的 36 路本体输入; - `M721`,即截图中的 `RC_FeedRightRev`; - `D610`、`D611`,即截图中的两个 `INT` 数值变量。 `192.168.1.3` 只是硬件手册中的 eth0 出厂地址;实际运行必须以现场配置为准。 ## 详细日志 每个 `sc3u::Client` 默认将详细通信日志输出到控制台,包含连接、M/X/D 地址映射、Modbus 请求/响应帧、事务号、响应校验、异常和断线。原始报文以十六进制输出,便于将上位机日志与抓包结果对照。 控制台输出无需额外配置: ```cpp sc3u::Client client({"192.168.1.3", 502, 1, std::chrono::milliseconds{3000}}); ``` 仅写入指定文件: ```cpp sc3u::Endpoint endpoint{"192.168.1.3", 502, 1, std::chrono::milliseconds{3000}}; endpoint.logging.destination = sc3u::LogDestination::File; endpoint.logging.filePath = "log/plc.log"; sc3u::Client client(std::move(endpoint)); ``` 日志文件的父目录不存在时会自动创建;文件以追加方式写入,不会覆盖既有现场记录。 同时写入控制台与文件: ```cpp endpoint.logging.destination = sc3u::LogDestination::ConsoleAndFile; ``` 若高频轮询导致日志量过大,可保留连接和错误日志,但关闭原始报文输出: ```cpp endpoint.logging.includeRawFrames = false; ``` ## Qt 集成 核心库没有 Qt 依赖。Qt 工程使用 CMake 时,可将本项目作为子目录并链接目标: ```cmake add_subdirectory(path/to/upper) target_link_libraries(your_qt_target PRIVATE sc3u_modbus) ``` 在 Qt 的工作线程中创建并使用一个 `sc3u::Client`。同一个实例不要跨线程共享:它维护持久 TCP 连接和递增事务编号,跨线程并发调用会破坏请求与响应的对应关系。 ```cpp sc3u::Client client({"192.168.1.3", 502, 1, std::chrono::milliseconds{3000}}); const auto sensors = client.readXbits("X0", 36); const auto enabled = client.readMbits(721, 1); const auto feedValues = client.readDint16(610, 2); ``` ## PLC 侧前提 1. 在 LeadStudio 的“设备 -> 系统配置”中确认“通讯地址兼容模式”为“模式一”。 2. 在 `Ethernet` 下添加并启用 `ModbusTcpSlave`,PLC 作为 Modbus TCP 服务端。 3. 确认 PLC 的实际 IP;客户端默认使用端口 `502`、站号 `1`。 4. 将上位机网卡设置为与 PLC 相同网段,且不与任何现有设备冲突。 5. 先在 LeadStudio 在线监控中确认目标 X/M/D 变量确实随现场传感器变化;通信模块只能读取 PLC 当前已采集的数据。 ## 技术选型与风险 ### 为什么采用独立 C++ 套接字实现 - 原因:核心层不绑定 Qt,Qt Widgets、Qt Quick、命令行工具或服务程序都可复用。 - 优势:没有第三方运行时依赖;使用持久 TCP 连接,避免高频轮询时反复握手;对 MBAP 长度、事务编号、站号、功能码和响应字节数逐项校验;默认详细日志便于现场定位。 - 风险:地址兼容模式或 Modbus 从站未正确配置时,会出现超时或非法地址异常;高频轮询时保留原始报文会产生较多日志;现场网络应与办公网络隔离,避免非授权设备访问 PLC。 ### 故障定位顺序 1. 超时或连接失败:检查网线、交换机、电脑与 PLC 网段、PLC IP、端口 `502` 与 Modbus TCP 从站使能状态。 2. Modbus 异常码:检查地址兼容模式、功能码与软元件类型是否匹配,以及是否跨越模式一的地址断层。 3. 能读取但数值不对:确认变量数据类型;`D610` 是有符号 `INT`,模拟量还需要按 PLC 程序的量程和比例换算。 4. 输入不变化:先在 LeadStudio 中确认对应 X 输入灯、变量或程序映射是否变化,再检查上位机读取。 ## 本地验证 以下验证已由本地自动执行: - MinGW 与 MSVC 均可编译; - CTest 覆盖地址映射、M/D 地址断层、请求帧、位/字解析和 Modbus 异常响应; - 回环模拟 PLC 覆盖真实 TCP 连接、`M721 -> 0x0AD1` 请求及响应解析; - 源文件为 UTF-8 无 BOM,公开接口不包含 Modbus 写功能码。 真实 PLC 尚未连接到开发环境,因此现场仍需执行“运行最小示例”完成最终联机验证。 ## 资料来源 - [LeadStudio 编程及应用手册](https://test.leisai.com/upload/file/2025/11/07/LeadStudio3.1%E7%BC%96%E7%A8%8B%E5%8F%8A%E5%BA%94%E7%94%A8%E6%89%8B%E5%86%8C20250915.pdf):用于确认通讯地址兼容模式、M/D/X 地址表、Modbus TCP 功能码和从站默认端口。 - [SC3U 系列运动控制小型 PLC 用户手册(硬件篇)]():用于确认 SC3U-60A12D 的本体 I/O 规模与以太网硬件能力。