使用asio库读取com端口。校验和不匹配。 Python->c++

问题描述 投票:0回答:1

我有一个 C++ 的小项目。读取 COM 端口。我有打开 COM 端口并从中读取值的代码。在校验和检查期间压缩缓冲区时,与生成的校验和的值不匹配。

就是这个意思

 if (checksum != generated_checksum) {
     return;
 }

已实现。

这是我的代码。

#include <boost/asio.hpp>
#include <iostream>
using namespace std::chrono_literals;
namespace asio = boost::asio;
using std::this_thread::sleep_for;

// mocking question code
struct {
    void info(std::string const& msg) const {
        static auto const start = std::chrono::steady_clock::now();

        auto ts = (std::chrono::steady_clock::now() - start);
        if (ts > 1s)
            std::cout << std::setw(6) << ts / 1.s << "s  " << msg << std::endl;
        else
            std::cout << std::setw(6) << ts / 1ms << "ms " << msg << std::endl;
    }
} static g_logger;
// end mocks

struct COM {
    asio::thread_pool m_ioc{1};
    asio::serial_port m_port{m_ioc};
    std::atomic_int   m_Data = 0;

    std::weak_ptr<void> m_readoperation; // only when read operation active

    void ReadLoop(std::shared_ptr<std::vector<unsigned char>> buffer = {}) {
        if (!buffer) {
            assert(m_readoperation.expired()); // do not post overlapping read operations
            buffer          = std::make_shared<std::vector<unsigned char>>();
            m_readoperation = buffer;
        }

        asio::async_read(
            m_port, asio::dynamic_buffer(*buffer), asio::transfer_at_least(1),
            [this, buffer](boost::system::error_code ec, size_t /*length*/) {
                if (!ec) {
                    try {
                        // Directly process binary data from buffer here.
                        size_t length = buffer->size();
                        m_Data.store(length);

                        // Example processing based on your description
                        for (size_t i = 0; i < length; ++i) {
                            if (i + 2 < buffer->size() && (*buffer)[i] == 170 && (*buffer)[i + 1] == 170) {
                                size_t packetLength = (*buffer)[i + 2];
                                if (packetLength < 4 || packetLength >= 170) {
                                    continue; // Invalid packet length, move to next byte.
                                }
                                if (i + 2 + packetLength <= buffer->size()) {
                                    // Assume parse_packet processes a packet starting from a specific index
                                    // and uses packetLength to determine the end.
                                    // This function needs to be adapted to accept a start index and length,
                                    // or you need to create a sub-vector to pass to it.
                                    parse_packet({buffer->begin() + i, buffer->begin() + i + packetLength});
                                    i += packetLength - 1; // Move to the end of the packet.
                                }
                            }
                        }

                        // Clear buffer after processing, or handle remaining bytes as needed.
                        buffer->clear();
                        // Continue reading into the same buffer for new data.
                        ReadLoop(buffer);
                    } catch (const std::exception& e) {
                        g_logger.info("Error processing received data: " + std::string(e.what()));
                    }
                } else {
                    // Handle read errors.
                    g_logger.info("ReadLoop error: " + ec.message());
                }
            });
    }

    void parse_packet(std::vector<unsigned char> const& data) {
        if (data.size() < 4)
            return; // Minimal packet length

        int generated_checksum = 0;
        for (size_t i = 0; i < data.size() - 1; ++i) { // Exclude the last byte, which is the checksum
            generated_checksum += data[i];
        }
        generated_checksum = 255 - (generated_checksum % 256);

        int checksum = data.back(); // Assume the last byte is the checksum
        g_logger.info("Checksum: " + std::to_string(checksum) +
                      ", Generated checksum: " + std::to_string(generated_checksum));
        if (checksum != generated_checksum) {
            g_logger.info("Checksum failed ");
            return; // Checksum mismatch
        }

        // Iterate through packet data, excluding the checksum
        size_t i = 0;
        while (i < data.size() - 1) {
            unsigned char data_type = data[i++];
            switch (data_type) {
            case 1: { // Battery level
                //..rest of code..//
                //
            }
            }
        }
    }

    void initialize(bool enable = true) {
        sleep_for(100ms);

        m_port.open("COM5");

        // Configure serial port settings
        using P = asio::serial_port;
        m_port.set_option(P::baud_rate(115200));
        m_port.set_option(P::character_size(8));
        m_port.set_option(P::parity(P::parity::none));
        m_port.set_option(P::stop_bits(P::stop_bits::one));
        m_port.set_option(P::flow_control(P::flow_control::none));

        g_logger.info("Connected");

        setEnabled(enable);
    }

    void setEnabled(bool enable) {
        if (enable == !m_readoperation.expired())
            return; // nothing to do

        if (!enable)
            m_port.cancel(); // cancels read operation (asio::error::operation_aborted)
        else
            ReadLoop();
    }

    void terminate() {
        setEnabled(false);
        g_logger.info("Bye!");
    }
};

