큐 디스플레이 프로토콜 리버스 엔지니어링: BCD4 인코딩과 캐리 연산
한 엔지니어가 문서 없이 독점 큐 디스플레이 프로토콜을 리버스 엔지니어링했습니다. 이 프로토콜은 BCD4 인코딩을 사용하며, 각 데이터 비트는 1200 보드의 긴 RS-232 라인에서 노이즈 내성을 위해 전체 바이트를 차지합니다. 패킷 구조는 33바이트로, 마커, 숫자 자릿수, 창 번호, 캐리가 포함된 체크섬 필드로 구성됩니다.
BCD4: 신뢰할 수 있는 전송을 위한 인코딩
프로토콜은 노이즈 방지를 위한 하드웨어 방식을 사용합니다: 숫자(0–15)의 각 비트는 바이트로 인코딩됩니다. 0x7F는 1을 나타내고(명확한 펄스를 위해 7개의 1), 0x00은 0을 나타냅니다. 이는 4비트 값을 4바이트로 확장합니다.
인코딩 예시:
| 숫자 | 비트 | BCD4 바이트 |
|--------|--------|------------------|
| 0 | 0000 | 00 00 00 00 |
| 1 | 0001 | 7F 00 00 00 |
| 2 | 0010 | 00 7F 00 00 |
| 5 | 0101 | 7F 00 7F 00 |
| 9 | 1001 | 7F 00 00 7F |
| 12 | 1100 | 00 00 7F 7F |
Python 구현:
def _bcd4(value: int) -> bytes:
return bytes([0x7F if value & (1 << i) else 0x00 for i in range(4)])
print(_bcd4(5).hex(' ')) # 7f 00 7f 00
print(_bcd4(9).hex(' ')) # 7f 00 00 7f
1200 보드 속도는 처리량을 희생하면서 신뢰성을 보장합니다.
숫자 디스플레이를 위한 패킷 구조
33바이트 패킷은 창에 숫자를 표시합니다:
- 바이트 0:
0x55— 시작 마커. - 바이트 1–4: 모드 헤더.
- 바이트 5–8: BCD4(일의 자리).
- 바이트 9–12: BCD4(십의 자리).
- 바이트 13–16: BCD4(백의 자리).
- 바이트 17–20: BCD4(창 일의 자리).
- 바이트 21–24: BCD4(창 십의 자리).
- 바이트 25–28: BCD4(c1) — 하위 체크섬 부분.
- 바이트 29–32: BCD4(c2) — 캐리가 포함된 상위 부분.
숫자 487을 창 19에 표시하는 예시:
55 00 7F 00 00 # 헤더
7F 7F 7F 00 # BCD4(7)
00 00 00 7F # BCD4(8)
00 00 7F 00 # BCD4(4)
7F 00 00 7F # BCD4(9)
7F 00 00 00 # BCD4(1)
[체크섬 필드]
캐리를 사용한 체크섬 계산
체크섬 필드 c1과 c2는 캐리 메커니즘을 가진 8비트 값을 형성합니다:
- 패킷 필드의 선형 조합으로 raw_c1을 계산합니다.
- raw_c1 >= 16이면: carry = 1, c1 = raw_c1 - 16.
- 그렇지 않으면: carry = 0, c1 = raw_c1.
- c2는 다른 필드의 캐리로 계산됩니다.
캐리는 창에 따라 80–90과 같은 숫자에서 발생합니다. AI(Claude)는 가설을 정제한 후 30개 패킷 예시에서 패턴을 식별했습니다: 산술에서처럼 캐리가 있는 두 개의 4비트 니블.
"모든 창" 모드(25바이트)에서는 캐리가 없고 공식이 더 간단합니다.
창 프로그래밍 모드는 곱셈과 상수를 사용한 두 개의 독립적인 캐리 체인을 사용합니다:
- 체인 A: W_lo에서 raw_A, carry_A = raw_A >> 4, field_A1 = raw_A & 0xF, field_A2는 W_hi와 carry_A에서.
- 체인 B: 다른 선형 함수에 대해 유사하게.
패킷 조립 및 전송
패킷 조립 함수:
def build_window_packet(window: int, number: int) -> bytes:
d1 = number % 10
d2 = (number // 10) % 10
d3 = (number // 100) % 10
w_lo = window % 10
w_hi = window // 10
c1, c2 = _checksum(d1, d2, d3, w_lo, w_hi)
packet = bytearray(b'\x55\x00\x7F\x00\x00')
packet += _bcd4(d1) + _bcd4(d2) + _bcd4(d3)
packet += _bcd4(w_lo) + _bcd4(w_hi)
packet += _bcd4(c1) + _bcd4(c2)
assert len(packet) == 33
return bytes(packet)
패킷은 COM 포트로 전송됩니다.
웹 시스템 통합
Python 서버는 HTTP POST를 수락합니다:
POST / {"window": 5, "ticket": "487"}
JSON을 파싱하고, 패킷을 조립하며, 포트에 씁니다. 하트비트 메커니즘이 연결을 복원합니다:
- 정상일 때 60초 간격.
- 오류 시 10초.
def _heartbeat_loop():
while True:
ok = _try_register(...)
was = _server_state['connected']
_server_state['connected'] = ok
if not was and ok:
print("[OK] 연결 복원됨")
elif was and not ok:
print("[WARN] 연결 끊김")
time.sleep(60 if ok else 10)
핵심 요약
- BCD4 인코딩은 1200 보드에서 노이즈 내성을 위해 1비트를 바이트로 확장합니다.
- 체크섬은 니블 c1/c2 사이의 캐리를 사용합니다.
- AI를 통한 리버스 엔지니어링은 정확한 가설과 패킷 예시로 효과적입니다.
- 서버는 레거시 장비를 웹과 원활하게 통합할 수 있게 합니다.
- 프로토콜: 33바이트, 마커 0x55, 자릿수 + 창 + 체크섬.
— Editorial Team
아직 댓글이 없습니다.