Retour à l'accueil

Ingénierie inverse du protocole d'affichage de file d'attente BCD4

L'article décrit l'ingénierie inverse d'un protocole propriétaire d'affichage électronique de file d'attente avec codage BCD4 et retenue dans la somme de contrôle. Structure de paquets, code d'assemblage Python et intégration dans un système web via HTTP et port COM sont fournis. Utile pour travailler avec des équipements anciens.

BCD4 et retenue dans l'inverse d'affichage de file d'attente
Advertisement 728x90

Rétro-ingénierie d'un protocole d'affichage de file d'attente : encodage BCD4 et arithmétique de retenue

Un ingénieur a réalisé la rétro-ingénierie d'un protocole propriétaire d'affichage de file d'attente sans documentation. Le protocole utilise un encodage BCD4, où chaque bit de données occupe un octet complet pour une immunité au bruit sur de longues lignes RS-232 à 1200 bauds. La structure du paquet est de 33 octets avec un marqueur, des chiffres du numéro, un numéro de fenêtre et des champs de somme de contrôle avec retenue.

BCD4 : Encodage pour une transmission fiable

Le protocole emploie une méthode matérielle de protection contre le bruit : chaque bit d'un nombre (0–15) est encodé comme un octet. 0x7F dénote 1 (sept uns pour une impulsion claire), 0x00 dénote 0. Cela étire une valeur de 4 bits sur 4 octets.

Exemples d'encodage :

Google AdInline article slot

| Nombre | Bits | Octets 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 |

Implémentation en 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

Le débit de 1200 bauds assure la fiabilité au détriment du débit.

Structure du paquet pour l'affichage d'un numéro

Un paquet de 33 octets affiche un numéro sur une fenêtre :

  • Octet 0 : 0x55 — marqueur de début.
  • Octets 1–4 : en-tête de mode.
  • Octets 5–8 : BCD4(chiffre des unités).
  • Octets 9–12 : BCD4(chiffre des dizaines).
  • Octets 13–16 : BCD4(chiffre des centaines).
  • Octets 17–20 : BCD4(chiffre des unités de la fenêtre).
  • Octets 21–24 : BCD4(chiffre des dizaines de la fenêtre).
  • Octets 25–28 : BCD4(c1) — partie inférieure de la somme de contrôle.
  • Octets 29–32 : BCD4(c2) — partie supérieure avec retenue.

Exemple pour le numéro 487 sur la fenêtre 19 :

55 00 7F 00 00          # en-tête
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)
[champs de somme de contrôle]

Calcul de la somme de contrôle avec retenue

Les champs de somme de contrôle c1 et c2 forment une valeur de 8 bits avec une mécanique de retenue :

  • Calculer raw_c1 comme une combinaison linéaire des champs du paquet.
  • Si raw_c1 >= 16 : retenue = 1, c1 = raw_c1 - 16.
  • Sinon : retenue = 0, c1 = raw_c1.
  • c2 est calculé avec la retenue des autres champs.

La retenue se produit avec des nombres comme 80–90 selon la fenêtre. L'IA (Claude) a identifié le motif à partir de 30 exemples de paquets après avoir affiné l'hypothèse : deux nibbles de 4 bits avec retenue comme en arithmétique.

Pour le mode "toutes les fenêtres" (25 octets), il n'y a pas de retenue, et la formule est plus simple.

Le mode de programmation de fenêtre utilise deux chaînes de retenue indépendantes avec multiplication et constantes :

  • Chaîne A : raw_A à partir de W_lo, carry_A = raw_A >> 4, field_A1 = raw_A & 0xF, field_A2 à partir de W_hi et carry_A.
  • Chaîne B : similaire pour une autre fonction linéaire.

Assemblage et envoi du paquet

Fonction d'assemblage du paquet :

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)

Le paquet est envoyé à un port COM.

Intégration dans un système web

Un serveur Python accepte une requête HTTP POST :

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

Il analyse le JSON, construit le paquet et écrit sur le port. Un mécanisme de pulsation restaure la connexion :

  • Intervalle de 60 secondes quand tout va bien.
  • 10 secondes en cas d'erreur.
def _heartbeat_loop():
    while True:
        ok = _try_register(...)
        was = _server_state['connected']
        _server_state['connected'] = ok
        if not was and ok:
            print("[OK] Connexion rétablie")
        elif was and not ok:
            print("[WARN] Connexion perdue")
        time.sleep(60 if ok else 10)

Points clés à retenir

  • L'encodage BCD4 étire 1 bit sur un octet pour une immunité au bruit à 1200 bauds.
  • La somme de contrôle utilise une retenue entre les nibbles c1/c2.
  • La rétro-ingénierie avec l'IA est efficace avec des hypothèses précises et des exemples de paquets.
  • Le serveur permet une intégration transparente d'équipements hérités avec le web.
  • Protocole : 33 octets, marqueur 0x55, chiffres + fenêtre + somme de contrôle.

— Editorial Team

Advertisement 728x90

Lire ensuite