86 lines
2.8 KiB
Python
86 lines
2.8 KiB
Python
import pytest
|
|
|
|
from lingkong_motor_service.protocol import (
|
|
CONTROL_FRAME_ID,
|
|
MIXED_COMMAND_FRAME_ID,
|
|
MOTOR_REPLY_BASE_ID,
|
|
MS_OPEN_LOOP_MAX,
|
|
CanFrame,
|
|
MotorCommand,
|
|
MotorStatus2,
|
|
ProtocolError,
|
|
build_mixed_command_frame,
|
|
build_open_loop_frame,
|
|
build_status_2_request,
|
|
parse_status_2_reply,
|
|
)
|
|
|
|
|
|
def test_pdf_golden_vector_positive_100_motor_1_negative_100_motor_3() -> None:
|
|
frame = build_open_loop_frame({1: 100, 3: -100})
|
|
|
|
assert frame.arbitration_id == CONTROL_FRAME_ID
|
|
assert frame.is_extended_id is False
|
|
assert frame.data == bytes.fromhex("64 00 00 00 9C FF 00 00")
|
|
|
|
|
|
def test_requested_open_and_close_vectors_for_motors_1_and_2() -> None:
|
|
opening = build_open_loop_frame({1: 400, 2: -400})
|
|
closing = build_open_loop_frame({1: -400, 2: 400})
|
|
|
|
assert opening.data == bytes.fromhex("90 01 70 FE 00 00 00 00")
|
|
assert closing.data == bytes.fromhex("70 FE 90 01 00 00 00 00")
|
|
|
|
|
|
@pytest.mark.parametrize("value", [-851, 851])
|
|
def test_rejects_values_outside_ms_open_loop_range(value: int) -> None:
|
|
with pytest.raises(ProtocolError, match="MS open_loop_raw"):
|
|
build_open_loop_frame({1: value})
|
|
|
|
|
|
@pytest.mark.parametrize("value", [-MS_OPEN_LOOP_MAX, MS_OPEN_LOOP_MAX])
|
|
def test_accepts_ms_open_loop_range_boundaries(value: int) -> None:
|
|
frame = build_open_loop_frame({2: value})
|
|
assert len(frame.data) == 8
|
|
|
|
|
|
@pytest.mark.parametrize("motor_id", [0, 5, True])
|
|
def test_rejects_invalid_broadcast_motor_id(motor_id: int) -> None:
|
|
with pytest.raises(ProtocolError):
|
|
build_open_loop_frame({motor_id: 1})
|
|
|
|
|
|
def test_mixed_enable_stop_and_disable_vectors() -> None:
|
|
enable = build_mixed_command_frame({1: MotorCommand.ENABLE})
|
|
stop = build_mixed_command_frame({4: MotorCommand.STOP})
|
|
disable = build_mixed_command_frame({2: MotorCommand.DISABLE})
|
|
|
|
assert enable.arbitration_id == MIXED_COMMAND_FRAME_ID
|
|
assert enable.data == bytes.fromhex("88 00 00 00 00 00 00 00")
|
|
assert stop.data == bytes.fromhex("00 00 00 00 00 00 81 00")
|
|
assert disable.data == bytes.fromhex("00 00 80 00 00 00 00 00")
|
|
|
|
|
|
def test_status_2_request_and_reply_temperature() -> None:
|
|
request = build_status_2_request((1, 2))
|
|
reply = CanFrame(
|
|
arbitration_id=MOTOR_REPLY_BASE_ID + 2,
|
|
data=bytes.fromhex("9C 50 64 00 0A 00 34 12"),
|
|
)
|
|
|
|
assert request.arbitration_id == MIXED_COMMAND_FRAME_ID
|
|
assert request.data == bytes.fromhex("9C 00 9C 00 00 00 00 00")
|
|
assert parse_status_2_reply(reply) == MotorStatus2(
|
|
motor_id=2,
|
|
temperature_c=80,
|
|
power_raw=100,
|
|
speed_dps=10,
|
|
encoder_raw=0x1234,
|
|
)
|
|
|
|
|
|
def test_status_2_parser_ignores_unrelated_frame() -> None:
|
|
frame = CanFrame(arbitration_id=CONTROL_FRAME_ID, data=bytes(8))
|
|
|
|
assert parse_status_2_reply(frame) is None
|