devices modbus rtu 1.0

This commit is contained in:
flower_linux
2026-04-07 14:20:04 +08:00
parent 28b5082742
commit 51175ffd18
38 changed files with 1006 additions and 745 deletions

View File

@@ -1,3 +1,14 @@
# Placeholder for modbus_rtu plugin
add_library(modbus_rtu SHARED plugin_stub.cpp)
set_target_properties(modbus_rtu PROPERTIES OUTPUT_NAME "modbus_rtu")
# Runtime-loadable Modbus RTU parser + framer plugin
add_library(modbus_rtu SHARED
plugin_stub.cpp
../modbusrtu/parser/ModbusRtuProtocolPlugin.cpp
../modbusrtu/codec/ModbusRtuCodec.cpp
../modbusrtu/framer/ModbusRtuFramerPlugin.cpp
../modbusrtu/framer/ModbusRtuFraming.cpp
)
target_include_directories(modbus_rtu PRIVATE
${CMAKE_SOURCE_DIR}
${CMAKE_SOURCE_DIR}/src
)
target_link_libraries(modbus_rtu PRIVATE Qt6::Core)
set_target_properties(modbus_rtu PROPERTIES OUTPUT_NAME "modbus_rtu")

View File

@@ -1,2 +1,17 @@
extern "C" void
softbus_modbus_rtu_plugin_stub() {}
#include "plugins/protocols/modbusrtu/framer/ModbusRtuFramerPlugin.h"
#include "plugins/protocols/modbusrtu/parser/ModbusRtuProtocolPlugin.h"
extern "C" int softbus_plugin_abi_version()
{
return 1;
}
extern "C" softbus::core::plugin_system::IProtocolPlugin* softbus_create_protocol_plugin()
{
return softbus::core::plugin_system::createModbusRtuProtocolPluginRaw();
}
extern "C" softbus::core::plugin_system::IProtocolFramerPlugin* softbus_create_framer_plugin()
{
return softbus::message_bus::pipeline::protocol::modbusrtu::createModbusRtuFramerPluginRaw();
}

View File

