Add production installer and observability tooling
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
import argparse
|
||||
import logging
|
||||
import signal
|
||||
import threading
|
||||
import time
|
||||
from dataclasses import dataclass
|
||||
|
||||
from pyModbusTCP.server import DataBank, ModbusServer
|
||||
|
||||
|
||||
LOGGER = logging.getLogger("nfs320-simulator")
|
||||
REGISTER_COUNT = 600
|
||||
|
||||
STATUS_NORMAL = 5120
|
||||
STATUS_INCIDENTE = 3088
|
||||
STATUS_ACK = 3344
|
||||
STATUS_REMOVIDO = 13312
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SimPoint:
|
||||
address: int
|
||||
label: str
|
||||
values: tuple[int, ...]
|
||||
|
||||
|
||||
SIM_POINTS = [
|
||||
SimPoint(10, "Detector Recepcao", (STATUS_NORMAL, STATUS_NORMAL, STATUS_INCIDENTE, STATUS_ACK)),
|
||||
SimPoint(11, "Detector CPD", (STATUS_NORMAL, STATUS_INCIDENTE, STATUS_INCIDENTE, STATUS_ACK)),
|
||||
SimPoint(12, "Detector Almoxarifado", (STATUS_NORMAL, STATUS_NORMAL, STATUS_NORMAL, STATUS_REMOVIDO)),
|
||||
SimPoint(165, "Modulo Bomba Incendio", (STATUS_NORMAL, STATUS_NORMAL, STATUS_ACK, STATUS_NORMAL)),
|
||||
SimPoint(166, "Modulo Porta Corta-Fogo", (STATUS_NORMAL, STATUS_ACK, STATUS_NORMAL, STATUS_NORMAL)),
|
||||
SimPoint(167, "Modulo Sirene Pavimento 1", (STATUS_NORMAL, STATUS_NORMAL, STATUS_NORMAL, STATUS_INCIDENTE)),
|
||||
SimPoint(352, "Painel Repetidor", (STATUS_NORMAL, STATUS_NORMAL, STATUS_NORMAL, STATUS_NORMAL)),
|
||||
]
|
||||
|
||||
|
||||
def build_registers(step: int) -> list[int]:
|
||||
registers = [0] * REGISTER_COUNT
|
||||
for point in SIM_POINTS:
|
||||
registers[point.address - 1] = point.values[step % len(point.values)]
|
||||
return registers
|
||||
|
||||
|
||||
def apply_step(data_bank: DataBank, step: int) -> None:
|
||||
data_bank.set_holding_registers(1, build_registers(step))
|
||||
status_summary = ", ".join(
|
||||
f"{point.label}={point.values[step % len(point.values)]}" for point in SIM_POINTS
|
||||
)
|
||||
LOGGER.info("Ciclo %s aplicado: %s", step, status_summary)
|
||||
|
||||
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser(description="Simulador Modbus/TCP de central Notifier NFS-320")
|
||||
parser.add_argument("--host", default="127.0.0.1", help="Host de bind do simulador")
|
||||
parser.add_argument("--port", default=1502, type=int, help="Porta TCP do simulador")
|
||||
parser.add_argument("--interval", default=5.0, type=float, help="Intervalo entre mudancas de estado")
|
||||
parser.add_argument("--log-level", default="INFO", help="Nivel de log")
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def main():
|
||||
args = parse_args()
|
||||
logging.basicConfig(
|
||||
level=getattr(logging, args.log_level.upper(), logging.INFO),
|
||||
format="%(asctime)s [%(levelname)s] %(message)s",
|
||||
)
|
||||
|
||||
stop_event = threading.Event()
|
||||
data_bank = DataBank()
|
||||
apply_step(data_bank, 0)
|
||||
server = ModbusServer(host=args.host, port=args.port, no_block=True, data_bank=data_bank)
|
||||
|
||||
def rotate_states():
|
||||
step = 1
|
||||
while not stop_event.wait(args.interval):
|
||||
apply_step(data_bank, step)
|
||||
step += 1
|
||||
|
||||
def shutdown(*_args):
|
||||
LOGGER.info("Encerrando simulador NFS-320")
|
||||
stop_event.set()
|
||||
server.stop()
|
||||
|
||||
signal.signal(signal.SIGINT, shutdown)
|
||||
signal.signal(signal.SIGTERM, shutdown)
|
||||
|
||||
LOGGER.info("Subindo simulador NFS-320 em %s:%s", args.host, args.port)
|
||||
LOGGER.info("Points simulados: %s", ", ".join(f"{point.address}:{point.label}" for point in SIM_POINTS))
|
||||
server.start()
|
||||
|
||||
worker = threading.Thread(target=rotate_states, daemon=True, name="nfs320-state-rotator")
|
||||
worker.start()
|
||||
|
||||
try:
|
||||
while not stop_event.wait(1):
|
||||
pass
|
||||
finally:
|
||||
shutdown()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user