From d7e2cab38d712976fd630d7f2a5a15154b70657e Mon Sep 17 00:00:00 2001 From: Don Neill Date: Mon, 26 Oct 2026 12:06:29 -0700 Subject: [PATCH 1/7] Initial Revision (not working) --- daemons/hscal/gammavac | 196 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 196 insertions(+) create mode 100755 daemons/hscal/gammavac diff --git a/daemons/hscal/gammavac b/daemons/hscal/gammavac new file mode 100755 index 0000000..f6ae4cb --- /dev/null +++ b/daemons/hscal/gammavac @@ -0,0 +1,196 @@ +#!/usr/bin/env python3 +""" +Gamma Vacuum Vac-Ion Pressure Daemon + +Exposes the Gamma Vacuum Vac-Ion pressure commands as libby keywords. +""" + +import argparse +import sys + +from hispec import HispecDaemon +from hispec.driver.gammavac.SPCe import SpceController + +# pylint: disable=too-many-instance-attributes +class GammaVacDaemon(HispecDaemon): # pylint: disable=W0223 + """Daemon for controlling the Gamma Vacuum Vac-Ion pressure gauges via keywords.""" + + def __init__(self): + super().__init__() + + self.ip_address = None + self.tcp_port = None + self.n_gauges = None + self.pressure1 = None + self.pressure2 = None + self._pressure1_keyword = None + self._pressure2_keyword = None + self.units = None + self.error = None + self.controller = SpceController(log=True) + self.daemon_description = "Gamma Vacuum Vac-Ion Pressure Daemon" + + def on_start(self, libby): + """Called when the daemon starts - register keywords and connect.""" + # from_config populates _config after __init__, so read it here + self.ip_address = self.get_config("hardware.ip_address") + self.tcp_port = self.get_config("hardware.tcp_port") + self.n_gauges = self.get_config("gauges.n_gauges") + + self.logger.info("Starting %s daemon with %d gauge(s)", self.peer_id, self.n_gauges) + + self.keyword_registry.bool("isconnected", + getter=self.controller.is_connected, + setter=self._set_connected, + description="Gamma Vacuum Vac-Ion Pressure controller is connected.") + self.keyword_registry.string("error", + getter=self._error_string, + description="Last error from the Gamma Vacuum Vac-Ion pressure controller.") + self._pressure1_keyword = self.keyword_registry.float( + "pressure1", + getter=self._get_pressure1, + description="Pressure from gauge 1.") + self._pressure2_keyword = self.keyword_registry.float( + "pressure2", + getter=self._get_pressure2, + description="Pressure from gauge 2.") + self.keyword_registry.float("temperature", + getter=self.controller.read_temperature, + units="C", + description="Temperature from Gamma Vacuum Vac-Ion pressure controller.") + self.keyword_registry.string("units", + getter=self._get_pressure_units, + description="Pressure units.") + self.keyword_registry.int("units_code", + setter=self._set_pressure_units, + description="Pressure units code: 0 - mbar, 1 - Torr, 2 - Pascal," + " 3 - Micron, 4 - hPascal, 5 - Volt.") + self.keyword_registry.trigger("shutdown", + action=self._shutdown, + description="Shutdown the daemon now.") + + if not (self.ip_address and self.tcp_port): + self.logger.error("No IP address or port specified") + return + + try: + self._connect_hardware() + self.logger.info("Daemon started successfully and connected to hardware") + except Exception as e: # pylint: disable=W0718 + self.logger.error("Failed to connect to hardware: %s", e) + self.error = str(e) + self.logger.warning("Daemon will start but hardware is not available") + + def _shutdown(self): + """Shutdown the daemon""" + self.logger.info("Shutting down") + # on_stop handles teardown; a driver error here would block shutdown + self.request_stop() + + def on_stop(self, libby=None): + """Cleanup when the daemon shuts down.""" + self.logger.info("Shutting down %s daemon", self.peer_id) + try: + self.controller.disconnect() + self.logger.info("Disconnected from Gamma Vacuum Vac-Ion pressure controller") + except Exception as e: # pylint: disable=W0718 + self.logger.error("Error disconnecting: %s", e) + self.error = str(e) + + def _connect_hardware(self): + self.logger.info("Connecting to Gamma Vacuum Vac-Ion pressure controller at %s:%s", + self.ip_address, self.tcp_port) + self.controller.connect(host=self.ip_address, port=int(self.tcp_port)) + # The drivers signal failure by staying disconnected rather than raising + if not self.controller.is_connected(): + raise RuntimeError( + f"no connection to {self.ip_address}:{self.tcp_port}") + self.controller.initialize() + + def _set_connected(self, value: bool) -> None: + if value: + self._connect_hardware() + else: + self.controller.disconnect() + + def _get_pressure1(self): + """Read the gauge 1 pressure from the Gamma Vacuum Vac-Ion Pressure controller""" + try: + self.pressure1 = self.controller.read_pressure(gauge=1) + # Read the unit with the value: the front panel can change it between reads + self._pressure1_keyword.units = self._get_pressure_units() + except UnknownResponse as e: + self.logger.error("Error reading pressure1: %s", e) + self.error = str(e) + return self.pressure1 + + def _get_pressure2(self): + """Read the gauge 2 pressure from the Gamma Vacuum Vac-Ion Pressure controller""" + try: + self.pressure2 = self.controller.read_pressure(gauge=2) + self._pressure2_keyword.units = self._get_pressure_units() + except UnknownResponse as e: + self.logger.error("Error reading pressure2: %s", e) + self.error = str(e) + return self.pressure2 + + def _get_pressure_units(self): + """Read the pressure units from the Gamma Vacuum Vac-Ion Pressure controller""" + try: + _ = self.controller.get_pressure_unit() + self.units = self.controller.pressure_units + except UnknownResponse as e: + self.logger.error("Error reading pressure_units: %s", e) + self.error = str(e) + return self.units + + def _set_pressure_units(self, code: int =1) -> bool: + """Set the pressure units from the Gamma Vacuum Vac-Ion Pressure controller + + :parm code: The integer new pressure units code: + 0 - mbar, 1 - Torr, 2 - Pascal, 3 - Micron, 4 - hPascal, 5 - Volt + """ + try: + if self.controller.set_pressure_unit(code): + self.units = self.controller.pressure_units + self.error = "" + return True + + self.logger.error("Units code not set: %d", code) + self.error = "Units code not set" + return False + except UnknownResponse as e: + self.logger.error("Error setting pressure_units: %s", e) + self.error = str(e) + return False + + def _error_string(self) -> str: + """Return last error string""" + return self.error + + +def main(): + """Main entry point for the daemon.""" + parser = argparse.ArgumentParser(description='Gamma Vacuum Vac-Ion Pressure Daemon') + parser.add_argument('-c', '--config', type=str, + help='Path to config file (YAML or JSON)') + + args = parser.parse_args() + + if not args.config: + print("--config is required", file=sys.stderr) + sys.exit(2) + + try: + daemon = GammaVacDaemon.from_config_file(args.config) + daemon.serve() + except KeyboardInterrupt: + print("\nDaemon interrupted by user") + sys.exit(0) + except Exception as e: # pylint: disable=W0718 + print(f"Error running daemon: {e}", file=sys.stderr) + sys.exit(1) + + +if __name__ == '__main__': + main() From 1b3fae067d16b6e385ad464bf889e87ce63b0807 Mon Sep 17 00:00:00 2001 From: Don Neill Date: Mon, 26 Oct 2026 14:00:21 -0700 Subject: [PATCH 2/7] More updates --- daemons/hscal/gammavac | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/daemons/hscal/gammavac b/daemons/hscal/gammavac index f6ae4cb..88e37da 100755 --- a/daemons/hscal/gammavac +++ b/daemons/hscal/gammavac @@ -20,11 +20,10 @@ class GammaVacDaemon(HispecDaemon): # pylint: disable=W0223 self.ip_address = None self.tcp_port = None - self.n_gauges = None - self.pressure1 = None - self.pressure2 = None - self._pressure1_keyword = None - self._pressure2_keyword = None + self.bus_address = None + self.pressure = None + self.current = None + self.voltage = None self.units = None self.error = None self.controller = SpceController(log=True) @@ -35,17 +34,19 @@ class GammaVacDaemon(HispecDaemon): # pylint: disable=W0223 # from_config populates _config after __init__, so read it here self.ip_address = self.get_config("hardware.ip_address") self.tcp_port = self.get_config("hardware.tcp_port") - self.n_gauges = self.get_config("gauges.n_gauges") + self.bus_address = self.get_config("hardware.bus_address") - self.logger.info("Starting %s daemon with %d gauge(s)", self.peer_id, self.n_gauges) + self.logger.info("Starting %s daemon with %d bus address", self.peer_id, self.bus_address) self.keyword_registry.bool("isconnected", getter=self.controller.is_connected, setter=self._set_connected, - description="Gamma Vacuum Vac-Ion Pressure controller is connected.") + description="Gamma Vacuum Vac-Ion Pressure controller is " + "connected.") self.keyword_registry.string("error", getter=self._error_string, - description="Last error from the Gamma Vacuum Vac-Ion pressure controller.") + description="Last error from the Gamma Vacuum Vac-Ion " + "pressure controller.") self._pressure1_keyword = self.keyword_registry.float( "pressure1", getter=self._get_pressure1, From e78de87be59bf930fb07ce21885a8cf3c2a38e16 Mon Sep 17 00:00:00 2001 From: Don Neill Date: Tue, 27 Oct 2026 10:35:53 -0700 Subject: [PATCH 3/7] Getting closer --- daemons/hscal/gammavac | 83 ++++++++++++++---------------------------- 1 file changed, 28 insertions(+), 55 deletions(-) diff --git a/daemons/hscal/gammavac b/daemons/hscal/gammavac index 88e37da..48b40a5 100755 --- a/daemons/hscal/gammavac +++ b/daemons/hscal/gammavac @@ -22,9 +22,10 @@ class GammaVacDaemon(HispecDaemon): # pylint: disable=W0223 self.tcp_port = None self.bus_address = None self.pressure = None + self._pressure_keyword = None self.current = None self.voltage = None - self.units = None + self.units = 'T' self.error = None self.controller = SpceController(log=True) self.daemon_description = "Gamma Vacuum Vac-Ion Pressure Daemon" @@ -47,25 +48,18 @@ class GammaVacDaemon(HispecDaemon): # pylint: disable=W0223 getter=self._error_string, description="Last error from the Gamma Vacuum Vac-Ion " "pressure controller.") - self._pressure1_keyword = self.keyword_registry.float( - "pressure1", - getter=self._get_pressure1, - description="Pressure from gauge 1.") - self._pressure2_keyword = self.keyword_registry.float( - "pressure2", - getter=self._get_pressure2, - description="Pressure from gauge 2.") - self.keyword_registry.float("temperature", - getter=self.controller.read_temperature, - units="C", - description="Temperature from Gamma Vacuum Vac-Ion pressure controller.") + self.keyword_registry.string("status", + getter=self._status, + description="Status of the PDU as a (code, message) string.") + self._pressure_keyword = self.keyword_registry.float("pressure", + getter=self._get_pressure, + description="Pressure from gauge") self.keyword_registry.string("units", getter=self._get_pressure_units, description="Pressure units.") self.keyword_registry.int("units_code", setter=self._set_pressure_units, - description="Pressure units code: 0 - mbar, 1 - Torr, 2 - Pascal," - " 3 - Micron, 4 - hPascal, 5 - Volt.") + description="Pressure units code: M - mbar, T - Torr, P - Pascal") self.keyword_registry.trigger("shutdown", action=self._shutdown, description="Shutdown the daemon now.") @@ -114,56 +108,35 @@ class GammaVacDaemon(HispecDaemon): # pylint: disable=W0223 else: self.controller.disconnect() - def _get_pressure1(self): - """Read the gauge 1 pressure from the Gamma Vacuum Vac-Ion Pressure controller""" - try: - self.pressure1 = self.controller.read_pressure(gauge=1) - # Read the unit with the value: the front panel can change it between reads - self._pressure1_keyword.units = self._get_pressure_units() - except UnknownResponse as e: - self.logger.error("Error reading pressure1: %s", e) - self.error = str(e) - return self.pressure1 + def _status(self): + """Handles status.""" + if not self.controller.is_connected(): + return str((-1, "ERROR: Not connected to hardware")) + return str(self.controller.get_status()) - def _get_pressure2(self): - """Read the gauge 2 pressure from the Gamma Vacuum Vac-Ion Pressure controller""" - try: - self.pressure2 = self.controller.read_pressure(gauge=2) - self._pressure2_keyword.units = self._get_pressure_units() - except UnknownResponse as e: - self.logger.error("Error reading pressure2: %s", e) - self.error = str(e) - return self.pressure2 + def _get_pressure(self): + """Read the pressure from the Gamma Vacuum Vac-Ion Pressure controller""" + self.pressure = self.controller.read_pressure() + # Read the unit with the value: the front panel can change it between reads + self._pressure_keyword.units = self.units + return self.pressure def _get_pressure_units(self): """Read the pressure units from the Gamma Vacuum Vac-Ion Pressure controller""" - try: - _ = self.controller.get_pressure_unit() - self.units = self.controller.pressure_units - except UnknownResponse as e: - self.logger.error("Error reading pressure_units: %s", e) - self.error = str(e) return self.units - def _set_pressure_units(self, code: int =1) -> bool: + def _set_pressure_units(self, code: str ='T') -> bool: """Set the pressure units from the Gamma Vacuum Vac-Ion Pressure controller :parm code: The integer new pressure units code: - 0 - mbar, 1 - Torr, 2 - Pascal, 3 - Micron, 4 - hPascal, 5 - Volt + M - mbar, T - Torr, P - Pascal """ - try: - if self.controller.set_pressure_unit(code): - self.units = self.controller.pressure_units - self.error = "" - return True - - self.logger.error("Units code not set: %d", code) - self.error = "Units code not set" - return False - except UnknownResponse as e: - self.logger.error("Error setting pressure_units: %s", e) - self.error = str(e) - return False + if self.controller.set_units(code): + self.units = code + self.error = "" + return True + self.error = "Could not set pressure units" + return False def _error_string(self) -> str: """Return last error string""" From d11ca61b0f8fe3e8b0029d23a4c5b3fe983340d6 Mon Sep 17 00:00:00 2001 From: Don Neill Date: Wed, 28 Oct 2026 11:05:46 -0700 Subject: [PATCH 4/7] Should be a working version. --- daemons/hscal/gammavac | 40 +++++++++++++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/daemons/hscal/gammavac b/daemons/hscal/gammavac index 48b40a5..e93fde7 100755 --- a/daemons/hscal/gammavac +++ b/daemons/hscal/gammavac @@ -50,16 +50,45 @@ class GammaVacDaemon(HispecDaemon): # pylint: disable=W0223 "pressure controller.") self.keyword_registry.string("status", getter=self._status, - description="Status of the PDU as a (code, message) string.") + description="Status of the GammaVac as a " + "(code, message) string.") + self.keyword_registry.string("model", + getter=self.controller.read_model, + description="Model of the Gamma Vacuum Vac-Ion") + self.keyword_registry.string("version", + getter=self.controller.read_version, + description="Version of the Gamma Vacuum Vac-Ion") + self.keyword_registry.int("pump_status", + getter=self.controller.get_pump_status, + description="Status of the pump as a number: " + "0 = standby, 1 = starting, 2 = running, " + "3 = cooldown, 4 = error") + self.keyword_registry.int("pump_size", + getter=self.controller.get_pump_size, + description="Size of the pump in L/s") + self.keyword_registry.int("hv_status", + getter=self.controller.high_voltage_on, + description="Is High Voltage on?") + self.keyword_registry.trigger("start_pump", + action=self.controller.start_pump, + description="Start pump") + self.keyword_registry.trigger("stop_pump", + action=self.controller.stop_pump, + description="Stop pump") + self.keyword_registry.float("current", + getter=self.controller.read_current, + description="Current in Amps of the Gamma Vacuum Vac-Ion") + self.keyword_registry.float("voltage", + getter=self.controller.read_voltage, + description="Voltage of the Gamma Vacuum Vac-Ion") self._pressure_keyword = self.keyword_registry.float("pressure", getter=self._get_pressure, description="Pressure from gauge") self.keyword_registry.string("units", getter=self._get_pressure_units, - description="Pressure units.") - self.keyword_registry.int("units_code", - setter=self._set_pressure_units, - description="Pressure units code: M - mbar, T - Torr, P - Pascal") + setter=self._set_pressure_units, + description="Pressure units code: " + "M - mbar, T - Torr, P - Pascal") self.keyword_registry.trigger("shutdown", action=self._shutdown, description="Shutdown the daemon now.") @@ -101,6 +130,7 @@ class GammaVacDaemon(HispecDaemon): # pylint: disable=W0223 raise RuntimeError( f"no connection to {self.ip_address}:{self.tcp_port}") self.controller.initialize() + self.controller.set_units(self.units) def _set_connected(self, value: bool) -> None: if value: From 10ab9f13a87b4c2684796a0f82e6f5079b661a1b Mon Sep 17 00:00:00 2001 From: Don Neill Date: Wed, 28 Oct 2026 14:11:23 -0700 Subject: [PATCH 5/7] Add config and instance files --- config/hscal/hscal_hketvacion.yaml | 15 +++++++++++++++ config/hscal/hscal_yjetvacion.yaml | 15 +++++++++++++++ systemd/instances/hscal_hketvacion.env | 9 +++++++++ systemd/instances/hscal_yjetvacion.env | 9 +++++++++ 4 files changed, 48 insertions(+) create mode 100644 config/hscal/hscal_hketvacion.yaml create mode 100644 config/hscal/hscal_yjetvacion.yaml create mode 100644 systemd/instances/hscal_hketvacion.env create mode 100644 systemd/instances/hscal_yjetvacion.env diff --git a/config/hscal/hscal_hketvacion.yaml b/config/hscal/hscal_hketvacion.yaml new file mode 100644 index 0000000..20fe46f --- /dev/null +++ b/config/hscal/hscal_hketvacion.yaml @@ -0,0 +1,15 @@ +# HKEtVacIon — Gamma Vacuum SPCe Vac-Ion pressure gauge +# +# Usage: +# daemons/hscal -c config/hscal/hscal_hketvacion.yaml + +peer_id: hketvacion +group_id: hscal + +hardware: + ip_address: 192.168.29.130 + tcp_port: 10011 + bus_address: 5 + +logging: + level: INFO diff --git a/config/hscal/hscal_yjetvacion.yaml b/config/hscal/hscal_yjetvacion.yaml new file mode 100644 index 0000000..3cf34d3 --- /dev/null +++ b/config/hscal/hscal_yjetvacion.yaml @@ -0,0 +1,15 @@ +# YJEtVacIon — Gamma Vacuum SPCe Vac-Ion pressure gauge +# +# Usage: +# daemons/hscal -c config/hscal/hscal_yjetvacion.yaml + +peer_id: yjetvacion +group_id: hscal + +hardware: + ip_address: 192.168.29.153 + tcp_port: 10016 + bus_address: 5 + +logging: + level: INFO diff --git a/systemd/instances/hscal_hketvacion.env b/systemd/instances/hscal_hketvacion.env new file mode 100644 index 0000000..997048b --- /dev/null +++ b/systemd/instances/hscal_hketvacion.env @@ -0,0 +1,9 @@ +# HKEtVacIon — Gamma Vacuum SPCe Vac-Ion pressure gauge (hscal) +# +# hispec deploy hscal_yjetvacion +# +# copies this file to /etc/hispec/instances/hscal_hketvacion.env and +# config/hscal/hscal_hketvacion.yaml to /etc/hispec/hscal_hketvacion.yaml, then +# enables and starts the daemon. +HISPEC_DAEMON=hscal/gammavac +HISPEC_HOST=hispecserver diff --git a/systemd/instances/hscal_yjetvacion.env b/systemd/instances/hscal_yjetvacion.env new file mode 100644 index 0000000..d69020f --- /dev/null +++ b/systemd/instances/hscal_yjetvacion.env @@ -0,0 +1,9 @@ +# YJEtVacIon — Gamma Vacuum SPCe Vac-Ion pressure gauge (hscal) +# +# hispec deploy hscal_yjetvacion +# +# copies this file to /etc/hispec/instances/hscal_yjetvacion.env and +# config/hscal/hscal_yjetvacion.yaml to /etc/hispec/hscal_yjetvacion.yaml, then +# enables and starts the daemon. +HISPEC_DAEMON=hscal/gammavac +HISPEC_HOST=hispecserver From 420f98d22c867ee31c33ce15d30d97c2b0209511 Mon Sep 17 00:00:00 2001 From: Don Neill Date: Wed, 28 Oct 2026 14:14:16 -0700 Subject: [PATCH 6/7] Update config files --- config/hscal/hscal_hketvacion.yaml | 2 +- config/hscal/hscal_yjetvacion.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/config/hscal/hscal_hketvacion.yaml b/config/hscal/hscal_hketvacion.yaml index 20fe46f..4ccb1d9 100644 --- a/config/hscal/hscal_hketvacion.yaml +++ b/config/hscal/hscal_hketvacion.yaml @@ -1,7 +1,7 @@ # HKEtVacIon — Gamma Vacuum SPCe Vac-Ion pressure gauge # # Usage: -# daemons/hscal -c config/hscal/hscal_hketvacion.yaml +# daemons/hscal/gammavac -c config/hscal/hscal_hketvacion.yaml peer_id: hketvacion group_id: hscal diff --git a/config/hscal/hscal_yjetvacion.yaml b/config/hscal/hscal_yjetvacion.yaml index 3cf34d3..ca18372 100644 --- a/config/hscal/hscal_yjetvacion.yaml +++ b/config/hscal/hscal_yjetvacion.yaml @@ -1,7 +1,7 @@ # YJEtVacIon — Gamma Vacuum SPCe Vac-Ion pressure gauge # # Usage: -# daemons/hscal -c config/hscal/hscal_yjetvacion.yaml +# daemons/hscal/gammavac -c config/hscal/hscal_yjetvacion.yaml peer_id: yjetvacion group_id: hscal From a1f37e7a8010a2d55f09058246a89f2e870cbedf Mon Sep 17 00:00:00 2001 From: Don Neill Date: Wed, 28 Oct 2026 14:22:41 -0700 Subject: [PATCH 7/7] Fix bus address --- daemons/hscal/gammavac | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/daemons/hscal/gammavac b/daemons/hscal/gammavac index e93fde7..60d194b 100755 --- a/daemons/hscal/gammavac +++ b/daemons/hscal/gammavac @@ -20,14 +20,14 @@ class GammaVacDaemon(HispecDaemon): # pylint: disable=W0223 self.ip_address = None self.tcp_port = None - self.bus_address = None + self.bus_address = 5 self.pressure = None self._pressure_keyword = None self.current = None self.voltage = None self.units = 'T' self.error = None - self.controller = SpceController(log=True) + self.controller = SpceController(log=True, bus_address=self.bus_address) self.daemon_description = "Gamma Vacuum Vac-Ion Pressure Daemon" def on_start(self, libby): @@ -36,6 +36,7 @@ class GammaVacDaemon(HispecDaemon): # pylint: disable=W0223 self.ip_address = self.get_config("hardware.ip_address") self.tcp_port = self.get_config("hardware.tcp_port") self.bus_address = self.get_config("hardware.bus_address") + self.controller.bus_address = self.bus_address self.logger.info("Starting %s daemon with %d bus address", self.peer_id, self.bus_address)