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 ;
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')