Initializare dupa curatare folder
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
"""RDM6300 wire format: type + id + checksum, 12 hex nibbles per frame.
|
||||
|
||||
The checksum is the XOR of the type byte *and* the four id bytes. Getting this
|
||||
wrong (e.g. XOR-ing only the id) makes every card read fail its checksum, so
|
||||
the exact bytes are pinned here.
|
||||
"""
|
||||
|
||||
import struct
|
||||
|
||||
import pytest
|
||||
|
||||
from rdm_card_reader.card import CardData
|
||||
|
||||
START = 0x02
|
||||
END = 0x03
|
||||
|
||||
|
||||
def encode_frame(value, card_type=0x0F):
|
||||
"""Encode a card as the 12 nibbles the RDM6300 emits between STX and ETX."""
|
||||
id_bytes = struct.pack(">I", value)
|
||||
checksum = card_type
|
||||
for byte in id_bytes:
|
||||
checksum ^= byte
|
||||
frame = bytes([card_type]) + id_bytes + bytes([checksum])
|
||||
encoded = frame.hex().upper().encode("ascii")
|
||||
assert len(encoded) == 12
|
||||
return encoded
|
||||
|
||||
|
||||
def encode_stream(value, card_type=0x0F):
|
||||
return bytes([START]) + encode_frame(value, card_type) + bytes([END])
|
||||
|
||||
|
||||
def decode_frame(frame):
|
||||
"""Reference decoder mirroring rdm6300.BaseReader._parse_fragment."""
|
||||
nibbles = [int(ch, 16) for ch in frame.decode("ascii")]
|
||||
|
||||
def to_int(chunk):
|
||||
out = 0
|
||||
for item in chunk:
|
||||
out = (out << 4) | item
|
||||
return out
|
||||
|
||||
calculated = 0
|
||||
for i in range(0, 10, 2):
|
||||
calculated ^= (nibbles[i] << 4) | nibbles[i + 1]
|
||||
received = to_int(nibbles[10:12])
|
||||
|
||||
return CardData(
|
||||
value=to_int(nibbles[2:10]),
|
||||
checksum=received,
|
||||
is_valid=received == calculated,
|
||||
type=to_int(nibbles[0:2]),
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"value,expected",
|
||||
[
|
||||
(0x00C4A0B5, b"0F00C4A0B5DE"),
|
||||
(0x1A2B3C4D, b"0F1A2B3C4D4F"),
|
||||
],
|
||||
)
|
||||
def test_encoded_frame_bytes_are_stable(value, expected):
|
||||
assert encode_frame(value) == expected
|
||||
|
||||
|
||||
def test_frame_is_twelve_nibbles():
|
||||
assert len(encode_frame(0x00C4A0B5)) == 12
|
||||
|
||||
|
||||
def test_checksum_covers_type_byte_not_only_id():
|
||||
"""Same id with a different type byte must change the checksum."""
|
||||
assert encode_frame(0x00C4A0B5, 0x0F) != encode_frame(0x00C4A0B5, 0x10)
|
||||
|
||||
|
||||
def test_round_trip_yields_valid_card():
|
||||
card = decode_frame(encode_frame(0x00C4A0B5))
|
||||
assert card.value == 0x00C4A0B5
|
||||
assert card.type == 0x0F
|
||||
assert card.checksum == 0xDE
|
||||
assert card.is_valid is True
|
||||
|
||||
|
||||
def test_round_trip_of_second_sample():
|
||||
card = decode_frame(encode_frame(0x1A2B3C4D))
|
||||
assert card.value == 0x1A2B3C4D
|
||||
assert card.is_valid is True
|
||||
|
||||
|
||||
def test_corrupted_checksum_is_invalid():
|
||||
frame = bytearray(encode_frame(0x00C4A0B5))
|
||||
frame[-1] = ord("0")
|
||||
assert decode_frame(bytes(frame)).is_valid is False
|
||||
|
||||
|
||||
def test_frame_with_wrong_nibble_count_is_rejected():
|
||||
"""A 14 nibble frame leaves 2 nibbles buffered in the real reader."""
|
||||
assert len(encode_frame(0x00C4A0B5)) == 12
|
||||
fourteen = b"0F00C4A0B5DE00"
|
||||
assert len(fourteen) == 14
|
||||
|
||||
|
||||
def test_stream_wrapper_uses_stx_and_etx():
|
||||
stream = encode_stream(0x00C4A0B5)
|
||||
assert stream[0] == START
|
||||
assert stream[-1] == END
|
||||
assert stream[1:-1] == encode_frame(0x00C4A0B5)
|
||||
Reference in New Issue
Block a user