Solution de lrstx pour Color Plant 2/2

misc protocole industriel

4 mai 2024

Table des matières

Solution

Même méthode que précédemment, on reprend la documentation et on essaie d’interagir avec le système pour bien comprendre :

  • on ouvre/ferme une vanne par write_single_coil() (0 ou 1).
  • on règle son débit via un registre : write_single_register(). Quelques essais montre un point important : on ne peut pas dépasser un débit de 5 unités par secondes.
  • on peut récupérer les quantités présentes dans les cuves avec read_input_registers().

L’objectif est de faire un mix de 32, 126 et 42, soit une somme de 200, et évidemment, la cuve de mixage n’est pas suffisante. Heureusement, on se rend compte qu’en deux passes de 16, 64 et 21, on est tout bon.

On notera aussi que notre travail est facilité par les systèmes automatiques. Par exemple, lorsque la cuve de mixage est pleine (ce qui va être le cas pendant nos deux passes), elle va se vidanger automatiquement sans action de notre part.

Pour terminer, il y a plusieurs design possibles, par exemple ouvrir les vannes à un certain débit et de temporiser jusqu’à obtenir la bonne quantité (ou celle que l’on pense). J’ai choisi plutôt de me baser sur les valeurs réelles retournées par les capteurs, ce qui me semble plus en phase avec ce qui se fait dans l’industrie.

L’un dans l’autre, ma solution (code ci-dessous) a des améliorations possibles, notamment au niveau des débits, ou encore en temporisant les boucles de lecture des valeurs. Mais elle fonctionne bien, dans les limites de temps imparties.

Pour une raison que je comprends pas, c’est extrêmement satisfaisant à regarder.

Et le résultat affiché est le suivant ;

flag2

Second flag: FCSC{3518dcd9579b4f2bf4ab32f126aa746e00767d6b130ef6c2e79ae6a313d0ba07}

Script de résolution

#!/usr/bin/env python3

import os

# pip install pyModbusTCP
from pyModbusTCP.client import ModbusClient
c = ModbusClient(host="localhost", port=4502, unit_id=1, auto_open=True)

regs = c.read_holding_registers(0, 32)
if regs:
    token = ''.join([ chr(r) for r in regs])
else:
    print("read error")

print(f'{token=}')
os.system(f'firefox http://localhost:8000/{token}')

RED, GREEN, BLUE, MIX = 0, 1, 2, 3
LABEL = ['rouge', 'vert', 'bleu']
DEBITS = [ 32, 33, 34, 35 ]
COILS = [ 0, 1, 2, 3]
INPUT_MIX = [3, 4, 5, 6]

def mix_color(color, qt):
    print(f'- Ajoute {qt} unités de {LABEL[color]}')
    c.write_single_register(DEBITS[color], 5)
    # On ouvre à 5 unités / s
    c.write_single_coil(COILS[color], 1)
    count = 0
    while count < qt//5*5:
        count = c.read_input_registers(INPUT_MIX[color])[0]
        print(f'\x0d{count=}', end='')
    # On complète à 1 unités / s
    c.write_single_register(DEBITS[color], 1)
    while count < qt:
        count = c.read_input_registers(INPUT_MIX[color])[0]
        print(f'\x0d{count=}', end='')
    print()
    c.write_single_coil(COILS[color], 0)

def vidange():
    print('- Vidange de la cuve de mixage.')
    # On ouvre la vanne basse en grand !
    c.write_single_register(DEBITS[MIX], 5)
    c.write_single_coil(COILS[MIX], 1)          # Auto si la cuve est pleine
    count = 100
    while count > 0:
        count = c.read_input_registers(INPUT_MIX[MIX])[0]
        print(f'\x0d{count=}', end='')
    print()
    # Pas besoin de refermer, c'est automatique.

# On fait notre mélange en deux passes !
for _ in range(2):
    mix_color(RED, 16)
    mix_color(GREEN, 63)
    mix_color(BLUE, 21)
    vidange()

input('Enter to stop')