183 lines
5.4 KiB
Python
Executable File
183 lines
5.4 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
import json
|
|
import sys
|
|
import time
|
|
from pathlib import Path
|
|
|
|
from pymodbus.client import ModbusTcpClient
|
|
|
|
UNIT = 1
|
|
|
|
DO0_BLUE_COIL = 16
|
|
DO1_RED_COIL = 17
|
|
|
|
WISE_NODES_FILE = Path("wise_nodes.json")
|
|
|
|
|
|
def load_nodes():
|
|
data = json.loads(WISE_NODES_FILE.read_text())
|
|
nodes = data.get("nodes", [])
|
|
|
|
result = {}
|
|
for node in nodes:
|
|
machine_id = node.get("machine_id")
|
|
host = node.get("wise_ip") or node.get("wise_host") or node.get("host") or node.get("ip")
|
|
|
|
if machine_id and host:
|
|
result[machine_id] = {
|
|
"machine_id": machine_id,
|
|
"source_node": node.get("source_node", ""),
|
|
"host": host,
|
|
}
|
|
|
|
return result
|
|
|
|
|
|
def write_coil_checked(client, address, value):
|
|
rr = client.write_coil(address=address, value=value, device_id=UNIT)
|
|
if rr.isError():
|
|
raise RuntimeError(f"Error escribiendo coil {address}: {rr}")
|
|
|
|
|
|
def read_outputs(client):
|
|
rr = client.read_coils(address=DO0_BLUE_COIL, count=2, device_id=UNIT)
|
|
if rr.isError():
|
|
raise RuntimeError(f"Error leyendo coils DO: {rr}")
|
|
|
|
do0 = bool(rr.bits[0])
|
|
do1 = bool(rr.bits[1])
|
|
|
|
blue_on = do0
|
|
red_on = not do1
|
|
|
|
return do0, do1, blue_on, red_on
|
|
|
|
|
|
def set_beacon(client, blue_on, red_on):
|
|
# DO0 gobierna azul directamente.
|
|
write_coil_checked(client, DO0_BLUE_COIL, bool(blue_on))
|
|
|
|
# DO1 gobierna roja invertida por contacto NC.
|
|
# red_on=True -> DO1 False
|
|
# red_on=False -> DO1 True
|
|
write_coil_checked(client, DO1_RED_COIL, not bool(red_on))
|
|
|
|
|
|
def print_state(client, label):
|
|
do0, do1, blue_on, red_on = read_outputs(client)
|
|
print(
|
|
f"{label}: "
|
|
f"DO0_BLUE={do0} DO1_RED={do1} "
|
|
f"=> blue_on={blue_on} red_on={red_on}"
|
|
)
|
|
|
|
|
|
def apply_mode(client, mode, seconds):
|
|
blink_period = 1.0
|
|
end = time.time() + seconds
|
|
|
|
if mode == "safe":
|
|
set_beacon(client, blue_on=False, red_on=True)
|
|
print_state(client, "safe")
|
|
time.sleep(seconds)
|
|
|
|
elif mode == "ok_running":
|
|
set_beacon(client, blue_on=True, red_on=False)
|
|
print_state(client, "ok_running")
|
|
time.sleep(seconds)
|
|
|
|
elif mode == "ok_stopped":
|
|
set_beacon(client, blue_on=True, red_on=True)
|
|
print_state(client, "ok_stopped")
|
|
time.sleep(seconds)
|
|
|
|
elif mode == "odoo_bad_running":
|
|
print("odoo_bad_running: azul intermitente, rojo apagado")
|
|
while time.time() < end:
|
|
set_beacon(client, blue_on=True, red_on=False)
|
|
time.sleep(blink_period)
|
|
set_beacon(client, blue_on=False, red_on=False)
|
|
time.sleep(blink_period)
|
|
|
|
elif mode == "machine_fault":
|
|
print("machine_fault: azul fijo, rojo intermitente")
|
|
while time.time() < end:
|
|
set_beacon(client, blue_on=True, red_on=True)
|
|
time.sleep(blink_period)
|
|
set_beacon(client, blue_on=True, red_on=False)
|
|
time.sleep(blink_period)
|
|
|
|
elif mode == "odoo_bad_and_fault":
|
|
print("odoo_bad_and_fault: azul intermitente, rojo intermitente")
|
|
while time.time() < end:
|
|
set_beacon(client, blue_on=True, red_on=True)
|
|
time.sleep(blink_period)
|
|
set_beacon(client, blue_on=False, red_on=False)
|
|
time.sleep(blink_period)
|
|
|
|
elif mode == "system_fault":
|
|
print("system_fault: azul apagado, rojo intermitente")
|
|
while time.time() < end:
|
|
set_beacon(client, blue_on=False, red_on=True)
|
|
time.sleep(blink_period)
|
|
set_beacon(client, blue_on=False, red_on=False)
|
|
time.sleep(blink_period)
|
|
|
|
else:
|
|
raise ValueError(f"Modo no reconocido: {mode}")
|
|
|
|
|
|
def test_machine(machine_id, host, mode, seconds):
|
|
print(f"Probando {machine_id} en {host} modo={mode} durante {seconds}s")
|
|
|
|
client = ModbusTcpClient(host, port=502, timeout=3)
|
|
|
|
if not client.connect():
|
|
raise RuntimeError(f"No conecta con {machine_id} {host}:502")
|
|
|
|
try:
|
|
apply_mode(client, mode, seconds)
|
|
print_state(client, "estado final antes de safe")
|
|
|
|
# Dejamos siempre estado seguro al terminar la prueba manual.
|
|
set_beacon(client, blue_on=False, red_on=True)
|
|
print_state(client, "safe final")
|
|
|
|
finally:
|
|
client.close()
|
|
|
|
|
|
def main():
|
|
if len(sys.argv) < 3:
|
|
print("Uso:")
|
|
print(" python beacon_manual_test.py CORT-00 safe 5")
|
|
print(" python beacon_manual_test.py CORT-00 ok_running 5")
|
|
print(" python beacon_manual_test.py CORT-00 ok_stopped 5")
|
|
print(" python beacon_manual_test.py CORT-00 odoo_bad_running 10")
|
|
print(" python beacon_manual_test.py CORT-00 machine_fault 10")
|
|
print(" python beacon_manual_test.py CORT-00 odoo_bad_and_fault 10")
|
|
print(" python beacon_manual_test.py CORT-00 system_fault 10")
|
|
print(" python beacon_manual_test.py all safe 5")
|
|
raise SystemExit(1)
|
|
|
|
target = sys.argv[1]
|
|
mode = sys.argv[2]
|
|
seconds = float(sys.argv[3]) if len(sys.argv) >= 4 else 5.0
|
|
|
|
nodes = load_nodes()
|
|
|
|
if target == "all":
|
|
for machine_id, node in nodes.items():
|
|
test_machine(machine_id, node["host"], mode, seconds)
|
|
return
|
|
|
|
if target not in nodes:
|
|
raise SystemExit(f"No encuentro {target} en wise_nodes.json")
|
|
|
|
node = nodes[target]
|
|
test_machine(target, node["host"], mode, seconds)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|