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

@@ -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