modbus rtu 1.1 plugins
This commit is contained in:
172
plugins/protocols/modbus_rtu/framer/ModbusRtuFramerPlugin.cpp
Normal file
172
plugins/protocols/modbus_rtu/framer/ModbusRtuFramerPlugin.cpp
Normal file
@@ -0,0 +1,172 @@
|
||||
#include "plugins/protocols/modbus_rtu/framer/ModbusRtuFramerPlugin.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
#include "plugins/protocols/modbus_rtu/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) {
|
||||
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
|
||||
44
plugins/protocols/modbus_rtu/framer/ModbusRtuFramerPlugin.h
Normal file
44
plugins/protocols/modbus_rtu/framer/ModbusRtuFramerPlugin.h
Normal 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
|
||||
88
plugins/protocols/modbus_rtu/framer/ModbusRtuFraming.cpp
Normal file
88
plugins/protocols/modbus_rtu/framer/ModbusRtuFraming.cpp
Normal file
@@ -0,0 +1,88 @@
|
||||
#include "plugins/protocols/modbus_rtu/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
|
||||
17
plugins/protocols/modbus_rtu/framer/ModbusRtuFraming.h
Normal file
17
plugins/protocols/modbus_rtu/framer/ModbusRtuFraming.h
Normal 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
|
||||
Reference in New Issue
Block a user