"""Run secsgem-py as passive equipment so a C++ active host can drive it. Listens on 0.0.0.0:, waits to be reached by an active host. secsgem-py's GemEquipmentHandler answers S1F13/F14 (establish communications), S1F17/F18 (online request), S1F1/F2 (are-you-there) out of the box. Status variables, equipment constants, and one collection event are registered so that S1F3/S1F11/S1F23 round-trip yield non-empty bodies. """ from __future__ import annotations import argparse import logging import signal import sys import threading import secsgem.common import secsgem.gem import secsgem.hsms import secsgem.secs.variables as v LOG = logging.getLogger("interop.equipment") class InteropEquipment(secsgem.gem.GemEquipmentHandler): def __init__(self, settings): super().__init__(settings) # One status variable so S1F3/F11 yield a non-empty list. sv = secsgem.gem.StatusVariable(1, "ChamberTemp", "C", v.U4) sv.value = 25 self.status_variables[1] = sv # One equipment constant. self.equipment_constants[10] = secsgem.gem.EquipmentConstant( 10, "EnergyCap", 0, 100, 50, "kW", v.U4 ) # One collection event (visible via S1F23). self.collection_events[1001] = secsgem.gem.CollectionEvent( 1001, "DemoEvent", [] ) def main() -> int: ap = argparse.ArgumentParser() ap.add_argument("--port", type=int, default=5000) ap.add_argument("--session-id", type=int, default=0) ap.add_argument("--log-level", default="INFO") args = ap.parse_args() logging.basicConfig( level=args.log_level, format="%(asctime)s %(levelname)s %(name)s: %(message)s", ) if args.log_level.upper() != "DEBUG": logging.getLogger("communication").setLevel(logging.WARNING) logging.getLogger("hsms_connection").setLevel(logging.WARNING) settings = secsgem.hsms.HsmsSettings( address="0.0.0.0", port=args.port, session_id=args.session_id, connect_mode=secsgem.hsms.HsmsConnectMode.PASSIVE, device_type=secsgem.common.DeviceType.EQUIPMENT, ) eq = InteropEquipment(settings) eq.enable() LOG.info("passive equipment listening on 0.0.0.0:%d session=%d", args.port, args.session_id) stop = threading.Event() def shutdown(_signo, _frame): LOG.info("signal received, stopping") stop.set() signal.signal(signal.SIGINT, shutdown) signal.signal(signal.SIGTERM, shutdown) try: stop.wait() finally: try: eq.disable() except Exception as e: # noqa: BLE001 LOG.warning("disable raised: %s", e) return 0 if __name__ == "__main__": sys.exit(main())