返回教程正文

配套源码

test_protocol.py

app/tests/test_protocol.py
zdt_motor Brick:用 Python 控制一台 ZDT 电机app/tests/test_protocol.py
Python108 行
"""ZDT CAN 扩展 ID、分包和应答校验测试。"""

import unittest

from zdt_motor import ZDTProtocolError
from zdt_motor.backends import CanFrame
from zdt_motor.protocols import (
    LogicalCommand,
    ZDTProtocol,
    arbitration_id,
    parse_arbitration_id,
    reassemble_can_frames,
    split_can_frames,
)


class ProtocolTests(unittest.TestCase):
    """逐字节验证手册第4章 CAN 规则。"""

    def test_extended_id_generation(self):
        """
        @description         : 校验地址和分包号生成扩展CAN ID
        @param               : 无参数
        @return              : 无返回值
        """
        self.assertEqual(arbitration_id(1, 0), 0x0100)
        self.assertEqual(arbitration_id(4, 2), 0x0402)
        self.assertEqual(parse_arbitration_id(0x0402), (4, 2))

    def test_single_frame_omits_address(self):
        """
        @description         : 校验串口01 36 6B在CAN中变成ID0100和DATA36 6B
        @param               : 无参数
        @return              : 无返回值
        """
        frames = ZDTProtocol().encode_command(1, LogicalCommand(0x36, b"", 7))
        self.assertEqual(len(frames), 1)
        self.assertEqual(frames[0].arbitration_id, 0x0100)
        self.assertTrue(frames[0].is_extended)
        self.assertEqual(frames[0].data, bytes.fromhex("36 6B"))

    def test_manual_fd_packet_split(self):
        """
        @description         : 复现手册01 FD命令的两包CAN示例
        @param               : 无参数
        @return              : 无返回值
        """
        logical = bytes.fromhex("FD 01 0F A0 00 00 01 FA 00 00 00 6B")
        frames = split_can_frames(1, logical)
        self.assertEqual(
            [(frame.arbitration_id, frame.data.hex(" ").upper()) for frame in frames],
            [
                (0x0100, "FD 01 0F A0 00 00 01 FA"),
                (0x0101, "FD 00 00 00 6B"),
            ],
        )

    def test_manual_fd_reassembly(self):
        """
        @description         : 校验后续包重复功能码不会进入逻辑数据
        @param               : 无参数
        @return              : 无返回值
        """
        logical = bytes.fromhex("FD 01 0F A0 00 00 01 FA 00 00 00 6B")
        self.assertEqual(
            reassemble_can_frames(split_can_frames(1, logical), len(logical)),
            logical,
        )

    def test_rejects_wrong_continuation_code(self):
        """
        @description         : 校验后续分包功能码不一致时拒绝重组
        @param               : 无参数
        @return              : 无返回值
        """
        frames = (
            CanFrame(0x0100, bytes.fromhex("FD 01 02 03 04 05 06 07")),
            CanFrame(0x0101, bytes.fromhex("F6 08 6B")),
        )
        with self.assertRaises(ZDTProtocolError):
            reassemble_can_frames(frames, 10)

    def test_response_checksum_and_function(self):
        """
        @description         : 校验应答功能码和校验码
        @param               : 无参数
        @return              : 无返回值
        """
        response = ZDTProtocol().validate_response(1, bytes.fromhex("35 00 00 6B"))
        self.assertEqual(response.function_code, 0x35)
        with self.assertRaises(ZDTProtocolError):
            ZDTProtocol().validate_response(1, bytes.fromhex("35 00 00 00"))

    def test_xor_checksum_keeps_address_out_of_can_data(self):
        """
        @description         : 校验XOR包含地址但CAN DATA仍不发送地址
        @param               : 无参数
        @return              : 无返回值
        """
        frame = ZDTProtocol("xor").encode_command(
            1,
            LogicalCommand(0x06, b"\x45", 3),
        )[0]
        self.assertEqual(frame.data, bytes.fromhex("06 45 42"))


if __name__ == "__main__":
    unittest.main()