Powrót do strony głównej

Inżynieria odwrotna protokołu tablicy kolejki BCD4

Artykuł opisuje inżynierię odwrotną własnościowego protokołu tablicy elektronicznej kolejki z kodowaniem BCD4 i carry w sumie kontrolnej. Podano strukturę pakietów, kod Python do składania i integrację w system webowy przez HTTP i port COM. Przydatne do pracy z legacy-owym sprzętem.

BCD4 i carry w inżynierii odwrotnej tablicy kolejki
Advertisement 728x90

Inżynieria wsteczna protokołu tablicy kolejki elektronicznej: kodowanie BCD4 i arytmetyka przeniesienia

Inżynier przeanalizował zastrzeżony protokół tablicy kolejki elektronicznej bez dokumentacji. Protokół wykorzystuje kodowanie BCD4, gdzie każdy bit danych zajmuje cały bajt dla odporności na zakłócenia na długich liniach RS-232 przy 1200 bodach. Struktura pakietu to 33 bajty ze znacznikiem, cyframi liczby, numerem okienka i polami kontrolnymi z przeniesieniem.

BCD4: kodowanie dla niezawodnej transmisji

Protokół stosuje sprzętową metodę ochrony przed zakłóceniami: każdy bit liczby (0–15) jest kodowany bajtem. 0x7F oznacza 1 (siedem jedynek dla wyraźnego impulsu), 0x00 — 0. To rozciąga 4-bitową wartość do 4 bajtów.

Przykłady kodowania:

Google AdInline article slot

| Liczba | Bity | Bajty BCD4 |

|--------|--------|------------------|

| 0 | 0000 | 00 00 00 00 |

Google AdInline article slot

| 1 | 0001 | 7F 00 00 00 |

| 2 | 0010 | 00 7F 00 00 |

| 5 | 0101 | 7F 00 7F 00 |

Google AdInline article slot

| 9 | 1001 | 7F 00 00 7F |

| 12 | 1100 | 00 00 7F 7F |

Implementacja w Pythonie:

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

Prędkość 1200 bodów zapewnia niezawodność, kosztem przepustowości.

Struktura pakietu dla wyświetlania numeru

Pakiet o długości 33 bajtów do wyświetlenia liczby na okienku:

  • Bajt 0: 0x55 — znacznik startowy.
  • Bajty 1–4: nagłówek trybu.
  • Bajty 5–8: BCD4(jedności liczby).
  • Bajty 9–12: BCD4(dziesiątki liczby).
  • Bajty 13–16: BCD4(setki liczby).
  • Bajty 17–20: BCD4(jedności okienka).
  • Bajty 21–24: BCD4(dziesiątki okienka).
  • Bajty 25–28: BCD4(c1) — młodsza część sumy kontrolnej.
  • Bajty 29–32: BCD4(c2) — starsza część z uwzględnieniem przeniesienia.

Przykład dla liczby 487 na okienku 19:

55 00 7F 00 00          # nagłówek
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)
[pola kontrolne]

Obliczanie sumy kontrolnej z przeniesieniem

Pola kontrolne c1 i c2 tworzą 8-bitową wartość z mechaniką przeniesienia:

  • Obliczane jest raw_c1 jako liniowa kombinacja pól pakietu.
  • Jeśli raw_c1 >= 16: carry = 1, c1 = raw_c1 - 16.
  • W przeciwnym razie: carry = 0, c1 = raw_c1.
  • c2 jest obliczane z udziałem carry z innych pól.

Przeniesienie występuje przy liczbach takich jak 80–90 w zależności od okienka. SI (Claude) wykryło wzór na podstawie 30 przykładów pakietów po doprecyzowaniu hipotezy: dwa 4-bitowe półbajty z przeniesieniem jak w arytmetyce.

Dla trybu "wszystkie okienka" (25 bajtów) przeniesienie nie występuje, formuła jest prostsza.

Tryb programowania okienka używa dwóch niezależnych łańcuchów przeniesienia z mnożeniem i stałymi:

  • Łańcuch A: raw_A od W_lo, carry_A = raw_A >> 4, field_A1 = raw_A & 0xF, field_A2 od W_hi i carry_A.
  • Łańcuch B: analogicznie dla innej funkcji liniowej.

Budowanie i wysyłanie pakietu

Funkcja budowania pakietu:

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)

Pakiet jest wysyłany do portu COM.

Integracja z systemem webowym

Serwer Python przyjmuje HTTP POST:

POST / {"window": 5, "ticket": "487"}

Parsuje JSON, buduje pakiet, zapisuje do portu. Mechanizm heartbeat przywraca połączenie:

  • Interwał 60 sekund przy OK.
  • 10 sekund przy błędzie.
def _heartbeat_loop():
    while True:
        ok = _try_register(...)
        was = _server_state['connected']
        _server_state['connected'] = ok
        if not was and ok:
            print("[OK] Połączenie przywrócone")
        elif was and not ok:
            print("[WARN] Połączenie utracone")
        time.sleep(60 if ok else 10)

Co jest ważne

  • Kodowanie BCD4 rozciąga 1 bit do bajta dla odporności na zakłócenia przy 1200 bodach.
  • Suma kontrolna używa przeniesienia między półbajtami c1/c2.
  • Inżynieria wsteczna z SI jest efektywna przy precyzyjnej hipotezie i przykładach pakietów.
  • Serwer zapewnia płynną integrację starszego sprzętu z webem.
  • Protokół: 33 bajty, znacznik 0x55, cyfry + okienko + suma kontrolna.

— Editorial Team

Advertisement 728x90

Czytaj dalej