// demo code
int main() {
    std::cout << std::fixed << std::setprecision(2);
    g_logger.info("Demo start");
    COM g_com;

    g_com.initialize(false);

    for (auto i = 0; i < 10; ++i) {
        sleep_for(3.5s);
        g_logger.info("Main thread observed atomic int value: " + to_string(g_com.m_Data));

        bool enable = i % 2; // enable if odd, disable if even
        g_logger.info(enable ? "Enabling read operation" : "Disabling read operation");
        g_com.setEnabled(i % 2);
    }

    g_com.terminate();
}

这是输出

Checksum: 128, Generated checksum: 167
Checksum failed 
Checksum: 128, Generated checksum: 167
Checksum failed 
Checksum: 128, Generated checksum: 167
Checksum failed 
Checksum: 128, Generated checksum: 167
Checksum failed 

校验和根本没有改变,生成的校验和也没有改变。

在 Python 中,校验和如下所示:

91 , 91
109 , 109
114 , 114
83 , 83
38 , 38
25 , 25
45 , 45
57 , 57
76 , 76
82 , 82
83 , 83
84 , 84
67 , 67
69 , 69
100 , 100

我找不到校验和错误的原因

Python代码:

def receive_byte():
    global CurrentPosition
    if CurrentPosition >= len(ReceivedString):
        return 0
    result = ReceivedString[CurrentPosition]
    CurrentPosition += 1
    return result

def skip_byte():
    global CurrentPosition
    CurrentPosition += 1

def drop_parsing():
    global UnparsedRemainingString, ReceivedString, CurrentPosition
    UnparsedRemainingString = b""
    if CurrentPosition > 3:
        UnparsedRemainingString = ReceivedString[CurrentPosition-3:]
    if len(UnparsedRemainingString) > 128:
        UnparsedRemainingString = b""
    ReceivedString = b""

def parse_packet(length):
    global Raw_count, needSave, SaveFile
    generated_checksum = 0
    payload_data = [0 for _ in range(171)]  # Initializing payloadData array
    for i in range(length):
        payload_data[i] = receive_byte()
        generated_checksum += payload_data[i]
    generated_checksum = 255 - generated_checksum % 256
    checksum = receive_byte()
    print(checksum,",", generated_checksum)
    if checksum != generated_checksum:
        return
    
    if length < 4:
        return

    i = 0
    while i < length - 1:
        data_type = payload_data[i]
        i += 1
        if data_type == 1:  # battery level
            battery_level = payload_data[i]
            i += 1
            print(f"battery_level: {battery_level}") 
 //....//
def read_data_from_serial(port='/dev/ttyUSB0', baudrate=9600):
    global ReceivedString, CurrentPosition, UnparsedRemainingString
    ser = serial.Serial(port, baudrate, timeout=1)
    while True:
        data = ser.read(ser.in_waiting or 1)
        if data:
            ReceivedString = UnparsedRemainingString + data
            CurrentPosition = 0
            while CurrentPosition < len(ReceivedString) - 2:
                if receive_byte() == 170 and receive_byte() == 170:
                    length = receive_byte()
                    if length < 4 or length >= 170:
                        continue
                    if CurrentPosition + length > len(ReceivedString):
                        drop_parsing()
                        break
                    else:
                        parse_packet(length)

if __name__ == "__main__":
    read_data_from_serial('COM6', 115200)  # Example usage

我做错了什么?

c++ com boost-asio
1个回答
0
投票

从C++代码进行逆向工程,你的消息结构是:

\xaa \xaa LEN 数据[LEN]

i
处匹配“\xaa\xaa”时,您仅传递缓冲区范围
[i, i+LEN)
parser_packet
。也就是少了 3 个字节。这为我解决了这个问题:

asio::async_read(
    m_port, asio::dynamic_buffer(*pbuf), asio::transfer_at_least(1),
    [this, pbuf](boost::system::error_code ec, size_t /*length*/) {
        if (!ec) {
            try {
                // Directly process binary data from buffer here.
                auto&        buffer = *pbuf;
                size_t const length = buffer.size();
                m_Data.store(length);

                // message \xaa \xaa LEN DATA[LEN]

                // Example processing based on your description
                if (length > 2)
                    for (size_t i = 0; i < length - 2; ++i) {
                        if (buffer[i] == 170 && buffer[i + 1] == 170) {
                            size_t packetLength = buffer[i + 2];
                            if (packetLength < 4 || packetLength >= 170) {
                                continue; // Invalid packet length, move to next byte.
                            }
                            if (i + 3 + packetLength <= buffer.size()) {
                                parse_packet({buffer.begin() + i + 3, packetLength});
                                i += packetLength - 1; // Move to the end of the packet.
                            }
                        }
                    }

                // Clear buffer after processing, or handle remaining bytes as needed.
                buffer.clear();
                // Continue reading into the same buffer for new data.
                ReadLoop(pbuf);
            } catch (const std::exception& e) {
                g_logger.info("Error processing received data: " + std::string(e.what()));
            }
        } else {
            // Handle read errors.
            g_logger.info("ReadLoop error: " + ec.message());
        }
    });

也就是说,当使用