@@ -0,0 +1,37 @@
# Modbus RTU 协议模块(`modbusrtu`
本目录实现 **Modbus RTU** 的成帧、编解码与解析插件,供守护进程静态编译与 `plugins/protocols/modbus_rtu` 动态库(`.so`)共用同一套源码。
## 目录结构
| 子目录 | 职责 |
|--------|------|
| `types/` | PDU 数据模型:`ModbusRtuPdu` 及各类请求/响应结构体,供编解码与 `ProtocolEnvelope` 使用。 |
| `codec/` | ADU 与 `ModbusRtuPdu` 的互转:`decodeModbusRtu` / `encodeModbusRtu`(依赖 `types``framer` 中的 CRC 等工具函数)。 |
| `framer/` | 字节流成帧:`ModbusRtuFraming`长度预测、CRC`ModbusRtuFramerPlugin`Stage1 插件会话)。 |
| `parser/` | Stage2 协议插件:`ModbusRtuProtocolPlugin`,将完整帧解析为 `ProtocolEnvelope` / 兼容 JSON。 |
## 依赖方向(建议遵守)
```
types (无本模块内依赖)
codec、framer (可依赖 typescodec 使用 framer 中的 crc16/crcOk
parser (依赖 codec、types不反向依赖 framer 插件类)
```
成帧与解析在流水线中分属不同阶段;**解析侧只应处理 `CompleteFrame`**,与 `PipelineContext::frameKind` 约定一致。
## 与动态库的关系
- 动态库入口:`plugins/protocols/modbus_rtu/plugin_stub.cpp`,导出 `softbus_create_protocol_plugin` / `softbus_create_framer_plugin` 等符号。
- 主工程 `CMakeLists.txt` 同样列出本目录下源文件,用于无 `.so` 时的内置链接;路径与 include 均以 `plugins/protocols/modbusrtu/...` 为准。
## 配置提示
`daemon_config.json``plugins.framers` 中配置 `libmodbus_rtu.so` 路径即可加载成帧插件;解析插件加载方式与 `PluginHost` / 工程内注册逻辑一致。
## 扩展其它协议时的参考
新增协议(如 Modbus TCP、CANopen建议沿用类似分层**types → codec/framer → parser**,并在各自 `plugin_stub` 中导出统一 ABI 工厂函数,由 `PluginHost` 注册到 `PluginManager`

View File

@@ -0,0 +1,428 @@
#include "plugins/protocols/modbusrtu/codec/ModbusRtuCodec.h"
#include <variant>
#include "message_bus/pipeline/PipelineContext.h"
#include "plugins/protocols/modbusrtu/framer/ModbusRtuFraming.h"
namespace softbus::message_bus::pipeline::protocol
{
namespace
{
using softbus::message_bus::pipeline::BusDirection;
using softbus::message_bus::pipeline::protocol::modbusrtu::crc16;
using softbus::message_bus::pipeline::protocol::modbusrtu::crcOk;
void appendCrcLe(QByteArray& adu)
{
const auto crc = crc16(reinterpret_cast<const std::uint8_t*>(adu.constData()),
static_cast<std::size_t>(adu.size()));
adu.append(static_cast<char>(crc & 0xFF));
adu.append(static_cast<char>((crc >> 8) & 0xFF));
}
} // namespace
// 功能 解码Modbus RTU帧 如果解码失败 则返回false 如果解码成功 则返回true
bool decodeModbusRtu(BusDirection dir,
const std::uint8_t* p,
std::size_t n,
ModbusRtuPdu& out,
QString* errMsg)
{
auto fail = [&](const char* msg) {
if (errMsg) {
*errMsg = QString::fromUtf8(msg);
}
return false;
};
if (!p || n < 4) {
return fail("adu_too_short");
}
if (p[1] & 0x80) {
if (n < 5) {
return fail("exception_incomplete");
}
if (!crcOk(p, 5)) {
return fail("exception_crc");
}
out = ModbusExceptionPdu{p[0], p[1], p[2]};
return true;
}
const std::uint8_t unit = p[0];
const std::uint8_t fc = p[1];
auto parseReadReq = [&]() -> bool {
if (n != 8 || !crcOk(p, 8)) {
return false;
}
const std::uint16_t addr = static_cast<std::uint16_t>((p[2] << 8) | p[3]);
const std::uint16_t qty = static_cast<std::uint16_t>((p[4] << 8) | p[5]);
switch (fc) {
case 0x01:
out = ReadCoilsRequest{unit, addr, qty};
return true;
case 0x02:
out = ReadDiscreteInputsRequest{unit, addr, qty};
return true;
case 0x03:
out = ReadHoldingRegistersRequest{unit, addr, qty};
return true;
case 0x04:
out = ReadInputRegistersRequest{unit, addr, qty};
return true;
default:
return false;
}
};
auto parseWriteSingleReqOrResp = [&]() -> bool {
if (n != 8 || !crcOk(p, 8)) {
return false;
}
const std::uint16_t addr = static_cast<std::uint16_t>((p[2] << 8) | p[3]);
const std::uint16_t val16 = static_cast<std::uint16_t>((p[4] << 8) | p[5]);
if (fc == 0x05) {
const std::uint8_t coil = (val16 == 0xFF00) ? 1 : 0;
if (dir == BusDirection::Upstream) {
out = WriteSingleCoilResponse{unit, addr, coil};
} else {
out = WriteSingleCoilRequest{unit, addr, coil};
}
return true;
}
if (fc == 0x06) {
if (dir == BusDirection::Upstream) {
out = WriteSingleRegisterResponse{unit, addr, val16};
} else {
out = WriteSingleRegisterRequest{unit, addr, val16};
}
return true;
}
return false;
};
auto parseWriteMultiReq = [&]() -> bool {
if (n < 9) {
return false;
}
const std::uint16_t start = static_cast<std::uint16_t>((p[2] << 8) | p[3]);
const std::uint16_t qty = static_cast<std::uint16_t>((p[4] << 8) | p[5]);
const std::uint8_t byteCount = p[6];
if (n != static_cast<std::size_t>(9) + byteCount) {
return false;
}
if (!crcOk(p, n)) {
return false;
}
if (fc == 0x0F) {
QVector<std::uint8_t> vals;
vals.reserve(byteCount);
for (std::uint8_t i = 0; i < byteCount; ++i) {
vals.append(p[7 + i]);
}
out = WriteMultipleCoilsRequest{unit, start, qty, std::move(vals)};
return true;
}
if (fc == 0x10) {
QVector<std::uint16_t> vals;
vals.reserve(byteCount / 2);
for (std::uint8_t i = 0; i + 1 < byteCount; i += 2) {
vals.append(static_cast<std::uint16_t>((p[7 + i] << 8) | p[7 + i + 1]));
}
out = WriteMultipleRegistersRequest{unit, start, qty, std::move(vals)};
return true;
}
return false;
};
auto parseWriteMultiResp = [&]() -> bool {
if (n != 8 || !crcOk(p, 8)) {
return false;
}
const std::uint16_t start = static_cast<std::uint16_t>((p[2] << 8) | p[3]);
const std::uint16_t qty = static_cast<std::uint16_t>((p[4] << 8) | p[5]);
if (fc == 0x0F) {
out = WriteMultipleCoilsResponse{unit, start, qty, {}};
return true;
}
if (fc == 0x10) {
out = WriteMultipleRegistersResponse{unit, start, qty, {}};
return true;
}
return false;
};
// 功能 解析Modbus的读取响应帧
// function code range 01 02 03 04
auto parseReadResp = [&]() -> bool {
if (n < 5) {
return false;
}
const std::uint8_t bc = p[2];
const std::size_t total = static_cast<std::size_t>(5) + bc;
if (n < total || !crcOk(p, total)) {
return false;
}
if (fc == 0x01) {
QVector<std::uint8_t> coils;
coils.reserve(bc);
for (std::uint8_t i = 0; i < bc; ++i) {
coils.append(p[3 + i]);
}
out = ReadCoilsResponse{unit, std::move(coils)};
return true;
}
if (fc == 0x02) {
QVector<std::uint8_t> inputs;
inputs.reserve(bc);
for (std::uint8_t i = 0; i < bc; ++i) {
inputs.append(p[3 + i]);
}
out = ReadDiscreteInputsResponse{unit, std::move(inputs)};
return true;
}
if (fc == 0x03 || fc == 0x04) {
QVector<std::uint16_t> regs;
regs.reserve(bc / 2);
for (std::uint8_t i = 0; i + 1 < bc; i += 2) {
regs.append(static_cast<std::uint16_t>((p[3 + i] << 8) | p[3 + i + 1]));
}
if (fc == 0x03) {
out = ReadHoldingRegistersResponse{unit, std::move(regs)};
} else {
out = ReadInputRegistersResponse{unit, std::move(regs)};
}
return true;
}
return false;
};
// if (dir == BusDirection::Downstream) {
// // 功能 解析Modbus的下行请求帧
// // function code range 01 02 03 04 05 06 0F 10
// if (parseReadReq() || parseWriteSingleReqOrResp() || parseWriteMultiReq()) {
// return true;
// }
// out = ModbusRtuUnsupportedPdu{unit, fc};
// return true;
// }
// if (dir == BusDirection::Upstream) {
// // 功能 解析Modbus的上行响应帧
// // function code range 01 02 03 04 05 06 0F 10
// if (parseReadResp() || parseWriteSingleReqOrResp() || parseWriteMultiResp()) {
// return true;
// }
// // 功能 解析Modbus的下行请求帧
// if (parseReadReq() || parseWriteMultiReq()) {
// return true;
// }
// out = ModbusRtuUnsupportedPdu{unit, fc};
// return true;
// }
if (parseReadReq() || parseWriteSingleReqOrResp() || parseWriteMultiReq() || parseReadResp()
|| parseWriteMultiResp()) {
return true;
}
out = ModbusRtuUnsupportedPdu{unit, fc};
return true;
}
bool encodeModbusRtu(const ModbusRtuPdu& pdu, QByteArray& out, QString* errMsg)
{
auto fail = [&](const char* msg) {
if (errMsg) {
*errMsg = QString::fromUtf8(msg);
}
return false;
};
out.clear();
if (const auto* ex = std::get_if<ModbusExceptionPdu>(&pdu)) {
out.append(static_cast<char>(ex->unitId));
out.append(static_cast<char>(ex->function));
out.append(static_cast<char>(ex->exceptionCode));
appendCrcLe(out);
return true;
}
auto encodeReadReq = [&](std::uint8_t unit, std::uint8_t fc, std::uint16_t start, std::uint16_t qty) {
out.append(static_cast<char>(unit));
out.append(static_cast<char>(fc));
out.append(static_cast<char>((start >> 8) & 0xFF));
out.append(static_cast<char>(start & 0xFF));
out.append(static_cast<char>((qty >> 8) & 0xFF));
out.append(static_cast<char>(qty & 0xFF));
appendCrcLe(out);
return true;
};
auto encodeReadRegResp = [&](std::uint8_t unit, std::uint8_t fc, const QVector<std::uint16_t>& regs) {
if (regs.size() > 127) {
return false;
}
out.append(static_cast<char>(unit));
out.append(static_cast<char>(fc));
out.append(static_cast<char>(regs.size() * 2));
for (std::uint16_t v : regs) {
out.append(static_cast<char>((v >> 8) & 0xFF));
out.append(static_cast<char>(v & 0xFF));
}
appendCrcLe(out);
return true;
};
auto encodeReadBitsResp = [&](std::uint8_t unit, std::uint8_t fc, const QVector<std::uint8_t>& bytes) {
if (bytes.size() > 255) {
return false;
}
out.append(static_cast<char>(unit));
out.append(static_cast<char>(fc));
out.append(static_cast<char>(bytes.size()));
for (std::uint8_t b : bytes) {
out.append(static_cast<char>(b));
}
appendCrcLe(out);
return true;
};
if (const auto* req = std::get_if<ReadCoilsRequest>(&pdu)) {
return encodeReadReq(req->unitId, 0x01, req->startAddress, req->quantity);
}
if (const auto* req = std::get_if<ReadDiscreteInputsRequest>(&pdu)) {
return encodeReadReq(req->unitId, 0x02, req->startAddress, req->quantity);
}
if (const auto* req = std::get_if<ReadHoldingRegistersRequest>(&pdu)) {
return encodeReadReq(req->unitId, 0x03, req->startAddress, req->quantity);
}
if (const auto* req = std::get_if<ReadInputRegistersRequest>(&pdu)) {
return encodeReadReq(req->unitId, 0x04, req->startAddress, req->quantity);
}
if (const auto* resp = std::get_if<ReadHoldingRegistersResponse>(&pdu)) {
if (!encodeReadRegResp(resp->unitId, 0x03, resp->registers)) {
return fail("too_many_registers");
}
return true;
}
if (const auto* resp = std::get_if<ReadInputRegistersResponse>(&pdu)) {
if (!encodeReadRegResp(resp->unitId, 0x04, resp->inputRegisters)) {
return fail("too_many_registers");
}
return true;
}
if (const auto* resp = std::get_if<ReadCoilsResponse>(&pdu)) {
if (!encodeReadBitsResp(resp->unitId, 0x01, resp->coils)) {
return fail("too_many_bytes");
}
return true;
}
if (const auto* resp = std::get_if<ReadDiscreteInputsResponse>(&pdu)) {
if (!encodeReadBitsResp(resp->unitId, 0x02, resp->discreteInputs)) {
return fail("too_many_bytes");
}
return true;
}
if (const auto* req = std::get_if<WriteSingleCoilRequest>(&pdu)) {
out.append(static_cast<char>(req->unitId));
out.append(static_cast<char>(0x05));
out.append(static_cast<char>((req->address >> 8) & 0xFF));
out.append(static_cast<char>(req->address & 0xFF));
const std::uint16_t coilWord = req->value ? 0xFF00 : 0x0000;
out.append(static_cast<char>((coilWord >> 8) & 0xFF));
out.append(static_cast<char>(coilWord & 0xFF));
appendCrcLe(out);
return true;
}
if (const auto* resp = std::get_if<WriteSingleCoilResponse>(&pdu)) {
out.append(static_cast<char>(resp->unitId));
out.append(static_cast<char>(0x05));
out.append(static_cast<char>((resp->address >> 8) & 0xFF));
out.append(static_cast<char>(resp->address & 0xFF));
const std::uint16_t coilWord = resp->value ? 0xFF00 : 0x0000;
out.append(static_cast<char>((coilWord >> 8) & 0xFF));
out.append(static_cast<char>(coilWord & 0xFF));
appendCrcLe(out);
return true;
}
if (const auto* req = std::get_if<WriteSingleRegisterRequest>(&pdu)) {
out.append(static_cast<char>(req->unitId));
out.append(static_cast<char>(0x06));
out.append(static_cast<char>((req->address >> 8) & 0xFF));
out.append(static_cast<char>(req->address & 0xFF));
out.append(static_cast<char>((req->value >> 8) & 0xFF));
out.append(static_cast<char>(req->value & 0xFF));
appendCrcLe(out);
return true;
}
if (const auto* resp = std::get_if<WriteSingleRegisterResponse>(&pdu)) {
out.append(static_cast<char>(resp->unitId));
out.append(static_cast<char>(0x06));
out.append(static_cast<char>((resp->address >> 8) & 0xFF));
out.append(static_cast<char>(resp->address & 0xFF));
out.append(static_cast<char>((resp->value >> 8) & 0xFF));
out.append(static_cast<char>(resp->value & 0xFF));
appendCrcLe(out);
return true;
}
if (const auto* req = std::get_if<WriteMultipleCoilsRequest>(&pdu)) {
if (req->values.size() > 255) {
return fail("too_many_bytes");
}
out.append(static_cast<char>(req->unitId));
out.append(static_cast<char>(0x0F));
out.append(static_cast<char>((req->startAddress >> 8) & 0xFF));
out.append(static_cast<char>(req->startAddress & 0xFF));
out.append(static_cast<char>((req->quantity >> 8) & 0xFF));
out.append(static_cast<char>(req->quantity & 0xFF));
out.append(static_cast<char>(req->values.size()));
for (std::uint8_t b : req->values) {
out.append(static_cast<char>(b));
}
appendCrcLe(out);
return true;
}
if (const auto* resp = std::get_if<WriteMultipleCoilsResponse>(&pdu)) {
out.append(static_cast<char>(resp->unitId));
out.append(static_cast<char>(0x0F));
out.append(static_cast<char>((resp->startAddress >> 8) & 0xFF));
out.append(static_cast<char>(resp->startAddress & 0xFF));
out.append(static_cast<char>((resp->quantity >> 8) & 0xFF));
out.append(static_cast<char>(resp->quantity & 0xFF));
appendCrcLe(out);
return true;
}
if (const auto* req = std::get_if<WriteMultipleRegistersRequest>(&pdu)) {
if (req->values.size() > 127) {
return fail("too_many_registers");
}
out.append(static_cast<char>(req->unitId));
out.append(static_cast<char>(0x10));
out.append(static_cast<char>((req->startAddress >> 8) & 0xFF));
out.append(static_cast<char>(req->startAddress & 0xFF));
out.append(static_cast<char>((req->quantity >> 8) & 0xFF));
out.append(static_cast<char>(req->quantity & 0xFF));
out.append(static_cast<char>(req->values.size() * 2));
for (std::uint16_t v : req->values) {
out.append(static_cast<char>((v >> 8) & 0xFF));
out.append(static_cast<char>(v & 0xFF));
}
appendCrcLe(out);
return true;
}
if (const auto* resp = std::get_if<WriteMultipleRegistersResponse>(&pdu)) {
out.append(static_cast<char>(resp->unitId));
out.append(static_cast<char>(0x10));
out.append(static_cast<char>((resp->startAddress >> 8) & 0xFF));
out.append(static_cast<char>(resp->startAddress & 0xFF));
out.append(static_cast<char>((resp->quantity >> 8) & 0xFF));
out.append(static_cast<char>(resp->quantity & 0xFF));
appendCrcLe(out);
return true;
}
return fail("unsupported_encode");
}
} // namespace softbus::message_bus::pipeline::protocol

View File

@@ -0,0 +1,27 @@
#pragma once
#include <cstddef>
#include <cstdint>
#include <QByteArray>
#include <QString>
#include "plugins/protocols/modbusrtu/types/ModbusRtuPdu.h"
namespace softbus::message_bus::pipeline
{
enum class BusDirection;
}
namespace softbus::message_bus::pipeline::protocol
{
bool decodeModbusRtu(softbus::message_bus::pipeline::BusDirection dir,
const std::uint8_t* p,
std::size_t n,
ModbusRtuPdu& out,
QString* errMsg = nullptr);
bool encodeModbusRtu(const ModbusRtuPdu& pdu, QByteArray& out, QString* errMsg = nullptr);
} // namespace softbus::message_bus::pipeline::protocol

View File

@@ -0,0 +1,173 @@
#include "plugins/protocols/modbusrtu/framer/ModbusRtuFramerPlugin.h"
#include <algorithm>
#include <cstring>
#include "plugins/protocols/modbusrtu/framer/ModbusRtuFraming.h"
#include "utils/logs/logging.h"
namespace softbus::message_bus::pipeline::protocol::modbusrtu
{
namespace
{
class ModbusRtuFramerSession final : public softbus::core::plugin_system::IProtocolFramerSession
{
public:
ModbusRtuFramerSession(std::shared_ptr<softbus::core::memory::MemoryPool> pool,
ModbusRtuFramerPluginConfig cfg,
QString endpoint)
: m_pool(std::move(pool))
, m_cfg(cfg)
, m_endpoint(std::move(endpoint))
{
m_buf.resize(m_cfg.maxAdu);
}
void feedChunk(softbus::message_bus::pipeline::PipelineContext& in,
std::vector<softbus::message_bus::pipeline::PipelineContext>& out) override
{
using softbus::message_bus::pipeline::FrameKind;
if (!m_pool || !in.payload.valid()) {
return;
}
const std::uint8_t* p = in.payload.bytes();
const std::size_t n = in.payloadSize ? in.payloadSize : in.payload.length;
if (!p || n == 0) {
return;
}
m_lastRx = std::chrono::steady_clock::now();
if (m_cfg.interFrameTimeoutMs > 0 && m_len > 0) {
const auto age = std::chrono::duration_cast<std::chrono::milliseconds>(m_lastRx - m_lastActivity).count();
if (age >= m_cfg.interFrameTimeoutMs) {
m_len = 0;
}
}
m_lastActivity = m_lastRx;
if (m_len + n > m_cfg.maxAdu) {
LOG_WARNING() << "ModbusRtuFramerPlugin: overflow endpoint=" << m_endpoint;
m_len = 0;
return;
}
std::memcpy(m_buf.data() + m_len, p, n);
m_len += n;
while (m_len > 0) {
auto pred = predictAduLength(m_buf.data(), m_len, m_cfg.maxAdu);
if (!pred || m_len < *pred) {
break;
}
if (!crcOk(m_buf.data(), *pred)) {
if (m_len <= 1) {
m_len = 0;
break;
}
std::memmove(m_buf.data(), m_buf.data() + 1, m_len - 1);
--m_len;
continue;
}
auto blk = m_pool->allocate(*pred);
if (!blk || !blk->data || blk->capacity < *pred) {
in.framerError = QStringLiteral("pool_exhausted");
return;
}
std::memcpy(blk->data, m_buf.data(), *pred);
blk->size = *pred;
softbus::message_bus::pipeline::PipelineContext framed;
framed.deviceId = in.deviceId;
framed.stableKey = in.stableKey;
framed.endpoint = in.endpoint;
framed.timestamp = in.timestamp;
framed.protocolHint = in.protocolHint;
framed.traceId = in.traceId;
framed.direction = in.direction;
framed.frameKind = FrameKind::CompleteFrame;
framed.payload.block = std::move(blk);
framed.payload.offset = 0;
framed.payload.length = *pred;
framed.payloadSize = *pred;
out.push_back(std::move(framed));
const std::size_t rest = m_len - *pred;
if (rest > 0) {
std::memmove(m_buf.data(), m_buf.data() + *pred, rest);
}
m_len = rest;
}
in.payload = {};
in.payloadSize = 0;
in.frameKind = FrameKind::StreamChunk;
}
void reset() override { m_len = 0; }
void shutdown() override { m_len = 0; }
private:
std::shared_ptr<softbus::core::memory::MemoryPool> m_pool;
ModbusRtuFramerPluginConfig m_cfg;
QString m_endpoint;
std::vector<std::uint8_t> m_buf;
std::size_t m_len{0};
std::chrono::steady_clock::time_point m_lastRx{std::chrono::steady_clock::now()};
std::chrono::steady_clock::time_point m_lastActivity{std::chrono::steady_clock::now()};
};
} // namespace
ModbusRtuFramerPlugin::ModbusRtuFramerPlugin(std::shared_ptr<softbus::core::memory::MemoryPool> pool,
ModbusRtuFramerPluginConfig cfg)
: m_pool(std::move(pool))
, m_cfg(cfg)
{
}
QString ModbusRtuFramerPlugin::pluginId() const
{
return QStringLiteral("modbus_rtu_framer");
}
bool ModbusRtuFramerPlugin::supports(const QString& protocolHint) const
{
return isModbusRtuHint(protocolHint);
}
void ModbusRtuFramerPlugin::bindMemoryPool(std::shared_ptr<softbus::core::memory::MemoryPool> pool)
{
m_pool = std::move(pool);
}
std::unique_ptr<softbus::core::plugin_system::IProtocolFramerSession>
ModbusRtuFramerPlugin::createFramerSession(const QString& endpoint, const QJsonObject& params)
{
if (!m_pool) {
return {};
}
ModbusRtuFramerPluginConfig cfg = m_cfg;
if (params.contains(QStringLiteral("modbusRtuMaxFrameBytes"))) {
cfg.maxAdu = static_cast<std::size_t>(
std::max(8, params.value(QStringLiteral("modbusRtuMaxFrameBytes")).toInt(static_cast<int>(cfg.maxAdu))));
}
if (params.contains(QStringLiteral("modbusRtuInterFrameTimeoutMs"))) {
cfg.interFrameTimeoutMs = params.value(QStringLiteral("modbusRtuInterFrameTimeoutMs")).toInt(cfg.interFrameTimeoutMs);
}
return std::make_unique<ModbusRtuFramerSession>(m_pool, cfg, endpoint);
}
std::shared_ptr<softbus::core::plugin_system::IProtocolFramerPlugin> makeModbusRtuFramerPlugin(
std::shared_ptr<softbus::core::memory::MemoryPool> pool, ModbusRtuFramerPluginConfig cfg)
{
return std::make_shared<ModbusRtuFramerPlugin>(std::move(pool), cfg);
}
softbus::core::plugin_system::IProtocolFramerPlugin* createModbusRtuFramerPluginRaw()
{
return new ModbusRtuFramerPlugin(nullptr, {});
}
} // namespace softbus::message_bus::pipeline::protocol::modbusrtu

View File

@@ -0,0 +1,44 @@
#pragma once
#include <atomic>
#include <chrono>
#include <memory>
#include <vector>
#include <QHash>
#include <QString>
#include "core/memory/MemoryPool.h"
#include "core/plugin_system/IProtocolFramerPlugin.h"
namespace softbus::message_bus::pipeline::protocol::modbusrtu
{
struct ModbusRtuFramerPluginConfig
{
std::size_t maxAdu{256};
int interFrameTimeoutMs{0};
};
class ModbusRtuFramerPlugin final : public softbus::core::plugin_system::IProtocolFramerPlugin
{
public:
explicit ModbusRtuFramerPlugin(std::shared_ptr<softbus::core::memory::MemoryPool> pool,
ModbusRtuFramerPluginConfig cfg = {});
QString pluginId() const override;
bool supports(const QString& protocolHint) const override;
void bindMemoryPool(std::shared_ptr<softbus::core::memory::MemoryPool> pool) override;
std::unique_ptr<softbus::core::plugin_system::IProtocolFramerSession> createFramerSession(
const QString& endpoint, const QJsonObject& params) override;
private:
std::shared_ptr<softbus::core::memory::MemoryPool> m_pool;
ModbusRtuFramerPluginConfig m_cfg;
};
std::shared_ptr<softbus::core::plugin_system::IProtocolFramerPlugin> makeModbusRtuFramerPlugin(
std::shared_ptr<softbus::core::memory::MemoryPool> pool, ModbusRtuFramerPluginConfig cfg = {});
softbus::core::plugin_system::IProtocolFramerPlugin* createModbusRtuFramerPluginRaw();
} // namespace softbus::message_bus::pipeline::protocol::modbusrtu

View File

@@ -0,0 +1,88 @@
#include "plugins/protocols/modbusrtu/framer/ModbusRtuFraming.h"
namespace softbus::message_bus::pipeline::protocol::modbusrtu
{
std::uint16_t crc16(const std::uint8_t* data, std::size_t len)
{
std::uint16_t crc = 0xFFFF;
for (std::size_t i = 0; i < len; ++i) {
crc ^= data[i];
for (int k = 0; k < 8; ++k) {
crc = (crc & 1) ? static_cast<std::uint16_t>((crc >> 1) ^ 0xA001)
: static_cast<std::uint16_t>(crc >> 1);
}
}
return crc;
}
bool crcOk(const std::uint8_t* adu, std::size_t aduLen)
{
if (!adu || aduLen < 4) {
return false;
}
const std::size_t body = aduLen - 2;
const std::uint16_t got =
static_cast<std::uint16_t>(adu[body] | (static_cast<std::uint16_t>(adu[body + 1]) << 8));
return got == crc16(adu, body);
}
static bool isReadFc(std::uint8_t fc)
{
return fc == 0x01 || fc == 0x02 || fc == 0x03 || fc == 0x04;
}
std::optional<std::size_t> predictAduLength(const std::uint8_t* buf, std::size_t len, std::size_t maxAdu)
{
if (!buf || len < 2) {
return std::nullopt;
}
const std::uint8_t fc = buf[1];
if (fc & 0x80) {
return len >= 5 ? std::optional<std::size_t>(5) : std::nullopt;
}
if (isReadFc(fc)) {
if (len >= 8 && crcOk(buf, 8)) {
return std::optional<std::size_t>(8); // read request
}
if (len < 3) {
return std::nullopt;
}
const std::size_t total = static_cast<std::size_t>(5) + buf[2];
if (total > maxAdu || len < total) {
return std::nullopt; // wait more bytes
}
return crcOk(buf, total) ? std::optional<std::size_t>(total) : std::nullopt;
}
if (fc == 0x05 || fc == 0x06) {
return len >= 8 ? std::optional<std::size_t>(8) : std::nullopt;
}
if (fc == 0x16) {
return len >= 10 ? std::optional<std::size_t>(10) : std::nullopt;
}
if (fc == 0x0F || fc == 0x10) {
if (len >= 8 && crcOk(buf, 8)) {
return std::optional<std::size_t>(8); // response
}
if (len < 7) {
return std::nullopt;
}
const std::size_t req = static_cast<std::size_t>(9) + buf[6];
if (req > maxAdu || len < req) {
return std::nullopt;
}
return crcOk(buf, req) ? std::optional<std::size_t>(req) : std::nullopt;
}
return len >= 8 ? std::optional<std::size_t>(8) : std::nullopt;
}
bool isModbusRtuHint(const QString& hint)
{
const QString h = hint.trimmed().toLower();
return h == QStringLiteral("modbus_rtu") || h == QStringLiteral("modbus-rtu")
|| h == QStringLiteral("modbusrtu");
}
} // namespace softbus::message_bus::pipeline::protocol::modbusrtu

View File

@@ -0,0 +1,17 @@
#pragma once
#include <cstddef>
#include <cstdint>
#include <optional>
#include <QString>
namespace softbus::message_bus::pipeline::protocol::modbusrtu
{
std::uint16_t crc16(const std::uint8_t* data, std::size_t len);
bool crcOk(const std::uint8_t* adu, std::size_t aduLen);
std::optional<std::size_t> predictAduLength(const std::uint8_t* buf, std::size_t len, std::size_t maxAdu);
bool isModbusRtuHint(const QString& hint);
} // namespace softbus::message_bus::pipeline::protocol::modbusrtu

View File

@@ -0,0 +1,249 @@
#include "plugins/protocols/modbusrtu/parser/ModbusRtuProtocolPlugin.h"
#include "core/plugin_system/IProtocolPlugin.h"
#include "message_bus/pipeline/PipelineContext.h"
#include "message_bus/pipeline/protocol/ProtocolEnvelope.h"
#include "plugins/protocols/modbusrtu/codec/ModbusRtuCodec.h"
#include "plugins/protocols/modbusrtu/framer/ModbusRtuFraming.h"
#include <QJsonArray>
#include <QJsonObject>
#include <memory>
#include <utility>
#include <variant>
namespace softbus::core::plugin_system
{
namespace
{
using softbus::message_bus::pipeline::protocol::ModbusExceptionPdu;
using softbus::message_bus::pipeline::protocol::ModbusRtuPdu;
using softbus::message_bus::pipeline::protocol::ModbusRtuUnsupportedPdu;
using softbus::message_bus::pipeline::protocol::ProtocolFamily;
using softbus::message_bus::pipeline::protocol::ReadCoilsRequest;
using softbus::message_bus::pipeline::protocol::ReadCoilsResponse;
using softbus::message_bus::pipeline::protocol::ReadDiscreteInputsRequest;
using softbus::message_bus::pipeline::protocol::ReadDiscreteInputsResponse;
using softbus::message_bus::pipeline::protocol::ReadHoldingRegistersRequest;
using softbus::message_bus::pipeline::protocol::ReadHoldingRegistersResponse;
using softbus::message_bus::pipeline::protocol::ReadInputRegistersRequest;
using softbus::message_bus::pipeline::protocol::ReadInputRegistersResponse;
using softbus::message_bus::pipeline::protocol::WriteSingleCoilRequest;
using softbus::message_bus::pipeline::protocol::WriteSingleCoilResponse;
using softbus::message_bus::pipeline::protocol::WriteSingleRegisterRequest;
using softbus::message_bus::pipeline::protocol::WriteSingleRegisterResponse;
using softbus::message_bus::pipeline::protocol::WriteMultipleCoilsRequest;
using softbus::message_bus::pipeline::protocol::WriteMultipleCoilsResponse;
using softbus::message_bus::pipeline::protocol::WriteMultipleRegistersRequest;
using softbus::message_bus::pipeline::protocol::WriteMultipleRegistersResponse;
using softbus::message_bus::pipeline::protocol::decodeModbusRtu;
template <class... Ts>
struct overloaded : Ts...
{
using Ts::operator()...;
};
template <class... Ts>
overloaded(Ts...) -> overloaded<Ts...>;
void syncLegacyJson(const ModbusRtuPdu& pdu, QJsonObject& o)
{
o = QJsonObject();
std::visit(
overloaded{
[&](const ModbusExceptionPdu& e) {
o.insert(QStringLiteral("unitId"), e.unitId);
o.insert(QStringLiteral("function"), e.function);
o.insert(QStringLiteral("exceptionCode"), e.exceptionCode);
o.insert(QStringLiteral("exception"), true);
},
[&](const ReadHoldingRegistersResponse& r) {
QJsonArray regs;
for (std::uint16_t v : r.registers) {
regs.append(static_cast<int>(v));
}
o.insert(QStringLiteral("unitId"), r.unitId);
o.insert(QStringLiteral("function"), 3);
o.insert(QStringLiteral("byteCount"), r.registers.size() * 2);
o.insert(QStringLiteral("registers"), regs);
},
[&](const ReadHoldingRegistersRequest& r) {
o.insert(QStringLiteral("unitId"), r.unitId);
o.insert(QStringLiteral("function"), 3);
o.insert(QStringLiteral("startAddress"), r.startAddress);
o.insert(QStringLiteral("quantity"), r.quantity);
},
[&](const ReadCoilsRequest& r) {
o.insert(QStringLiteral("unitId"), r.unitId);
o.insert(QStringLiteral("function"), 1);
o.insert(QStringLiteral("startAddress"), r.startAddress);
o.insert(QStringLiteral("quantity"), r.quantity);
},
[&](const ReadDiscreteInputsRequest& r) {
o.insert(QStringLiteral("unitId"), r.unitId);
o.insert(QStringLiteral("function"), 2);
o.insert(QStringLiteral("startAddress"), r.startAddress);
o.insert(QStringLiteral("quantity"), r.quantity);
},
[&](const ReadInputRegistersRequest& r) {
o.insert(QStringLiteral("unitId"), r.unitId);
o.insert(QStringLiteral("function"), 4);
o.insert(QStringLiteral("startAddress"), r.startAddress);
o.insert(QStringLiteral("quantity"), r.quantity);
},
[&](const ReadCoilsResponse& r) {
QJsonArray bits;
for (std::uint8_t b : r.coils) {
bits.append(static_cast<int>(b));
}
o.insert(QStringLiteral("unitId"), r.unitId);
o.insert(QStringLiteral("function"), 1);
o.insert(QStringLiteral("byteCount"), r.coils.size());
o.insert(QStringLiteral("coils"), bits);
},
[&](const ReadDiscreteInputsResponse& r) {
QJsonArray bits;
for (std::uint8_t b : r.discreteInputs) {
bits.append(static_cast<int>(b));
}
o.insert(QStringLiteral("unitId"), r.unitId);
o.insert(QStringLiteral("function"), 2);
o.insert(QStringLiteral("byteCount"), r.discreteInputs.size());
o.insert(QStringLiteral("discreteInputs"), bits);
},
[&](const ReadInputRegistersResponse& r) {
QJsonArray regs;
for (std::uint16_t v : r.inputRegisters) {
regs.append(static_cast<int>(v));
}
o.insert(QStringLiteral("unitId"), r.unitId);
o.insert(QStringLiteral("function"), 4);
o.insert(QStringLiteral("byteCount"), r.inputRegisters.size() * 2);
o.insert(QStringLiteral("inputRegisters"), regs);
},
[&](const WriteSingleCoilRequest& r) {
o.insert(QStringLiteral("unitId"), r.unitId);
o.insert(QStringLiteral("function"), 5);
o.insert(QStringLiteral("address"), r.address);
o.insert(QStringLiteral("value"), r.value);
},
[&](const WriteSingleCoilResponse& r) {
o.insert(QStringLiteral("unitId"), r.unitId);
o.insert(QStringLiteral("function"), 5);
o.insert(QStringLiteral("address"), r.address);
o.insert(QStringLiteral("value"), r.value);
},
[&](const WriteSingleRegisterRequest& r) {
o.insert(QStringLiteral("unitId"), r.unitId);
o.insert(QStringLiteral("function"), 6);
o.insert(QStringLiteral("address"), r.address);
o.insert(QStringLiteral("value"), r.value);
},
[&](const WriteSingleRegisterResponse& r) {
o.insert(QStringLiteral("unitId"), r.unitId);
o.insert(QStringLiteral("function"), 6);
o.insert(QStringLiteral("address"), r.address);
o.insert(QStringLiteral("value"), r.value);
},
[&](const WriteMultipleCoilsRequest& r) {
QJsonArray vals;
for (std::uint8_t b : r.values) {
vals.append(static_cast<int>(b));
}
o.insert(QStringLiteral("unitId"), r.unitId);
o.insert(QStringLiteral("function"), 15);
o.insert(QStringLiteral("startAddress"), r.startAddress);
o.insert(QStringLiteral("quantity"), r.quantity);
o.insert(QStringLiteral("values"), vals);
},
[&](const WriteMultipleCoilsResponse& r) {
o.insert(QStringLiteral("unitId"), r.unitId);
o.insert(QStringLiteral("function"), 15);
o.insert(QStringLiteral("startAddress"), r.startAddress);
o.insert(QStringLiteral("quantity"), r.quantity);
},
[&](const WriteMultipleRegistersRequest& r) {
QJsonArray vals;
for (std::uint16_t v : r.values) {
vals.append(static_cast<int>(v));
}
o.insert(QStringLiteral("unitId"), r.unitId);
o.insert(QStringLiteral("function"), 16);
o.insert(QStringLiteral("startAddress"), r.startAddress);
o.insert(QStringLiteral("quantity"), r.quantity);
o.insert(QStringLiteral("values"), vals);
},
[&](const WriteMultipleRegistersResponse& r) {
o.insert(QStringLiteral("unitId"), r.unitId);
o.insert(QStringLiteral("function"), 16);
o.insert(QStringLiteral("startAddress"), r.startAddress);
o.insert(QStringLiteral("quantity"), r.quantity);
},
[&](const ModbusRtuUnsupportedPdu&) {},
[&](const auto&) {}},
pdu);
}
class ModbusRtuSession final : public IProtocolSession
{
public:
bool feed(softbus::message_bus::pipeline::PipelineContext& ctx) override
{
using softbus::message_bus::pipeline::FrameKind;
if (ctx.frameKind != FrameKind::CompleteFrame || !ctx.payload.valid()) {
return false;
}
const std::size_t n = ctx.payloadSize ? ctx.payloadSize : ctx.payload.length;
const std::uint8_t* p = ctx.payload.bytes();
if (!p || n < 4) {
return false;
}
ModbusRtuPdu pdu;
if (!decodeModbusRtu(ctx.direction, p, n, pdu, nullptr)) {
ctx.protocol.family = ProtocolFamily::None;
ctx.protocol.pdu = {};
ctx.parsed = QJsonObject();
return false;
}
ctx.protocol.family = ProtocolFamily::ModbusRtu;
ctx.protocol.pdu = pdu;
syncLegacyJson(pdu, ctx.parsed);
return true;
}
void reset() override {}
};
} // namespace
class ModbusRtuProtocolPlugin final : public IProtocolPlugin
{
public:
QString pluginId() const override { return QStringLiteral("modbus_rtu"); }
bool supports(const QString& protocolHint) const override
{
return softbus::message_bus::pipeline::protocol::modbusrtu::isModbusRtuHint(protocolHint);
}
std::unique_ptr<IProtocolSession> createSession() override
{
return std::make_unique<ModbusRtuSession>();
}
};
std::shared_ptr<IProtocolPlugin> makeModbusRtuProtocolPlugin()
{
return std::make_shared<ModbusRtuProtocolPlugin>();
}
IProtocolPlugin* createModbusRtuProtocolPluginRaw()
{
return new ModbusRtuProtocolPlugin();
}
} // namespace softbus::core::plugin_system

View File

@@ -0,0 +1,13 @@
#pragma once
#include <memory>
#include "core/plugin_system/IProtocolPlugin.h"
namespace softbus::core::plugin_system
{
std::shared_ptr<IProtocolPlugin> makeModbusRtuProtocolPlugin();
IProtocolPlugin* createModbusRtuProtocolPluginRaw();
} // namespace softbus::core::plugin_system

View File

@@ -0,0 +1,304 @@
#pragma once
// 定义ModbusRTU PDU 类型 包括
// 异常响应、
// 读保持寄存器请求、
// 读保持寄存器响应、不支持的PDU
#include <cstdint>
#include <QChar>
#include <QString>
#include <QStringList>
#include <QVector>
#include <type_traits>
#include <variant> // 用于定义variant类型 variant类型可以存储多种类型的数据
namespace softbus::message_bus::pipeline::protocol
{
struct ModbusExceptionPdu
{
std::uint8_t unitId{0};
std::uint8_t function{0};
std::uint8_t exceptionCode{0};
};
// 01 功能码 读线圈
struct ReadCoilsRequest
{
std::uint8_t unitId{0};
std::uint16_t startAddress{0};
std::uint16_t quantity{0};
};
// 01 功能码 读线圈响应
struct ReadCoilsResponse
{
std::uint8_t unitId{0};
QVector<std::uint8_t> coils;
};
// 02 功能码 读离散输入
struct ReadDiscreteInputsRequest
{
std::uint8_t unitId{0};
std::uint16_t startAddress{0};
std::uint16_t quantity{0};
};
// 02 功能码 读离散输入响应
struct ReadDiscreteInputsResponse
{
std::uint8_t unitId{0};
QVector<std::uint8_t> discreteInputs;
};
// 03 功能码 读保持寄存器
struct ReadHoldingRegistersRequest
{
std::uint8_t unitId{0};
std::uint16_t startAddress{0};
std::uint16_t quantity{0};
};
// 03 功能码 读保持寄存器响应
struct ReadHoldingRegistersResponse
{
std::uint8_t unitId{0};
QVector<std::uint16_t> registers;
};
// 04 功能码 读输入寄存器
struct ReadInputRegistersRequest
{
std::uint8_t unitId{0};
std::uint16_t startAddress{0};
std::uint16_t quantity{0};
};
// 04 功能码 读输入寄存器响应
struct ReadInputRegistersResponse
{
std::uint8_t unitId{0};
QVector<std::uint16_t> inputRegisters;
};
// 05 功能码 写单个线圈
struct WriteSingleCoilRequest
{
std::uint8_t unitId{0};
std::uint16_t address{0};
std::uint8_t value{0};
};
// 05 功能码 写单个线圈响应
struct WriteSingleCoilResponse
{
std::uint8_t unitId{0};
std::uint16_t address{0};
std::uint8_t value{0};
};
// 06 功能码 写单个寄存器
struct WriteSingleRegisterRequest
{
std::uint8_t unitId{0};
std::uint16_t address{0};
std::uint16_t value{0};
};
// 06 功能码 写单个寄存器响应
struct WriteSingleRegisterResponse
{
std::uint8_t unitId{0};
std::uint16_t address{0};
std::uint16_t value{0};
};
// 15 功能码 写多个线圈
struct WriteMultipleCoilsRequest
{
std::uint8_t unitId{0};
std::uint16_t startAddress{0};
std::uint16_t quantity{0};
QVector<std::uint8_t> values;
};
// 15 功能码 写多个线圈响应
struct WriteMultipleCoilsResponse
{
std::uint8_t unitId{0};
std::uint16_t startAddress{0};
std::uint16_t quantity{0};
QVector<std::uint8_t> values;
};
// 16 功能码 写多个寄存器
struct WriteMultipleRegistersRequest
{
std::uint8_t unitId{0};
std::uint16_t startAddress{0};
std::uint16_t quantity{0};
QVector<std::uint16_t> values;
};
// 16 功能码 写多个寄存器响应
struct WriteMultipleRegistersResponse
{
std::uint8_t unitId{0};
std::uint16_t startAddress{0};
std::uint16_t quantity{0};
QVector<std::uint16_t> values;
};
struct ModbusRtuUnsupportedPdu
{
std::uint8_t unitId{0};
std::uint8_t functionCode{0};
};
using ModbusRtuPdu = std::variant<ModbusExceptionPdu,
ReadCoilsRequest,
ReadCoilsResponse,
ReadDiscreteInputsRequest,
ReadDiscreteInputsResponse,
ReadHoldingRegistersRequest,
ReadHoldingRegistersResponse,
ReadInputRegistersRequest,
ReadInputRegistersResponse,
WriteSingleCoilRequest,
WriteSingleCoilResponse,
WriteSingleRegisterRequest,
WriteSingleRegisterResponse,
WriteMultipleCoilsRequest,
WriteMultipleCoilsResponse,
WriteMultipleRegistersRequest,
WriteMultipleRegistersResponse,
ModbusRtuUnsupportedPdu>;
//
inline QString toText(const ModbusRtuPdu& pdu)
{
return std::visit([](const auto& value) -> QString {
using T = std::decay_t<decltype(value)>;
auto bytesToHex = [](const QVector<std::uint8_t>& bytes) -> QString {
QStringList parts;
parts.reserve(bytes.size());
for (const std::uint8_t b : bytes) {
parts.push_back(QStringLiteral("0x%1").arg(b, 2, 16, QChar('0')));
}
return QStringLiteral("[%1]").arg(parts.join(QStringLiteral(", ")));
};
auto wordsToHex = [](const QVector<std::uint16_t>& words) -> QString {
QStringList parts;
parts.reserve(words.size());
for (const std::uint16_t w : words) {
parts.push_back(QStringLiteral("0x%1").arg(w, 4, 16, QChar('0')));
}
return QStringLiteral("[%1]").arg(parts.join(QStringLiteral(", ")));
};
if constexpr (std::is_same_v<T, ModbusExceptionPdu>) {
return QStringLiteral("ModbusExceptionPdu: unitId=%1, function=0x%2, exceptionCode=0x%3")
.arg(value.unitId)
.arg(value.function, 2, 16, QChar('0'))
.arg(value.exceptionCode, 2, 16, QChar('0'));
}
if constexpr (std::is_same_v<T, ReadCoilsRequest>) {
const ReadCoilsRequest& request = value;
return QStringLiteral("ReadCoilsRequest: unitId=%1, startAddress=%2, quantity=%3")
.arg(request.unitId)
.arg(request.startAddress)
.arg(request.quantity);
}
if constexpr (std::is_same_v<T, ReadCoilsResponse>) {
return QStringLiteral("ReadCoilsResponse: unitId=%1, coils=%2")
.arg(value.unitId)
.arg(bytesToHex(value.coils));
}
if constexpr (std::is_same_v<T, ReadDiscreteInputsRequest>) {
return QStringLiteral("ReadDiscreteInputsRequest: unitId=%1, startAddress=%2, quantity=%3")
.arg(value.unitId)
.arg(value.startAddress)
.arg(value.quantity);
}
if constexpr (std::is_same_v<T, ReadDiscreteInputsResponse>) {
return QStringLiteral("ReadDiscreteInputsResponse: unitId=%1, discreteInputs=%2")
.arg(value.unitId)
.arg(bytesToHex(value.discreteInputs));
}
if constexpr (std::is_same_v<T, ReadHoldingRegistersRequest>) {
return QStringLiteral("ReadHoldingRegistersRequest: unitId=%1, startAddress=%2, quantity=%3")
.arg(value.unitId)
.arg(value.startAddress)
.arg(value.quantity);
}
if constexpr (std::is_same_v<T, ReadHoldingRegistersResponse>) {
return QStringLiteral("ReadHoldingRegistersResponse: unitId=%1, registers=%2")
.arg(value.unitId)
.arg(wordsToHex(value.registers));
}
if constexpr (std::is_same_v<T, ReadInputRegistersRequest>) {
return QStringLiteral("ReadInputRegistersRequest: unitId=%1, startAddress=%2, quantity=%3")
.arg(value.unitId)
.arg(value.startAddress)
.arg(value.quantity);
}
if constexpr (std::is_same_v<T, ReadInputRegistersResponse>) {
return QStringLiteral("ReadInputRegistersResponse: unitId=%1, inputRegisters=%2")
.arg(value.unitId)
.arg(wordsToHex(value.inputRegisters));
}
if constexpr (std::is_same_v<T, WriteSingleCoilRequest>) {
return QStringLiteral("WriteSingleCoilRequest: unitId=%1, address=%2, value=0x%3")
.arg(value.unitId)
.arg(value.address)
.arg(value.value, 2, 16, QChar('0'));
}
if constexpr (std::is_same_v<T, WriteSingleCoilResponse>) {
return QStringLiteral("WriteSingleCoilResponse: unitId=%1, address=%2, value=0x%3")
.arg(value.unitId)
.arg(value.address)
.arg(value.value, 2, 16, QChar('0'));
}
if constexpr (std::is_same_v<T, WriteSingleRegisterRequest>) {
return QStringLiteral("WriteSingleRegisterRequest: unitId=%1, address=%2, value=0x%3")
.arg(value.unitId)
.arg(value.address)
.arg(value.value, 4, 16, QChar('0'));
}
if constexpr (std::is_same_v<T, WriteSingleRegisterResponse>) {
return QStringLiteral("WriteSingleRegisterResponse: unitId=%1, address=%2, value=0x%3")
.arg(value.unitId)
.arg(value.address)
.arg(value.value, 4, 16, QChar('0'));
}
if constexpr (std::is_same_v<T, WriteMultipleCoilsRequest>) {
return QStringLiteral("WriteMultipleCoilsRequest: unitId=%1, startAddress=%2, quantity=%3, values=%4")
.arg(value.unitId)
.arg(value.startAddress)
.arg(value.quantity)
.arg(bytesToHex(value.values));
}
if constexpr (std::is_same_v<T, WriteMultipleCoilsResponse>) {
return QStringLiteral("WriteMultipleCoilsResponse: unitId=%1, startAddress=%2, quantity=%3, values=%4")
.arg(value.unitId)
.arg(value.startAddress)
.arg(value.quantity)
.arg(bytesToHex(value.values));
}
if constexpr (std::is_same_v<T, WriteMultipleRegistersRequest>) {
return QStringLiteral("WriteMultipleRegistersRequest: unitId=%1, startAddress=%2, quantity=%3, values=%4")
.arg(value.unitId)
.arg(value.startAddress)
.arg(value.quantity)
.arg(wordsToHex(value.values));
}
if constexpr (std::is_same_v<T, WriteMultipleRegistersResponse>) {
return QStringLiteral("WriteMultipleRegistersResponse: unitId=%1, startAddress=%2, quantity=%3, values=%4")
.arg(value.unitId)
.arg(value.startAddress)
.arg(value.quantity)
.arg(wordsToHex(value.values));
}
if constexpr (std::is_same_v<T, ModbusRtuUnsupportedPdu>) {
return QStringLiteral("ModbusRtuUnsupportedPdu: unitId=%1, functionCode=0x%2")
.arg(value.unitId)
.arg(value.functionCode, 2, 16, QChar('0'));
}
return QStringLiteral("Unknown");
}, pdu);
}
} // namespace softbus::message_bus::pipeline::protocol