#include "message_bus/pipeline/stages/1_framers/ModbusRtuFramer.h" #include "message_bus/pipeline/stages/1_framers/ModbusRtuFraming.h" #include #include #include using softbus::core::memory::MemoryPool; using softbus::message_bus::pipeline::FrameKind; using softbus::message_bus::pipeline::PipelineContext; using softbus::message_bus::pipeline::stages::framers::ModbusRtuFramer; using softbus::message_bus::pipeline::stages::framers::ModbusRtuFramerConfig; using softbus::message_bus::pipeline::stages::framers::modbusRtuCrc16; namespace { void appendCrcLe(std::vector& v) { const std::uint16_t c = modbusRtuCrc16(v.data(), v.size()); v.push_back(static_cast(c & 0xFF)); v.push_back(static_cast(c >> 8)); } MemoryPool::BlockPtr makeBlock(MemoryPool& pool, const std::vector& bytes) { auto blk = pool.allocate(bytes.size()); if (!blk || !blk->data) { QTest::qFail("pool.allocate failed", __FILE__, __LINE__); return {}; } std::memcpy(blk->data, bytes.data(), bytes.size()); blk->size = bytes.size(); return blk; } } // namespace class ModbusRtuFramerTest : public QObject { Q_OBJECT private slots: void half_then_rest_emits_one_frame(); void sticky_two_frames_in_one_chunk(); void crc_error_triggers_resync_and_recovery(); }; void ModbusRtuFramerTest::half_then_rest_emits_one_frame() { auto pool = std::make_shared(512, 64); ModbusRtuFramer framer(pool, ModbusRtuFramerConfig{}, QStringLiteral("/ttyUSB0")); std::vector a{0x01, 0x03, 0x04, 0x12, 0x34, 0x56, 0x78}; appendCrcLe(a); std::vector part1(a.begin(), a.begin() + 4); std::vector part2(a.begin() + 4, a.end()); PipelineContext c1; c1.endpoint = QStringLiteral("/ttyUSB0"); c1.protocolHint = QStringLiteral("modbus_rtu"); c1.frameKind = FrameKind::StreamChunk; c1.payload.block = makeBlock(*pool, part1); c1.payload.offset = 0; c1.payload.length = part1.size(); c1.payloadSize = part1.size(); std::vector out; framer.feed(c1, out); QCOMPARE(int(out.size()), 0); PipelineContext c2; c2.endpoint = QStringLiteral("/ttyUSB0"); c2.protocolHint = QStringLiteral("modbus_rtu"); c2.frameKind = FrameKind::StreamChunk; c2.payload.block = makeBlock(*pool, part2); c2.payload.offset = 0; c2.payload.length = part2.size(); c2.payloadSize = part2.size(); framer.feed(c2, out); QCOMPARE(int(out.size()), 1); QCOMPARE(out[0].frameKind, FrameKind::CompleteFrame); QCOMPARE(out[0].payloadSize, a.size()); } void ModbusRtuFramerTest::sticky_two_frames_in_one_chunk() { auto pool = std::make_shared(512, 64); ModbusRtuFramer framer(pool, ModbusRtuFramerConfig{}, QStringLiteral("/ttyUSB0")); std::vector f; f.insert(f.end(), {0x01, 0x03, 0x02, 0x00, 0x01}); appendCrcLe(f); std::vector g; g.insert(g.end(), {0x02, 0x03, 0x04, 0xAA, 0xBB, 0xCC, 0xDD}); appendCrcLe(g); std::vector both; both.insert(both.end(), f.begin(), f.end()); both.insert(both.end(), g.begin(), g.end()); PipelineContext c; c.endpoint = QStringLiteral("/ttyUSB0"); c.protocolHint = QStringLiteral("modbus_rtu"); c.frameKind = FrameKind::StreamChunk; c.payload.block = makeBlock(*pool, both); c.payload.offset = 0; c.payload.length = both.size(); c.payloadSize = both.size(); std::vector out; framer.feed(c, out); QCOMPARE(int(out.size()), 2); } void ModbusRtuFramerTest::crc_error_triggers_resync_and_recovery() { auto pool = std::make_shared(512, 64); ModbusRtuFramer framer(pool, ModbusRtuFramerConfig{}, QStringLiteral("/ttyUSB0")); std::vector bad{0x01, 0x03, 0x02, 0x00, 0x00, 0xDE, 0xAD}; std::vector good; good.insert(good.end(), {0x01, 0x03, 0x02, 0x12, 0x34}); appendCrcLe(good); std::vector stream; stream.insert(stream.end(), bad.begin(), bad.end()); stream.insert(stream.end(), good.begin(), good.end()); PipelineContext c; c.endpoint = QStringLiteral("/ttyUSB0"); c.protocolHint = QStringLiteral("modbus_rtu"); c.frameKind = FrameKind::StreamChunk; c.payload.block = makeBlock(*pool, stream); c.payload.offset = 0; c.payload.length = stream.size(); c.payloadSize = stream.size(); std::vector out; framer.feed(c, out); QVERIFY(!out.empty()); QCOMPARE(out[0].payloadSize, good.size()); } QTEST_MAIN(ModbusRtuFramerTest) #include "modbus_rtu_framer_test.moc"