"\xaa\xaa\x04abc\xd9"
的有效负载进行测试时,它会正确解码回 217 (0xd9)。

我还建议使用

span
传递给
parse_packet
而不是复制数据:

void parse_packet(std::span<uint8_t const> data) {

现场演示

住在科里鲁

#include <boost/asio.hpp>
#include <iostream>
using namespace std::chrono_literals;
using namespace std::string_literals;
namespace asio = boost::asio;
using std::this_thread::sleep_for;

// mocking question code
struct {
    void info(std::string const& msg) const {
        static auto const start = std::chrono::steady_clock::now();

        auto ts = (std::chrono::steady_clock::now() - start);
        if (ts > 1s)
            std::cout << std::setw(6) << ts / 1.s << "s  " << msg << std::endl;
        else
            std::cout << std::setw(6) << ts / 1ms << "ms " << msg << std::endl;
    }
} static g_logger;
// end mocks

struct COM {
    asio::thread_pool m_ioc{1};
    asio::serial_port m_port{m_ioc};
    std::atomic_int   m_Data = 0;

    std::weak_ptr<void> m_readoperation; // only when read operation active

    ~COM() { m_ioc.join(); }

    void ReadLoop(std::shared_ptr<std::vector<unsigned char>> pbuf = {}) {
        g_logger.info("Readloop" + (pbuf ? " with buffer"s : ""));
        if (!pbuf) {
            assert(m_readoperation.expired()); // do not post overlapping read operations
            pbuf          = std::make_shared<std::vector<unsigned char>>();
            m_readoperation = pbuf;
        }

        asio::async_read(
            m_port, asio::dynamic_buffer(*pbuf), asio::transfer_at_least(1),
            [this, pbuf](boost::system::error_code ec, size_t /*length*/) {
                if (!ec) {
                    try {
                        // Directly process binary data from buffer here.
                        auto&        buffer = *pbuf;
                        size_t const length = buffer.size();
                        m_Data.store(length);

                        // message \xaa \xaa LEN DATA[LEN]

                        // Example processing based on your description
                        if (length > 2)
                            for (size_t i = 0; i < length - 2; ++i) {
                                if (buffer[i] == 170 && buffer[i + 1] == 170) {
                                    size_t packetLength = buffer[i + 2];
                                    if (packetLength < 4 || packetLength >= 170) {
                                        continue; // Invalid packet length, move to next byte.
                                    }
                                    if (i + 3 + packetLength <= buffer.size()) {
                                        parse_packet({buffer.begin() + i + 3, packetLength});
                                        i += packetLength - 1; // Move to the end of the packet.
                                    }
                                }
                            }

                        // Clear buffer after processing, or handle remaining bytes as needed.
                        buffer.clear();
                        // Continue reading into the same buffer for new data.
                        ReadLoop(pbuf);
                    } catch (const std::exception& e) {
                        g_logger.info("Error processing received data: " + std::string(e.what()));
                    }
                } else {
                    // Handle read errors.
                    g_logger.info("ReadLoop error: " + ec.message());
                }
            });
    }

    void parse_packet(std::span<uint8_t const> data) {
        if (data.size() < 4)
            return; // Minimal packet length

        int generated_checksum = 0;
        for (size_t i = 0; i < data.size() - 1; ++i) { // Exclude the last byte, which is the checksum
            generated_checksum += data[i];
        }
        generated_checksum = 255 - (generated_checksum % 256);

        int checksum = data.back(); // Assume the last byte is the checksum
        g_logger.info("Checksum: " + std::to_string(checksum) +
                      ", Generated checksum: " + std::to_string(generated_checksum));
        if (checksum != generated_checksum) {
            g_logger.info("Checksum failed ");
            return; // Checksum mismatch
        }

        // Iterate through packet data, excluding the checksum
        size_t i = 0;
        while (i < data.size() - 1) {
            unsigned char data_type = data[i++];
            switch (data_type) {
            case 1: { // Battery level
                //..rest of code..//
                //
            }
            }
        }
    }

    void initialize(bool enable = true) {
        sleep_for(100ms);

        m_port.open("COM5");

        // Configure serial port settings
        using P = asio::serial_port;
        m_port.set_option(P::baud_rate(115200));
        m_port.set_option(P::character_size(8));
        m_port.set_option(P::parity(P::parity::none));
        m_port.set_option(P::stop_bits(P::stop_bits::one));
        m_port.set_option(P::flow_control(P::flow_control::none));

        g_logger.info("Connected");

        setEnabled(enable);
    }

    void setEnabled(bool enable) {
        if (enable == !m_readoperation.expired())
            return; // nothing to do

        if (!enable)
            m_port.cancel(); // cancels read operation (asio::error::operation_aborted)
        else
            ReadLoop();
    }

    void terminate() {
        setEnabled(false);
        g_logger.info("Bye!");
    }
};

// demo code
int main() {
    std::cout << std::fixed << std::setprecision(2);
    g_logger.info("Demo start");
    COM g_com;

    g_com.initialize();
    // g_com.terminate();
}

© www.soinside.com 2019 - 2024. All rights reserved.